Подключаем TFT экран на драйвере R61520 к микроконтроллеру STM32f103.

Сегодня мы подключим TFT экран на драйвере R61520 (аналог драйвера ILI9341).

Данный экран будет у нас подключен по параллельному интерфейсу 8080. Документацию на данный экран можно посмотреть тут.

Схема подключения экрана к микроконтроллеру STM32F103:

Основные команды драйвера, которые нам понадобятся:

#define SWRESET 0x01 // программный сброс
#define DISPOFF 0x28 // выключение дисплея
#define DISPON 0x29 // включение дисплея
#define CASET 0x2A // выбор первого и последнего пикселя по оси X
#define PASET 0x2B // выбор первого и последнего пикселя по оси Y
#define RAMWR 0x2C // запись пикселя в дисплей
#define PWCTR1 0xC0 // настройка питания
#define SLPIN 0x10 // перевод дисплея в режим сна
#define SLPOUT 0x11 // пробуждение
#define PIXFMT 0x3A // установка формата пикселя
#define MADCTL 0x36 // настройки порядка вывода пикселей на экран
#define TRANSFER 0xB0 // Управление режимом интерфейса
#define WBCONTROL 0x55 // настройка параметров управления яркостью
//0x21 — Инверсия дисплея включена
//0x22 — Инверсия дисплея выключена

Код программы:

/* USER CODE BEGIN 0 */
#define LCD_IOP GPIOB // LCD_PINS
#define LCD_CTP GPIOC // LCD_CONT
#define CS GPIO_PIN_0
#define RS GPIO_PIN_1
#define WR GPIO_PIN_2
#define RD GPIO_PIN_3
#define RST GPIO_PIN_11
// reset
#define RESET_IDLE           HAL_GPIO_WritePin(GPIOA,GPIO_PIN_11 ,SET);
#define RESET_ACTIVE      HAL_GPIO_WritePin(GPIOA,GPIO_PIN_11,RESET); 
// chip select
#define CS_IDLE                   HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0 ,SET);
#define CS_ACTIVE              HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,RESET); 
// command/data
#define RS_IDLE                   HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1 ,SET);
#define RS_ACTIVE              HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,RESET); 
//read
#define RD_IDLE                  HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3 ,SET);
#define RD_ACTIVE             HAL_GPIO_WritePin(GPIOC,GPIO_PIN_3,RESET); 
//write
#define WR_IDLE                 HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2 ,SET);
#define WR_ACTIVE            HAL_GPIO_WritePin(GPIOC,GPIO_PIN_2,RESET); 
//write strobe
#define WR_STROBE          {WR_ACTIVE; WR_IDLE;}

void TFT1520_SendCommand(uint16_t index);
void TFT1520_Reset();
void TFT1520_init();
void pushData(unsigned char data);
void TFT1520_SendCommand(uint16_t index);
void TFT1520_SendData(uint16_t data);
void TFT1520_InvertDisplay(uint8_t i);
void TFT1520_SetRotation(uint8_t r);
void TFT1520_SetAddrWindow(uint16_t x, uint16_t y, uint16_t x1, uint16_t y1);

void TFT1520_Reset() {
     CS_IDLE ; RD_IDLE ; WR_IDLE ;
     RESET_IDLE ;
     HAL_Delay(1);
     RESET_ACTIVE ;
     HAL_Delay(1);
     RESET_IDLE ;
     HAL_Delay(1);
}

void TFT1520_init() {
    TFT1520_Reset();
    TFT1520_SendCommand(SWRESET);
    HAL_Delay(3);
    TFT1520_SendCommand(DISPOFF);
    TFT1520_SendCommand(TRANSFER );
    TFT1520_SendData(0x00); //40 ?
    TFT1520_SendCommand(PWCTR1);
    TFT1520_SendData(0x0A);
    TFT1520_SendCommand(SLPOUT);
    HAL_Delay(2);
    TFT1520_SendCommand(DISPON);
    TFT1520_SendCommand(PIXFMT);
    TFT1520_SendData(WBCONTROL);
    TFT1520_SetRotation(0);
    TFT1520_InvertDisplay(0);
}

