关于STC8H4K64TLCD的断码液晶驱动代码问题
本帖最后由 zcb18969755773 于 2024-4-24 16:31 编辑我手里有一个项目用到LCD断码液晶驱动,之前用到的友商一款MCU也是带LCD液晶断码驱动的,
现在想转用STC的MCU,于是特意研究了一下8H4K64TLCD这款芯片关于LCD驱动逻辑;
首先讲一下我们这款LCD断码液晶屏,他是由4COM口组合2个SEG口对应一个数字8,如下图所示:
COM1至COM4的工作逻辑我们可以忽略,主要是对SEG1与SEG2的显存赋值问题;
1.友商的方案如下:
//LCD的初始化就省略不写了,硬件连接是COMx对应COMx,SEGx对应SEGx,一一对应;
uchar xdata LCD_buf _at_0x1000; //指定显存地址
//地址1000H SEG1的bit
//地址1001H SEG2的bit以此类推
//要显示一个数字“1”,则建立一个断码真值表0-9
uchar code numtab={0xAF,0xA0,0xCB,0xE9,0xE4,0x6D,0x6F,0xA8,0xEF,0xED};
uchar show_num = 1;
LCD_buf = numtab & 0x0f; //0000赋值给SEG1的显存,DEFA位断全部被关闭
LCD_buf = (numtab>>4) & 0x0f;//1010赋值给SEG2的显存,因此BC断被点亮,显示数字"1"
由于每个SEGx口在COM1-COM4启动期间有4种电平状态来使能对应的LCD对应的断,到此显示目的已达成;
2.接下来在讲STC这款LCD驱动方案,
//首先LCD初始化依旧省略,硬件连接同等对应如上所述;
//显存数组建立我列举对应8个SEGx端口 :SEG0-7
由图可见,SEG0对应的C0S0、C1S0、C2S0、C3S0与SEG1对应的C0S1、C1S1、C2S1、C3S1组成的一个字节(A-dp)8位的显存变量
无法通过字节赋值将numtab 这样一个8位的变量通过1次或者两次赋值给SEG0与SEG1的显存,
而是需要将numtab 这个变量的每一位分别赋值给对应的显存位,这个显存位是离散的,因此需要用到数据转换,
这个问题比较困扰我,C语言基础没到位,如果单独每次赋值某一位,这样写出来的代码可能就是传说中的屎山代码,因为我
的这款LCD来说粗略讲有120个点,当然Ua/Ia这样的字算一个小数点,如下图所示:
在论坛中我也看到过一些遇到同样问题没弄清楚的朋友,我觉得都是我这样没搞懂数据转换的情况,
我本人是非常喜欢STC的单片机,寄存器比较熟悉,抗干扰能力强,不易破解,
我是非常希望将STC的MCU推向我司各类产品的应用,市场上我这样的LCD断码液晶也非常流行,
希望官方的专业大神能将我图中这种类型的LCD断码液晶驱动示例代出一个数据的转换函数,感谢指导!!!!
自己写很简单,直接写对应的寄存器就可以,也可以参考例程 小飞侠 发表于 2024-4-24 17:21
自己写很简单,直接写对应的寄存器就可以,也可以参考例程
感谢您的回复,这个代码我已经看过了,没吃透笔断赋值的那个函数,没达到改写的能力{:titter:} 可以每个寄存器赋不同数值,每次只赋值1个bit,看看那个段码显示,然后就明白了 今天上午又研究了一下,把LCD_buff的表格从新做了一遍,然后瞬间看懂了,
原因就在于一个数码管的8,对应2个SEG线,注释的时候像我这样去表示就好理解一点,
原代码的注释,需要自己去找SEG对应的断,如下:
/*
B7 B6 B5 B4 B3 B2 B1 B0
LCD_buff: 2H 3D 1H 2D 左下 1D B1 B0 左下为左下角的箭头, B0为电池框, B1为电量1
LCD_buff: -- -- -- -- 4H 5D 3H 4D
LCD_buff: 3C 3E 2C 2E 1C 1E B2 右上 右上为右上角的箭头, B2为电量2
LCD_buff: -- -- -- -- 5C 5E 4C 4E
LCD_buff: 3B 3G 2B 2G 1B 1G B3 - B3为电量3, -为左边符号
LCD_buff: -- -- -- -- 5B 5G 4B 4G
LCD_buff: 3A 3F 2A 2F 1A 1F B4 左上 左上为左上角的箭头
LCD_buff: -- -- -- -- 5A 5F 4A 4F
*/
早上自己整理了一下,然后我的注释如下:
// 第3个8 第2个8 第1个8 电池电量显示
// | SEG15 SEG14 | SEG13 SEG12 | SEG11SEG10 | SEG9SEG8 |
// LCD_buff = |C0S15-2H C0S14-3D|C0S13-1H C0S12-2D |C0S11-LD C0S10-1D| C0S9-B1 C0S8-B0|
// LCD_buff = |C1S15-3C C1S14-3E|C1S13-2C C1S12-2E |C1S11-1C C1S10-1E| C1S9-B2 C1S8-RU|
// LCD_buff = |C2S15-3B C2S14-3G|C2S13-2B C2S12-2G |C2S11-1B C2S10-1G| C2S9-B3 C2S8-* |
// LCD_buff = |C3S15-3A C3S14-3F|C3S13-2A C3S12-2F |C3S11-1A C3S10-1F| C3S9-B4 C3S8-LU|
// 空 空 第5个8 第4个8
// | SEG23SEG22 | SEG21 SEG20 | SEG19SEG18 | SEG17SEG16 |
// LCD_buff = |C0S23-NC C0S22-NC|C0S21-NC C0S20-NC |C0S19-4H C0S18-5D| C0S17-3H C0S16-4D|
// LCD_buff = |C1S23-NC C1S22-NC|C1S21-NC C1S20-NC |C1S19-5C C1S18-5E| C1S17-4C C1S16-4E|
// LCD_buff = |C2S23-NC C2S22-NC|C2S21-NC C2S20-NC |C2S19-5B C2S18-5G| C2S17-4B C2S16-4G|
// LCD_buff = |C3S23-NC C3S22-NC|C3S21-NC C3S20-NC |C3S19-5A C3S18-5F| C3S17-4A C3S16-4F|
这样一来就可以识别SEG线对应数码管的段,根据示例代码所示,继续往下看
u8 code T_LCD_mask= {0x00,0x0C,0x30,0xC0,0x03,0x0C};//选中对应的SEG线CxSx横向2断(CE、BG、AF)填0
u8 code T_LCD_mask7 = {0x00,0x04,0x10,0x40,0x01,0x04};//选中对应的SEG线C0Sx断,D断 填0
u8 code T_SEG_ABC = {0x00,0x08,0x20,0x80,0x02,0x08};//选中对应SEG线的C0Sx断,(dp、C、B、A)填1
u8 code T_SEG_DEFG= {0x00,0x04,0x10,0x40,0x01,0x04};//选中对应SEG线的C0Sx断,(D、E、F、G)填1
上面这个数组,定义了每个SEG线纵向给LCD断码填0和填1的对应bit位;
下面这个函数,则根据所需要的对应n个数码管,选中对应的显存LCD_buff,去对LCD进行填0,然后需要显示的断填1;
详细可见如下我代入的试数值:
/********************** 装载显示5个8字 *****************************/
void LCD_load(u8 n, u8 dat) //n为第几个数字,为1~5,dat为要显示的数字
{
u8 i,k;
if((n == 0) || (n >= 6)) return; //合法值 1~5
dat =t_display; //取数字库真值:0-y; 设dat=1,真值=0x06二进制:0000 0110
k = ~T_LCD_mask; //设n=1 取反0x0c :k = 1111 0011,将LCD_buff对应的第1个8两位对应的SEG10-11位置0
//设n=2 取反0x30 :k = 1100 1111,将LCD_buff对应的第2个8两位对应的SEG12-13位置0
//意为将选中的缓存数组的SEG线,对应8的A-G断 清零
if(n <= 3)//选第1~3个数码管,SEG<=15:对应SEG线为: SEG10-11, SEG12-13, SEG14-15
{ //将A-G 断码清零
LCD_buff &= ~T_LCD_mask7; //04H取反:1111 1011 为 1D 清零
LCD_buff &= k; //设n=1则k=1111 0011 为 1C 1E 清零
LCD_buff &= k; // 同上 1B 1G 清零
LCD_buff &= k; // 同上 1A 1F 清零
//设n=1,i设为SEG11线对应的 LD、1C、1B、1A断码 填1;
i = T_SEG_ABC; //i=08H=0000 1000
//设n=1,i设为SEG10线对应的 1D、1E、1G、1F断码 填1;
k = T_SEG_DEFG; //k=04H=0000 0100
//设dat=06H = 0000 0110 :BC段点亮 显示数字"1"
if(dat & 0x01) LCD_buff |= i; //T_SEG_ABC; //A
if(dat & 0x02) LCD_buff |= i; //T_SEG_ABC; //B
if(dat & 0x04) LCD_buff |= i; //T_SEG_ABC; //C
if(dat & 0x08) LCD_buff |= k; //T_SEG_DEFG; //D
if(dat & 0x10) LCD_buff |= k; //T_SEG_DEFG; //E
if(dat & 0x20) LCD_buff |= k; //T_SEG_DEFG; //F
if(dat & 0x40) LCD_buff |= k; //T_SEG_DEFG; //G
}
else//第4 or 5个数码管,上面指定SEG<=19:对应SEG线为: SEG16-17, SEG18-19
{ //将A-G 断码清零
LCD_buff &= ~T_LCD_mask7; //设n=4 取反01H=1111 1110 断码4D 清零
LCD_buff &= k; //设n=4则k=1111 1100 为 4C 4E 清零
LCD_buff &= k; // 同上 4B 4G 清零
LCD_buff &= k; // 同上 4A 4F 清零
i = T_SEG_ABC; //设n=4,i=02H=0000 0010断码H C B A 填1
k = T_SEG_DEFG; //设n=4,k=01H=0000 0001断码D E G F 填1
if(dat & 0x01) LCD_buff |= i; //T_SEG_ABC; //A
if(dat & 0x02) LCD_buff |= i; //T_SEG_ABC; //B
if(dat & 0x04) LCD_buff |= i; //T_SEG_ABC; //C
if(dat & 0x08) LCD_buff |= k; //T_SEG_DEFG; //D
if(dat & 0x10) LCD_buff |= k; //T_SEG_DEFG; //E
if(dat & 0x20) LCD_buff |= k; //T_SEG_DEFG; //F
if(dat & 0x40) LCD_buff |= k; //T_SEG_DEFG; //G
}
} zcb18969755773 发表于 2024-4-25 09:31
今天上午又研究了一下,把LCD_buff的表格从新做了一遍,然后瞬间看懂了,
原因就在于一个数码管的8,对 ...
我从新整理的这个代码注释,不是说我这个方式比源示例代码好,而是我个人C语言代码的基础不深,这样的注释便于个人的理解,感谢阅读! 本帖最后由 梁工 于 2024-4-25 12:28 编辑
昨天忙到没空回复。
楼主的LCD段码的排列方式跟STC LCD的排列方式对应起来,8个段分散到4个寄存器了,LCD段码的COM与SEG的排列一般会根据LCD驱动的具体连接和显存映射来设计,这样对应起来操作方便。
但是不管怎么排列,都是将段码对应的显存(显示数据寄存器)里的位写1显示写0不显示,就这么简单。
具体到某个屏,与COM和SEG的连接一旦确定,则显存与段码对应关系就确定了。
假设楼主的COM1~COM4对应STC的COM0~COM3,楼主的SEG1~SEG8对应STC的SEG0~SEG7,则楼主的屏有4个8的话,安排如下(比我买的屏有规律、映射简单得多):
SEG7SEG6 SEG5SEG4SEG3SEG2SEG1SEG0
COMx 显存 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
COM0 C0SEGV0 P4 D4 P3 D3 P2 D2 P1 D1
COM1 C1SEGV0 C4 E4 C3 E3 C2 E2 C1 E1
COM2 C2SEGV0 G4 F4 G3 F3 G2 F2 G1 F1
COM3 C3SEGV0 B4 A4 B3 A3 B2 A2 B1 A1
4个8对应序号n=1~4,dat为第n个数字显示的内容:
先清除第n个数字对应显存的8个bit:
i = ~(0x03<<((n-1)*2));
C0SEGV0 &= i;//清除Pn Dn
C1SEGV0 &= i;//清除Cn En
C2SEGV0 &= i;//清除Gn Fn
C3SEGV0 &= i;//清除Bn An
然后将新显示的内容填入即可。
i = 1<<((n-1)*2); //D E F A
j = 2<<((n-1)*2); //P C G B
if(dat & 0x01)C3SEGV0 |= i;//A
if(dat & 0x02)C3SEGV0 |= j;//B
if(dat & 0x04)C1SEGV0 |= j;//C
if(dat & 0x08)C0SEGV0 |= i;//D
if(dat & 0x10)C1SEGV0 |= i;//E
if(dat & 0x20)C2SEGV0 |= i;//F
if(dat & 0x40)C2SEGV0 |= j;//G
if(dat & 0x80)C0SEGV0 |= j;//P
其实很多时候我都是用一个数组处理好,再写入显存寄存器,连接好屏后,用串口一个个点亮段码,就知道了数组与段码的对应关系,不需要花时间去分析真值表与显存对应关系,这个方法请参考我的例程。 梁工 发表于 2024-4-25 12:24
昨天忙到没空回复。
楼主的LCD段码的排列方式跟STC LCD的排列方式对应起来,8个段分散到4个寄存器了,LCD段 ...
收到,这个代码省很多内存!非常感谢梁工的指导!!! zcb18969755773 发表于 2024-4-26 09:03
收到,这个代码省很多内存!非常感谢梁工的指导!!!
我只说明原理,我没有实物屏验证,你可以按此原理处理。 梁工 发表于 2024-4-26 10:38
我只说明原理,我没有实物屏验证,你可以按此原理处理。
是这样的,一点没错,非常理想
页:
[1]