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