// Формирование команды на порту
void pushData(unsigned char data) {
    LCD_IOP->ODR = (int)((int)data << 8);
    WR_STROBE;
}

void TFT1520_SendCommand(uint16_t index) {
   CS_ACTIVE;
   RS_ACTIVE;
   pushData(index);
   CS_IDLE; }

void TFT1520_SendData(uint16_t data) {
   CS_ACTIVE;
   RS_IDLE;
   pushData(data);
   CS_IDLE;
}

void TFT1520_InvertDisplay(uint8_t i) {
   TFT1520_SendCommand(i ? 0x21 : 0x20);
}

uint16_t X_SIZE = 0;
uint16_t Y_SIZE = 0;
void TFT1520_SetRotation(uint8_t r) {
    TFT1520_SendCommand(0x36);
    switch(r) {
          case 0:
                     TFT1520_SendData(0x48);
                     X_SIZE = 240;
                     Y_SIZE = 320;
                     break;
         case 1:
                    TFT1520_SendData(0x28);
                    X_SIZE = 320;
                    Y_SIZE = 240;
                    break;
        case 2:
                   TFT1520_SendData(0x88);
                   X_SIZE = 240;
                   Y_SIZE = 320;
                   break;
        case 3:
                  TFT1520_SendData(0xE8);
                  X_SIZE = 320;
                  Y_SIZE = 240;
                 break;
     }
}

void TFT1520_SetAddrWindow(uint16_t x, uint16_t y, uint16_t x1, uint16_t y1) {
   TFT1520_SendCommand(0x2A);
   TFT1520_SendData(x >> 8);
   TFT1520_SendData(x);
   TFT1520_SendData(x1 >> 8);
   TFT1520_SendData(x1);
   TFT1520_SendCommand(0x2B);
   TFT1520_SendData(y >> 8);
    TFT1520_SendData(y);
    TFT1520_SendData(y1 >> 8);
    TFT1520_SendData(y1);
}

void TFT1520_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
   if((x<0)||(y<0)||(x>=X_SIZE)||(y>=Y_SIZE))
       return;
   TFT1520_SetAddrWindow(x, y, x, y);
   TFT1520_SendCommand(0x2C);
   TFT1520_SendData(color >> 8);
   TFT1520_SendData(color & 0xFF);
}

/* USER CODE END 0 */

/* USER CODE BEGIN 2 */
   uint16_t x = 0;
    uint16_t y = 0;
    TFT1520_init();
    HAL_Delay(1000);
    TFT1520_SetRotation(1);
/* USER CODE END 2 */

/* USER CODE BEGIN 3 */
    for(x = 0; x < 320; x++) {
         for(y = 0; y < 240; y++) {
             // заполнение дисплея белым по пиксельно
            TFT1520_DrawPixel(x, y, 0xFFFF);
            }
     }
    for(x = 0; x < 320; x++) {
          for(y = 0; y < 240; y++) {
         // заполнение дисплея чёрным по пиксельно
         TFT1520_DrawPixel(x, y, 0x0000);
       }
   }
   for(x = 0; x < 320; x++) {
       for(y = 0; y < 240; y++) {
       // заполнение дисплея чёрным по пиксельно
       TFT1520_DrawPixel(x, y, 0x00FF);
        }
   }
  for(x = 0; x < 320; x++) {
       for(y = 0; y < 240; y++) {
       // заполнение дисплея чёрным по пиксельно
       TFT1520_DrawPixel(x, y, 0xFF00);
      }
   }
/* USER CODE END 3 */

Теперь мы с Вами научимся выводим на экран геометрические фигуры, а так же выведем символы в виде букв и цифр.

По сути нам надо написать алгоритм по которому мы будем включать или выключать пиксели. 

В качестве примера сделаем прорисовку линии. Линия у нас обладает несколькими наборами данных: начальная точка (x1, y1) конечная точка (x2, y2) и цветом (clor).

