1.0 初始化GPIO
原理图展示
GPIO 初始化
#include "gd32f30x.h" // Device header
#include <stdint.h>static void GpioInit(void)
{// RCU 使能时钟rcu_periph_clock_enable(RCU_AF);// 使能GPIOA时钟rcu_periph_clock_enable(RCU_GPIOA);// 使能GPIOB时钟rcu_periph_clock_enable(RCU_GPIOB);// 使能GPIOC时钟rcu_periph_clock_enable(RCU_GPIOC);/**WR---PC0 RS---PC1 *CS---PC2 RD---PC3*RST--PC8 BK---PA8*D0--D15 PB0--15*/// 数据总线gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE); // GPIO 引脚重映射gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_MAX, GPIO_PIN_ALL); // 推挽输出,时钟频率开到最大gpio_port_write(GPIOB, 0); // 将GPIOB的所有引脚设置为低电平// 控制信号gpio_init(GPIOC, GPIO_MODE_OUT_PP, GPIO_OSPEED_MAX, GPIO_PIN_0 |GPIO_PIN_1 |GPIO_PIN_2 |GPIO_PIN_3 |GPIO_PIN_8);// 设置PIN_8号引脚为高电平gpio_bit_set(GPIOC,GPIO_PIN_8);gpio_bit_reset(GPIOC, GPIO_PIN_0 | GPIO_PIN_2 | GPIO_PIN_3);// 背光信号gpio_init(GPIOA,GPIO_MODE_OUT_PP, GPIO_OSPEED_2MHZ, GPIO_PIN_8);gpio_bit_reset(GPIOA, GPIO_PIN_8);
}
2.0 宏定义GPIO引脚
#define LCD_RST_SET() gpio_bit_set(GPIOC, GPIO_PIN_8)
#define LCD_RST_RESET() gpio_bit_reset(GPIOC, GPIO_PIN_8)#define LCD_RD_SET() gpio_bit_set(GPIOC, GPIO_PIN_3)
#define LCD_RD_RESET() gpio_bit_reset(GPIOC, GPIO_PIN_3)#define LCD_CS_SET() gpio_bit_set(GPIOC, GPIO_PIN_2)
#define LCD_CS_RESET() gpio_bit_reset(GPIOC, GPIO_PIN_2)#define LCD_RS_SET() gpio_bit_set(GPIOC, GPIO_PIN_1)
#define LCD_RS_RESET() gpio_bit_reset(GPIOC, GPIO_PIN_1)#define LCD_WR_SET() gpio_bit_set(GPIOC, GPIO_PIN_0)
#define LCD_WR_RESET() gpio_bit_set(GPIOC, GPIO_PIN_0)#define LCD_BK_ON() gpio_bit_set(GPIOA, GPIO_PIN_8)
#define LCD_BK_OFF() gpio_bit_reset(GPIOA, GPIO_PIN_8)#define LCD_DATA_WRITE(data) gpio_port_write(GPIOB, data)
#define LCD_DATA_READ() gpio_input_port_get(GPIOB)
3.0 LCD 时序
8080 接口时序引脚对应图
注:这里的 RS
代表的是“Register Select”信号,它是 LCD 控制信号之一,用于选择数据寄存器或命令寄存器。RS = 1 表示的数数据,RS = 0, 表示的是命令。
注:8080写时序,数据(RS=1)/命令(RS=0)在WR的上升沿,写入LCD驱动IC,RD保持高电平。
LCD 发送命令
static __forceinline void LcdSendCmd(uint16_t cmdVal) // LCD 写时序
{LCD_CS_RESET(); // 将CS(片选引脚)设置为低电平LCD_RS_RESET(); // 将RS (命令选择) 设置为低电平,表示发送命令LCD_RD_SET(); // 设置 RD 一直是高电平LCD_DATA_WRITE(cmdVal); // 写入数据LCD_WR_RESET(); // 拉低引脚电平LCD_WR_SET(); // 将引脚设置为高电平LCD_CS_SET(); // 结束片选信号
}
LCD 发送数据
static __forceinline void LcdSendData(uint16_t dataVal)
{LCD_CS_RESET(); // 将CS(片选引脚)设置为低电平LCD_RS_SET(); // 将RS (命令选择) 设置为低电平,表示发送数据LCD_WR_SET(); // 写数据一直是高电平LCD_DATA_WRITE(dataVal); // 发送数据LCD_WR_RESET(); // 拉低引脚电平LCD_WR_SET(); // 将引脚设置为高电平LCD_CS_SET(); // 结束片选信号
}
LCD 接收数据时序图
注:8080读时序,数据(RS=1)/命令(RS=0)在RD的上升沿,读取到MCU,WR保持高电平。
static __forceinline uint16_t LcdGetData(void)
{uint16_t data;LCD_CS_RESET(); // 将CS(片选引脚)设置为低电平LCD_RS_SET(); // 将RS (命令选择) 设置为低电平,表示发送数据LCD_RD_RESET();LCD_RD_SET();LCD_RD_RESET();LCD_RD_SET();data = LCD_DATA_READ();LCD_CS_SET(); // 结束片选信号return data;}
4.0 LCD 控制函数
注:LcdWriteReg
这个函数名通常用于液晶显示器(LCD)的驱动程序中,用来向LCD控制器写入一个寄存器的值。这个函数的主要作用是设置LCD控制器的各种寄存器,以控制显示的内容、格式、亮度等参数。
static __forceinline void LcdWriteReg(uint16_t addr, uint16_t data)
{LcdSendCmd(addr);LcdSendData(data);
}
5.0 LCD 读取寄存器数据
static __forceinline uint16_t LcdReadReg(uint16_t addr)
{uint16_t data;LcdSendCmd(addr);// GPIO 输入引脚gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_MAX, GPIO_PIN_ALL);// 获取数据data = LcdGetData();// 设置GPIO输出gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_MAX, GPIO_PIN_ALL);return data;
}
读取LCD9327寄存器数据:
static __forceinline uint16_t Lcd9327ReadReg(uint16_t addr)
{uint16_t data[6];LcdSendCmd(addr);// 设置GPIO输出gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_MAX, GPIO_PIN_ALL);LCD_CS_RESET();LCD_RS_SET();for (uint8_t i = 0; i < 6; i++){LCD_RD_RESET();LCD_RD_SET();data[i] = LCD_DATA_READ();}LCD_CS_SET();// 设置GPIO输出gpio_init(GPIOB, GPIO_MODE_OUT_PP, GPIO_OSPEED_MAX, GPIO_PIN_ALL);return (data[3] << 8 | (data[4] & 0x00ff));
}
注:
这个表达式 return (data[3] << 8 | (data[4] & 0x00ff));
实际上是在进行位操作和组合操作。这里没有直接的右移操作,但是我们可以分析一下这个表达式的含义:
-
(data[3] << 8)
:这部分将data[3]
的值左移 8 位。这意味着如果data[3]
是一个 8 位的字节(例如,类型为uint8_t
),那么它的最高有效位(MSB)将会变成最低有效位(LSB),并且在最高位会填充 0。例如,如果data[3]
的值是 0x55 (85 in decimal),经过左移后,其值将变为 0x5500。 -
(data[4] & 0x00ff)
:这部分将data[4]
的值与 0x00ff 进行按位与操作。由于data[4]
通常也是一个 8 位的字节,与 0x00ff 进行按位与操作实际上不会改变data[4]
的值。这一步是为了确保data[4]
的值保持在 8 位范围内。 -
|
运算符:按位或运算符。将(data[3] << 8)
的结果与(data[4] & 0x00ff)
的结果进行按位或操作。这意味着data[3]
的高位与data[4]
的低位合并成一个 16 位的整数。
综上所述,这个表达式实际上是在将 data[3]
和 data[4]
中的两个 8 位字节合并成一个 16 位的整数。data[3]
的值被左移 8 位成为高位部分,而 data[4]
的值保持不变作为低位部分。
例如,如果 data[3]
的值是 0x12 (18 in decimal) 并且 data[4]
的值是 0x34 (52 in decimal),那么最终的结果将是 0x1234。
注:上面设计两种不同型号的LCD屏幕,使用了两个LCD屏
6.0 LCD 初始化
注:以下初始化的是两种不同型号的LCD屏,具体来自不同的厂家,里面的参数是直接参考官方代码例程得来无需深究。
#define LCD_HX8352B_ID 0x65
#define LCD_ILI9327_ID 0x9327typedef enum // 创建枚举类型
{HX8352B,ILI9327,
}LcdChip_t;static LcdChip_t g_lcdChip; // 静态全局枚举变量static void Lcd8352bInit(void)
{LcdWriteReg(0x00E2, 0x0000);LcdWriteReg(0x00E5, 0x0000);LcdWriteReg(0x00E7, 0x0000);LcdWriteReg(0x00E8, 0x005E);LcdWriteReg(0x00EC, 0x0008);LcdWriteReg(0x00ED, 0x0047);LcdWriteReg(0x00EE, 0x0020);LcdWriteReg(0x00EF, 0x0059);LcdWriteReg(0x0029, 0x0000);LcdWriteReg(0x002A, 0x0010);LcdWriteReg(0x002B, 0x0003);LcdWriteReg(0x0023, 0x007C); // Power on SettingLcdWriteReg(0x0024, 0x0080);LcdWriteReg(0x0025, 0x004F);LcdWriteReg(0x0026, 0x0007);LcdWriteReg(0x0029, 0x0000); //Frame controlLcdWriteReg(0x002A, 0x0000);LcdWriteReg(0x002B, 0x0003);LcdWriteReg(0x001B, 0x001E);LcdWriteReg(0x0018, 0x00EF); // Power on sequenceLcdWriteReg(0x0019, 0x0001);DelayNms(10),LcdWriteReg(0x001F, 0x008C);LcdWriteReg(0x001F, 0x0084);DelayNms(10),LcdWriteReg(0x001F, 0x0094);DelayNms(10),LcdWriteReg(0x001F, 0x00D4);DelayNms(10),LcdWriteReg(0x002F, 0x0000);LcdWriteReg(0x0040, 0x0000); // Gamma SettingLcdWriteReg(0x0041, 0x0029);LcdWriteReg(0x0042, 0x0026);LcdWriteReg(0x0043, 0x003E);LcdWriteReg(0x0044, 0x003D);LcdWriteReg(0x0045, 0x003F);LcdWriteReg(0x0046, 0x001F);LcdWriteReg(0x0047, 0x0074);LcdWriteReg(0x0048, 0x0008);LcdWriteReg(0x0049, 0x0004);LcdWriteReg(0x004A, 0x0006);LcdWriteReg(0x004B, 0x000C);LcdWriteReg(0x004C, 0x0017);LcdWriteReg(0x0050, 0x0000);LcdWriteReg(0x0051, 0x0002);LcdWriteReg(0x0052, 0x0001);LcdWriteReg(0x0053, 0x0019);LcdWriteReg(0x0054, 0x0016);LcdWriteReg(0x0055, 0x003F);LcdWriteReg(0x0056, 0x000B);LcdWriteReg(0x0057, 0x0060);LcdWriteReg(0x0058, 0x0008);LcdWriteReg(0x0059, 0x0013);LcdWriteReg(0x005A, 0x0019);LcdWriteReg(0x005B, 0x001B);LcdWriteReg(0x005C, 0x0017);LcdWriteReg(0x005D, 0x00FF);LcdWriteReg(0x0016, 0x000B); // Display ON SettingLcdWriteReg(0x0017, 0x0055);LcdWriteReg(0x0028, 0x0020);DelayNms(40);LcdWriteReg(0x0028, 0x0038); DelayNms(40); // Waiting 2 frames al leastLcdWriteReg(0x0028, 0x003C); LcdWriteReg(0x0002, 0x0000); // Set active windowDelayNms(100);
}static void Lcd9327Init(void)
{LcdSendCmd(0xE9); LcdSendData(0x20); LcdSendCmd(0x11); // Exit Sleep DelayNms(5);LcdSendCmd(0xD1); // VCOM ControlLcdSendData(0x00); LcdSendData(0x40); LcdSendData(0x49); LcdSendCmd(0xD0); // Power_SettingLcdSendData(0x07);LcdSendData(0x01); LcdSendData(0x8A);LcdSendCmd(0x21);LcdSendCmd(0x36); // Set_address_modeLcdSendData(0x48);LcdSendCmd(0x3A); // Set_pixel_formatLcdSendData(0x55); // 0x55:16bit/pixel,65k;0x66:18bit/pixel,262k;LcdSendCmd(0xC1); // Display_Timing_Setting for Normal/Partial ModeLcdSendData(0x10); LcdSendData(0x10); LcdSendData(0x02); LcdSendData(0x02); LcdSendCmd(0xC0); // Set Default Gamma LcdSendData(0x00); LcdSendData(0x35); LcdSendData(0x00); LcdSendData(0x00); LcdSendData(0x01); LcdSendData(0x02); LcdSendCmd(0xC5); // Set frame rate LcdSendData(0x02); //WriteComm(0x21); //Enter_invert_modeLcdSendCmd(0xD2); // power setting LcdSendData(0x01); LcdSendData(0x22); LcdSendCmd(0xC8); // Set Gamma LcdSendData(0x02); LcdSendData(0x77); LcdSendData(0x77); LcdSendData(0x00); LcdSendData(0x00); LcdSendData(0x08); LcdSendData(0x00); LcdSendData(0x00); LcdSendData(0x57); LcdSendData(0x00); LcdSendData(0x08); LcdSendData(0x00); LcdSendData(0x08); LcdSendData(0x80); LcdSendData(0x00); LcdSendCmd(0x29); // display on DelayNms(100);
}
LCD 初始化
static void LcdInit(void)
{LCD_RST_RESET();DelayNms(100);LCD_RST_SET();DelayNms(100);uint16_t id;id = LcdReadReg(0); DBG_log("LCD ID:%x\n", id); if ((id & 0xFF) == LCD_HX8352B_ID){g_lcdChip = HX8352B;}else{id = Lcd9327ReadReg(0xEF); if (id == LCD_ILI9327_ID){g_lcdChip = ILI9327;}else{DBG_Error("LCD init error\n");return;}}switch (g_lcdChip){case HX8352B:Lcd8352bInit();break;case ILI9327:Lcd9327Init();break;}
}
7.0 LCD 坐标位置
static void Lcd8352bSetCursor(LcdArea_t *area)
{LcdWriteReg(0x02, area->x1 >> 8); LcdWriteReg(0x03, area->x1); LcdWriteReg(0x04, area->x2 >> 8); LcdWriteReg(0x05, area->x2);LcdWriteReg(0x06, area->y1 >> 8); LcdWriteReg(0x07, area->y1);LcdWriteReg(0x08, area->y2 >> 8);LcdWriteReg(0x09, area->y2); LcdWriteReg(0x80, area->x1 >> 8);LcdWriteReg(0x81, area->x1);LcdWriteReg(0x82, area->y1 >> 8);LcdWriteReg(0x83, area->y1);LcdSendCmd(0x22);
}static void Lcd9327SetCursor(LcdArea_t *area)
{LcdSendCmd(0x2A);LcdSendData(area->x1 >> 8);LcdSendData(area->x1 & 0xFF);LcdSendData(area->x2 >> 8);LcdSendData(area->x2 & 0xFF);LcdSendCmd(0x2B);LcdSendData(area->y1 >> 8);LcdSendData(area->y1 & 0xFF);LcdSendData(area->y2 >> 8);LcdSendData(area->y2 & 0xFF);LcdSendCmd(0x2C);
}
选择哪一个型号的LCD显示屏
static void LcdSetCursor(LcdArea_t *area)
{switch (g_lcdChip){case HX8352B:Lcd8352bSetCursor(area);break;case ILI9327:Lcd9327SetCursor(area);break;}
}
7.0 LCD屏幕颜色填充
// 填充颜色
void LcdFillPureColor(LcdArea_t *area, uint16_t color)
{if ((area->x > LCD_PIXEL_WIDTH - 1) || (area->x2 > LCD_PIXEL_WIDTH - 1)|| (area->y1 > LCD_PIXEL_HEIGHT - 1) || (area->y2 > LCD_PIXEL_HEIGHT - 1)){return;}LcdSetCursor(area);uint32_t totalPoint = (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1);LCD_CS_RESET();LCD_RD_SET();LCD_RS_SET();LCD_DATA_WRITE(color);for (uint32_t i = 0; i < totalPoint; i++){LCD_WR_RESET();LCD_WR_SET();}LCD_CS_SET();}
8.0 LCD驱动初始化
void LcdDrvInit(void)
{GpioInit();LcdInit();
// LCD_BK_ON();LcdArea_t area = {0, 0, LCD_PIXEL_WIDTH - 1, LCD_PIXEL_HEIGHT - 1};LcdFillPureColor(&area, BLACK);
}
9.0 LCD显示字符
void LcdDrawChar(uint16_t x, uint16_t y, uint8_t ascii, uint16_t textColor, uint16_t bkColor, AsciiFontDesc_t *font)
{uint8_t *bitmap;LcdArea_t area;uint8_t data;uint16_t i, j, k;if (!font->isJustNum){bitmap = (uint8_t *)font->bitmap + (ascii - ' ') * font->charHasBytes;}else{bitmap = (uint8_t *)font->bitmap + (ascii - '0') * font->charHasBytes;}area.x1 = x;area.y1 = y;area.x2 = x + (font->width - 1);area.y2 = y + (font->height - 1);LcdSetCursor(&area);for (i = 0; i < font->height; i++){for (j = 0; j < font->width / 8; j++){data = *(bitmap++);for (k = 0; k < 8; k++){if (data & 0x80){LcdSendData(textColor);}else{LcdSendData(bkColor);}data <<= 1;} }if (font->width % 8 == 0){continue;}data = *(bitmap++);for (k = 0; k < font->width % 8; k++){if (data & 0x80){LcdSendData(textColor);}else{LcdSendData(bkColor);}data <<= 1;}}
}
10.0 LCD显示字符串
void LcdDrawString(uint16_t x, uint16_t y, char *str, uint16_t textColor, uint16_t bkColor, AsciiFontDesc_t *font)
{uint8_t i = 0;while (*str != '\0'){LcdDrawChar(x + (i * font->width), y, *str, textColor, bkColor, font); str++;i++;}
}
11.0 LCD显示汉字
void LcdDrawChinese(uint16_t x, uint16_t y, uint16_t textColor, uint16_t bkColor, ChineseFontDesc_t *font)
{uint8_t *bitmap;LcdArea_t area;uint8_t data;uint16_t i, j, k, m;bitmap = (uint8_t *)font->bitmap;for (i = 0; i < font->charNums; i++){area.x1 = x + (i * font->width);area.y1 = y;area.x2 = area.x1 + (font->width - 1);area.y2 = y + (font->height - 1);LcdSetCursor(&area);for (j = 0; j < font->height; j++){for (k = 0; k < font->width / 8; k++){data = *(bitmap++);for (m = 0; m < 8; m++){if (data & 0x80){LcdSendData(textColor);}else{LcdSendData(bkColor);}data <<= 1;} }if (font->width % 8 == 0){continue;}data = *(bitmap++);for (m = 0; m < font->width % 8; m++){if (data & 0x80){LcdSendData(textColor);}else{LcdSendData(bkColor);}data <<= 1;}}}
}
12.0 LCD显示图片
void LcdDrawPicture(uint16_t x, uint16_t y, uint16_t pictureColor, uint16_t bkColor, PictureDesc_t *picDesc)
{uint8_t *picture;uint8_t data;LcdArea_t area;uint16_t i, j, k;picture = (uint8_t *)picDesc->picture;data = *picture;area.x1 = x;area.y1 = y;area.x2 = x + (picDesc->width - 1);area.y2 = y + (picDesc->height - 1);LcdSetCursor(&area);for (i = 0; i < picDesc->height; i++){for(j = 0; j < picDesc->width; ){for (k = 0; k < 8; k++){ if (data & 0x80){LcdSendData(pictureColor);}else{LcdSendData(bkColor);}data <<= 1; j++;if (j == picDesc->width){break;}}picture++;data = *picture;} }
}
13.0 LCD背光函数
void TurnOnScreen(void)
{LCD_BK_ON();
}void TurnOffScreen(void)
{LCD_BK_OFF();
}
14.0 LCD 头文件
#ifndef _LCD_DRV_H_
#define _LCD_DRV_H_#include <stdint.h>
//#include "fonts.h"
#include "picture.h"#define LCD_PIXEL_WIDTH 240
#define LCD_PIXEL_HEIGHT 400#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 0xA145 //褐色
#define BRRED 0xFC07 //棕红色
#define GRAY 0x8430 //灰色
#define IRONGRAY 0xAD55 //铁灰色
//#define IRONGRAY 0x62CA //铁灰色
#define ORANGE 0xFD20 //橙色
#define PURPLE 0x8010 //紫色
#define MIDBLUE 0x8430 typedef struct
{uint16_t x;uint16_t y;
} LcdPoint_t;/** Represents an area of the screen.*/
typedef struct
{uint16_t x1;uint16_t y1;uint16_t x2;uint16_t y2;
} LcdArea_t;void LcdDrvInit(void);
void LcdFillPureColor(LcdArea_t *area, uint16_t color);
void LcdDrawString(uint16_t x, uint16_t y, char *str, uint16_t textColor, uint16_t bkColor, AsciiFontDesc_t *font);
void LcdDrawPicture(uint16_t x, uint16_t y, uint16_t pictureColor, uint16_t bkColor, PictureDesc_t *picDesc);
void LcdDrawChinese(uint16_t x, uint16_t y, uint16_t textColor, uint16_t bkColor, ChineseFontDesc_t *font);
void TurnOnScreen(void);
void TurnOffScreen(void);#endif
注:以上内容仅供学习参考,后续会继续更新余下内容