В предыдущей части урока мы начали писать тесты для проверки работы дисплея, добавляя при этом дополнительные служебные функции.
Вернёмся в файл spi_ili9341.c и и выше функции TFT9341_ini добавим функцию вывода прямой линии на экран
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//------------------------------------------------------------------ void TFT9341_DrawLine(spi_device_handle_t spi, uint16_t color, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { int steep = abs(y2-y1)>abs(x2-x1); if(steep) { swap(x1,y1); swap(x2,y2); } if(x1>x2) { swap(x1,x2); swap(y1,y2); } int dx,dy; dx=x2-x1; dy=abs(y2-y1); int err=dx/2; int ystep; if(y1<y2) ystep=1; else ystep=-1; for(;x1<=x2;x1++) { if(steep) TFT9341_DrawPixel(spi, y1,x1,color); else TFT9341_DrawPixel(spi, x1,y1,color); err-=dy; if(err<0) { y1 += ystep; err=dx; } } } //------------------------------------------------------------------ |
Все эти функции нам знакомы ещё со времён работы с контроллерами AVR. На других контроллерах мы их только обкатывали и доводили до ума. Поэтому рассказывать о коде в их телах, думаю, что нет смысла.
Объявим на данную функцию прототип в заголовочной функции и добавим в бесконечном цикле в функции app_main файла main.c ещё один тест по выводу параллельных линий случайного цвета на экран
1 2 3 4 5 6 7 8 9 10 |
TFT9341_FillScreen(spi, TFT9341_BLACK); for(j=0;j<2;j++) { for(i=0;i<TFT9341_WIDTH;i++) { TFT9341_DrawLine(spi, rand()&0x0000FFFF,i,0,i,TFT9341_HEIGHT-1); } } vTaskDelay(500 / portTICK_PERIOD_MS); TFT9341_FillScreen(spi, TFT9341_BLACK); |
Посмотрим, как работает код данного теста
Линии выводятся не очень быстро, так как вывод идёт попиксельный. Можно, конечно сделать отдельные функции вывода горизонтальных и вертикальных линий, где выводить их как прямоугольники, но я заморачиваться не стал, думаю, это несложно и кому потребуется, тот сделает это самостоятельно.
Теперь добавим ещё один тест по выводу линий со случайными координатами
1 2 3 4 5 6 7 8 9 10 11 12 13 |
TFT9341_FillScreen(spi, TFT9341_BLACK); */ for(i=0;i<350;i++) { TFT9341_DrawLine(spi, rand()&0x0000FFFF, rand()%TFT9341_WIDTH, rand()%TFT9341_HEIGHT, rand()%TFT9341_WIDTH, rand()%TFT9341_HEIGHT); vTaskDelay(1 / portTICK_PERIOD_MS); } vTaskDelay(500 / portTICK_PERIOD_MS); TFT9341_FillScreen(spi, TFT9341_BLACK); |
Посмотрим, как работает тест
Вернёмся в файл spi_ili9341.c и выше функции TFT9341_ini добавим функцию вывода неокрашенного прямоугольника (только его границы)
1 2 3 4 5 6 7 8 9 10 |
//------------------------------------------------------------------ void TFT9341_DrawRect(spi_device_handle_t spi, uint16_t color, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { TFT9341_DrawLine(spi, color,x1,y1,x2,y1); TFT9341_DrawLine(spi, color,x2,y1,x2,y2); TFT9341_DrawLine(spi, color,x1,y1,x1,y2); TFT9341_DrawLine(spi, color,x1,y2,x2,y2); } //------------------------------------------------------------------- |
Добавим на функцию прототип в заголовочном файле и в бесконечном цикле в функции app_main файла main.c добавим ещё один тест по выводу прямоугольников на экран
1 2 3 4 5 6 7 8 9 10 |
TFT9341_FillScreen(spi, TFT9341_BLACK); for(j=0;j<2;j++) { for(i=0;i<TFT9341_WIDTH/2;i++) { TFT9341_DrawRect(spi, rand()&0x0000FFFF, i, i, TFT9341_WIDTH-i-1, TFT9341_HEIGHT-i-1); } } vTaskDelay(500 / portTICK_PERIOD_MS); TFT9341_FillScreen(spi, TFT9341_BLACK); |
Проверим, как работает тест
Перейдём в файл spi_ili9341.c и выше функции TFT9341_ini добавим функцию вывода окружности определённого цвета и радиуса на экран
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//------------------------------------------------------------------- void TFT9341_DrawCircle(spi_device_handle_t spi, uint16_t x0, uint16_t y0, int r, uint16_t color) { int f = 1-r; int ddF_x=1; int ddF_y=-2*r; int x = 0; int y = r; TFT9341_DrawPixel(spi, x0,y0+r,color); TFT9341_DrawPixel(spi, x0,y0-r,color); TFT9341_DrawPixel(spi, x0+r,y0,color); TFT9341_DrawPixel(spi, x0-r,y0,color); while (x<y) { if (f>=0) { y--; ddF_y+=2; f+=ddF_y; } x++; ddF_x+=2; f+=ddF_x; TFT9341_DrawPixel(spi, x0+x,y0+y,color); TFT9341_DrawPixel(spi, x0-x,y0+y,color); TFT9341_DrawPixel(spi, x0+x,y0-y,color); TFT9341_DrawPixel(spi, x0-x,y0-y,color); TFT9341_DrawPixel(spi, x0+y,y0+x,color); TFT9341_DrawPixel(spi, x0-y,y0+x,color); TFT9341_DrawPixel(spi, x0+y,y0-x,color); TFT9341_DrawPixel(spi, x0-y,y0-x,color); } } //------------------------------------------------------------------- |
Объявим прототип данной функции в заголовочном файле и в бесконечном цикле в функции app_main файла main.c добавим ещё один тест по выводу окружностей определённого радиуса случайного цвета в случайные места экрана
1 2 3 4 5 6 7 8 9 10 |
TFT9341_FillScreen(spi, TFT9341_BLACK); for(i=0;i<400;i++) { TFT9341_DrawCircle(spi, rand()%(TFT9341_WIDTH-40)+20, rand()%(TFT9341_HEIGHT-40)+20, 20,rand()&0x0000FFFF); vTaskDelay(1 / portTICK_PERIOD_MS); } vTaskDelay(500 / portTICK_PERIOD_MS); TFT9341_FillScreen(spi, TFT9341_BLACK); |
Проверим данный тест
Теперь вывод текста.
Из проекта урока 171 по контроллерам STM32 с именем ILI9341_SPI скопируем файлы fonts.h, font8.c, font12.c, font16.c, font20.c и font24.c в каталог main нашего проекта.
Подключим файл fonts.h в файле spi_ili9341.h
1 2 |
#include "sdkconfig.h" #include "fonts.h" |
Не забываем также подключить файлы в CMakeLists.txt
set(COMPONENT_SRCS "main.c spi_ili9341.c font24.c font20.c font16.c font12.c font8.c")
В файле spi_ili9341.c объявим глобальную структуру вместе с переменной
1 2 3 4 5 6 7 8 9 10 |
#include "spi_ili9341.h" //------------------------------------------------------------------- typedef struct { uint16_t TextColor; uint16_t BackColor; sFONT *pFont; }LCD_DrawPropTypeDef; LCD_DrawPropTypeDef lcdprop; //------------------------------------------------------------------- |
Выше функции TFT9341_ini добавим функции инициализации цвета текста и фона, а также размера шрифта
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//------------------------------------------------------------------- void TFT9341_SetTextColor(uint16_t color) { lcdprop.TextColor=color; } //------------------------------------------------------------------- void TFT9341_SetBackColor(uint16_t color) { lcdprop.BackColor=color; } //------------------------------------------------------------------- void TFT9341_SetFont(sFONT *pFonts) { lcdprop.pFont=pFonts; } //------------------------------------------------------------------- |
Ниже добавим функцию вывода символа на экран
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
//------------------------------------------------------------------- void TFT9341_DrawChar(spi_device_handle_t spi, uint16_t x, uint16_t y, uint8_t c) { uint32_t i = 0, j = 0; uint16_t height, width; uint8_t offset; uint8_t *c_t; uint8_t *pchar; uint32_t line=0; height = lcdprop.pFont->Height; width = lcdprop.pFont->Width; offset = 8 *((width + 7)/8) - width ; c_t = (uint8_t*) &(lcdprop.pFont->table[(c-' ') * lcdprop.pFont->Height * ((lcdprop.pFont->Width + 7) / 8)]); for(i = 0; i < height; i++) { pchar = ((uint8_t *)c_t + (width + 7)/8 * i); switch(((width + 7)/8)) { case 1: line = pchar[0]; break; case 2: line = (pchar[0]<< 8) | pchar[1]; break; case 3: default: line = (pchar[0]<< 16) | (pchar[1]<< 8) | pchar[2]; break; } for (j = 0; j < width; j++) { if(line & (1 << (width- j + offset- 1))) { TFT9341_DrawPixel(spi, (x + j), y, lcdprop.TextColor); } else { TFT9341_DrawPixel(spi, (x + j), y, lcdprop.BackColor); } } y++; } } //------------------------------------------------------------------- |
Ещё ниже — функцию вывода на экран строки текста
1 2 3 4 5 6 7 8 9 10 11 |
//------------------------------------------------------------------- void TFT9341_String(spi_device_handle_t spi, uint16_t x,uint16_t y, char *str) { while(*str) { TFT9341_DrawChar(spi, x,y,str[0]); x+=lcdprop.pFont->Width; (void)*str++; } } //------------------------------------------------------------------- |
Ещё ниже — функцию изменения ориентации экрана
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
//------------------------------------------------------------------- void TFT9341_SetRotation(spi_device_handle_t spi, uint8_t r) { uint8_t data[1]; lcd_cmd(spi, 0x36); switch(r) { case 0: data[0] = 0x48; lcd_data(spi, data, 1); TFT9341_WIDTH = 240; TFT9341_HEIGHT = 320; break; case 1: data[0] = 0x28; lcd_data(spi, data, 1); TFT9341_WIDTH = 320; TFT9341_HEIGHT = 240; break; case 2: data[0] = 0x88; lcd_data(spi, data, 1); TFT9341_WIDTH = 240; TFT9341_HEIGHT = 320; break; case 3: data[0] = 0xE8; lcd_data(spi, data, 1); TFT9341_WIDTH = 320; TFT9341_HEIGHT = 240; break; } } //------------------------------------------------------------------- |
В заголовочном файле добавим на все эти 6 функций прототипы и в бесконечном цикле в функции app_main файла main.c добавим тест по выводу текста на экран — сначала посимвольно, а затем строками, используя шрифты различного размера, цвета на различном фоне, а также меняя ориентацию экрана
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
TFT9341_FillScreen(spi, TFT9341_BLACK); TFT9341_SetTextColor(TFT9341_YELLOW); TFT9341_SetBackColor(TFT9341_BLUE); TFT9341_SetFont(&Font24); TFT9341_DrawChar(spi, 10,10,'E'); TFT9341_DrawChar(spi, 27,10,'s'); TFT9341_DrawChar(spi, 44,10,'p'); TFT9341_DrawChar(spi, 61,10,'3'); TFT9341_DrawChar(spi, 78,10,'2'); TFT9341_SetTextColor(TFT9341_GREEN); TFT9341_SetBackColor(TFT9341_RED); TFT9341_SetFont(&Font20); TFT9341_DrawChar(spi, 10,34,'E'); TFT9341_DrawChar(spi, 24,34,'s'); TFT9341_DrawChar(spi, 38,34,'p'); TFT9341_DrawChar(spi, 52,34,'3'); TFT9341_DrawChar(spi, 66,34,'2'); TFT9341_SetTextColor(TFT9341_BLUE); TFT9341_SetBackColor(TFT9341_YELLOW); TFT9341_SetFont(&Font16); TFT9341_DrawChar(spi, 10,54,'E'); TFT9341_DrawChar(spi, 21,54,'s'); TFT9341_DrawChar(spi, 32,54,'p'); TFT9341_DrawChar(spi, 43,54,'3'); TFT9341_DrawChar(spi, 54,54,'2'); TFT9341_SetTextColor(TFT9341_CYAN); TFT9341_SetBackColor(TFT9341_BLACK); TFT9341_SetFont(&Font12); TFT9341_DrawChar(spi, 10,70,'E'); TFT9341_DrawChar(spi, 17,70,'s'); TFT9341_DrawChar(spi, 24,70,'p'); TFT9341_DrawChar(spi, 31,70,'3'); TFT9341_DrawChar(spi, 38,70,'2'); TFT9341_SetTextColor(TFT9341_RED); TFT9341_SetBackColor(TFT9341_GREEN); TFT9341_SetFont(&Font8); TFT9341_DrawChar(spi, 10,82,'E'); TFT9341_DrawChar(spi, 15,82,'s'); TFT9341_DrawChar(spi, 20,82,'p'); TFT9341_DrawChar(spi, 25,82,'3'); TFT9341_DrawChar(spi, 30,82,'2'); TFT9341_SetTextColor(TFT9341_YELLOW); TFT9341_SetBackColor(TFT9341_BLUE); vTaskDelay(2000 / portTICK_PERIOD_MS); TFT9341_FillScreen(spi, TFT9341_BLACK); for(i=0;i<4;i++) { TFT9341_SetRotation(spi, i%4); TFT9341_SetFont(&Font24); TFT9341_FillScreen(spi, TFT9341_BLACK); TFT9341_String(spi, 1,100,"ABCDEF12345678"); TFT9341_SetFont(&Font20); TFT9341_String(spi, 1,124,"ABCDEFGHI12345678"); TFT9341_SetFont(&Font16); TFT9341_String(spi, 1,144,"ABCDEFGHIKL123456789"); TFT9341_SetFont(&Font12); TFT9341_String(spi, 1,160,"ABCDEFGHIKLMNOPQRSTUVWXY 123456789"); TFT9341_SetFont(&Font8); TFT9341_String(spi, 1,172,"ABCDEFGHIKLMNOPQRSTUVWXYZ 123456789ABCDEFGHIKL"); vTaskDelay(2000 / portTICK_PERIOD_MS); } TFT9341_SetRotation(spi, 0); vTaskDelay(10000 / portTICK_PERIOD_MS); |
Испытаем работу данного теста
Итак, на данном уроке мы закрепили знания по программированию передачи данных по шине SPI, также изучили некоторые новые тонкости по данной теме, подключив цветной дисплей TFT разрешением 320×240 к контроллеру ESP32.
Всем спасибо за внимание!
Предыдущая часть Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
2,8 дюймов 240×320 SPI TFT LCD
Логический анализатор 16 каналов можно приобрести здесь
Многофункциональный переходник JTAG UART FIFO SPI I2C можно приобрести здесь CJMCU FT232H USB к JTAG UART FIFO SPI I2C
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Здравствуйте!
Большое спасибо за уроки. Всё прекрасно работает.
Добавил в файлики шрифтов русские буквы,
в подпрограмму вывода символа — несколько строчек распознавания
кодов русских символов — и всё стало вообще великолепно.
(шрифты рисовал сам давно, изучая 37 урок по stm32)
в main немного похулиганил, добавив тесты вывода всех симолов
на экран для разных шрифтов
если интересно что получилось:
https://disk.yandex.ru/d/aeoWxwXu-Rzxgw
С уважением Андрей Охапкин, г.Калуга