解释一下为什么不能直接使用单片机管脚驱动段码LCD屏,因为需要输出四种的电平才能控制。一般单片机管脚就输出置0或者置1 也就是0V或者 5V(3.3V)
1.使用单片机直接驱动
STC8H4K32TLCD这款单片机自带驱动段码LCD屏幕的外设功能。可以参考STC8H手册
下面解释一下LCD外设寄存器的功能
P_SW2 |= 0x80; // 使用过STC单片机的都知道需要使能才可以修改
LCDCFG = 0x00 + 7; // 0x00:选择CPU时钟为LCD时钟, 0x80: 选择内部27M时钟. VLCD电压选择0~7对应0.65+VLCD*0.05.
LCD 时钟源选择内部高速 IRC
段码LCD驱动直接选择使用单片机供电电压输出
死区保护寄存器只有3位bit数据 范围 0~7
段码LCD刷新的频率一共20位bit数据 L低八位 M中8位 H高四位 刷新频率为60HZ的时候效果比较好
COMLENM = 0xDB; // COM时间长度设置 中字节COMLEN[15:8] LCD刷新率 = LCD时钟频率 / ((DBLEN[2:0]+COMLEN[19:0]+1) *2 * COM数)
COMLENL = 0xB9; // COM时间长度设置 低字节COMLEN[7:0] LCD刷新率 = 27000000/((52649+1)*2*4) = 60Hz
BLINKRATE = 60; // 闪烁率配置寄存器, LCD闪烁率 = LCD刷新率 / BLINKRATE[7:0] Hz
LCDCFG2没有使用到7以上的管脚默认为0 。
闪烁率配置寄存器BLINKRATE 在配置为闪缩模式下才能使用
void LCD_config(void)
{
P_SW2 |= 0x80; // 使能 才可以修改
LCDCFG = 0x00 + 7; // 0x00:选择CPU时钟为LCD时钟, 0x80: 选择内部27M时钟. VLCD电压选择0~7对应0.65+VLCD*0.05.
DBLEN = 2; // 设置LCD显示时的死区时间长度, 取值0~7.
COMLENH = 0; // COM时间长度设置 高字节COMLEN[19:16], 一共20bit.
COMLENM = 0xDB; // COM时间长度设置 中字节COMLEN[15:8] LCD刷新率 = LCD时钟频率 / ((DBLEN[2:0]+COMLEN[19:0]+1) *2 * COM数)
COMLENL = 0xB9; // COM时间长度设置 低字节COMLEN[7:0] LCD刷新率 = 27000000/((52649+1)*2*4) = 60Hz
BLINKRATE = 60; // 闪烁率配置寄存器, LCD闪烁率 = LCD刷新率 / BLINKRATE[7:0] Hz
COMON = 0x0F; // COM使能寄存器
SEGON1 = 0xF0; // SEG线使能寄存器1, SEG7~SEG0
SEGON2 = 0xFF; // SEG线使能寄存器2, SEG15~SEG8
SEGON3 = 0xFF; // SEG线使能寄存器3, SEG23~SEG16
SEGON4 = 0xFF; // SEG线使能寄存器4, SEG31~SEG24
SEGON5 = 0x00; // SEG线使能寄存器5, SEG39~SEG32
P0M0&=~0xFF;P0M1|=0xFF; //高祖输入
P1M0&=~0x03;P1M1|=0x03; //高祖输入
P2M0=0x00;P2M1=0xFF; //高祖输入
P3M0&=~0xE0;P3M1|=0xE0; //高祖输入
P4M0&=~0xFE;P4M1|=0xFE;//高祖输入
P5M0&=~0x0F;P5M1|=0x0F; //高祖输入
// LCDCFG2 = 0x0f; // SEG0~3切换到P7.7~7.4
C0SEGV0 = 0x00 ; //清除显示内容
C0SEGV1 = 0x00 ;
C0SEGV2 = 0x00 ;
C0SEGV4 = 0x00 ; //清除显示内容
C1SEGV0 = 0x00 ; //清除显示内容
C1SEGV1 = 0x00 ;
C1SEGV2 = 0x00 ;
C1SEGV4 = 0x00 ; //清除显示内容
C2SEGV0 = 0x00 ; //清除显示内容
C2SEGV1 = 0x00 ;
C2SEGV2 = 0x00 ;
C2SEGV4 = 0x00 ; //清除显示内容
C3SEGV0 = 0x00 ; //清除显示内容
C3SEGV1 = 0x00 ;
C3SEGV2 = 0x00 ;
C3SEGV4 = 0x00 ; //清除显示内容
LCDCR = (0<<1) + 1; // LCD控制寄存器, 0:普通模式, 1:长暗模式, 2:长亮模式, 3:闪烁模式. +0:禁止LCD模块, +1:允许LCD模块.
}
COMON = 0x0F; // COM使能寄存器 使用四个公共端
SEGON1 = 0xF0; // SEG线使能寄存器1, SEG7~SEG0
SEGON2 = 0xFF; // SEG线使能寄存器2, SEG15~SEG8
SEGON3 = 0xFF; // SEG线使能寄存器3, SEG23~SEG16
SEGON4 = 0xFF; // SEG线使能寄存器4, SEG31~SEG24
SEGON5 = 0x00; // SEG线使能寄存器5, SEG39~SEG32 使用到多少使能多少
P0M0&=~0xFF;P0M1|=0xFF; //高祖输入
P1M0&=~0x03;P1M1|=0x03; //高祖输入
P2M0=0x00;P2M1=0xFF; //高祖输入
P3M0&=~0xE0;P3M1|=0xE0; //高祖输入
P4M0&=~0xFE;P4M1|=0xFE;//高祖输入
P5M0&=~0x0F;P5M1|=0x0F; //高祖输入
使用的驱动管脚都设置为高阻输入
段码LCD的管脚图片
驱动电路图管脚使用如下
code uint8_t seg7code[ ]={//显示段码 数码管字跟
0x3f, //0
0x06, //1
0x5b, //2
0x4f, //3
0x66, //4
0x6d, //5
0x7d, //6
0x07, //7
0x7f, //8
0x6f, //9
0x77, //A 10
0x7c, //b 11
0x39, //c 12
0x5e, //d 13
0x73, //P 14
0x40, //_NET 15
0x00, //_TAP 16
0x79, //E 17
0x71, //F 18
0xc0, //19
0x5C, //o 20
0x54, /n 21
0x01, //22
0x02, //23
0x04, //24
0x08, //25
0x10, //26
0x20, //27
0x3E, //28 U
0x76, //29 H
0x38, //30 L
};
uint8_t LCD_Show_buff[4][4] = 0;
void Led(uint8_t a,uint8_t b,uint8_t c,uint8_t d)
{
if(seg7code[a] & 0x01 )
LCD_Show_buff[3][1] |= 0x02;
else
LCD_Show_buff[3][1] &= ~0x02; //com4 9
if(seg7code[a] & 0x20 )
LCD_Show_buff[2][1] |= 0x02;
else
LCD_Show_buff[2][1] &= ~0x02; //com3
if(seg7code[a] & 0x10 )
LCD_Show_buff[1][1] |= 0x02;
else
LCD_Show_buff[1][1] &= ~0x02; //com2
if(seg7code[a] & 0x08 )
LCD_Show_buff[0][1] |= 0x02;
else
LCD_Show_buff[0][1] &= ~0x02; //com1
if(seg7code[a] & 0x02 )
LCD_Show_buff[3][1] |= 0x01;
else
LCD_Show_buff[3][1] &= ~0x01; //com4 8
if(seg7code[a] & 0x40 )
LCD_Show_buff[2][1] |= 0x01;
else
LCD_Show_buff[2][1] &= ~0x01; //com3
if(seg7code[a] & 0x04 )
LCD_Show_buff[1][1] |= 0x01;
else
LCD_Show_buff[1][1] &= ~0x01; //com2
//if(f_flag >=5 )
// LCD_Show_buff[0][1] |= 0x02;
//else
// LCD_Show_buff[3][1] &= ~0x02; //com1
C0SEGV0 = LCD_Show_buff[0][0];
C0SEGV1 = LCD_Show_buff[0][1];
C0SEGV2 = LCD_Show_buff[0][2];
C0SEGV3 = LCD_Show_buff[0][3];
C1SEGV0 = LCD_Show_buff[1][0];
C1SEGV1 = LCD_Show_buff[1][1];
C1SEGV2 = LCD_Show_buff[1][2];
C1SEGV3 = LCD_Show_buff[1][3];
C2SEGV0 = LCD_Show_buff[2][0];
C2SEGV1 = LCD_Show_buff[2][1];
C2SEGV2 = LCD_Show_buff[2][2];
C2SEGV3 = LCD_Show_buff[2][3];
C3SEGV0 = LCD_Show_buff[3][0];
C3SEGV1 = LCD_Show_buff[3][1];
C3SEGV2 = LCD_Show_buff[3][2];
C3SEGV3 = LCD_Show_buff[3][3];
}
解释一下段码LCD显示代码
就解释一个数码管,显示其他的原理一样的。
比如第一个数码显示大写A,那么数码管的八段显示值是0x77;
对应段码LCD的管脚图显示A,那么
COM1 1D 和P1为0
CO2M 1E 和1C为1
COM3 1F 和1G为1
COM4 1A 和1B为1
对应驱动电路图驱动第一个数显的管脚是EG9和EG8
C0S9和C0S8为0 对应的 C0SEGV1 &=~0X03;
C1S9和C1S8为1 对应的 C1SEGV1 |=0X03;
C2S9和C0S8为1 对应的 C2SEGV1 |=0X03;
C3S9和C0S8为1 对应的 C3SEGV1 |=0X03;
写入LCD数据寄存器即可显示出大写A字母
2.考虑单片机成本,管脚紧凑的情况下可以选择TM1621B驱动
TM1621B的手册可以去半导小芯免费查看
TM1621B的驱动就不多讲自己看手册
#ifndef _TM1621_B_H_
#define _TM1621_B_H_
#include "main.h"
sbit HT1621_CS = P5^4;
sbit HT1621_DAT = P1^6;
sbit HT1621_WR = P1^7;
sbit HT1621_RD = P1^0;
#define HT1621_CS_H HT1621_CS = 1
#define HT1621_CS_L HT1621_CS = 0
#define HT1621_DAT_H HT1621_DAT = 1
#define HT1621_DAT_L HT1621_DAT = 0
#define HT1621_WR_H HT1621_WR = 1
#define HT1621_WR_L HT1621_WR = 0
#define HT1621_RD_H HT1621_RD = 1
#define HT1621_RD_L HT1621_RD = 0
#define TEST_NET 12
#define DELAY_TEST 1
#define COMMAND_CODE 0x80 //命令码---100
#define WRITE_DATA_CODE 0xA0 //写命令---101
#define READ_DATA_CODE 0xC0 //读命令---110
#define SYSDIS 0X800 //0B 1000 0000 000X 关振荡器和LCD偏压发生器
#define SYSEN 0X802 //0B 1000 0000 001X 打开系统振荡器
#define LCD_OFF 0X804 //0B 1000 0000 010X 关LCD偏压
#define LCD_ON 0x806 //0B 1000 0000 011X 开LCD偏压
#define TIMERDIS 0x808 //0B 1000 0000 100X 时基输出失效
#define WDTDIS 0X80A //0B 1000 0000 101X WDT溢出标志输出失效
#define TIMEREN 0X80C //0B 1000 0000 110X 时基输出使能
#define WDTEN 0X80E //0B 1000 0000 111X WDT溢出标志输出有效
#define TONE_OFF 0X810 //0B 1000 0001 000X 关闭声音输出
#define TONE_ON 0X812 //0B 1000 0001 001X 打开声音输出
#define CLRTIMER 0X818 //0B 1000 0001 100X 时基发生器清零
#define CLRWDT 0X81C //0B 1000 0001 110X 清除WDT状态
#define XTAL_32K 0X828 //0B 1000 0010 100X 系统时钟源,晶振
#define RC256 0X830 //0B 1000 0011 000X 系统时钟源,片内RC
#define EXT256 0X838 //0B 1000 0011 100X 系统时钟源,外部时钟源
#define BIAS1_2_2COM 0X840 //0B 1000 0100 000X LCD 1/2偏压选项,2个公共口
#define BIAS1_2_3COM 0X848 //0B 1000 0100 100X LCD 1/2偏压选项,3个公共口
#define BIAS1_2_4COM 0X850 //0B 1000 0101 000X LCD 1/2偏压选项,4个公共口
#define BIAS1_3_2COM 0X842 //0B 1000 0100 001X LCD 1/3偏压选项,2个公共口
#define BIAS1_3_3COM 0X84A //0B 1000 0100 101X LCD 1/3偏压选项,3个公共口
#define BIAS1_3_4COM 0X852 //0B 1000 0101 001X LCD 1/3偏压选项,4个公共口
#define TONE4K 0X880 //0B 1000 1000 000X 声音频率4K
#define TONE2K 0X8C0 //0B 1000 1100 000X 声音频率2K
#define IRQ_DIS 0X900 //0B 1001 0000 000X 使/IRQ输出失效
#define IRQ_EN 0X910 //0B 1001 0001 000X 使/IRQ输出有效
#define BIAS 0x852 //0b 1000 0101 0010 1/3duty 4com
void Write_Data(uint cmd);
void Write_Cmd(uint Data);
void LCD_Init(void);
void LCD_Clear(void);
void HT1621_WriteData(uchar addr,uchar sdat);
void HT1621_SendCmd(uchar command);
void HT1621_SendBit(uchar sdat,uchar cnt);
void HT1621_WriteData_much(uchar addr,uchar*sdat,uchar num ); //写多个数据
#endif
#include "main.h"
void HT1621_Delay(uint time_delay) //@27.000MHz US
{
while (time_delay--);
}
/*********************************************************
函数名:LCD_Init(void)
返回值:无
功 能:LCD初始化
*********************************************************/
void Write_Cmd(uint Cmd)
{
uint8_t i;
HT1621_CS_L;
for(i=0;i<12;i++)
{
if(Cmd & 0x800)
{
HT1621_DAT_H;
}
else
{
HT1621_DAT_L;
}
HT1621_Delay(DELAY_TEST);
HT1621_WR_L;
HT1621_Delay(DELAY_TEST);
HT1621_WR_H;
HT1621_Delay(DELAY_TEST);
Cmd<<=1;
}
HT1621_CS_H;
}
void HT1621_SendBit(uint8_t sdat,uint8_t cnt)
{
uint8_t i=0;
for(i=0;i<cnt;i++)
{
HT1621_WR_L;
HT1621_Delay(DELAY_TEST);
if(sdat&0x80)
{
HT1621_DAT_H;
}
else
{
HT1621_DAT_L;
}
HT1621_Delay(DELAY_TEST);
HT1621_WR_H;
HT1621_Delay(DELAY_TEST);
sdat<<=1;
}
}
void HT1621_WriteData(uint8_t addr,uint8_t sdat) //写单个数据
{
addr<<=2;//因为地址是6位的
sdat<<=4;//数据是4位
HT1621_CS_L;
HT1621_SendBit(0xa0,3);//1010
HT1621_SendBit(addr,6);
HT1621_SendBit(sdat,4); //一次只发送4位数据,一个地址发送1次,低四位
//HT1621_SendBit(sdat,8);
HT1621_CS_H;
}
void LCD_Clear(void)
{
uint8_t i;
for(i=13;i<29;i++)
{
HT1621_WriteData(i,0x00);
}
HT1621_WriteData(30,0x00);
HT1621_WriteData(31,0x00);
}
void LCD_Init(void)
{
Write_Cmd(RC256); //系统时钟源,片内RC
HT1621_Delay(15);
Write_Cmd(TONE_OFF); //关闭声音输出
HT1621_Delay(15);
Write_Cmd(WDTDIS); //WDT溢出标志输出失效
HT1621_Delay(15);
Write_Cmd(LCD_ON); //开LCD偏压
Write_Cmd(SYSEN); //打开系统振荡器
HT1621_Delay(15);
Write_Cmd(BIAS1_3_4COM); //0B 1000 0101 001X LCD 1/3偏压选项,4个公共口
}
驱动硬件电路如下 只需要使用三根管脚即可驱动段码LCD
段码LCD管脚图
段码LCD的第一个数字显示跟TM1621B对应的管脚SEG30和SEG29对应
SEG31跟SEG30写入数据即可显示第一个字母
uint8_t segbuff1(uint8_t dat) //根据段码LCD管脚图数据转化出来的对应TM1621B数码
{
uint8_t buff=0;
if(dat&0x01)buff|=0x80;
if(dat&0x02)buff|=0x08;
if(dat&0x04)buff|=0x02;
if(dat&0x08)buff|=0x10;
if(dat&0x10)buff|=0x20;
if(dat&0x20)buff|=0x40;
if(dat&0x40)buff|=0x04;
if(dat&0x80)buff|=0x01;
return buff;
}
void Led(uchar a,uchar b,uchar c,uchar d,uchar e,uchar f,uchar g,uchar h )
{
uchar show_data = 0 ;
if(f_flag == 4) //显示数码管小数点
HT1621_WriteData(30,segbuff1(seg7code[a]|0x80)>>4);
else HT1621_WriteData(30,segbuff1(seg7code[a])>>4);
if(f_flag == 4)
HT1621_WriteData(29,segbuff1(seg7code[a]|0x80));
else
HT1621_WriteData(29,segbuff1(seg7code[a]));