В предыдущей части урока мы подключили дисплей к плате, создали проект, написали инициализацию дисплея, некоторые служебные функции и первые тесты, проверив дисплей затем на практике.
Для следующего теста нужны будут маленькие задержки.
Добавим функцию задержки в микросекундах
1 2 3 4 5 6 |
/* USER CODE BEGIN 0 */ __STATIC_INLINE void DelayMicro(uint32_t __IO micros) { micros *=(SystemCoreClock/1000000)/5; while(micros--); } |
А также нужна будет функция вывода точек на экран.
Поэтому идём в файл spi_ili9341.c и ниже функции TFT9341_FillScreen добавим данную функцию
1 2 3 4 5 6 7 8 9 10 |
//------------------------------------------------------------------- void TFT9341_DrawPixel(int x, int y, uint16_t color) { if((x<0)||(y<0)||(x>=TFT9341_WIDTH)||(y>=TFT9341_HEIGHT)) return; TFT9341_SetAddrWindow(x,y,x,y); TFT9341_SendCommand(0x2C); TFT9341_SendData(color>>8); TFT9341_SendData(color & 0xFF); } //------------------------------------------------------------------- |
Добавим на данную функцию прототип, вернёмся в наш бесконечный цикл и напишем там тест, который будет выводить в случайные места точки случайного цвета
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
*/ for(i=0;i<15000;i++) { for(j=0;j<100;j++) { TFT9341_DrawPixel(HAL_RNG_GetRandomNumber(&hrng)%TFT9341_WIDTH, HAL_RNG_GetRandomNumber(&hrng)%TFT9341_HEIGHT, TFT9341_BLACK); } TFT9341_DrawPixel(HAL_RNG_GetRandomNumber(&hrng)%TFT9341_WIDTH, HAL_RNG_GetRandomNumber(&hrng)%TFT9341_HEIGHT, TFT9341_RandColor()); DelayMicro(100); } HAL_Delay(500); |
Проверим, как работает тест
Отлично!
В файле spi_ili9341.h подключим ещё одну библиотеку
1 2 |
#include "stm32f4xx_hal.h" #include <cstdlib> |
Добавим в файле spi_ili9341.c после функции TFT9341_DrawPixel функцию отрисовки на дисплее прямой линии
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(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(y1,x1,color); else TFT9341_DrawPixel(x1,y1,color); err-=dy; if(err<0) { y1 += ystep; err+=dx; } } } //------------------------------------------------------------------- |
Добавим для данной функции прототип в заголовочном файле и в бесконечном цикле функции main() файла main.c добавим ещё один тест, в котором будут рисоваться друг за другом параллельные отрезки во всю высоту экрана случайными цветами
1 2 3 4 5 6 7 8 9 10 |
*/ for(j=0;j<20;j++) { for(i=0;i<TFT9341_WIDTH;i++) { TFT9341_DrawLine(TFT9341_RandColor(),i,0,i,TFT9341_HEIGHT-1); } } HAL_Delay(500); TFT9341_FillScreen(TFT9341_BLACK); |
Посмотрим, как работает данный тест
В следующем тесте будем чертить отрезки случайного цвета из случайной точки в случайную точку
1 2 3 4 5 6 7 8 9 10 11 12 |
*/ for(i=0;i<1000;i++) { TFT9341_DrawLine(TFT9341_RandColor(), HAL_RNG_GetRandomNumber(&hrng)%TFT9341_WIDTH, HAL_RNG_GetRandomNumber(&hrng)%TFT9341_HEIGHT, HAL_RNG_GetRandomNumber(&hrng)%TFT9341_WIDTH, HAL_RNG_GetRandomNumber(&hrng)%TFT9341_HEIGHT); HAL_Delay(10); } HAL_Delay(500); TFT9341_FillScreen(TFT9341_BLACK); |
Посмотрим работу данного теста
В файле spi_ili9341.c ниже функции TFT9341_DrawLine добавим функцию отрисовки прямоугольника
1 2 3 4 5 6 7 8 9 |
void TFT9341_DrawRect(uint16_t color, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) { TFT9341_DrawLine(color,x1,y1,x2,y1); TFT9341_DrawLine(color,x2,y1,x2,y2); TFT9341_DrawLine(color,x1,y1,x1,y2); TFT9341_DrawLine(color,x1,y2,x2,y2); } //------------------------------------------------------------------- |
Добавим на данную функцию прототип в заголовочном файле и напишем тест в бесконечном цикле, который будет рисовать прямоугольники последовательно от края к центру экрана
1 2 3 4 5 6 7 8 9 10 |
*/ for(j=0;j<20;j++) { for(i=0;i<TFT9341_WIDTH/2;i++) { TFT9341_DrawRect(TFT9341_RandColor(), i, i, TFT9341_WIDTH-i-1, TFT9341_HEIGHT-i-1); } } HAL_Delay(500); TFT9341_FillScreen(TFT9341_BLACK); |
Посмотрим, как работает данный тест
Перейдём в файл spi_ili9341.c и ниже функции TFT9341_DrawRect добавим функцию отрисовки окружности определённого радиуса с центром в определённой точке определённым цветом
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(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(x0,y0+r,color); TFT9341_DrawPixel(x0,y0-r,color); TFT9341_DrawPixel(x0+r,y0,color); TFT9341_DrawPixel(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(x0+x,y0+y,color); TFT9341_DrawPixel(x0-x,y0+y,color); TFT9341_DrawPixel(x0+x,y0-y,color); TFT9341_DrawPixel(x0-x,y0-y,color); TFT9341_DrawPixel(x0+y,y0+x,color); TFT9341_DrawPixel(x0-y,y0+x,color); TFT9341_DrawPixel(x0+y,y0-x,color); TFT9341_DrawPixel(x0-y,y0-x,color); } } //------------------------------------------------------------------- |
Добавим на данную функцию прототип в заголовочном файле и добавим тест в бесконечном цикле, который будет выводить окружности одинакового радиуса в различные места экрана
1 2 3 4 5 6 7 8 9 10 |
*/ for(i=0;i<2000;i++) { TFT9341_DrawCircle(HAL_RNG_GetRandomNumber(&hrng)%(TFT9341_WIDTH-40)+20, HAL_RNG_GetRandomNumber(&hrng)%(TFT9341_HEIGHT-40)+20, 20,TFT9341_RandColor()); HAL_Delay(3); } HAL_Delay(500); TFT9341_FillScreen(TFT9341_BLACK); |
Испытаем тест
Всё работает.
Теперь поработаем с текстом.
Для этого из репозитория Cube не важно для какого контроллера, можно и для нашего — F4, из папки UtilitiesFonts добавим файлы шрифтов (все файлы кроме Release_Notes.html) в папку Inc нашего проекта.
Заголовочный файл подключим в файле spi_ili9341.h
1 2 |
#include <cstdlib> #include "fonts.h" |
А остальные файлы подключим в файле spi_ili9341.c
1 2 3 4 5 6 |
#include "spi_ili9341.h" #include "font24.c" #include "font20.c" #include "font16.c" #include "font12.c" #include "font8.c" |
Объявим тип структуры для инициализации шрифта и цвета текста
1 2 3 4 5 6 7 8 9 10 |
uint16_t TFT9341_HEIGHT; //------------------------------------------------------------------- typedef struct { uint16_t TextColor; uint16_t BackColor; sFONT *pFont; }LCD_DrawPropTypeDef; LCD_DrawPropTypeDef lcdprop; //------------------------------------------------------------------- |
Выше функции инициализации дисплея добавим функцию первоначальной инициализации шрифтов
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//------------------------------------------------------------------- void TFT9341_FontsIni(void) { Font8.Height = 8; Font8.Width = 5; Font12.Height = 12; Font12.Width = 7; Font16.Height = 16; Font16.Width = 11; Font20.Height = 20; Font20.Width = 14; Font24.Height = 24; Font24.Width = 17; lcdprop.BackColor=TFT9341_BLACK; lcdprop.TextColor=TFT9341_GREEN; lcdprop.pFont=&Font16; } //------------------------------------------------------------------- |
Вызовем её в самом конце тела функции инициализации дисплея
1 2 |
TFT9341_HEIGHT = h_size; TFT9341_FontsIni(); |
Ниже функции TFT9341_RandColor добавим три функции для установки цвета текста, цвета заливки и шрифта
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(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((x + j), y, lcdprop.TextColor); } else { TFT9341_DrawPixel((x + j), y, lcdprop.BackColor); } } y++; } } //------------------------------------------------------------------- |
Подобная функция у нас была в уроке 37 и не только, поэтому код её объяснять нет смысла.
Далее добавим функцию вывода строки в определённую позицию экрана
1 2 3 4 5 6 7 8 9 10 11 |
//------------------------------------------------------------------- void TFT9341_String(uint16_t x,uint16_t y, char *str) { while(*str) { TFT9341_DrawChar(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 |
//------------------------------------------------------------------- void TFT9341_SetRotation(uint8_t r) { TFT9341_SendCommand(0x36); switch(r) { case 0: TFT9341_SendData(0x48); TFT9341_WIDTH = 240; TFT9341_HEIGHT = 320; break; case 1: TFT9341_SendData(0x28); TFT9341_WIDTH = 320; TFT9341_HEIGHT = 240; break; case 2: TFT9341_SendData(0x88); TFT9341_WIDTH = 240; TFT9341_HEIGHT = 320; break; case 3: TFT9341_SendData(0xE8); TFT9341_WIDTH = 320; TFT9341_HEIGHT = 240; break; } } //------------------------------------------------------------------- |
Такая функция тоже у нас была.
Добавим на эти функции прототипы в заголовочном файле
1 2 3 4 5 6 7 |
void TFT9341_DrawCircle(uint16_t x0, uint16_t y0, int r, uint16_t color); void TFT9341_SetTextColor(uint16_t color); void TFT9341_SetBackColor(uint16_t color); void TFT9341_SetFont(sFONT *pFonts); void TFT9341_DrawChar(uint16_t x, uint16_t y, uint8_t c); void TFT9341_String(uint16_t x,uint16_t y, char *str); void TFT9341_SetRotation(uint8_t r); |
Вернёмся в бесконечный цикл и добавим там тест по выводу отдельных символов на экран дисплея
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 |
*/ TFT9341_SetTextColor(TFT9341_YELLOW); TFT9341_SetBackColor(TFT9341_BLUE); TFT9341_SetFont(&Font24); TFT9341_DrawChar(10,10,'S'); TFT9341_DrawChar(27,10,('t')); TFT9341_DrawChar(44,10,('m')); TFT9341_DrawChar(61,10,('3')); TFT9341_DrawChar(78,10,('2')); TFT9341_SetTextColor(TFT9341_GREEN); TFT9341_SetBackColor(TFT9341_RED); TFT9341_SetFont(&Font20); TFT9341_DrawChar(10,34,('S')); TFT9341_DrawChar(24,34,('t')); TFT9341_DrawChar(38,34,('m')); TFT9341_DrawChar(52,34,('3')); TFT9341_DrawChar(66,34,('2')); TFT9341_SetTextColor(TFT9341_BLUE); TFT9341_SetBackColor(TFT9341_YELLOW); TFT9341_SetFont(&Font16); TFT9341_DrawChar(10,54,('S')); TFT9341_DrawChar(21,54,('t')); TFT9341_DrawChar(32,54,('m')); TFT9341_DrawChar(43,54,('3')); TFT9341_DrawChar(54,54,('2')); TFT9341_SetTextColor(TFT9341_CYAN); TFT9341_SetBackColor(TFT9341_BLACK); TFT9341_SetFont(&Font12); TFT9341_DrawChar(10,70,('S')); TFT9341_DrawChar(17,70,('t')); TFT9341_DrawChar(24,70,('m')); TFT9341_DrawChar(31,70,('3')); TFT9341_DrawChar(38,70,('2')); TFT9341_SetTextColor(TFT9341_RED); TFT9341_SetBackColor(TFT9341_GREEN); TFT9341_SetFont(&Font8); TFT9341_DrawChar(10,82,('S')); TFT9341_DrawChar(15,82,('t')); TFT9341_DrawChar(20,82,('m')); TFT9341_DrawChar(25,82,('3')); TFT9341_DrawChar(30,82,('2')); TFT9341_SetTextColor(TFT9341_YELLOW); TFT9341_SetBackColor(TFT9341_BLUE); HAL_Delay(2000); TFT9341_FillScreen(TFT9341_BLACK); |
Соберём код, прошьём контроллер и посмотрим, как выводятся наши символы
Теперь попробуем повыводить целые строки, причём с разной ориентацией экрана
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
*/ for(i=0;i<16;i++) { TFT9341_SetRotation(i%4); TFT9341_SetFont(&Font24); TFT9341_FillScreen(TFT9341_BLACK); TFT9341_String(1,100,"ABCDEF12345678"); TFT9341_SetFont(&Font20); TFT9341_String(1,124,"ABCDEFGHI12345678"); TFT9341_SetFont(&Font16); TFT9341_String(1,144,"ABCDEFGHIKL123456789"); TFT9341_SetFont(&Font12); TFT9341_String(1,160,"ABCDEFGHIKLMNOPQRSTUVWXY 123456789"); TFT9341_SetFont(&Font8); TFT9341_String(1,172,"ABCDEFGHIKLMNOPQRSTUVWXYZ 123456789ABCDEFGHIKL"); HAL_Delay(2000); } HAL_Delay(10000); TFT9341_SetRotation(0); |
Проверим, как работает вывод строк
Теперь можно раскомментировать остальные тесты и запустить их все вместе.
Итак, сегодня нам удалось запустить дисплей на контроллере ILI9341, подключенный по шине SPI к контроллеру STM32F4, также запустить на нём несколько тестов, используя возможности библиотеки HAL.
Всем спасибо за внимание!
Предыдущая часть Программирование МК STM32 Следующий урок
Отладочную плату и дисплей можно приобрести здесь:
2,8 дюймов 240×320 SPI TFT LCD
Смотреть ВИДЕОУРОК (нажмите на картинку)
Спасибо, всё работает! Только одна опечатка закралась, исправьте:
Функция отрисовки линии (TFT9341_DrawLine()) некорректно работает, когда линия наклонна.
Надо в самом её конце заменить строчку «err=dx;» на «err+=dx;»
Спасибо большое! Исправил и на странице и в прикрепленном исходнике.
Здраствуйте. не могу подключить #include , подскажите где взять ?
если пишу #include «stdlib.h» библиотека подключается. но не компилируется проект. работаю в true studio .
Спасибо за библиотеку!
У меня при запуске в CubeIDE выдавал ошибку «Core/Src/spi_ili9341.o:C:/Users/Admin/STM32CubeIDE/workspace_1.4.0/STM32F407VE_TEST/Debug/../Core/Src/font20.c:87: multiple definition of `Font20_Table'
Core/Src/font20.o:C:/Users/Admin/STM32CubeIDE/workspace_1.4.0/STM32F407VE_TEST/Debug/../Core/Src/font20.c:87: first defined here» , хотя в Keil проект прошился без ошибок.
В файле spi_ili9341.с пришлось закомментировать все подключения
//#include «font24.c»
//#include «font20.c»
//#include «font16.c»
//#include «font12.c»
//#include «font8.c»
и контроллер прошился после этого без ошибок.
Почему-то у меня заливки работают быстро, а те функции, что требуют вычислений работают крайне медленно, к примеру, чтобы закрасить весь экран линиями, нужно где-то пол часа. Контроллер F407VE, CubeIde. Может быть это связано с тем, что я использую stdlib вместо cstdlib?
«потом применим DMA для того, чтобы сравнить, что нам даст эта периферия»
И, где это «применим»?