stm32数码管显示实时时间并有闹钟功能
stm32数码管显示实时时间并有闹钟功能功能描述数码管介绍数码管芯片段选与位选驱动数码管显示stm32开发板介绍完整代码main.h功能描述数码管介绍数码管芯片段选与位选驱动数码管显示stm32开发板介绍完整代码main.h...
stm32数码管显示实时时间并有闹钟功能
功能描述
通过stm32开发板上面的按键来实现时钟的调节和闹钟调节并响铃
数码管介绍
2个74HC595来驱动8位共阳数码管
共阳数码管0-9编码表:0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90
数码管芯片
74HC595是串行输入,并行输出的锁存器
DS:14脚,串行数据输入引脚
OE:13脚,输出使能控制脚,它是低电才使能输出,所以接GND
ST_CP:12脚,存储寄存器时钟输入引脚。上升沿时,数据从移位寄存器转存带存储寄存器。
SH_CP:11脚,移位寄存器时钟引脚,上升沿时,移位寄存器中的数据整体后移,并接受新的数据(从DS输入)。
MR:10脚,低电平时,清空移位寄存器中已有的数据,一般不用,接高电平即可。
Q7’:9脚,串行数据输出引脚。当移位寄存器中的数据多于8位时,会把已有的位“挤出去”,就是从这里出去的。用于595的级联。
Q1-Q7:1到7脚,并行输出引脚
74HC595具体介绍
段选与位选
位选:选择哪一个数码段点亮
段选:选择点亮的数码管显示什么数字
进行位选和段选时,要先进行透明在进行锁存,这样才能进行显示。
驱动数码管显示代码
void Smg_Diaplay()
{
u8 i;
for(i=0;i<8;i++)
{
GPIOA->ODR=~(1<<i);//选择哪一个数码管亮
GPIO_SetBits(GPIOA,GPIO_Pin_8);//位透明模式
GPIO_ResetBits(GPIOA,GPIO_Pin_8);//段锁存模式
GPIOA->ODR=DisPlayData[i];//数据输入
GPIO_SetBits(GPIOA,GPIO_Pin_9);//位透明
GPIO_ResetBits(GPIOA,GPIO_Pin_9);//段锁存
}
}
实验思路
1、让数码管亮,能直接在代码中设置数字让其显示
2、让数码管的1、2,4、5,7、8位分别显示秒,分,时的数字;3、5位显示短线表示分割
3、设置定时器3,让其定时1秒,控制秒的改变,进而控制分和时
4、设置按键,控制分和时的增加,从而改变当前时间。此时应该停止定时器3的计数,方便进行调整
5、在设置一个定时器2定时1秒来控制闹钟的计数
6、设置按键,控制闹钟分和时的改变,此时应该让定时器2停止定时,定时器3继续计数
7、设置一个函数来计算设置的闹钟时间与当前时间的差值,让定时器进行计数,当计数值达到差值时,蜂鸣器响,否则不响
完整代码
Led.h
#ifndef __LED_H
#define __LED_H
void LED_Init(void);//初始化
#endif
Led.c
#include "led.h"
#include "stm32f10x.h"
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOE, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//LED0
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_5);//输出高
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;//LED1
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE,GPIO_Pin_5); //输出高
}
Key.h
#ifndef __KEY_H
#define __KEY_H
void Key_Init(void);
#endif
Key.c
#include "stm32f10x.h"
#include "key.h"
extern u8 sec,min,hour;
void Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0设置成输入,默认下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
beep.h
#ifndef __BEEP_H
#define __BEEP_H
void Beep_Init(void); //初始化
#endif
beep.c
#include "beep.h"
#include "stm32f10x.h"
void Beep_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_8);//关闭蜂鸣器输出
}
smg.h
#ifndef __SMG_H
#define __SMG_H
#include "stm32f10x.h"
//#define RCLK PBout(10)//时钟脉冲信号——上升沿有效
//#define SCLK PBout(11)//打入信号————上升沿有效
//#define DIO PAout(8)//串行数据输入
void Smg_Init(void);
void Smg_DisPlay(void);
void Display_Data(u8 hour,u8 min,u8 sec);// Smg显示
void Smg_OUT(u8 X);// Smg单字节串行移位函数
#endif
smg.c
#include "smg.h"
#include "stm32f10x.h"
#include "delay.h"
extern u8 sec,min,hour;
u16 smgduan[] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
//u16 smgduan[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
u8 DisPlayData[8];
void Smg_Init(void) //IO初始化
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB,ENABLE);//使能PORTA,PORTB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
void Smg_DisPlay(void)
{
u8 i;
for(i=0;i<8;i++)
{
Smg_OUT(DisPlayData[i]);
Smg_OUT(0x01<<i);
//RCLK
GPIO_SetBits(GPIOB,GPIO_Pin_10);//位透明模式
GPIO_ResetBits(GPIOB,GPIO_Pin_10);//锁存模式
delay_ms(1);
}
}
void Display_Data(u8 hour,u8 min,u8 sec)
{
DisPlayData[0] = smgduan[sec%10];//秒
DisPlayData[1] = smgduan[sec/10];
DisPlayData[2] = 0xbf;
DisPlayData[3] = smgduan[min%10];//分
DisPlayData[4] = smgduan[min/10];
DisPlayData[5] = 0xbf;
DisPlayData[6] = smgduan[hour%10];//时
DisPlayData[7] = smgduan[hour/10];
}
void Smg_OUT(u8 x)
{
u8 i;
for(i=8;i>=1;i--)
{
if(x&0x80)
GPIO_SetBits(GPIOA,GPIO_Pin_8);
else
GPIO_ResetBits(GPIOA,GPIO_Pin_8);
x<<=1;
//SCLK
GPIO_SetBits(GPIOB,GPIO_Pin_11);//段透明模式
GPIO_ResetBits(GPIOB,GPIO_Pin_11);
}
}
/*
void Showtime()//时间显示
{
u8 i;
delay_init();
for(i=0;i<8;i++)
{
GPIOA->ODR=~(1<<i);//选位
GPIO_SetBits(GPIOA,GPIO_Pin_8);//透明模式
GPIO_ResetBits(GPIOA,GPIO_Pin_8);//锁存模式
GPIOA->ODR=DisPlayData[i];
GPIO_SetBits(GPIOA,GPIO_Pin_9);//透明模式
GPIO_ResetBits(GPIOA,GPIO_Pin_9);
delay_ms(1);
}
}
*/
timer.h
#ifndef __TIMER_H
#define __TIMER_H
#include "stm32f10x.h"
void Timer3_Init(u16 arr,u16 pse);
void Timer2_Init(u16 arr,u16 pse);
void TimeZJ();
int CountTime(u8 h,u8 m);
#endif
timer.c
#include "timer.h"
#include "delay.h"
#include "stm32f10x.h"
#include "smg.h"
extern u8 sec,min,hour;
extern u16 count;
//定时1s 4999 7199
void Timer3_Init(u16 arr,u16 pse)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
TIM_TimeBaseInitStruct.TIM_Period = arr;
TIM_TimeBaseInitStruct.TIM_Prescaler = pse;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority =2;
NVIC_Init(&NVIC_InitStruct);
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM3,ENABLE);
}
void Timer2_Init(u16 arr,u16 pse)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_TimeBaseInitStruct.TIM_Period = arr;
TIM_TimeBaseInitStruct.TIM_Prescaler = pse;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStruct);
NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority =2;
NVIC_Init(&NVIC_InitStruct);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
TIM_Cmd(TIM2,DISABLE);
}
//时钟定时
void TIM3_IRQHandler()
{
sec++;
TimeZJ();
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
}
//闹钟定时
void TIM2_IRQHandler()
{
count++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
void TimeZJ()
{
if(sec >= 60)
{
sec=0;
min++;
if(min >= 60)
{
min=0;
hour++;
if(hour >= 24)
{
hour = 0;
}
}
}
}
int CountTime(u8 h,u8 m)
{
if(h > hour)
return (h - hour - 1) * 3600 + (60 - min + m) * 60;
else if(h < hour)
return (23 - hour + h) * 3600 + (60 - min + m) * 60;
else
if(m >= min)
return (m - min) * 60;
else
return (23 - hour + h) * 3600 + (60 - min + m) * 60;
}
main.c
#include "stm32f10x.h"
#include "Key.h"
#include "Led.h"
#include "delay.h"
#include "smg.h"
#include "timer.h"
#include "delay.h"
#include "beep.h"
u8 sec,min,hour;//时钟
u8 h,m,s;//闹钟
u8 setflag;//时间设置
u8 a = 0;
u16 count;
u8 countflag;//闹钟设置
u8 flag;//响铃
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Timer3_Init(9999,7199);
Timer2_Init(9999,7199);
LED_Init();
Key_Init();
delay_init();
Smg_Init();
Beep_Init();
hour = 12;
min = 14;
sec = 15;
h = 0;
m = 0;
s = 0;
while(1)
{
//实时时间显示
Smg_DisPlay();
Display_Data(hour,min,sec);
//时间设置
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 1)//WK_UP
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 1)
{
while((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)));
setflag = 1;
TIM_Cmd(TIM3,DISABLE);
}
}
while(setflag)
{
GPIO_ResetBits(GPIOB,GPIO_Pin_5);//亮 LED0
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 1)
{
//退出
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 1)
{
//消抖
while((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)));
setflag = 0;
TIM_Cmd(TIM3,ENABLE);
GPIO_SetBits(GPIOB,GPIO_Pin_5);//灭
break;
}
}
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0)//KEY1 hour
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)));
hour++;
if(hour == 24)
hour = 0;
}
}
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) == 0)//KEY0 min
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) == 0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)));
min++;
if(min == 60)
min = 0;
}
}
Smg_DisPlay();
Display_Data(hour,min,sec);
}
//闹钟设置
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) == 0)//KEY0
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) == 0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)));
countflag = 1;//进入设置闹钟模式
if(a == 0)
{
a = 1;
TIM_Cmd(TIM2,DISABLE);
}
else
{
a = 0;
countflag = 0;//退出定时模式
TIM_Cmd(TIM2,ENABLE);//开启定时器
count = 0;
}
}
}
while(countflag)
{
GPIO_ResetBits(GPIOE,GPIO_Pin_5);//亮 LED1
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) == 0)//KEY0
{
//退出设置闹钟模式 进入时钟运行模式
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4) == 0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_4)));
countflag = 0;
flag = 1;
TIM_Cmd(TIM2,ENABLE);//开启定时器2定时
GPIO_SetBits(GPIOE,GPIO_Pin_5);//灭
break;
}
}
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0)//KEY1 h
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)));
m++;
if(m == 60)
m = 0;
}
}
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 1)//WK_UP m
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) == 1)
{
while((GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)));
h++;
if(h == 24)
h = 0;
}
}
Smg_DisPlay();
Display_Data(h,m,s);
}
//响铃
while(flag)
{
Smg_DisPlay();
Display_Data(hour,min,sec);
if(count == CountTime(h,m))
{
GPIO_SetBits(GPIOB,GPIO_Pin_8);//开
GPIO_ResetBits(GPIOB,GPIO_Pin_5);//LED0 亮
delay_ms(100);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0)//KEY1
{
delay_ms(10);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3) == 0)
{
while(!(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_3)));
flag = 0;
GPIO_ResetBits(GPIOB,GPIO_Pin_8);//关
GPIO_SetBits(GPIOB,GPIO_Pin_5);//灭 LED0
TIM_Cmd(TIM2,DISABLE);
break;
}
}
}
}
}
return 0;
}
实验难点
1、在进行闹钟设置时,会与当前的时钟产生矛盾,设置闹钟会改变时钟;
2、如何让闹钟的时间与当前时间进行比较;
3、当闹钟响铃时,按下按键还会响不会停或者闹钟响铃关闭时钟时间回到初始状态。
解决方法
1、在进行闹钟设置时在使用一个定时器来专门控制闹钟;一个定时器控制时钟。
2、使用一个函数来计数当前时间到闹钟的时间一共有多少秒,用一个定时器来进行计数并于这个时间进行比较,当两则相等时则表示闹钟一个响铃。在遇到闹钟问题时去询问了同学。
3、闹钟响铃时与时钟设置和闹钟设置时一样放入一个while()函数,但是在进入此函数和退出此函数是要进行变量设置,而且闹钟响铃后关闭闹钟的定时器。
总结
在进行时钟定时时,定时器的预分频系数和重装载值要设置正确,要为1秒。遇到问题时要去询问同学或查找相关资料。闹钟设置时一个难点,进行设置时要与时钟的设置区分开,否则很容易两则产生矛盾。
源码获取
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)