c99156f253543e7c6c6e6b615e8f31d9c4aecbf5
[cortex-from-scratch] / drivers / st7735s.c
1 /* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
2  * 
3  * $LOG$
4  * 2019/9/14 - ROBIN KRENS      
5  * Initial version 
6  * 
7  * $DESCRIPTION$
8  * 
9  * $USAGE$
10  *
11  * */
12
13 #include <stdbool.h>
14 #include <stddef.h>
15 #include <stdint.h>
16 #include <stdarg.h>
17
18 #include <sys/mmap.h>
19 #include <sys/robsys.h>
20
21 #include <lib/regfunc.h>
22 #include <lib/string.h>
23 #include <lib/tinyprintf.h>
24 #include <lib/fonts/wogfont.h>
25
26 #include <drivers/uart.h>
27 #include <drivers/st7735s.h>
28
29 #define TIMEOUT 500
30 #define SCRWIDTH 132
31 #define SCRHEIGHT 132
32 #define STARTX 5
33 #define STARTY 5
34 #define XPOS(x) (x * 6)
35 #define YPOS(y) (y * 8)
36 #define BUFFER 352
37
38 static struct {
39          uint16_t cpos;
40          uint8_t * textmemptr;
41          uint8_t buf[BUFFER];
42          uint8_t x;
43          uint8_t y;
44 } tftscreen;
45
46 void tft_init() {
47
48         tftscreen.x = 0;
49         tftscreen.y = 0;
50         tftscreen.cpos = 0;
51         tftscreen.textmemptr = tftscreen.buf;
52
53         memset(tftscreen.buf, 0x00, 352); 
54
55         /* Peripherial init */
56         rsetbit(RCC_APB1ENR, 14); // enable SPI2
57         rsetbit(RCC_APB2ENR, 3); // enable GPIOB
58         rsetbit(RCC_APB2ENR, 4); // enable GPIOC
59
60         /* The PINS used are PB12, PB13, PB15 and PC6 respectively
61          * NSS (or CS): alternative function pusp-pull
62          * NSS Output is (always high) enabled with this setting
63          * SCK Master: alternate function push-pull
64          * MOSI (or DI): alternate function push-pull 
65          * D/CX (or A0): Command or data write line PC6 */
66         rwrite(GPIOB_CRH, 0xA4AA4444);
67         rwrite(GPIOC_CRL, 0x42444444);
68
69         /* Chip select: software enabled
70          * In case for a hardware setup, connect NSS (CS) to 
71          * ground  */
72         rsetbit(SPI2_CR1, 9);
73         rsetbit(SPI2_CR1, 8);
74
75         rsetbit(SPI2_CR1, 15); // one-wire mode
76         rsetbit(SPI2_CR1, 14); // start with transfer
77         rsetbit(SPI2_CR1, 4); // FPLCK div 8
78         rsetbit(SPI2_CR1, 2); // master selection
79         rsetbit(SPI2_CR1, 6); // enable SPI
80
81         /* Init sequence */
82         tft_command(TFT_SWRESET, 0);
83         _block(120000); 
84         tft_command(0x11, 0);
85         _block(120000); 
86
87         /* Frame rate control */
88         tft_command(TFT_FRMCTR1, 3, 0x01, 0x2C, 0x2D);
89         tft_command(TFT_FRMCTR2, 3, 0x01, 0x2C, 0x2D);
90         tft_command(TFT_FRMCTR3, 6, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D);        
91
92         /* Power control */
93         tft_command(TFT_PWCTR1, 3, 0xA2, 0x02, 0x84);
94         tft_command(TFT_PWCTR2, 1, 0xC5);
95         tft_command(TFT_PWCTR3, 2, 0x0A, 0x00);
96         tft_command(TFT_PWCTR4, 2, 0x8A, 0x2A);
97         tft_command(TFT_PWCTR5, 2, 0x8A, 0xEE);
98         tft_command(TFT_VMCTR1, 1, 0x0E);
99
100         tft_command(TFT_INVOFF, 0);
101         tft_command(TFT_COLMOD, 1, 0x05); // 0x05
102         tft_command(TFT_MADCTL, 1, 0xC0); // TODO: check
103
104         tft_command(TFT_CASET, 4, 0x00, 0x00, 0x00, 0x7F);
105         tft_command(TFT_RASET, 4, 0x00, 0x00, 0x00, 0x9F);
106
107         /* Gamma Settings */
108         tft_command(TFT_GMCTRP1, 16, 0x02, 0x1C, 0x07, 0x12,
109                         0x37, 0x32, 0x29, 0x2D,
110                         0x29, 0x25, 0x2B, 0x39,
111                         0x00, 0x01, 0x03, 0x10);
112         tft_command(TFT_GMCTRN1, 16, 0x03, 0x1D, 0x07, 0x06,
113                         0x2E, 0x2C, 0x29, 0x2D,
114                         0x2E, 0x2E, 0x37, 0x3F,
115                         0x00, 0x00, 0x02, 0x10);
116
117         /* Before turning on the display, fill the display
118          * so no random display data is shown */
119         tft_fill(0,0,SCRWIDTH-1,SCRHEIGHT-1,0x0000);
120
121         /* Turn on */
122         tft_command(TFT_NORON, 0);
123         _block(10000);
124         tft_command(TFT_DISPON, 0);
125         _block(100000);
126         
127 }
128
129 /* Helper function */
130 static int txbuf_empty () {
131         int cnt = 0;
132         while(!rchkbit(SPI2_SR, 1)) {
133                 cnt++;
134                 if (cnt > TIMEOUT) {
135                         printf("Error: transmit timeout!\n");
136                         return 0;
137                 }
138         }
139         return 1;
140
141 }
142
143
144 /* Function to fill an area starting at (beginx, beginy)
145  * and end ending at (endx, endy */
146 int tft_fill(uint8_t beginx, uint8_t beginy, uint8_t endx,
147                 uint8_t endy, uint16_t color) {
148
149         /* The CASET and RASET commands set the begin X/Y
150          * and end X/Y of the fill area. Both are split over 
151          * two paramters/bytes. Most screen are small, so you will 
152          * only need to consider the first 8 MSBs. Parameter 1 and 
153          * 3 are always 0. 
154          *
155          * In theory, TFT screens of 65,535 by 65,535 could be
156          * supported */
157         tft_command(TFT_CASET, 4, 0x00, beginx, 0x00, endx);
158         tft_command(TFT_RASET, 4, 0x00, beginy, 0x00, endy);
159
160         /* After setting the fill area, we can send the color
161          * data. The chip autoincrements the address so we can
162          * keep writing continiously for the fill area. Note
163          * that each pixel requires two bytes to be send (16 
164          * bit mode: ...)   */
165         uint32_t totalwrites = (endx - beginx) * (endy - beginy) * 2;
166         //printf("tw: %d, x: %d, y: %d\n", totalwrites, beginx, beginy);
167         tft_command(TFT_RAMWR, 0);
168         rsetbit(GPIOC_ODR, 6); // data = 1      
169         for (int i = 0; i < totalwrites; i++) {
170                         rwrite(SPI2_DR, (uint8_t) (color >> 8));
171                         if (!txbuf_empty())
172                                 return -1;
173                         rwrite(SPI2_DR, (uint8_t) (color & 0xFF));
174                         if (!txbuf_empty())
175                                 return -1;
176         }
177         return 0;
178 }
179
180 /* Function to individually set a pixel 
181  * Refer to tft_fill, similar calls  */
182 int tft_setpixel(uint8_t x, uint8_t y, uint16_t color) {
183
184         tft_command(TFT_CASET, 4, 0x00, x, 0x00, x+1);
185         tft_command(TFT_RASET, 4, 0x00, y, 0x00, y+1);
186 tft_command(TFT_RAMWR, 2, (uint8_t) (color >> 8), (uint8_t) (color & 0xFF));
187         return 0;
188 }
189
190
191 int tft_puts(char * str) {
192
193         for (int i = 0; i < strlen(str); i++)  {
194                 tft_putc(0xFFFF, 0x0000, str[i]);
195
196         }
197         
198 }
199
200 void tft_clrln() {
201
202         tft_puts("                     ");
203         tftscreen.buf[BUFFER - 21] = '\0';
204         tftscreen.cpos -= 21;
205         tftscreen.y = 14;
206 }
207
208 /* Text scroll function 
209  * Refeed the buffer */
210 int tft_scroll() {
211
212         /* Scroll the buffer  */
213         memcpy(tftscreen.textmemptr, tftscreen.textmemptr + 21, BUFFER - 21);
214         //for (int i = 21; i >= 0; i--)
215         tftscreen.buf[BUFFER - 21] = '\0';
216
217         tftscreen.x = 0;
218         tftscreen.y = 0;
219         tftscreen.cpos = 0;
220
221         //for (int i = 0; i < 320; i++)     {
222         //      uart_putc(tftscreen.buf[i]);
223         //}
224
225         tft_puts(tftscreen.buf); // CHECK: ending
226         //tftscreen.y = 14;
227         //tft_puts("                     ");
228         tftscreen.y = 14;
229         tftscreen.x = 0;
230         tftscreen.cpos = BUFFER - 21;
231         // DINOSAUR tft_clrln();
232 }
233
234 void tft_nl() {
235
236         uint8_t blanks = 21 - tftscreen.x;
237
238         // filler with blank spaces
239         for (int i = 0; i < blanks; i++) {
240                 tft_putc(0xFFFF, 0x0000, ' ');
241         }
242         
243 }
244
245 /* Low-level function to print a character to the display
246  * Should not be used directly */
247 int tft_putc(uint16_t fg, uint16_t bg, int c) {
248
249         
250         int totalpixels = 35;
251         int column = 0;
252         int row = 0;
253         uint8_t current;
254
255
256         //if ((c == '\n') && (tftscreen.y == 14)) {
257         //      tft_nl();
258         //      tft_scroll2();
259         //      return 1;
260         //}
261
262         if (c == '\n') {
263                 if (tftscreen.y == 14) {
264                         tft_nl();
265                         return 1;
266                 }
267                 else {
268                         tft_nl();
269                 //if (tftscreen.y < 15)
270                         return 1;
271                 }
272         }
273         ////    else {
274         ////            tft_putc(0xFFFF, 0x0000, 'o');
275         ////    }
276         //      //else {
277         //      //      ENDFLAG = true;
278         //      //}
279         //}
280
281         if (tftscreen.y >= 15) {
282                 tft_scroll();
283                 //if (ENDFLAG) {
284                 //      ENDFLAG = false;
285                 //      return 1;
286                 //}
287         }
288
289         tft_command(TFT_CASET, 4, 0x00, STARTX + XPOS(tftscreen.x), 0x00, (STARTX + 4) + XPOS(tftscreen.x));
290         tft_command(TFT_RASET, 4, 0x00, STARTY + YPOS(tftscreen.y), 0x00, (STARTY + 6) + YPOS(tftscreen.y));
291         
292         tft_command(TFT_RAMWR, 0);
293         rsetbit(GPIOC_ODR, 6); // data = 1      
294         for (int i = 0; i < totalpixels; i++) {
295                 
296                 current = ASCII5x7[(c * 5) + column]; 
297
298                 if ((current >> (7 - row)) & 0x1) {
299                         rwrite(SPI2_DR, (uint8_t) (fg >> 8));
300                         if (!txbuf_empty())
301                                 return -1;
302                         rwrite(SPI2_DR, (uint8_t) (fg & 0xFF));
303                         if (!txbuf_empty())
304                                 return -1;
305                 }
306                 else {
307                         rwrite(SPI2_DR, (uint8_t) (bg >> 8));
308                         if (!txbuf_empty())
309                                 return -1;
310                         rwrite(SPI2_DR, (uint8_t) (bg & 0xFF));
311                         if (!txbuf_empty())
312                                 return -1;
313                 }
314
315                 /* Algoritm dependent on draw mode: top down, left right */
316                 column++;
317                 if (column > 4) {
318                         column = 0;
319                         row++;  
320                 }
321         }
322         tftscreen.buf[tftscreen.cpos] = c;
323         tftscreen.cpos++;
324
325         tftscreen.x++;
326         if (tftscreen.x > 20) {
327                 tftscreen.x = 0;
328                 tftscreen.y++;
329         }
330
331         return 0;
332 }
333
334
335
336 /* Invokes commands with a variable list of paramaters. Sending parameters
337  * requires the D/CX line to be high  */
338 int tft_command(uint8_t cmd, int argsc, ...) {
339
340         va_list ap;
341         // command
342         rclrbit(GPIOC_ODR, 6); // D/CX line low
343         rwrite(SPI2_DR, cmd);
344         if (!txbuf_empty()) 
345                 return -1;
346
347         // parameter or data
348         if (argsc > 0) {
349                 va_start(ap, argsc);
350                 rsetbit(GPIOC_ODR, 6); // D/CX line high        
351                 for (int i = 0; i < argsc; i++) {
352                         uint8_t p = (uint8_t) va_arg(ap, unsigned int);
353                         rwrite(SPI2_DR, p);
354                         if (!txbuf_empty())
355                                 return -1;
356                 }
357                 va_end(ap);
358         }
359         return 0;
360 }       
361