Для начала нам нужно определиться в каком направлении мы будем двигаться, то есть, нам нужно понять где находятся начальная и конечная точка линии, далее указываем цвет и делаем прорисовку. По такому алгоритму мы свами прорисовываем остальные геометрические фигуры. Для отрисовки букв, мы включаем в нужном месте пиксель и тем самым формируем нужный символ:

Остальная часть программного кода для отображения геометрических фигур и текста:

/* USER CODE BEGIN 0 */
#define swap(a,b) {int16_t t=a;a=b;b=t;} 

  void TFT1520_SetAddrWindow(uint16_t x, uint16_t y, uint16_t x1, uint16_t y1);
  void TFT9341_DrawLine(uint16_t color, uint16_t x1, uint16_t y1,uint16_t x2, uint16_t y2);
  void TFT9341_DrawRect(uint16_t color, uint16_t x1, uint16_t y1,uint16_t x2, uint16_t y2);
  void vivod_char(uint8_t g,uint8_t x, uint8_t y, uint16_t color, uint16_t fon);

uint16_t z[91][8]={ {0x00,0x0E,0x11,0x19,0x15,0x13,0x11,0x0E},// 0
{0x00,0x0E,0x04,0x04,0x04,0x04,0x0C,0x04},// 1
{0x00,0x1F,0x08,0x04,0x02,0x01,0x11,0x0E},// 2
{0x00,0x0E,0x11,0x01,0x02,0x04,0x02,0x1F},// 3
{0x00,0x02,0x02,0x1F,0x12,0x0A,0x06,0x02},// 4
{0x00,0x0E,0x11,0x01,0x01,0x1E,0x10,0x1F},// 5
{0x00,0x0E,0x11,0x11,0x1E,0x10,0x08,0x06},// 6
{0x00,0x08,0x08,0x08,0x04,0x02,0x01,0x1F},// 7
{0x00,0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E},// 8
{0x00,0x0C,0x02,0x01,0x0F,0x11,0x11,0x0E},// 9
{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},// square 10
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},// probel
{0x04,0x04,0x04,0x04,0x00,0x04,0x00,0x00},// !
{0x0A,0x0A,0x0A,0x00,0x00,0x00,0x00,0x00},// «
{0x0A,0x0A,0x1F,0x0A,0x1F,0x0A,0x0A,0x00},// #
{0x04,0x0F,0x14,0x0E,0x05,0x1E,0x04,0x00},// $
{0x18,0x19,0x02,0x04,0x08,0x13,0x03,0x00},// %
{0x0C,0x12,0x14,0x08,0x14,0x12,0x0D,0x00},// &
{0x0C,0x04,0x08,0x00,0x00,0x00,0x00,0x00},// ‘
{0x02,0x04,0x08,0x08,0x08,0x04,0x02,0x00},// (
{0x08,0x04,0x02,0x02,0x02,0x04,0x08,0x00},// )
{0x00,0x04,0x15,0x0E,0x15,0x04,0x00,0x00},// *
{0x00,0x04,0x04,0x1F,0x04,0x04,0x00,0x00},// +
{0x00,0x08,0x04,0x0C,0x00,0x00,0x00,0x00},// ,
{0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00},// —
{0x00,0x00,0x00,0x00,0x00,0x0C,0x0C,0x00},// .
{0x00,0x01,0x02,0x04,0x08,0x10,0x00,0x00},// /
{0x0E,0x11,0x01,0x0D,0x15,0x15,0x0E,0x00},// @
{0x0E,0x11,0x11,0x11,0x1F,0x11,0x11,0x00},// A
{0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E,0x00},// B
{0x0E,0x11,0x10,0x10,0x10,0x11,0x0E,0x00},// C
{0x1C,0x12,0x11,0x11,0x11,0x12,0x1C,0x00},// D
{0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F,0x00},// E
{0x1F,0x10,0x10,0x1E,0x10,0x10,0x10,0x00},// F
{0x0E,0x11,0x10,0x17,0x11,0x11,0x0E,0x00},// G
{0x11,0x11,0x11,0x1F,0x11,0x11,0x11,0x00},// H
{0x0E,0x04,0x04,0x04,0x04,0x04,0x0E,0x00},// I
{0x07,0x02,0x02,0x02,0x02,0x12,0x0C,0x00},// J
{0x11,0x12,0x14,0x18,0x14,0x12,0x11,0x00},// K
{0x10,0x10,0x10,0x10,0x10,0x10,0x1F,0x00},// L
{0x11,0x1B,0x15,0x15,0x11,0x11,0x11,0x00},// M
{0x11,0x11,0x19,0x15,0x13,0x11,0x11,0x00},// N
{0x0E,0x11,0x11,0x11,0x11,0x11,0x0E,0x00},// O
{0x1E,0x11,0x11,0x1E,0x10,0x10,0x10,0x00},// P
{0x0E,0x11,0x11,0x11,0x15,0x12,0x0D,0x00},// Q
{0x1E,0x11,0x11,0x1E,0x14,0x12,0x11,0x00},// R
{0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E,0x00},// S
{0x00,0x04,0x04,0x04,0x04,0x04,0x04,0x1F},// T
{0x11,0x11,0x11,0x11,0x11,0x11,0x0E,0x00},// U
{0x11,0x11,0x11,0x11,0x11,0x0A,0x04,0x00},// V
{0x11,0x11,0x11,0x11,0x15,0x15,0x0E,0x00},// W
{0x11,0x11,0x0A,0x04,0x0A,0x11,0x11,0x00},// X
{0x11,0x11,0x11,0x0A,0x04,0x04,0x04,0x00},// Y
{0x1F,0x01,0x02,0x04,0x08,0x10,0x1F,0x00},// Z
{0x0E,0x08,0x08,0x08,0x08,0x08,0x0E,0x00},// [
{0x11,0x0A,0x1F,0x04,0x1F,0x04,0x04,0x00},//
{0x0E,0x02,0x02,0x02,0x02,0x02,0x0E,0x00},// ]
{0x04,0x0A,0x11,0x00,0x00,0x00,0x00,0x00},// ^
{0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x00},// _
{0x08,0x04,0x00,0x00,0x00,0x00,0x00,0x00},// ‘
{0x00,0x00,0x0E,0x01,0x0F,0x11,0x0F,0x00},// a
{0x10,0x10,0x1E,0x11,0x11,0x11,0x1E,0x00},// b
{0x00,0x00,0x0E,0x10,0x10,0x11,0x0E,0x00},// c
{0x01,0x01,0x0D,0x13,0x11,0x11,0x0F,0x00},// d
{0x00,0x00,0x0E,0x11,0x1F,0x10,0x0E,0x00},// e
{0x06,0x09,0x08,0x1C,0x08,0x08,0x08,0x00},// f
{0x00,0x0F,0x11,0x11,0x0F,0x01,0x0E,0x00},// g
{0x10,0x10,0x16,0x19,0x11,0x11,0x11,0x00},// h
{0x04,0x00,0x0C,0x04,0x04,0x04,0x0E,0x00},// i
{0x02,0x00,0x06,0x02,0x02,0x12,0x0C,0x00},// j
{0x10,0x10,0x12,0x14,0x18,0x14,0x12,0x00},// k
{0x18,0x08,0x08,0x08,0x08,0x08,0x1C,0x00},// l
{0x00,0x00,0x1A,0x15,0x15,0x11,0x11,0x00},// m
{0x00,0x00,0x16,0x19,0x11,0x11,0x11,0x00},// n
{0x00,0x00,0x0E,0x11,0x11,0x11,0x0E,0x00},// o
{0x00,0x00,0x1E,0x11,0x1E,0x10,0x10,0x00},// p
{0x00,0x00,0x0F,0x11,0x0F,0x01,0x01,0x00},// q
{0x00,0x00,0x16,0x19,0x10,0x10,0x10,0x00},// r
{0x00,0x00,0x0E,0x10,0x0E,0x01,0x1E,0x00},// s
{0x08,0x08,0x1C,0x08,0x08,0x09,0x06,0x00},// t
{0x00,0x00,0x11,0x11,0x11,0x13,0x0D,0x00},// u
{0x00,0x00,0x11,0x11,0x11,0x0A,0x04,0x00},// v
{0x00,0x00,0x11,0x11,0x11,0x15,0x0A,0x00},// w
{0x00,0x00,0x11,0x0A,0x04,0x0A,0x11,0x00},// x
{0x00,0x00,0x11,0x11,0x0F,0x01,0x0E,0x00},// y
{0x00,0x00,0x1F,0x02,0x04,0x08,0x1F,0x00},// z
{0x17,0x15,0x15,0x15,0x17,0x00,0x00,0x00},// 10
{0x17,0x11,0x17,0x14,0x17,0x00,0x00,0x00},// 12
{0x17,0x14,0x17,0x11,0x17,0x00,0x00,0x00},// 15
{0x01,0x05,0x09,0x1F,0x08,0x04,0x00,0x00},//
{0x00,0x00,0xFF,0x00,0xFF,0x00,0x00,0x00}// =
};//

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) TFT1520_DrawPixel(y1,x1,color);
                   else TFT1520_DrawPixel(x1,y1,color);
    err-=dy;
    if (err<0){
            y1 += ystep;
            err+=dx;
       }
   }
}

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);
}

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;
  TFT1520_DrawPixel(x0,y0+r,color);
  TFT1520_DrawPixel(x0,y0-r,color);
  TFT1520_DrawPixel(x0+r,y0,color);
  TFT1520_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;
