前言 :这里我使用的是keil5.39(keil版本必须在5.38A以上),编译器使用的是6.21版本,安装教程官网上面有详细的介绍,这里就不过多的说明了,如果说在使用keil版本出现问题的话,还是建议大家学习一下ccs编译环境,真的很好用。
一:任务目标:
1:利用74HC595驱动单个数码管
2:利用按键实现数码管数据增加变化
3:利用定时器T0实现一个数字秒表
4:课后练习 :用定时器中断实现数码管数值的递减
时间范围(10 ~ 0)
递减步长1000ms
二:使用工具:
1.LP-MSPM0L1306开发板;
2.Mic USB线
3.74HC595数码管模块
4:杜邦线 5根
三:硬件介绍:
1:数码管介绍
数码管,也称作辉光管,是一种可以显示数字和其他信息的电子设备。玻璃管中包括一个金属丝网制成的阳极和多个阴极。大部分数码管阴极的形状为数字。
这里我们使用的是七段数码管外加DP引脚,可以显示0到9的数字和一些字母信息。 数码管的工作原理:
按发光二极管单元连接方式可分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管,共阳数码管在应用时应将公共极COM接到+5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮,当某一字段的阴极为高电平时,相应字段就不亮。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管,共阴数码管在应用时应将公共极COM接到地线GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮,当某一字段的阳极为低电平时,相应字段就不亮
数码管图片:
大家可以根据PPT去查找共阳和共阴极的编码方式,这里就不做说明了。
2:74HC595介绍
该芯片是一个8位串行输入、并行输出的位移缓存器。该缓存器在控制数码管中具有重要作用。
芯片引脚图:
引脚说明:
芯片的时序图:
四:硬件连接
74HC595模块的VCC和GND连接开发板的5V和GND引脚;
DI连接开发板的PA27,
SCLK连接开发板的PA26,
RCLK连接开发板的PA13.
五:软件开发过程:
为了开发方便,这里我是用CCS软件配置了一下使用到的IO口定义部分,配置图如下所示:以DIO口为例进行介绍
5.2 驱动步骤
1、首先将8位需要的传输的数据从引脚PA27(数据引脚)DI输入到芯片74HC595
2、将从DI上的数据串行移入移位寄存器,需要时钟驱动,即引脚11(SHCP)每产生一个上升沿,DI上的数据往移位寄存器送入一位,先送高位,后送低位,经过8个上升沿后,8bit全部送入移位寄存器了。
3、将移位寄存器里的数据送入存储寄存器,引脚12(STCP)产生一个上升沿后,该操作就完成了
4、引脚13(OE)为低电平,则步骤3送入存储寄存器的8bit数据(一个字节)就在Q7-Q0并行输出,并输出的数据会被所存起来。
注意:数据并行输出后,只要没有数据更新进来,原输出的数据保持不变,就是所谓的锁存(数据被锁存住)。在完成步骤123后,只要步骤4还没使能,输出都是保持不变的,当OE一使能,新的数据就输出覆盖旧输出。
我们的是8位共阳极数码管,原理图如下。两颗74HC595,一个是实现片选,一个输出8位并行数据。
5.3函数介绍:
//========================================================================
// 函数: void HC595_WriteData(uint8_t data)
// 描述: 向HC595芯片写入数据
// 参数:
// 返回: none.
// 版本: VER1.0
// 日期: 2024-5-8
// 备注:
//========================================================================
void HC595_WriteData(uint8_t data)
{
uint8_t i;
for(i = 0; i < 8; i++)
{
if((data & 0x80)>0)
{
HC595_DAT(1);
}
else
{
HC595_DAT(0);
}
HC595_CLK(0);
delay_cycles(32);
HC595_CLK(1);
delay_cycles(32);
data <<= 1;
}
}
函数解析:
作用:发送字节到74HC595芯片的缓冲器中。
分析:发送的过程很重要,需要搞清所使用的芯片是先低位发送还是先高位。这里的延时很重要,如果延时不准确的话,74HC595根本不能用。
这个芯片是先写入最高位,再依次发送到最低位。所以在该段函数中,我们通过该数据与(&)0x80,取出最高位,通过DIO口输入1/0,由于之前所提到的,唯有SCLK到达上升沿的时候,数据才能传入,于是我们在每次循环输入数据的时候,都将SCLK(PB13)拉高10us ,然后再将所要输入的byte左移一位,继续循环。
由于两片74HC595芯片的作用,当第一片74HC595芯片的缓冲区八位都存在数据的时候,此时DIO口继续输入数据,则将会将已有数据顶置到下一片74HC595的缓冲区。
因此所要显示数字的数据传入完成后,继续传入其所要求显示的数码管位置,该过程也同理于数字数据的传输过程。
//========================================================================
// 函数: void HC595_SEND_DATA(uint8_t Value, uint8_t BIT)
// 描述: 向HC595芯片写入数据和需要显示的位选
// 参数:
// 返回: none.
// 版本: VER1.0
// 日期: 2024-5-8
// 备注:
//========================================================================
void HC595_SEND_DATA(uint8_t Value, uint8_t BIT)
{
HC595_WriteData(Value);
HC595_WriteData(1<<BIT);
Display_Out();
}
作用:控制4位数码管的显示位置以及显示的数字
分析:该函数使用之前所定义和编写的void HC595_Send_Byte(unsigned char byte)函数继续工作,需要注意的是,当数据和位置全部输入后,需要拉高RCLK(PB12)的电平,使其完成显示
显示代码:
//========================================================================
// 函数: void Disp_Data(uint16_t DATA1, uint16_t DATA2)
// 描述: 数码管显示数字0-9
// 参数: DATA1:第一个数码管 数据 DATA2:第一个数码管 数据
// 返回: none.
// 版本: VER1.0
// 日期: 2024-5-8
// 备注:
//========================================================================
void Disp_Data(uint16_t DATA1, uint16_t DATA2)
{
uint16_t Temp_H,Temp_l;
uint8_t NUM_Q, NUM_B,NUM_S,NUM_G;
Temp_l = DATA2;
NUM_Q = Temp_l/1000;
NUM_B = Temp_l/100%10;
NUM_S = Temp_l/10%10;
NUM_G = Temp_l%10;
HC595_SEND_DATA(Disp_DX[NUM_Q]+0x80,3);
HC595_SEND_DATA(Disp_DX[NUM_B],2);
HC595_SEND_DATA(Disp_DX[NUM_S],1);
HC595_SEND_DATA(Disp_DX[NUM_G],0);
Temp_H = DATA1;
NUM_Q = Temp_H/1000;
NUM_B = Temp_H/100%10;
NUM_S = Temp_H/10%10;
NUM_G = Temp_H%10;
HC595_SEND_DATA(Disp_DX[13],7);
HC595_SEND_DATA(Disp_DX[12],6);
HC595_SEND_DATA(Disp_DX[11],5);
HC595_SEND_DATA(Disp_DX[10],4);
}
一次只能显示一个位置的数字,因此需要搭配主函数中的while(1){ }继续循环显示,利用人眼捕获的时间差,从而动态显示全部的数字。
//========================================================================
// 函数: void DispCheckLed(void)
// 描述: 程序开始时用于自检各个数码管的段选,判断数码管是否正常.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2023-07-01
//========================================================================
void DispCheckLed(void)
{
uint8_t i;
/*数码段测试*/
for(i=0;i<9;i++) //数码管自检,
{
HC595_SEND_DATA( LED_START[i],0);
delay_ms(200);
}
}
作用:主要为了检测使用个某个数码管上的段选是否正常。
主程序:
SYSCFG_DL_init();
NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
if(Function == 0)
{
DispCheckLed();
}
while (1)
{
/*该处代码为了检测最右面的数码管是否正常,自检完成后显示 数字 8*/
if(Function == 0)
{
HC595_SEND_DATA(0x00,0);
}
}
实现现象:上电之后右侧数码管自检,自检完成后将数码全部点亮,效果如下:
课程2-2: 利用按键实现数码管数据变化
主程序:
/*定义按下 和 抬起时按键的标志位 */
int KeyUp = 0 ;
int KeyDown= 0 ;
int work2 = 99 ;
KeyValue = DL_GPIO_readPins(Blinky_PORT,GPIO_LEDS_USER_KEY_01_PIN); // Read the state of key input
if(KeyValue == 0)
{
DL_GPIO_clearPins(Blinky_PORT, GPIO_LEDS_USER_Twinkle_LED_PIN); //PA0= 0;turn on led
KeyDown = 1 ;
KeyUp = 0 ;
}
else
{
DL_GPIO_setPins(Blinky_PORT, GPIO_LEDS_USER_Twinkle_LED_PIN); //PA0= 1;turn off led
KeyUp = 1 ;
}
Disp_Data_worK2(work2);
if(( KeyDown== 1) && (KeyUp == 1) )
{
KeyDown = 0 ;
work2 -- ;
}
说明:这里我定义了按键按下和抬起的两个状态位,防止在按下按键时,数据变化的太快,这样每次按下按键时,数据减少一个,试验效果如下:
课程2-3: 定时器实现数字秒表
M0L1306定时器介绍:
具有重复重新加载模式的 16 位或 32 位向上、向下或向上/向下计数器
用于对计数器时钟频率进行分频的 8 位可编程预分频器
最多两个独立通道,用于:
– 输出比较
– 输入捕捉
– PWM 输出(边沿对齐和中心对齐)
– 单次触发模式
定时器配置部分:
软件代码:
定时器初始化:
SYSCONFIG_WEAK void SYSCFG_DL_TIMER_0_init(void) {
DL_TimerG_setClockConfig(TIMER_0_INST,
(DL_TimerG_ClockConfig *) &gTIMER_0ClockConfig);
DL_TimerG_initTimerMode(TIMER_0_INST,
(DL_TimerG_TimerConfig *) &gTIMER_0TimerConfig);
DL_TimerG_enableInterrupt(TIMER_0_INST , DL_TIMERG_INTERRUPT_ZERO_EVENT);
DL_TimerG_enableClock(TIMER_0_INST);
}
定时器配置:10MS中断一次,并在中断函数里面对数据做处理:::
void TIMER_0_INST_IRQHandler(void)
{
switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST))
{
case DL_TIMER_IIDX_ZERO:
{
count++ ;
if(count >= 100)
{
count = 0 ;
Disp_num--;
if(Disp_num ==0)
{
Disp_num = 10 ;
}
}
}
break;
default:
break;
}
}
在主函数中,直接调用显示部分就可以:
Disp_Data(0000,Disp_num);
试验效果如下:
课后作业:
要求:
用定时器中断实现数码管数值的递减
时间范围(10 ~ 0)
递减步长1000ms
这里与2-3类似,唯一不同的是做减法,这里就不再重复介绍:
只需在定时器中断函数里面,写如下,然后再主函数中,显示变量 Disp_num 即可。
Disp_num--;
if(Disp_num ==0)
{
Disp_num = 10 ;
}
效果图如下::