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秒。遇到问题时要去询问同学或查找相关资料。闹钟设置时一个难点,进行设置时要与时钟的设置区分开,否则很容易两则产生矛盾。