TFT1520_DrawPixel(x0+x,y0+y,color);
TFT1520_DrawPixel(x0-x,y0+y,color);
TFT1520_DrawPixel(x0+x,y0-y,color);
TFT1520_DrawPixel(x0-x,y0-y,color);
TFT1520_DrawPixel(x0+y,y0+x,color);
TFT1520_DrawPixel(x0-y,y0+x,color);
TFT1520_DrawPixel(x0+y,y0-x,color);
TFT1520_DrawPixel(x0-y,y0-x,color);
}
}

void vivod_char(uint8_t g,uint8_t x2, uint8_t y2, uint16_t color, uint16_t fon){
  uint8_t x1=0,y1=0;
  uint8_t x,y;
  char t=0;
  uint16_t w=0x0000000000000001;
  uint16_t zr[91][8];
  for (x1=0;x1!=91;x1++){
     for(y1=0;y1!=8;y1++){
          zr[x1][y1]=z[x1][y1];
       }
   }
  for(y=0;y!=8;y++ ){
     for(x=0; x!=8; x++) {
         t=w&zr[g][y];
         if (t==1){
               TFT1520_DrawPixel(x+x2,y+y2, color);
          }
      else
            TFT1520_DrawPixel(x+x2,y+y2, fon);
      zr[g][y]=zr[g][y]>>1;
     }
 }
}

/* USER CODE END 0 */

/* USER CODE BEGIN WHILE */
   for(x = 0; x < 320; x++) {
        for(y = 0; y < 240; y++) {
              TFT1520_DrawPixel(x, y, 0x0000);
        }
   }

  vivod_char(47,220,100, 0x0FF0, 0x0000);
  vivod_char(90,210,100, 0x0FF0, 0x0000);

  vivod_char(1,(200-0*8),100, 0x0FF0, 0x0000);
  vivod_char(3,(200-1*8),100, 0x0FF0, 0x0000);
  vivod_char(23, (200-2*8),100, 0x0FF0, 0x0000);
  vivod_char(5,(200-3*8),100, 0x0FF0, 0x0000);  
while (1)
{
/* USER CODE END WHILE */

 

Запустив последний код, Вы увидите следующую картинку на экране:

***картинка***