相信大家对正点原子推出的精英板不陌生吧,作为一款入门STM32的高性价比开发板,可以练习许多开发应用。本次分享就精英板V2驱动LCD彩屏外设展开话题。正点原子精英STM32开发板选择的是STM32F103ZET6作为MCU,该芯片是STM32F103里面配置非常强大的了,它拥有的资源包括:64KB SRAM、512KB FLASH、2个基本定时器、4个通用定时器、2个高级定时器、2个DMA控制器(共12个通道)、3个 SPI、2个IIC、5个串口、1个USB、1个CAN、3个12位ADC、1个12位DAC、1个SDIO接口、1个FSMC接口以及112个通用IO口。该芯片的配置十分强悍,并且还带外部总线(FSMC)可以用来外扩SRAM和连接LCD等,通过FSMC驱动LCD,可以显著提高LCD的刷屏速度,是STM32F1家族常用型号里面,最高配置的芯片了。
本次驱动的LCD屏并不是正点原子提供的,而是采用另外一个扩展板,通过GPIO去模拟SPI通讯,因此不占用精英板上的TFT_LCD固定硬件接口。这里不得不谈及这块扩展板,扩展板上的资源也很丰富,有集成多个传感器,包括温湿度传感器、三轴磁场传感器、加速度角速度传感器、环境光传感器,一个RGB彩灯、一块128*160分辨率的电阻式触摸屏,一个旋钮,一个双轴电位计摇杆。当然此次分享就触摸屏的显示驱动部分进行讲解。
本次采用JLink调试器与精英板相连接,扩展板上的触摸屏通过杜邦线与精英板相连接。两者之间的关联可通过下方的原理图可知。
在硬件连线的基础上进行代码编写,这里我们采用GPIO模拟SPI通讯,屏的芯片是基于ST7735S的,因此我们在基础工程框架中增加驱动代码,部分参考代码如下:
lcd_init.h
#ifndef __LCD_INIT_H #define __LCD_INIT_H #include "stm32f1xx.h" #include "./SYSTEM/sys/sys.h" #include "./SYSTEM/delay/delay.h" #define USE_HORIZONTAL 1 //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏 typedef unsigned char u8; typedef unsigned short u16; typedef unsigned int u32; #if USE_HORIZONTAL==0||USE_HORIZONTAL==1 #define LCD_W 128 #define LCD_H 160 #else #define LCD_W 160 #define LCD_H 128 #endif #define BOARD_LCD_GPIO GPIOA #define BOARD_LCD_GPIO_SCLK_PIN GPIO_PIN_0 #define BOARD_LCD_GPIO_CS_PIN GPIO_PIN_1 #define BOARD_LCD_GPIO_MOSI_PIN GPIO_PIN_4 #define BOARD_LCD_GPIO_DC_PIN GPIO_PIN_5 #define BOARD_LCD_GPIO_RES_PIN GPIO_PIN_6 #define BOARD_LCD_BLK_GPIO GPIOA #define BOARD_LCD_GPIO_BLK_PIN GPIO_PIN_7 //-----------------LCD端口定义---------------- #define LCD_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PB口时钟使能 */ #define LCD_SCLK_Clr() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_SCLK_PIN,GPIO_PIN_RESET)//SCL SCLK #define LCD_SCLK_Set() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_SCLK_PIN,GPIO_PIN_SET) #define LCD_CS_Clr() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_CS_PIN,GPIO_PIN_RESET)//CS #define LCD_CS_Set() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_CS_PIN,GPIO_PIN_SET) #define LCD_MOSI_Clr() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_MOSI_PIN,GPIO_PIN_RESET)//SDA MOSI #define LCD_MOSI_Set() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_MOSI_PIN,GPIO_PIN_SET) #define LCD_DC_Clr() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_DC_PIN,GPIO_PIN_RESET)//DC #define LCD_DC_Set() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_DC_PIN,GPIO_PIN_SET) #define LCD_RES_Clr() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_RES_PIN,GPIO_PIN_RESET)//RES #define LCD_RES_Set() HAL_GPIO_WritePin(BOARD_LCD_GPIO,BOARD_LCD_GPIO_RES_PIN,GPIO_PIN_SET) #define LCD_BLK_Clr() HAL_GPIO_WritePin(BOARD_LCD_BLK_GPIO,BOARD_LCD_GPIO_BLK_PIN,GPIO_PIN_RESET)//BLK #define LCD_BLK_Set() HAL_GPIO_WritePin(BOARD_LCD_BLK_GPIO,BOARD_LCD_GPIO_BLK_PIN,GPIO_PIN_SET) void LCD_GPIO_Init(void);//初始化GPIO void LCD_Writ_Bus(u8 dat);//模拟SPI时序 void LCD_WR_DATA8(u8 dat);//写入一个字节 void LCD_WR_DATA(u16 dat);//写入两个字节 void LCD_WR_REG(u8 dat);//写入一个指令 void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2);//设置坐标函数 void LCD_Init(void);//LCD初始化 #endif
lcd_init.c
#include "lcd_init.h" #include "stm32f1xx.h" /****************************************************************************** 函数说明:LCD串行数据写入函数 入口数据:dat 要写入的串行数据 返回值: 无 ******************************************************************************/ void LCD_Writ_Bus(u8 dat) { u8 i; LCD_CS_Clr(); for(i=0;i<8;i++) { LCD_SCLK_Clr(); if(dat&0x80) { LCD_MOSI_Set(); } else { LCD_MOSI_Clr(); } LCD_SCLK_Set(); dat<<=1; } LCD_CS_Set(); } /****************************************************************************** 函数说明:LCD写入数据 入口数据:dat 写入的数据 返回值: 无 ******************************************************************************/ void LCD_WR_DATA8(u8 dat) { LCD_Writ_Bus(dat); } /****************************************************************************** 函数说明:LCD写入数据 入口数据:dat 写入的数据 返回值: 无 ******************************************************************************/ void LCD_WR_DATA(u16 dat) { LCD_Writ_Bus(dat>>8); LCD_Writ_Bus(dat); } /****************************************************************************** 函数说明:LCD写入命令 入口数据:dat 写入的命令 返回值: 无 ******************************************************************************/ void LCD_WR_REG(u8 dat) { LCD_DC_Clr();//写命令 LCD_Writ_Bus(dat); LCD_DC_Set();//写数据 } /****************************************************************************** 函数说明:设置起始和结束地址 入口数据:x1,x2 设置列的起始和结束地址 y1,y2 设置行的起始和结束地址 返回值: 无 ******************************************************************************/ void LCD_Address_Set(u16 x1,u16 y1,u16 x2,u16 y2) { if(USE_HORIZONTAL==0) { LCD_WR_REG(0x2a);//列地址设置 LCD_WR_DATA(x1+2); LCD_WR_DATA(x2+2); LCD_WR_REG(0x2b);//行地址设置 LCD_WR_DATA(y1+1); LCD_WR_DATA(y2+1); LCD_WR_REG(0x2c);//储存器写 } else if(USE_HORIZONTAL==1) { LCD_WR_REG(0x2a);//列地址设置 LCD_WR_DATA(x1+2); LCD_WR_DATA(x2+2); LCD_WR_REG(0x2b);//行地址设置 LCD_WR_DATA(y1+1); LCD_WR_DATA(y2+1); LCD_WR_REG(0x2c);//储存器写 } else if(USE_HORIZONTAL==2) { LCD_WR_REG(0x2a);//列地址设置 LCD_WR_DATA(x1+1); LCD_WR_DATA(x2+1); LCD_WR_REG(0x2b);//行地址设置 LCD_WR_DATA(y1+2); LCD_WR_DATA(y2+2); LCD_WR_REG(0x2c);//储存器写 } else { LCD_WR_REG(0x2a);//列地址设置 LCD_WR_DATA(x1+1); LCD_WR_DATA(x2+1); LCD_WR_REG(0x2b);//行地址设置 LCD_WR_DATA(y1+2); LCD_WR_DATA(y2+2); LCD_WR_REG(0x2c);//储存器写 } } void LCD_Init(void) { LCD_GPIO_CLK_ENABLE(); GPIO_InitTypeDef gpio_init_struct; gpio_init_struct.Pin = BOARD_LCD_GPIO_SCLK_PIN|BOARD_LCD_GPIO_CS_PIN|BOARD_LCD_GPIO_MOSI_PIN|\ BOARD_LCD_GPIO_DC_PIN|BOARD_LCD_GPIO_RES_PIN|BOARD_LCD_GPIO_BLK_PIN; /* LED0引脚 */ gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(BOARD_LCD_GPIO, &gpio_init_struct); /* 初始化LCD引脚 */ delay_ms(20); LCD_RES_Clr();//复位 delay_ms(1); LCD_RES_Set(); delay_ms(1); LCD_BLK_Set();//打开背光 delay_ms(120); LCD_WR_REG(0x11); //Sleep out delay_ms(120); LCD_WR_REG(0xB1); LCD_WR_DATA8(0x05); LCD_WR_DATA8(0x3C); LCD_WR_DATA8(0x3C); LCD_WR_REG(0xB2); LCD_WR_DATA8(0x05); LCD_WR_DATA8(0x3C); LCD_WR_DATA8(0x3C); LCD_WR_REG(0xB3); LCD_WR_DATA8(0x05); LCD_WR_DATA8(0x3C); LCD_WR_DATA8(0x3C); LCD_WR_DATA8(0x05); LCD_WR_DATA8(0x3C); LCD_WR_DATA8(0x3C); LCD_WR_REG(0xB4); //Dot inversion LCD_WR_DATA8(0x03); LCD_WR_REG(0xC0); LCD_WR_DATA8(0x28); LCD_WR_DATA8(0x08); LCD_WR_DATA8(0x04); LCD_WR_REG(0xC1); LCD_WR_DATA8(0XC0); LCD_WR_REG(0xC2); LCD_WR_DATA8(0x0D); LCD_WR_DATA8(0x00); LCD_WR_REG(0xC3); LCD_WR_DATA8(0x8D); LCD_WR_DATA8(0x2A); LCD_WR_REG(0xC4); LCD_WR_DATA8(0x8D); LCD_WR_DATA8(0xEE); LCD_WR_REG(0xC5); //VCOM LCD_WR_DATA8(0x1A); LCD_WR_REG(0x36); //MX, MY, RGB mode if(USE_HORIZONTAL==0)LCD_WR_DATA8(0x00); else if(USE_HORIZONTAL==1)LCD_WR_DATA8(0xC0); else if(USE_HORIZONTAL==2)LCD_WR_DATA8(0x70); else LCD_WR_DATA8(0xA0); LCD_WR_REG(0xE0); LCD_WR_DATA8(0x04); LCD_WR_DATA8(0x22); LCD_WR_DATA8(0x07); LCD_WR_DATA8(0x0A); LCD_WR_DATA8(0x2E); LCD_WR_DATA8(0x30); LCD_WR_DATA8(0x25); LCD_WR_DATA8(0x2A); LCD_WR_DATA8(0x28); LCD_WR_DATA8(0x26); LCD_WR_DATA8(0x2E); LCD_WR_DATA8(0x3A); LCD_WR_DATA8(0x00); LCD_WR_DATA8(0x01); LCD_WR_DATA8(0x03); LCD_WR_DATA8(0x13); LCD_WR_REG(0xE1); LCD_WR_DATA8(0x04); LCD_WR_DATA8(0x16); LCD_WR_DATA8(0x06); LCD_WR_DATA8(0x0D); LCD_WR_DATA8(0x2D); LCD_WR_DATA8(0x26); LCD_WR_DATA8(0x23); LCD_WR_DATA8(0x27); LCD_WR_DATA8(0x27); LCD_WR_DATA8(0x25); LCD_WR_DATA8(0x2D); LCD_WR_DATA8(0x3B); LCD_WR_DATA8(0x00); LCD_WR_DATA8(0x01); LCD_WR_DATA8(0x04); LCD_WR_DATA8(0x13); LCD_WR_REG(0x3A); LCD_WR_DATA8(0x05); LCD_WR_REG(0x29); }
lcd.h
#ifndef __LCD_H #define __LCD_H #include "stm32f1xx.h" #include "lcd_init.h" #define LCD_WIDTH 128 #define LCD_HEIGHT 160 typedef struct { unsigned int index; unsigned int value[64]; }LCD_Plot_Data; struct drv_lcd_coordinate { int8_t ymin; int8_t ymax; uint8_t x; uint8_t y; uint8_t xsize; uint8_t ysize; }; void LCD_Fill(u16 xsta,u16 ysta,u16 xend,u16 yend,u16 color);//指定区域填充颜色 void LCD_DrawPoint(u16 x,u16 y,u16 color);//在指定位置画一个点 void LCD_DrawLine(u16 x1,u16 y1,u16 x2,u16 y2,u16 color);//在指定位置画一条线 void LCD_DrawRectangle(u16 x1, u16 y1, u16 x2, u16 y2,u16 color);//在指定位置画一个矩形 void Draw_Circle(u16 x0,u16 y0,u8 r,u16 color);//在指定位置画一个圆 void LCD_ShowChinese(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示汉字串 void LCD_ShowChinese12x12(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示单个12x12汉字 void LCD_ShowChinese16x16(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示单个16x16汉字 void LCD_ShowChinese24x24(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示单个24x24汉字 void LCD_ShowChinese32x32(u16 x,u16 y,u8 *s,u16 fc,u16 bc,u8 sizey,u8 mode);//显示单个32x32汉字 void LCD_ShowChar(u16 x,u16 y,u8 num,u16 fc,u16 bc,u8 sizey,u8 mode);//显示一个字符 void LCD_ShowString(u16 x,u16 y,const u8 *p,u16 fc,u16 bc,u8 sizey,u8 mode);//显示字符串 void LCD_ShowIntNum(u16 x,u16 y,u16 num,u8 len,u16 fc,u16 bc,u8 sizey);//显示整数变量 void LCD_ShowFloatNum1(u16 x,u16 y,float num,u8 len,u16 fc,u16 bc,u8 sizey);//显示两位小数变量 void LCD_ShowPicture(u16 x,u16 y,u16 length,u16 width,const u8 pic[]);//显示图片 u32 mypow(u8 m,u8 n);//求幂 void Plot_Data_Add(LCD_Plot_Data *Plot_Data, u32 Data);//曲线绘制结构体 void LCD_Draw_Plot(LCD_Plot_Data *Plot_Data, u32 color);//曲线绘制函数 void drv_lcd_draw_line_with_color(u16 xbegin, u16 ybegin, u16 xend, u16 yend, u16 color); void drv_lcd_draw_coordinate_with_color(struct drv_lcd_coordinate *coord, u8 x, u8 y, u8 xsize, u8 ysize, u16 color); void drv_lcd_draw_coordinate(struct drv_lcd_coordinate *coord, u8 x, u8 y, u8 xsize, u8 ysize); void drv_lcd_draw_coordinate_point_with_color(struct drv_lcd_coordinate *coord, int8_t *buf, u8 num, u16 color); void drv_lcd_coordinate_create(struct drv_lcd_coordinate *coord, int8_t ymin, int8_t ymax); //画笔颜色 #define WHITE 0xFFFF #define BLACK 0x0000 #define BLUE 0x001F #define BRED 0XF81F #define GRED 0XFFE0 #define GBLUE 0X07FF #define RED 0xF800 #define MAGENTA 0xF81F #define GREEN 0x07E0 #define CYAN 0x7FFF #define YELLOW 0xFFE0 #define BROWN 0XBC40 //棕色 #define BRRED 0XFC07 //棕红色 #define GRAY 0X8430 //灰色 #define DARKBLUE 0X01CF //深蓝色 #define LIGHTBLUE 0X7D7C //浅蓝色 #define GRAYBLUE 0X5458 //灰蓝色 #define LIGHTGREEN 0X841F //浅绿色 #define LGRAY 0XC618 //浅灰色(PANNEL),窗体背景色 #define LGRAYBLUE 0XA651 //浅灰蓝色(中间层颜色) #define LBBLUE 0X2B12 //浅棕蓝色(选择条目的反色) #endif
main.c
#include "./SYSTEM/sys/sys.h" #include "./SYSTEM/usart/usart.h" #include "./SYSTEM/delay/delay.h" #include "./BSP/LED/led.h" #include "./BSP/LCD/lcd.h" #include "./BSP/LCD/lcd_init.h" int main(void) { static uint8_t num = 0; HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init(); /* 初始化LED */ LCD_Init(); /* 初始化LCD */ while (1) { switch (num) { case 0: LCD_Fill(0,0,LCD_W,LCD_H,WHITE); break; case 1: LCD_Fill(0,0,LCD_W,LCD_H,BLACK); break; case 2: LCD_Fill(0,0,LCD_W,LCD_H,BLUE); break; case 3: LCD_Fill(0,0,LCD_W,LCD_H,RED); break; case 4: LCD_Fill(0,0,LCD_W,LCD_H,MAGENTA); break; case 5: LCD_Fill(0,0,LCD_W,LCD_H,GREEN); break; case 6: LCD_Fill(0,0,LCD_W,LCD_H,CYAN); break; case 7: LCD_Fill(0,0,LCD_W,LCD_H,YELLOW); break; case 8: LCD_Fill(0,0,LCD_W,LCD_H,BRRED); break; case 9: LCD_Fill(0,0,LCD_W,LCD_H,GRAY); break; case 10: LCD_Fill(0,0,LCD_W,LCD_H,LGRAY); break; case 11: LCD_Fill(0,0,LCD_W,LCD_H,BROWN); break; } LCD_ShowChinese(30,20,"0",RED,WHITE,16,0); LCD_ShowChinese(46,20,"1",RED,WHITE,16,0); LCD_ShowChinese(62,20,"2",RED,WHITE,16,0); LCD_ShowChinese(78,20,"3",RED,WHITE,16,0); LCD_ShowString(10, 50, (const u8 *)"STM32F103ZET6", RED, WHITE, 16, 0); LCD_ShowString(10, 80, (const u8 *)"TFTLCD TEST", RED,WHITE, 16, 0); LCD_ShowString(10, 110, (const u8 *)"EEPW.COM.CN", RED, WHITE, 16, 0); num++; if (num == 12) num = 0; LED0_TOGGLE(); /*红灯闪烁*/ delay_ms(500); } }
精英板与扩展板及调试器实物连线如下:
扩展板上的触摸显示屏会依次循环刷出12种颜色的背景,展示如下图所示,具体移步优酷:https://v.youku.com/v_show/id_XNjM4MTQ4ODQ4OA==.html