st7735s: tft_fill and tft_setpixel basic functionality
[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
25 #include <drivers/st7735s.h>
26
27 #define TIMEOUT 500
28 #define SCRWIDTH 132
29 #define SCRHEIGHT 132
30
31 void tft_init() {
32
33         /* Peripherial init */
34         rsetbit(RCC_APB1ENR, 14); // enable SPI2
35         rsetbit(RCC_APB2ENR, 3); // enable GPIOB
36         rsetbit(RCC_APB2ENR, 4); // enable GPIOC
37
38         /* The PINS used are PB12, PB13, PB15 and PC6 respectively
39          * NSS (or CS): alternative function pusp-pull
40          * NSS Output is (always high) enabled with this setting
41          * SCK Master: alternate function push-pull
42          * MOSI (or DI): alternate function push-pull 
43          * D/CX (or A0): Command or data write line PC6 */
44         rwrite(GPIOB_CRH, 0xA4AA4444);
45         rwrite(GPIOC_CRL, 0x42444444);
46
47         /* Chip select: software enabled
48          * In case for a hardware setup, connect NSS (CS) to 
49          * ground  */
50         rsetbit(SPI2_CR1, 9);
51         rsetbit(SPI2_CR1, 8);
52
53         rsetbit(SPI2_CR1, 15); // one-wire mode
54         rsetbit(SPI2_CR1, 14); // start with transfer
55         rsetbit(SPI2_CR1, 4); // FPLCK div 8
56         rsetbit(SPI2_CR1, 2); // master selection
57         rsetbit(SPI2_CR1, 6); // enable SPI
58
59         /* Init sequence */
60         tft_command(TFT_SWRESET, 0);
61         _block(120000); 
62         tft_command(0x11, 0);
63         _block(120000); 
64
65         /* Frame rate control */
66         tft_command(TFT_FRMCTR1, 3, 0x01, 0x2C, 0x2D);
67         tft_command(TFT_FRMCTR2, 3, 0x01, 0x2C, 0x2D);
68         tft_command(TFT_FRMCTR3, 6, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D);        
69
70         /* Power control */
71         tft_command(TFT_PWCTR1, 3, 0xA2, 0x02, 0x84);
72         tft_command(TFT_PWCTR2, 1, 0xC5);
73         tft_command(TFT_PWCTR3, 2, 0x0A, 0x00);
74         tft_command(TFT_PWCTR4, 2, 0x8A, 0x2A);
75         tft_command(TFT_PWCTR5, 2, 0x8A, 0xEE);
76         tft_command(TFT_VMCTR1, 1, 0x0E);
77
78         tft_command(TFT_INVOFF, 0);
79         tft_command(TFT_COLMOD, 1, 0x05); // 0x05
80         tft_command(TFT_MADCTL, 1, 0xC0); // TODO: check
81
82         tft_command(TFT_CASET, 4, 0x00, 0x00, 0x00, 0x7F);
83         tft_command(TFT_RASET, 4, 0x00, 0x00, 0x00, 0x9F);
84
85         /* Gamma Settings */
86         tft_command(TFT_GMCTRP1, 16, 0x02, 0x1C, 0x07, 0x12,
87                         0x37, 0x32, 0x29, 0x2D,
88                         0x29, 0x25, 0x2B, 0x39,
89                         0x00, 0x01, 0x03, 0x10);
90         tft_command(TFT_GMCTRN1, 16, 0x03, 0x1D, 0x07, 0x06,
91                         0x2E, 0x2C, 0x29, 0x2D,
92                         0x2E, 0x2E, 0x37, 0x3F,
93                         0x00, 0x00, 0x02, 0x10);
94
95         /* Before turning on the display, fill the display
96          * so no random display data is shown */
97         tft_fill(0,0,SCRWIDTH-1,SCRHEIGHT-1,0x001F);
98         tft_setpixel(50,50,0xFFFF);
99         
100         /* Turn on */
101         tft_command(TFT_NORON, 0);
102         _block(10000);
103         tft_command(TFT_DISPON, 0);
104         _block(100000);
105
106
107         /* //_block(10000); 
108
109         tft_command(TFT_CASET, 4, 0x00, 0x08, 0x00, 0x09);
110         tft_command(TFT_RASET, 4, 0x00, 0x08, 0x00, 0x09);
111         tft_command(TFT_RAMRD, 0);
112
113         //tft_command(0x0C, 0);
114         //tft_command(0x0A, 0);
115         
116         rclrbit(SPI2_CR1, 14); // receive
117
118         while(!rchkbit(SPI2_SR, 0));
119         uint8_t chip_id = *SPI2_DR;
120         printf("COLMOD: %#x\n", chip_id); */
121
122         rclrbit(SPI2_CR1, 8); // deselect
123 }
124
125 /* Helper function */
126 static int txbuf_empty () {
127         int cnt = 0;
128         while(!rchkbit(SPI2_SR, 1)) {
129                 cnt++;
130                 if (cnt > TIMEOUT) {
131                         printf("Error: transmit timeout!\n");
132                         return 0;
133                 }
134         }
135         return 1;
136
137 }
138
139
140 /* Function to fill an area starting at (beginx, beginy)
141  * and end ending at (endx, endy */
142 int tft_fill(uint8_t beginx, uint8_t beginy, uint8_t endx,
143                 uint8_t endy, uint16_t color) {
144
145         /* The CASET and RASET commands set the begin X/Y
146          * and end X/Y of the fill area. Both are split over 
147          * two paramters/bytes. Most screen are small, so you will 
148          * only need to consider the first 8 MSBs. Parameter 1 and 
149          * 3 are always 0. 
150          *
151          * In theory, TFT screens of 65,535 by 65,535 could be
152          * supported */
153         tft_command(TFT_CASET, 4, 0x00, beginx, 0x00, endx);
154         tft_command(TFT_RASET, 4, 0x00, beginy, 0x00, endy);
155
156         /* After setting the fill area, we can send the color
157          * data. The chip autoincrements the address so we can
158          * keep writing continiously for the fill area. Note
159          * that each pixel requires two bytes to be send (16 
160          * bit mode: ...)   */
161         uint32_t totalwrites = (endx - beginx) * (endy - beginy) * 2;
162         //printf("tw: %d, x: %d, y: %d\n", totalwrites, beginx, beginy);
163         tft_command(TFT_RAMWR, 0);
164         rsetbit(GPIOC_ODR, 6); // data = 1      
165         for (int i = 0; i < totalwrites; i++) {
166                         rwrite(SPI2_DR, (uint8_t) (color >> 8));
167                         if (!txbuf_empty())
168                                 return -1;
169                         rwrite(SPI2_DR, (uint8_t) (color & 0xFF));
170                         if (!txbuf_empty())
171                                 return -1;
172         }
173         return 0;
174 }
175
176 /* Function to individually set a pixel 
177  * Refer to tft_fill, similar calls  */
178 int tft_setpixel(uint8_t x, uint8_t y, uint16_t color) {
179
180         tft_command(TFT_CASET, 4, 0x00, x, 0x00, x+1);
181         tft_command(TFT_RASET, 4, 0x00, y, 0x00, y+1);
182         tft_command(TFT_RAMWR, 2, (uint8_t) (color >> 8), (uint8_t) (color & 0xFF));
183         return 0;
184 }
185
186 /* Low-level function to print a character to the display
187  * Should not be used directly, since it does not set
188  * the location */
189 int tft_putc(uint16_t fg, uint16_t bg, char c) {
190
191         // lookup table
192 }
193
194
195 /* Invokes commands with a variable list of paramaters. Sending paramters
196  * requires the D/CX line to be high  */
197 int tft_command(uint8_t cmd, int argsc, ...) {
198
199         va_list ap;
200         // command
201         rclrbit(GPIOC_ODR, 6); // D/CX line low
202         rwrite(SPI2_DR, cmd);
203         if (!txbuf_empty()) 
204                 return -1;
205
206         // parameter or data
207         if (argsc > 0) {
208                 va_start(ap, argsc);
209                 rsetbit(GPIOC_ODR, 6); // D/CX line high        
210                 for (int i = 0; i < argsc; i++) {
211                         uint8_t p = (uint8_t) va_arg(ap, unsigned int);
212                         rwrite(SPI2_DR, p);
213                         if (!txbuf_empty())
214                                 return -1;
215                 }
216                 va_end(ap);
217         }
218         return 0;
219 }       
220