4.1.数码管显示原理
(来源:CSDN,原文链接:https://blog.csdn.net/qq_42189951/article/details/133347707)
数码管的显示原理是由多个发光的二极管共阴极或者共阳极组成的成“8”字形的显示器件。数码管通过不同的组合可用来显示数字0~9、字符A ~ F及小数点“.”。数码管的工作原理是通过控制外部的I/O端口进行驱动数码管的各个段码,使用不同的段码从而形成字符显示出我们要的数字。数码管实际上是由七个发光管组成8字形构成的,加上小数点就是8个。这些段分别由字母A、B、C、D、E、F、G、DP来表示。
当数码管特定的引脚加上高电平后,这些特定的发光二极管就会发亮,以形成我们眼睛看到的字样了。如:在一个共阴极数码管上显示一个“8”字,那么就对A、B、C、D、E、F、G对应的引脚置高电平。发光二极管的阳极共同连接至电源的正极称为共阳极数码管,这种类型的数码管点亮需要对引脚置低电平;发光二极管的阴极共同连接到电源的负极称为共阴极数码管,点亮共阴极数码管需要对相应的引脚置高电平。常用LED数码管显示的数字和字符是0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F。
图4-1 数码管模型图
共阳极数码管的8个发光二极管的阳极(二极管正端)连接在一起。通常,公共阳极接高电平(一般接电源),其它管脚接段驱动电路输出端。当某段驱动电路的输出端为低电平时,则该端所连接的字段导通并点亮。根据发光字段的不同组合可显示出各种数字或字符。此时,要求段驱动电路能吸收额定的段导通电流,还需根据外接电源及额定段导通电流来确定相应的限流电阻。
共阴极数码管的8个发光二极管的阴极(二极管负端)连接在一起。通常,公共阴极接低电平(一般接地),其它管脚接段驱动电路输出端。当某段驱动电路的输出端为高电平时,则该端所连接的字段导通并点亮,根据发光字段的不同组合可显示出各种数字或字符。此时,要求段驱动电路能提供额定的段导通电流,还需根据外接电源及额定段导通电流来确定相应的限流电阻。
4.2.数码管原理图与实物图
如果数码管可以显示多位数字,如我们的电压电流表所示。那么除了控制段码来选择要显示的内容,还要选择位码来控制某一个数码管的亮灭。
图4-2 电压电流表三位数码管
数码管的原理图如下,可以看出除了上述的段码引脚之外,还有COM1、COM2、COM3的位码引脚,三个位码引脚分别控制三个数码管的亮灭情况,且低电平有效。
图4-3 三位数码管原理图
4.3.数码管驱动显示
驱动显示数码管的思路是:先将A、B、C、D、E、F、G所代表的引脚从低到高编号,列出数码要显示数字的段码值。比如要显示数字5,则段码值为0x6d,二进制表示为01101101,这说明G置1,F置1,E置0,D置1,C置1,B置0,A置1,最高位则是DP的值。将要显示的数字以段码值的方式储存在数组里以供调用,可以简化程序。接着以循环的方式结合switch语句对A、B、C、D、E、F、G的亮灭情况进行单独计算,先将段码值确定后再进行位码的选择,可以避免因单片机执行程序的时间而造成显示效果的不足。
具体程序如下,将所有与数码管显示相关的函数保存在新建的 Seg_Reg.c 文件
uint8_t Seg_Table[20] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f,
0xbf, 0x86, 0xdb, 0xcf, 0xe6, 0xed, 0xfd, 0x87, 0xff, 0xef};
void Seg_Init(void)
{
__RCC_GPIOA_CLK_ENABLE();
__RCC_GPIOB_CLK_ENABLE();
__RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pins = GPIO_PIN_0 | GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.IT = GPIO_IT_NONE;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pins = GPIO_PIN_6 | GPIO_PIN_4 | GPIO_PIN_2 | GPIO_PIN_0 | GPIO_PIN_3 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.IT = GPIO_IT_NONE;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOB, &GPIO_InitStruct);
GPIO_InitStruct.Pins = GPIO_PIN_4 | GPIO_PIN_3 | GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.IT = GPIO_IT_NONE;
GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
GPIO_Init(CW_GPIOC, &GPIO_InitStruct);
}
void Seg_Dis(uint8_t Pos,uint8_t Num)
{
int i;
uint8_t Dis_Value,Location;
Location = Pos;
Dis_Value = Seg_Table[Num];
for(i = 0; i < 8; i++)
{
switch(i)
{
case 0:
GPIO_WritePin(CW_GPIOC,GPIO_PIN_4,(Dis_Value >> i) & 0x01);
break;
case 1:
GPIO_WritePin(CW_GPIOB,GPIO_PIN_6,(Dis_Value >> i) & 0x01);
break;
case 2:
GPIO_WritePin(CW_GPIOB,GPIO_PIN_4,(Dis_Value >> i) & 0x01);
break;
case 3:
GPIO_WritePin(CW_GPIOB,GPIO_PIN_2,(Dis_Value >> i) & 0x01);
break;
case 4:
GPIO_WritePin(CW_GPIOA,GPIO_PIN_0,(Dis_Value >> i) & 0x01);
break;
case 5:
GPIO_WritePin(CW_GPIOB,GPIO_PIN_0,(Dis_Value >> i) & 0x01);
break;
case 6:
GPIO_WritePin(CW_GPIOA,GPIO_PIN_4,(Dis_Value >> i) & 0x01);
break;
case 7:
GPIO_WritePin(CW_GPIOB,GPIO_PIN_3,(Dis_Value >> i) & 0x01);
break;
default:
break;
}
}
switch(Location)
{
case 0:
GPIO_WritePin(CW_GPIOB,GPIO_PIN_7,GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOC,GPIO_PIN_3,GPIO_Pin_SET);
GPIO_WritePin(CW_GPIOC,GPIO_PIN_2,GPIO_Pin_SET);
break;
case 1:
GPIO_WritePin(CW_GPIOB,GPIO_PIN_7,GPIO_Pin_SET);
GPIO_WritePin(CW_GPIOC,GPIO_PIN_3,GPIO_Pin_RESET);
GPIO_WritePin(CW_GPIOC,GPIO_PIN_2,GPIO_Pin_SET);
break;
case 2:
GPIO_WritePin(CW_GPIOB,GPIO_PIN_7,GPIO_Pin_SET);
GPIO_WritePin(CW_GPIOC,GPIO_PIN_3,GPIO_Pin_SET);
GPIO_WritePin(CW_GPIOC,GPIO_PIN_2,GPIO_Pin_RESET);
break;
default:
break;
}
}
在主函数里调用Seg_Dis函数即可在对应位置显示相应数字(别忘了初始化),各位学员熟练之后可以通过define定义每个引脚的写入,使代码更加简洁美观。