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 0x9327
typedef 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 Setting
LcdWriteReg(0x0024, 0x0080);
LcdWriteReg(0x0025, 0x004F);
LcdWriteReg(0x0026, 0x0007);
LcdWriteReg(0x0029, 0x0000); //Frame control
LcdWriteReg(0x002A, 0x0000);
LcdWriteReg(0x002B, 0x0003);
LcdWriteReg(0x001B, 0x001E);
LcdWriteReg(0x0018, 0x00EF); // Power on sequence
LcdWriteReg(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 Setting
LcdWriteReg(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 Setting
LcdWriteReg(0x0017, 0x0055);
LcdWriteReg(0x0028, 0x0020);
DelayNms(40);
LcdWriteReg(0x0028, 0x0038);
DelayNms(40); // Waiting 2 frames al least
LcdWriteReg(0x0028, 0x003C);
LcdWriteReg(0x0002, 0x0000); // Set active window
DelayNms(100);
}
static void Lcd9327Init(void)
{
LcdSendCmd(0xE9);
LcdSendData(0x20);
LcdSendCmd(0x11); // Exit Sleep
DelayNms(5);
LcdSendCmd(0xD1); // VCOM Control
LcdSendData(0x00);
LcdSendData(0x40);
LcdSendData(0x49);
LcdSendCmd(0xD0); // Power_Setting
LcdSendData(0x07);
LcdSendData(0x01);
LcdSendData(0x8A);
LcdSendCmd(0x21);
LcdSendCmd(0x36); // Set_address_mode
LcdSendData(0x48);
LcdSendCmd(0x3A); // Set_pixel_format
LcdSendData(0x55); // 0x55:16bit/pixel,65k;0x66:18bit/pixel,262k;
LcdSendCmd(0xC1); // Display_Timing_Setting for Normal/Partial Mode
LcdSendData(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_mode
LcdSendCmd(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
注:以上内容仅供学习参考,后续会继续更新余下内容