【WCH蓝牙系列芯片】-基于CH584发板—段码LCD屏驱动显示
------------------------------------------------------------------------------------------------------------------------------------
1、段式LCD显示原理:
LCD本身不发光,LCD的显示只是由于液晶分子按顺序排列导致光无法透过而形成的图案,把液晶显示器件(LCD)的电极做成一段一段的形式,通过控制不同段位的亮灭来实现数字和字符的显示。
段码屏通常有一个公共电极和多个段电极。当在公共电极和特定的段电极之间施加适当的电压时,对应的波晶段就会被驱动,改变其光学状态,使该段显示出来。例如,段码屏中的数字“8”其实是由七个小分段组成的,通过控制这七个小分段亮灭来实现0~9数字的显示。
在芯片手册中,段式LCD的驱动引脚的公共驱动端COM和区段驱动SEG对应芯片的GPIO口的说明如下:

这次采用的段式LCD屏公共驱动COM和区段驱动SEG分布如下:


针对于段式LCD驱动程序,需要对照上面LDC屏的手册

1、在LCD初始化中,
typedef enum{ LCD_PS_3V3 = 0, // 3.3V 驱动 LCD_PS_2V5, // 2.5V 驱动 }LCDDrvPowerTypeDef; //定义LCD的偏压比,偏压比是指LCD的SEG/COM驱动波形中,各档模拟电压相对于LCD输出最高电压的比例 typedef enum{ LCD_1_2_Bias = 0, // 2级分压 表示2级分压,即偏压比为1/2 LCD_1_3_Bias, // 3级分压 表示3级分压,即偏压比为1/3 }LCDBiasTypeDef; //定义LCD的占空比占空比是指每个COM的有效选通时间与整个扫描周期的比值 typedef enum{ LCD_1_2_Duty = 0, // COM0-COM1 表示占空比为1/2,即有2个COM线 每个COM线的选通时间占整个扫描周期的一半 LCD_1_3_Duty, // COM0-COM2 表示占空比为1/3,即有3个COM线 每个COM线的选通时间占整个扫描周期的三分之一 LCD_1_4_Duty, // COM0-COM3 表示占空比为1/4,即有4个COM线 每个COM线的选通时间占整个扫描周期的四分之一 }LCDDutyTypeDef; //配置 LCD 的扫描时钟频率。扫描时钟频率决定了 LCD 屏幕的刷新速度,即每秒扫描的次数。 typedef enum{ LCD_CLK_256 = 0, // 256Hz 扫描时钟频率为 256Hz,即每秒扫描 256 次 LCD_CLK_512, // 512Hz 扫描时钟频率为 512Hz,即每秒扫描 512 次 LCD_CLK_1000, // 1KHz 扫描时钟频率为 1KHz,即每秒扫描 1000 次 LCD_CLK_128 // 128Hz 扫描时钟频率为 128Hz,即每秒扫描 128 次 }LCDSCANCLKTypeDef; //初始化LED void LCD_Init(LCDDutyTypeDef duty, LCDBiasTypeDef bias) { // 关闭相关引脚的数字输入功能,避免干扰LCD驱动 R32_PIN_IN_DIS |= 0x0000238F; // 关闭特定引脚的数字输入功能 R32_PIN_IN_DIS |= RB_PBLx_IN_DIS; // 关闭特定引脚的数字输入功能 R16_PIN_CONFIG |= RB_PBHx_IN_DIS; // 操作LCD时,需关闭debug,关闭特定引脚的数字输入功能 // 使能所有段的驱动功能,确保所有段都可以被控制 R32_LCD_SEG_EN = 0x0FFFFFFF; // 配置LCD的控制寄存器,设置相关参数 R8_LCD_CMD = RB_LCD_SYS_EN | RB_LCD_ON | // 使能LCD系统和开启LCD功能 (LCD_CLK_128 << 5) | // 设置扫描时钟频率为128Hz (duty << 3) | // 设置占空比 (bias << 2); // 设置偏压比 }
void LCD_Init(LCDDutyTypeDef duty, LCDBiasTypeDef bias)函数中,
LCDDutyTypeDef duty结构体是选择公共驱动端COM
LCDBiasTypeDef bias结构体是选择对应的驱动电压
2、在主函数中:
//1-6的数字显示 unsigned char const lcd[7]={0x61, 0xD7, 0xF5, 0xFF, 0xC6, 0xCB, 0xEB}; unsigned char const first_three_segment_codes[10] = {0xFB, 0x61, 0xD7, 0xF5, 0x6D, 0xBD, 0xBF, 0xE1, 0xFF, 0xFD}; unsigned char const last_three_segment_codes[10] = {0xAF, 0x06, 0x6D, 0x4F, 0xC6, 0xCB, 0xEB, 0x0E, 0xEF, 0xCF}; int main(){ uint32_t VER = 0; HSECFG_Capacitance(HSECap_18p); // 配置外部高速晶振的电容值为18pF,用于调整晶振的频率稳定性 SetSysClock(CLK_SOURCE_HSE_PLL_62_4MHz); // 设置系统时钟源为外部高速晶振(HSE)并通过PLL倍频到62.4MHz LCD_Init(LCD_1_4_Duty, LCD_1_3_Bias); // 初始化LCD,设置为1/4占空比和1/3偏置 // 将数组中的段码依次写入LCD的不同位置,从而在LCD上显示特定的图案或字符 LCD_WriteData0( lcd[0] ); LCD_WriteData1( lcd[1] ); LCD_WriteData2( lcd[2] ); LCD_WriteData3( lcd[3] ); LCD_WriteData4( lcd[6] ); LCD_WriteData5( lcd[5] ); LCD_WriteData6( lcd[4] ); DelayMs(1000); // 延时函数 while(1){ for (uint8_t i = 0; i < 10; i++) { // 将数字的段码写入 LCD 的不同位置 LCD_WriteData0(first_three_segment_codes[i]); LCD_WriteData1(first_three_segment_codes[i]); LCD_WriteData2(first_three_segment_codes[i]); LCD_WriteData3( lcd[3] ); LCD_WriteData4(last_three_segment_codes[i]); LCD_WriteData5(last_three_segment_codes[i]); LCD_WriteData6(last_three_segment_codes[i]); // 延时一段时间,以便观察显示效果 DelayMs(1000); // 延时函数, } } }
先是让LCD屏幕显示123456,然后1秒之后,就一直循环显示111111到999999,以1秒的时间进行循环播放。

从图中可以看出,LCD的前三个段码和后三个段码的SEG分布不是一样的方式。
1、SEG0和SEG1是接的LDC的PIN5和PIN6,控制第一个段码8和“网络”字符,当LCD_WriteData0中写入0110 0001(0x61)的数据,段码显示数字“1”
#define LCD_WriteData0( d ) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xffffff00) | ((uint32_t)d)) /* 填充LCD0驱动数值 */
R32_LCD_RAM0 & 0xffffff00:通过按位与操作,保留 R32_LCD_RAM0 的高24位,将低8位清零。
-
((uint32_t)d):将要写入的数据d转换为uint32_t类型, -
| ((uint32_t)d):通过按位或操作,将低8位设置为d的值。 -
将数据
d写入到R32_LCD_RAM0寄存器的低八位,用于控制LCD屏的SEG0和SEG1段,
2、SEG2和SEG3是接的LDC的PIN7和PIN8,控制第二个段码8和“拉闸”字符,当LCD_WriteData1中写入1101 0111(0xD7)的数据,段码显示数字“2”
#define LCD_WriteData1( d ) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xffff00ff) | ((uint32_t)d<<8)) /* 填充LCD1驱动数值 */
0xffff00ff的二进制表示为11111111 11111111 00000000 11111111,与R32_LCD_RAM0相与后,只有第9到16位会变为0,其余位保持不变;
-
|按位或操作:将清零后的R32_LCD_RAM0与左移后的数据d进行按位或操作,将d的值写入到R32_LCD_RAM0的第9到16位,从而实现对LCD屏SEG2和SEG3段的驱动数值的更新。
3、SEG4和SEG5是接的LDC的PIN9和PIN10,控制第三个段码8和“电流”字符,当LCD_WriteData1中写入111 0101(0xF5)的数据,段码显示数字“3”
#define LCD_WriteData2( d ) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0xff00ffff) | ((uint32_t)d<<16)) /* 填充LCD2驱动数值 */
-
R32_LCD_RAM0 & 0xff00ffff:通过按位与操作,将R32_LCD_RAM0寄存器的第17到24位清零,保留其余位的原有值。0xff00ffff的二进制表示为11111111 00000000 11111111 11111111,与R32_LCD_RAM0相与后,只有第17到24位会变为0,其余位保持不变。 -
((uint32_t)d<<16):将输入的数据d强制转换为uint32_t类型后,左移16位。这样原本在低八位的数据d就会移动到第17到24位的位置,低十六位变为0。 -
|按位或操作:将清零后的R32_LCD_RAM0与左移后的数据d进行按位或操作,将d的值写入到R32_LCD_RAM0的第17到24位,从而实现对LCD屏SEG4和SEG5段的驱动数值的更新。
4、SEG6和SEG7是接的LDC的PIN11和PIN12,控制第“电压、功率、总剩余、VAkWh”字符,当LCD_WriteData1中写入1111 1111(0xFF)的数据,段码显示数字“电压、功率、总剩余、VAkWh”
#define LCD_WriteData3( d ) (R32_LCD_RAM0 = (R32_LCD_RAM0 & 0x00ffffff) | ((uint32_t)d<<24)) /* 填充LCD3驱动数值 */
-
R32_LCD_RAM0 & 0x00ffffff:通过按位与操作,将R32_LCD_RAM0寄存器的第25到32位清零,保留其余位的原有值。0x00ffffff的二进制表示为00000000 11111111 11111111 11111111,与R32_LCD_RAM0相与后,只有第25到32位会变为0,其余位保持不变。 -
((uint32_t)d<<24):将输入的数据d强制转换为uint32_t类型后,左移24位。这样原本在低八位的数据d就会移动到第25到32位的位置,低二十四位变为0。 -
|按位或操作:将清零后的R32_LCD_RAM0与左移后的数据d进行按位或操作,将d的值写入到R32_LCD_RAM0的第25到32位,从而实现对LCD屏SEG6和SEG7段的驱动数值的更新。
5、SEG8和SEG9是接的LDC的PIN13和PIN14,控制第四个段码8和S14字符,当LCD_WriteData1中写入0100 1111(0xC6)的数据,段码显示数字“4”
#define LCD_WriteData4( d ) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xffffff00) | ((uint32_t)d)) /* 填充LCD4驱动数值 */
-
R32_LCD_RAM1 & 0xffffff00:先通过按位与操作,将R32_LCD_RAM1寄存器的低八位清零,保留高24位的原有值。0xffffff00的二进制表示为11111111 11111111 11111111 00000000,与R32_LCD_RAM1相与后,只有低八位会变为0,其余位保持不变。 -
((uint32_t)d):将输入的数据d强制转换为uint32_t类型,确保其为32位数据,以便后续的按位或操作。 -
|按位或操作:将清零后的R32_LCD_RAM1与转换后的数据d进行按位或操作,将d的值写入到R32_LCD_RAM1的低八位,从而实现对LCD屏SEG8和SEG9段的驱动数值的更新。
6、SEG10和SEG11是接的LDC的PIN15和PIN16,控制第五个段码8和S13字符,当LCD_WriteData1中写入1100 1011(0xCB)的数据,段码显示数字“5”
#define LCD_WriteData5( d ) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xffff00ff) | ((uint32_t)d<<8)) /* 填充LCD5驱动数值 */
-
R32_LCD_RAM1 & 0xffff00ff:通过按位与操作,将R32_LCD_RAM1寄存器的第9到16位清零,保留其余位的原有值。0xffff00ff的二进制表示为11111111 11111111 00000000 11111111,与R32_LCD_RAM1相与后,只有第9到16位会变为0,其余位保持不变。 -
((uint32_t)d<<8):将输入的数据d强制转换为uint32_t类型后,左移8位。这样原本在低八位的数据d就会移动到第9到16位的位置,低八位变为0。 -
|按位或操作:将清零后的R32_LCD_RAM1与左移后的数据d进行按位或操作,将d的值写入到R32_LCD_RAM1的第9到16位,从而实现对LCD屏SEG10和SEG11段的驱动数值的更新。
7、SEG12和SEG13是接的LDC的PIN17和PIN18,控制第六个段码8和S13字符,当LCD_WriteData1中写入1110 1011(0xEB)的数据,段码显示数字“6”
#define LCD_WriteData6( d ) (R32_LCD_RAM1 = (R32_LCD_RAM1 & 0xff00ffff) | ((uint32_t)d<<16)) /* 填充LCD6驱动数值 */
-
R32_LCD_RAM1 & 0xff00ffff:通过按位与操作,将R32_LCD_RAM1寄存器的第17到24位清零,保留其余位的原有值。0xff00ffff的二进制表示为11111111 00000000 11111111 11111111,与R32_LCD_RAM1相与后,只有第17到24位会变为0,其余位保持不变。 -
((uint32_t)d<<16):将输入的数据d强制转换为uint32_t类型后,左移16位。这样原本在低八位的数据d就会移动到第17到24位的位置,低十六位变为0。 -
|按位或操作:将清零后的R32_LCD_RAM1与左移后的数据d进行按位或操作,将d的值写入到R32_LCD_RAM1的第17到24位,从而实现对LCD屏SEG12和SEG13段的驱动数值的更新。

注意:
在LCD中的COM脚和SEG脚的对应的IO配置如下:

但是在LCD驱动中,只用的了部分SEG脚,其他的SEG引脚用,比如PA8和PA9就没有再用到SEG脚,这样为了防止PA8和PA9的串口功能的使用,再LCD初始化中,需要将部分没有用的引脚,进行关闭。
原先得R32_PIN_IN_DIS |= 0x0000238F; // 关闭数字输入
0x0000238F-------0010 0011 1000 1111 正好是对应得PA0~PA3、PA7~PA9 还有一个PA13,只要那个引脚没有用,就对应位为0
还有R32_LCD_SEG_EN设置对应得SEG

void LCD_Init(LCDDutyTypeDef duty, LCDBiasTypeDef bias) { // R32_PIN_IN_DIS |= 0x0000238F; // 关闭数字输入 R32_PIN_IN_DIS |= 0x0000108F; // 关闭数字输入 (不让SEG25和SEG26使能) R32_PIN_IN_DIS |= RB_PBLx_IN_DIS; // 关闭数字输入 R16_PIN_CONFIG |= RB_PBHx_IN_DIS; // 操作LCD时,需关闭debug // R32_LCD_SEG_EN = 0x0FFFFFFF; // 使能 SEG0 到 SEG23 uint32_t seg_enable = RB_LCD_SEG0_7_EN | RB_LCD_SEG8_15_EN | RB_LCD_SEG16_23_EN; R32_LCD_SEG_EN = seg_enable; //(不让SEG25和SEG26使能) R8_LCD_CMD = RB_LCD_SYS_EN | RB_LCD_ON | (LCD_CLK_128 << 5) | (duty << 3) | (bias << 2); }
浙公网安备 33010602011771号