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