* Initial version
*
* $DESCRIPTION$
+ * Basic driver for the ST7735s TFT screen. Initializes the screen
+ * Low-level commands can be called by tft_command. See header (.h)
+ * file for an overview of all commands. tft_command can accepts
+ * an unlimited amount of data or parameter commands.
*
* $USAGE$
+ * I added the following functionality:
+ * a. tft_fill: fills a certain area of the screen
+ * b. tft_setpixel: sets a single pixel
+ * c. tft_putc, tft_put: outputs a char/string to the screen, starts
+ * at the upper top right and tracks the position. If the screen is
+ * full, it 'scrolls' automatically. So you basically have a mini
+ * sized terminal. You can link standard output to this function!
*
* */
#include <lib/regfunc.h>
#include <lib/string.h>
#include <lib/tinyprintf.h>
+#include <lib/fonts/wogfont.h>
+#include <drivers/uart.h>
#include <drivers/st7735s.h>
#define TIMEOUT 500
+#define SCRWIDTH 132
+#define SCRHEIGHT 132
+#define STARTX 5
+#define STARTY 5
+#define XPOS(x) (x * 6)
+#define YPOS(y) (y * 8)
+#define BUFFER 352
+
+
+static struct {
+ uint16_t cpos;
+ uint8_t * textmemptr;
+ uint8_t buf[BUFFER];
+ uint8_t x;
+ uint8_t y;
+} tftscreen;
-void tft_test();
-int tft_command(uint8_t cmd, int argsc, ...);
void tft_init() {
+ tftscreen.x = 0;
+ tftscreen.y = 0;
+ tftscreen.cpos = 0;
+ tftscreen.textmemptr = tftscreen.buf;
+
+ memset(tftscreen.buf, 0x00, 352);
+
/* Peripherial init */
rsetbit(RCC_APB1ENR, 14); // enable SPI2
rsetbit(RCC_APB2ENR, 3); // enable GPIOB
tft_command(TFT_PWCTR5, 2, 0x8A, 0xEE);
tft_command(TFT_VMCTR1, 1, 0x0E);
-
tft_command(TFT_INVOFF, 0);
tft_command(TFT_COLMOD, 1, 0x05); // 0x05
tft_command(TFT_MADCTL, 1, 0xC0); // TODO: check
0x2E, 0x2E, 0x37, 0x3F,
0x00, 0x00, 0x02, 0x10);
+ /* Before turning on the display, fill the display
+ * so no random display data is shown */
+ tft_fill(0,0,SCRWIDTH-1,SCRHEIGHT-1,0x0000);
+
/* Turn on */
tft_command(TFT_NORON, 0);
_block(10000);
tft_command(TFT_DISPON, 0);
_block(100000);
-
- /* Test pixel */
- //tft_command(.., 3, 0xFC, 0xFC, 0xFC);
-
- for (int i = 0; i < 100; i ++) {
- tft_command(TFT_CASET, 4, 0x00, i, 0x00, i+1);
- tft_command(TFT_RASET, 4, 0x00, i, 0x00, i+1);
- tft_command(TFT_RAMWR, 2, 0xFF, 0xFF);
- }
- /* tft_command(TFT_RAMWR, 100,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
- 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0
- );
-
- _block(10000); */
-
- tft_command(TFT_CASET, 4, 0x00, 0x08, 0x00, 0x09);
- tft_command(TFT_RASET, 4, 0x00, 0x08, 0x00, 0x09);
- tft_command(TFT_RAMRD, 0);
-
- //tft_command(0x0C, 0);
- //tft_command(0x0A, 0);
-
- rclrbit(SPI2_CR1, 14); // receive
-
- while(!rchkbit(SPI2_SR, 0));
- uint8_t chip_id = *SPI2_DR;
- printf("COLMOD: %#x\n", chip_id);
-
- rclrbit(SPI2_CR1, 8); // deselect
}
/* Helper function */
}
+/* Function to fill an area starting at (beginx, beginy)
+ * and end ending at (endx, endy */
+int tft_fill(uint8_t beginx, uint8_t beginy, uint8_t endx,
+ uint8_t endy, uint16_t color) {
+
+ /* The CASET and RASET commands set the begin X/Y
+ * and end X/Y of the fill area. Both are split over
+ * two paramters/bytes. Most screen are small, so you will
+ * only need to consider the first 8 MSBs. Parameter 1 and
+ * 3 are always 0.
+ *
+ * In theory, TFT screens of 65,535 by 65,535 could be
+ * supported */
+ tft_command(TFT_CASET, 4, 0x00, beginx, 0x00, endx);
+ tft_command(TFT_RASET, 4, 0x00, beginy, 0x00, endy);
+
+ /* After setting the fill area, we can send the color
+ * data. The chip autoincrements the address so we can
+ * keep writing continiously for the fill area. Note
+ * that each pixel requires two bytes to be send (16
+ * bit mode: ...) */
+ uint32_t totalwrites = (endx - beginx) * (endy - beginy) * 2;
+ //printf("tw: %d, x: %d, y: %d\n", totalwrites, beginx, beginy);
+ tft_command(TFT_RAMWR, 0);
+ rsetbit(GPIOC_ODR, 6); // data = 1
+ for (int i = 0; i < totalwrites; i++) {
+ rwrite(SPI2_DR, (uint8_t) (color >> 8));
+ if (!txbuf_empty())
+ return -1;
+ rwrite(SPI2_DR, (uint8_t) (color & 0xFF));
+ if (!txbuf_empty())
+ return -1;
+ }
+ return 0;
+}
-int tft_command(uint8_t cmd, int argsc, ...) {
+/* Function to individually set a pixel
+ * Refer to tft_fill, similar calls */
+int tft_setpixel(uint8_t x, uint8_t y, uint16_t color) {
- va_list ap;
+ tft_command(TFT_CASET, 4, 0x00, x, 0x00, x+1);
+ tft_command(TFT_RASET, 4, 0x00, y, 0x00, y+1);
+tft_command(TFT_RAMWR, 2, (uint8_t) (color >> 8), (uint8_t) (color & 0xFF));
+ return 0;
+}
- // command
- rclrbit(GPIOC_ODR, 6);
- rwrite(SPI2_DR, cmd);
- if (!txbuf_empty())
- return -1;
- // parameter or data
- if (argsc > 0) {
-
- va_start(ap, argsc);
+/* Basic puts function that loops over a string */
+int tft_puts(char * str) {
- rsetbit(GPIOC_ODR, 6);
- for (int i = 0; i < argsc; i++) {
- uint8_t p = (uint8_t) va_arg(ap, unsigned int);
- rwrite(SPI2_DR, p);
- if (!txbuf_empty())
- return -1;
- //printf("%c", (uint8_t) va_arg(ap, unsigned int));
- }
+ for (int i = 0; i < strlen(str); i++) {
+ tft_putc(0xFFFF, 0x0000, str[i]);
- va_end(ap);
}
+
+}
- return 0;
-}
+/* Used by scroll function to overwrite and clear the last
+ * line on the screen */
+void tft_clrln() {
+
+ tft_puts(" ");
+ tftscreen.buf[BUFFER - 21] = '\0';
+ tftscreen.cpos -= 21;
+ tftscreen.y = 14;
+}
+
+/* Text scroll function
+ * Refeed the buffer */
+int tft_scroll() {
+ /* Scroll the buffer */
+ memcpy(tftscreen.textmemptr, tftscreen.textmemptr + 21, BUFFER - 21);
+ tftscreen.buf[BUFFER - 21] = '\0';
-/* write command: CS stay low
- * read command: */
+ tftscreen.x = 0;
+ tftscreen.y = 0;
+ tftscreen.cpos = 0;
-void tft_test() {
+ tft_puts(tftscreen.buf); // CHECK: ending
+ tftscreen.y = 14;
+ tftscreen.x = 0;
+ tftscreen.cpos = BUFFER - 21;
+}
+
+/* Fills a line with blank characters, returns to
+ * the next line */
+void tft_nl() {
+
+ uint8_t blanks = 21 - tftscreen.x;
+ // filler with blank spaces
+ for (int i = 0; i < blanks; i++) {
+ tft_putc(0xFFFF, 0x0000, ' ');
+ }
- // DC is data/command pin set and reset
+}
+
+/* Low-level function to print a character to the display
+ * Should not be used directly */
+int tft_putc(uint16_t fg, uint16_t bg, int c) {
+
+ int totalpixels = 35;
+ int column = 0;
+ int row = 0;
+ uint8_t current;
+
+ if (c == '\n') {
+ if (tftscreen.y == 14) {
+ tft_nl();
+ return 1;
+ }
+ else {
+ tft_nl();
+ //if (tftscreen.y < 15)
+ return 1;
+ }
+ }
+
+ if (tftscreen.y >= 15) {
+ tft_scroll();
+ }
+
+ tft_command(TFT_CASET, 4, 0x00, STARTX + XPOS(tftscreen.x), 0x00, (STARTX + 4) + XPOS(tftscreen.x));
+ tft_command(TFT_RASET, 4, 0x00, STARTY + YPOS(tftscreen.y), 0x00, (STARTY + 6) + YPOS(tftscreen.y));
- /* uint8_t cmd = 0x11;
- rwrite(SPI2_DR, cmd);
- while (!rchkbit(SPI2_SR, 1));
+ tft_command(TFT_RAMWR, 0);
+ rsetbit(GPIOC_ODR, 6); // data = 1
+ for (int i = 0; i < totalpixels; i++) {
+
+ current = ASCII5x7[(c * 5) + column];
- for (int i = 0; i < 10; i++)
- _block(0xFFFF); */
+ if ((current >> (7 - row)) & 0x1) {
+ rwrite(SPI2_DR, (uint8_t) (fg >> 8));
+ if (!txbuf_empty())
+ return -1;
+ rwrite(SPI2_DR, (uint8_t) (fg & 0xFF));
+ if (!txbuf_empty())
+ return -1;
+ }
+ else {
+ rwrite(SPI2_DR, (uint8_t) (bg >> 8));
+ if (!txbuf_empty())
+ return -1;
+ rwrite(SPI2_DR, (uint8_t) (bg & 0xFF));
+ if (!txbuf_empty())
+ return -1;
+ }
- uint8_t cmd = 0x3A;
- uint8_t p1 = 0x5;
+ /* Algoritm dependent on draw mode: top down, left right */
+ column++;
+ if (column > 4) {
+ column = 0;
+ row++;
+ }
+ }
+ tftscreen.buf[tftscreen.cpos] = c;
+ tftscreen.cpos++;
- // command
- rwrite(SPI2_DR, cmd);
- while (!rchkbit(SPI2_SR, 1)); // check line busy?
+ tftscreen.x++;
+ if (tftscreen.x > 20) {
+ tftscreen.x = 0;
+ tftscreen.y++;
+ }
+ return 0;
+}
- // paramater
- rsetbit(GPIOC_ODR, 6);
- rwrite(SPI2_DR, p1);
- while (!rchkbit(SPI2_SR, 1)); //
- cmd = 0x0C;
- rclrbit(GPIOC_ODR, 6);
- rwrite(SPI2_DR, cmd);
- while (!rchkbit(SPI2_SR, 1)); //
- rclrbit(SPI2_CR1, 14); // receive
+/* Invokes commands with a variable list of paramaters. Sending parameters
+ * requires the D/CX line to be high */
+int tft_command(uint8_t cmd, int argsc, ...) {
- while(!rchkbit(SPI2_SR, 0));
- uint8_t chip_id = *SPI2_DR;
- printf("Chip id: %#x\n", chip_id);
+ va_list ap;
+ // command
+ rclrbit(GPIOC_ODR, 6); // D/CX line low
+ rwrite(SPI2_DR, cmd);
+ if (!txbuf_empty())
+ return -1;
+ // parameter or data
+ if (argsc > 0) {
+ va_start(ap, argsc);
+ rsetbit(GPIOC_ODR, 6); // D/CX line high
+ for (int i = 0; i < argsc; i++) {
+ uint8_t p = (uint8_t) va_arg(ap, unsigned int);
+ rwrite(SPI2_DR, p);
+ if (!txbuf_empty())
+ return -1;
+ }
+ va_end(ap);
+ }
+ return 0;
+}
-}