在进行课程2之前,先补一补数码管相关的知识。数码管,可以理解成摆成数字8形状的几个发光二极管组成的器件,在显示原理上,其实就是点亮对应的笔段的LED,实现不同数字的表示。点亮LED,需要根据LED的压降、电源电压,计算出需要的限流电阻的大小。切记,不能像点白炽灯那样直接把电压是加到LED上,很容易瞬间烧毁LED。对数码管也是一样的道理。根据驱动方式的不同,数码管有两种模式,共阴和共阳。共阴模式,表示所有笔段共用阴极,点亮不同笔段时,需要控制输出高电平给各个笔段的阳极;共阳模式,表示所有笔段共用阳极,点亮时不同笔段时,需要控制输出低电平给各个笔段的阴极;
为了方便驱动数码管,人们做出了对应的驱动集成块,典型的如4511,74HC595,前者是CMOS电路,输出能力有限,驱动数码管时需要加功率器件作为缓冲。而74HC595是高速硅栅CMOS器件和引脚兼容低功耗肖特基TTL(LSTTL),是8级串行移位寄存器与存储寄存器和三态输出。在SCK的上升沿,串行数据由SDL输入到内部的8位位移缓存器,并行输出则是在LCK的上升沿将在8位位移缓存器的数据存入到8位并行输出缓存器。当串行数据输入端OE的控制信号为低使能时,并行输出端的输出值等于并行输出缓存器所存储的值。
74HC595的引脚说明:
74HC595的处理逻辑真值表:
74HC595的工作方式:
第一步:目的:将要准备输入的位数据移入74HC595数据输入端上。
方法:送位数据到_595。
第二步:目的:将位数据逐位移入74HC595,即数据串入
方法:SH_CP产生一上升沿,将DS上的数据移入74HC595移位寄存器中,先送低位,后送高位。
第三步:目的:并行输出数据。即数据并出
方法:ST_CP产生一上升沿,将由DS上已移入数据寄存器中的数据送入到输出锁存器。
说明: 从上可分析:从SH_CP产生一上升沿(移入数据)和ST_CP产生一上升沿(输出数据)是二个独立过程,实际应用时互不干扰。即可输出数据的 同时移入数据。
用74HC595点亮数码管,只需要将合适的数据输出给75HC595,开放输出,完成对数码管各个笔段的控制,实现自己想要的数字显示效果。
活动中配套的数码管显示模块,使用了两组数码管模块和两个74HC595。数码管模块型号为3461BS1-7.3
原理图为
由原理图看,这是共阳数码管,显示数字0-9的段位码为:
0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90
笔段输出高电平,笔段不亮;笔段输出低电平,笔段被点亮。
配套的数码管显示模组的原理图:
这个显示模组的构思很巧妙,一用一个74HC595的输出控制笔段,另一个74HC595控制数码管的位,只要配合好船型数据,就可以动态显示8个数字。而且模组的构成上,是可以做很多级联的。由于所有数码管共用A~G、小数点这个八个字段,因此一次只能显示一种字符。理论上可以同时点亮所有数码管为同一个字符
这个模块需要提供三个信号:
1、SCLK
这个是串行时钟信号,在时钟的上升沿,把串行数据送入移位寄存器。
2、RCLK
这个是锁存信号,在时钟的上升沿,将数据从移位寄存器存入存储寄存器,呈现到74HC595的Qn输出引脚上。
3、DIO
这个是串行数据输入端
这三个信号都是输入信号,对于开发板而言就是输出信号,刚好可以利用上次试验使用到的那三个驱动LED的信号,也就是PA13,PA26,PA27三个GPIO口。程序的处理逻辑如下:
实际上就是循环显示10 到0 的过程。由于8个笔段对所有数码管是共用的,所以显示10时,不能同时在两个数码管上显示,只能通过动态扫描方式显示。
主程序代码如下:
#include "ti_msp_dl_config.h" void delay(uint32_t cnt); void DispNum(unsigned char num, unsigned char pos); /* This results in approximately 0.5s of delay assuming 32MHz CPU_CLK */ #define DELAY (16000000) // 500ms #define ms50 (1600000) // 50ms #define s1 (32000000) // 1s #define ms1 (320) // 0.01ms模式延迟 // 16进制数字显示信史段码(0-9,A-F),段码位=1时,数码管的对应的笔段不会被点亮 unsigned char TAB_SEG[] = { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90, 0x77, 0x83, 0xC6, 0xA1, 0x86, 0x8E, 0xFF, 0xBF }; // 数码管模块的显示位置,自左向右 unsigned char TAB_POS[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; // 发送数据给数码管模块用的串行时钟,时钟上升沿有效 #define SCLK_0 DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN) #define SCLK_1 DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN) // 使数码管唯一寄存器数据被锁存到存储寄存器用的锁存信号,上升沿有效 #define RCLK_0 DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN) #define RCLK_1 DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN) // 发送数据给数码管模块用的串行数据位 #define DIO_0 {DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_3_PIN);} #define DIO_1 {DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_3_PIN);} void delay(uint32_t cnt) { uint32_t d=cnt; while(d--); } /** * 显示一位数码管数据 * chr : 显示的数值字符(‘0’-‘9’,‘A’-‘F’) * pos :显示的位置(最左侧开始:0-7, 对应的位置数据:0x80, 0x40,0x20,0x10,0x08,0x04,0x02,0x01) * 暂时不考虑小数点的处理。数字后面有小数点的场合,相当于该数字的小数点笔段位清0 */ void DispNum(unsigned char chr, unsigned char pos) { unsigned char i, num; // 根据字符,获取对应的段位码数据 if (chr>=0 && chr<16) { num=TAB_SEG[chr]; } else if (chr>=' ') { // 空格,所有笔段都不显示 num = 0xFF; } else if (chr>='-') { num = 0xBF; } else { return; } // 将段位码数据以串行方式发送给数码管显示模块 // 每个字符有八个笔段,小数点通常不用 for(i=0;i<8;i++) { // 输出笔段对应的数据位 if ((num & 0x80)>0) { // 笔段数据为1 的,DIO对应GPIO口输出高电平 DIO_1; } else { // 笔段数据为0 的,DIO对应GPIO口输出低高电平 DIO_0; } // 下一个笔段 num<<=1; // 发出移位用串行时钟上升沿脉冲 SCLK_0; delay_cycles(ms1); SCLK_1; delay_cycles(ms1); } // 发送完笔段数据,发送显示位置数据 num = TAB_POS[pos%8]; for(i=0;i<8;i++) { // 输出对应的数据位 if ((num & 0x80)>0) { DIO_1; } else { DIO_0; } // 下一个数码管位置 num<<=1; // 发出移位用串行时钟上升沿脉冲 SCLK_0; delay_cycles(ms1); SCLK_1; delay_cycles(ms1); } // 发出锁存信号,点亮数码管 RCLK_0; delay_cycles(ms1); RCLK_1; delay_cycles(ms1); } int main(void) { uint32_t i=0, j=0; /* Power on GPIO, initialize pins as digital outputs */ SYSCFG_DL_init(); /* Default: LED1 and LED3 ON, LED2 OFF */ DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_2_PIN); DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_LED_3_PIN | GPIO_LEDS_USER_TEST_PIN); DL_GPIO_clearPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN | GPIO_LEDS_USER_LED_3_PIN | GPIO_LEDS_USER_TEST_PIN); while (1) { // 循环显示10递减到0 的过程 for (i=0; i<11; i++) { if (i==0) { // 因为笔段共用,10的显示不同于其他数字,只能通过动态扫描方式显示 for (j=0; j<1400; j++) { DispNum(1, 6); DispNum(0, 7); } } else { // 显示字符9-0 DispNum(10-i, 7); // 保持显示时长为1秒 delay_cycles(s1); } } } }
运行效果