1 /* (CC-BY-NC-SA) ROBIN KRENS - ROBIN @ ROBINKRENS.NL
4 * 2019/9/14 - ROBIN KRENS
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.
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!
30 #include <sys/robsys.h>
32 #include <lib/regfunc.h>
33 #include <lib/string.h>
34 #include <lib/tinyprintf.h>
35 #include <lib/fonts/wogfont.h>
37 #include <drivers/uart.h>
38 #include <drivers/st7735s.h>
45 #define XPOS(x) (x * 6)
46 #define YPOS(y) (y * 8)
63 tftscreen.textmemptr = tftscreen.buf;
65 memset(tftscreen.buf, 0x00, 352);
67 /* Peripherial init */
68 rsetbit(RCC_APB1ENR, 14); // enable SPI2
69 rsetbit(RCC_APB2ENR, 3); // enable GPIOB
70 rsetbit(RCC_APB2ENR, 4); // enable GPIOC
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);
81 /* Chip select: software enabled
82 * In case for a hardware setup, connect NSS (CS) to
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
94 tft_command(TFT_SWRESET, 0);
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);
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);
112 tft_command(TFT_INVOFF, 0);
113 tft_command(TFT_COLMOD, 1, 0x05); // 0x05
114 tft_command(TFT_MADCTL, 1, 0xC0); // TODO: check
116 tft_command(TFT_CASET, 4, 0x00, 0x00, 0x00, 0x7F);
117 tft_command(TFT_RASET, 4, 0x00, 0x00, 0x00, 0x9F);
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);
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);
134 tft_command(TFT_NORON, 0);
136 tft_command(TFT_DISPON, 0);
141 /* Helper function */
142 static int txbuf_empty () {
144 while(!rchkbit(SPI2_SR, 1)) {
147 printf("Error: transmit timeout!\n");
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) {
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
167 * In theory, TFT screens of 65,535 by 65,535 could be
169 tft_command(TFT_CASET, 4, 0x00, beginx, 0x00, endx);
170 tft_command(TFT_RASET, 4, 0x00, beginy, 0x00, endy);
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
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));
185 rwrite(SPI2_DR, (uint8_t) (color & 0xFF));
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) {
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));
203 /* Basic puts function that loops over a string */
204 int tft_puts(char * str) {
206 for (int i = 0; i < strlen(str); i++) {
207 tft_putc(0xFFFF, 0x0000, str[i]);
213 /* Used by scroll function to overwrite and clear the last
214 * line on the screen */
218 tftscreen.buf[BUFFER - 21] = '\0';
219 tftscreen.cpos -= 21;
223 /* Text scroll function
224 * Refeed the buffer */
227 /* Scroll the buffer */
228 memcpy(tftscreen.textmemptr, tftscreen.textmemptr + 21, BUFFER - 21);
229 tftscreen.buf[BUFFER - 21] = '\0';
235 tft_puts(tftscreen.buf); // CHECK: ending
238 tftscreen.cpos = BUFFER - 21;
241 /* Fills a line with blank characters, returns to
245 uint8_t blanks = 21 - tftscreen.x;
247 // filler with blank spaces
248 for (int i = 0; i < blanks; i++) {
249 tft_putc(0xFFFF, 0x0000, ' ');
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) {
258 int totalpixels = 35;
264 if (tftscreen.y == 14) {
270 //if (tftscreen.y < 15)
275 if (tftscreen.y >= 15) {
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));
282 tft_command(TFT_RAMWR, 0);
283 rsetbit(GPIOC_ODR, 6); // data = 1
284 for (int i = 0; i < totalpixels; i++) {
286 current = ASCII5x7[(c * 5) + column];
288 if ((current >> (7 - row)) & 0x1) {
289 rwrite(SPI2_DR, (uint8_t) (fg >> 8));
292 rwrite(SPI2_DR, (uint8_t) (fg & 0xFF));
297 rwrite(SPI2_DR, (uint8_t) (bg >> 8));
300 rwrite(SPI2_DR, (uint8_t) (bg & 0xFF));
305 /* Algoritm dependent on draw mode: top down, left right */
312 tftscreen.buf[tftscreen.cpos] = c;
316 if (tftscreen.x > 20) {
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, ...) {
332 rclrbit(GPIOC_ODR, 6); // D/CX line low
333 rwrite(SPI2_DR, cmd);
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);