1.方案选择
数码管的显示电路有两种方案,可以用数码管显示专用IC,如simHT16K33;也可以用74HC138和74HC164两个芯片来分别进行位选和段选。前者可以节省CPU资源,只需通过IIC等通讯方式来进行显示控制和按键读取。后者则需要CPU花费一定的时间来进行位选和段选控制,并以一定的频率刷新。本次采取了后者,CPU为STM32F103VE。
2.原理图设计
原理图如下所示:一个138对应8个数码管的位选,1个164对应一个数码管的段选。使用4个138和4个164即可进行32个数码管的显示,即8个4位数码管。
LED灯也接在164上进行显示控制,按键检测接在138上进行检测。显示电路通过排线与主板相连。
3.程序设计
驱动程序包含三部分:数码管的显示,LED灯的显示,按键的检测。而且这三者占用资源一样,在RTOS中不能同步运行。
数码管显示部分程序如下:
u8 number[20]={
0x3f,
0x6,
0x5b,
0x4f,
0x66,
0x6d,
0x7d,
0x07,
0x7f,
0x6f,
0xbf,
0x86,
0xdb,
0xcf,
0xe6,
0xed,
0xfd,
0x87,
0xff,
0xef
};//0,1,2,3,4,5,6,7,8,9,0.,1.,2.,3.,4.,5.,6.,7.,8.,9.
void send_byte(unsigned char dat1,unsigned char dat2,unsigned char dat3,unsigned char dat4)
{
unsigned char i;
DISPLAY_SDA_H;
for(i=0;i<8;i++)
{
DISPLAY_SCL_L;
if((dat4<<i)&0x80)
DISPLAY_SDA_H;
else DISPLAY_SDA_L;
DISPLAY_SCL_H;
}
DISPLAY_SCL_L;
DISPLAY_SDA_H;
for(i=0;i<8;i++)
{
DISPLAY_SCL_L;
if((dat3<<i)&0x80)
DISPLAY_SDA_H;
else DISPLAY_SDA_L;
DISPLAY_SCL_H;
}
DISPLAY_SCL_L;
DISPLAY_SDA_H;
for(i=0;i<8;i++)
{
DISPLAY_SCL_L;
if((dat2<<i)&0x80)
DISPLAY_SDA_H;
else DISPLAY_SDA_L;
DISPLAY_SCL_H;
}
DISPLAY_SCL_L;
DISPLAY_SDA_H;
for(i=0;i<8;i++)
{
DISPLAY_SCL_L;
if((dat1<<i)&0x80)
DISPLAY_SDA_H;
else DISPLAY_SDA_L;
DISPLAY_SCL_H;
}
DISPLAY_SCL_L;
} //164位选数字,一次位选4个数码管
void DISPLAY( u8 number1,u8 number2,
u8 number3,u8 number4,
u8 number5,u8 number6,
u8 number7,u8 number8)//数码管显示整数
{
DISPLAY_E1_H;
DISPLAY_E2_H;
DISPLAY_E3_H;
DISPLAY_E4_H;
u8 n1[4],n2[4],n3[4],n4[4],n5[4],n6[4],n7[4],n8[4];
n1[0]=number1%10;
n2[0]=number2%10;
n3[0]=number3%10;
n4[0]=number4%10;
n5[0]=number5%10;
n6[0]=number6%10;
n7[0]=number7%10;
n8[0]=number8%10;
n1[1]=number1/10%10;
n2[1]=number2/10%10;
n3[1]=number3/10%10;
n4[1]=number4/10%10;
n5[1]=number5/10%10;
n6[1]=number6/10%10;
n7[1]=number7/10%10;
n8[1]=number8/10%10;
n1[2]=number1/100%10;
n2[2]=number2/100%10;
n3[2]=number3/100%10;
n4[2]=number4/100%10;
n5[2]=number5/100%10;
n6[2]=number6/100%10;
n7[2]=number7/100%10;
n8[2]=number8/100%10;
n1[3]=number1/1000%10;
n2[3]=number2/1000%10;
n3[3]=number3/1000%10;
n4[3]=number4/1000%10;
n5[3]=number5/1000%10;
n6[3]=number6/1000%10;
n7[3]=number7/1000%10;
n8[3]=number8/1000%10;
DISPLAY_A0_L;
DISPLAY_A1_L;
DISPLAY_A2_L;
send_byte(number[n1[3]],number[n3[3]],number[n5[3]],number[n7[3]]);//扫描第一个
rt_thread_delay(1);
send_byte(0x00,0x00,0x00,0x00);
DISPLAY_A0_H;
DISPLAY_A1_L;
DISPLAY_A2_L;
send_byte(number[n1[2]],number[n3[2]],number[n5[2]],number[n7[2]]);//扫描第二个
rt_thread_delay(1);
send_byte(0x00,0x00,0x00,0x00);
DISPLAY_A0_L;
DISPLAY_A1_H;
DISPLAY_A2_L;
send_byte(number[n1[1]],number[n3[1]],number[n5[1]],number[n7[1]]);//扫描第三个
rt_thread_delay(1);
send_byte(0x00,0x00,0x00,0x00);
DISPLAY_A0_H;
DISPLAY_A1_H;
DISPLAY_A2_L;
send_byte(number[n1[0]],number[n3[0]],number[n5[0]],number[n7[0]]);//扫描第四个
rt_thread_delay(1);
send_byte(0x00,0x00,0x00,0x00);
DISPLAY_A0_L;
DISPLAY_A1_L;
DISPLAY_A2_H;
send_byte(number[n2[3]],number[n4[3]],number[n6[3]],number[n8[3]]);//扫描第五个
rt_thread_delay(1);
send_byte(0x00,0x00,0x00,0x00);
DISPLAY_A0_H;
DISPLAY_A1_L;
DISPLAY_A2_H;
send_byte(number[n2[2]],number[n4[2]],number[n6[2]],number[n8[2]]);//扫描第六个
rt_thread_delay(1);
send_byte(0x00,0x00,0x00,0x00);
DISPLAY_A0_L;
DISPLAY_A1_H;
DISPLAY_A2_H;
send_byte(number[n2[1]],number[n4[1]],number[n6[1]],number[n8[1]]);//扫描第七个
rt_thread_delay(1);
send_byte(0x00,0x00,0x00,0x00);
DISPLAY_A0_H;
DISPLAY_A1_H;
DISPLAY_A2_H;
send_byte(number[n2[0]],number[n4[0]],number[n6[0]],number[n8[0]]);//扫描第八个
rt_thread_delay(1);
send_byte(0x00,0x00,0x00,0x00);
}
void DISPLAYfloatp( float number1,float number2,
float number3,float number4,
float number5,float number6,
float number7,float number8)//数码管显示浮点数,两位小数
{ u16 int_number1,int_number2,int_number3,int_number4,int_number5,int_number6,int_number7,int_number8;
u16 point1[4],point2[4],point3[4],point4[4],point5[4],point6[4],point7[4],point8[4];
if(number1/100>1)
{
int_number1=number1*10;
point1[1]=10;
point1[2]=0;
}
else
{
int_number1=number1*100;
point1[1]=0;
point1[2]=10;
}
if(number2/100>1)
{
int_number2=number2*10;
point2[1]=10;
point2[2]=0;
}
else
{
int_number2=number2*100;
point2[1]=0;
point2[2]=10;
}
if(number3/100>1)
{
int_number3=number3*10;
point3[1]=10;
point3[2]=0;
}
else
{
int_number3=number3*100;
point3[1]=0;
point3[2]=10;
}
if(number4/100>1)
{
int_number4=number4*10;
point4[1]=10;
point4[2]=0;
}
else
{
int_number4=number4*100;
point4[1]=0;
point4[2]=10;
}
if(number5/100>1)
{
int_number5=number5*10;
point5[1]=10;
point5[2]=0;
}
else
{
int_number5=number5*100;
point5[1]=0;
point5[2]=10;
}
if(number6/100>1)
{
int_number6=number6*10;
point6[1]=10;
point6[2]=0;
}
else
{
int_number6=number6*100;
point6[1]=0;
point6[2]=10;
}
if(number7/100>1)
{
int_number7=number7*10;
point7[1]=10;
point7[2]=0;
}
else
{
int_number7=number7*100;
point7[1]=0;
point7[2]=10;
}
if(number8/100>1)
{
int_number8=number8*10;
point8[1]=10;
point8[2]=0;
}
else
{
int_number8=number8*100;
point8[1]=0;
point8[2]=10;
}
//整数化
u8 n1[4],n2[4],n3[4],n4[4],n5[4],n6[4],n7[4],n8[4];
n1[0]=int_number1%10;
n2[0]=int_number2%10;
n3[0]=int_number3%10;
n4[0]=int_number4%10;
n5[0]=int_number5%10;
n6[0]=int_number6%10;
n7[0]=int_number7%10;
n8[0]=int_number8%10; //分离第一位
n1[1]=int_number1/10%10;
n2[1]=int_number2/10%10;
n3[1]=int_number3/10%10;
n4[1]=int_number4/10%10;
n5[1]=int_number5/10%10;
n6[1]=int_number6/10%10;
n7[1]=int_number7/10%10;
n8[1]=int_number8/10%10; //分列第二位
n1[2]=int_number1/100%10;
n2[2]=int_number2/100%10;
n3[2]=int_number3/100%10;
n4[2]=int_number4/100%10;
n5[2]=int_number5/100%10;
n6[2]=int_number6/100%10;
n7[2]=int_number7/100%10;
n8[2]=int_number8/100%10; //分离第三位
n1[3]=int_number1/1000%10;
n2[3]=int_number2/1000%10;
n3[3]=int_number3/1000%10;
n4[3]=int_number4/1000%10;
n5[3]=int_number5/1000%10;
n6[3]=int_number6/1000%10;
n7[3]=int_number7/1000%10;
n8[3]=int_number8/1000%10; //分离第四位
DISPLAY_E1_H;
DISPLAY_E2_H;
DISPLAY_E3_H;
DISPLAY_E4_H; //开启所有位选开关
DISPLAY_LED1_H;
DISPLAY_LED2_H;
DISPLAY_A0_L;
DISPLAY_A1_L;
DISPLAY_A2_L;//位选第一个
send_byte(number[n1[3]],number[n3[3]],number[n5[3]],number[n7[3]]);//扫描第一个
rt_thread_delay(2);
DISPLAY_A0_H;
DISPLAY_A1_L;
DISPLAY_A2_L;
send_byte(number[n1[2]+point1[2]],number[n3[2]+point3[2]],number[n5[2]+point5[2]],number[n7[2]+point7[2]] );//扫描第二个
rt_thread_delay(2);
DISPLAY_A0_L;
DISPLAY_A1_H;
DISPLAY_A2_L;
send_byte(number[n1[1]+point1[1]],number[n3[1]+point3[1]],number[n5[1]+point5[1]],number[n7[1]+point7[1]]);//扫描第三个
rt_thread_delay(2);
DISPLAY_A0_H;
DISPLAY_A1_H;
DISPLAY_A2_L;
send_byte(number[n1[0]],number[n3[0]],number[n5[0]],number[n7[0]]);//扫描第四个
rt_thread_delay(2);
DISPLAY_A0_L;
DISPLAY_A1_L;
DISPLAY_A2_H;
send_byte(number[n2[3]],number[n4[3]],number[n6[3]],number[n8[3]]);//扫描第五个
rt_thread_delay(2);
DISPLAY_A0_H;
DISPLAY_A1_L;
DISPLAY_A2_H;
send_byte(number[n2[2]+point2[2]],number[n4[2]+point4[2]],number[n6[2]+point6[2]],number[n8[2]+point8[2]]);//扫描第六个
rt_thread_delay(2);
DISPLAY_A0_L;
DISPLAY_A1_H;
DISPLAY_A2_H;
send_byte(number[n2[1]+point2[1]],number[n4[1]+point4[1]],number[n6[1]+point6[1]],number[n8[1]+point8[1]]);//扫描第七个
rt_thread_delay(2);
DISPLAY_A0_H;
DISPLAY_A1_H;
DISPLAY_A2_H;
send_byte(number[n2[0]],number[n4[0]],number[n6[0]],number[n8[0]]);//扫描第八个
rt_thread_delay(2);
send_byte(0,0,0,0);
DISPLAY_E1_L;
DISPLAY_E2_L;
DISPLAY_E3_L;
DISPLAY_E4_L; //关闭所有位选开关
}
显示程序写的有点复杂,应该可以用循环来解决,但是最近有点忙,先不管了。第一个可以显示整数,第二个可以自适应显示一位或两位小数。
LED显示程序和数码管显示类似,但是没有用到138:
void display_led(u8 Num1[8],u8 Num2[8])
{
DISPLAY_E1_L;
DISPLAY_E2_L;
DISPLAY_E3_L;
DISPLAY_E4_L; //关闭所有位选开关
DISPLAY_LED1_L;
DISPLAY_LED2_L;
u8 i,D1,D2;
u8 num1[8],num2[8];
for(i=0;i<8;i++)
{
num1[i]=!Num1[i];
num2[i]=!Num2[i];
}
for(i=3;i>0;i--)
{
num1[i-1]=(num1[i]<<1)|num1[i-1];
num2[i-1]=(num2[i]<<1)|num2[i-1];
}
for(i=7;i>4;i--)
{
num1[i-1]=(num1[i]<<1)|num1[i-1];
num2[i-1]=(num2[i]<<1)|num2[i-1];
}
send_byte(num1[0],num2[0],num1[4],(num2[4]<<1));
DISPLAY_E1_H;
DISPLAY_E2_H;
DISPLAY_E3_H;
DISPLAY_E4_H; //开启所有位选开关
rt_thread_delay(5);
DISPLAY_LED1_H;
DISPLAY_LED2_H;
}
按键检测程序则只用到了138,程序如下:
u8 KEY[12];
void read_key()
{
send_byte(0,0,0,0);
DISPLAY_E1_L;
DISPLAY_E2_L;
DISPLAY_E3_L;
DISPLAY_E4_H; //开启所有位选开关
DISPLAY_A0_H;
DISPLAY_A1_H;
DISPLAY_A2_H;
KEY[0]=GET_KEY;//扫描第八个
DISPLAY_A0_L;
DISPLAY_A1_H;
DISPLAY_A2_H;
KEY[1]=GET_KEY;//扫描第七个
DISPLAY_A0_H;
DISPLAY_A1_L;
DISPLAY_A2_H;
KEY[2]=GET_KEY;//扫描第六个
DISPLAY_A0_L;
DISPLAY_A1_L;
DISPLAY_A2_H;
KEY[3]=GET_KEY;//扫描第五个
DISPLAY_A0_H;
DISPLAY_A1_H;
DISPLAY_A2_L;
KEY[11]=GET_KEY;//扫描第四个
DISPLAY_A0_L;
DISPLAY_A1_H;
DISPLAY_A2_L;
KEY[10]=GET_KEY;//扫描第九个
DISPLAY_A0_H;
DISPLAY_A1_L;
DISPLAY_A2_L;
KEY[9]=GET_KEY;//扫描第十个
DISPLAY_A0_L;
DISPLAY_A1_L;
DISPLAY_A2_L;
KEY[8]=GET_KEY;//扫描第十一个
DISPLAY_E3_H;
DISPLAY_E4_L; //开启所有位选开关
DISPLAY_A0_L;
DISPLAY_A1_L;
DISPLAY_A2_L;//位选第一个
KEY[7]=GET_KEY;//扫描第一个
DISPLAY_A0_H;
DISPLAY_A1_L;
DISPLAY_A2_L;
KEY[6]=GET_KEY;//扫描第二个
DISPLAY_A0_L;
DISPLAY_A1_H;
DISPLAY_A2_L;
KEY[5]=GET_KEY;//扫描第三个
DISPLAY_A0_H;
DISPLAY_A1_H;
DISPLAY_A2_L;
KEY[4]=GET_KEY;//扫描第十二个
DISPLAY_E1_L;
DISPLAY_E2_L;
DISPLAY_E3_L;
DISPLAY_E4_L; //关闭所有位选开关
}
按键存放的顺序按照PCB布局来的,这个可以自己定义,为了省事,直接用的全局变量。。。GET_KEY是一个宏定义,读取连接到CPU上的key_1。
4.结语
程序只是自己工作之余写的,应该还有很多可以优化的地方。。。