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