stm32数码管显示实时时间并有闹钟功能

stm32数码管显示实时时间并有闹钟功能

功能描述

通过stm32开发板上面的按键来实现时钟的调节和闹钟调节并响铃

数码管介绍

2个74HC595来驱动8位共阳数码管
共阳数码管0-9编码表:0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90

数码管芯片

74HC595是串行输入,并行输出的锁存器stm32数码管显示实时时间并有闹钟功能
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秒。遇到问题时要去询问同学或查找相关资料。闹钟设置时一个难点,进行设置时要与时钟的设置区分开,否则很容易两则产生矛盾。

上一篇:stm32-手把手妈宝级-三种跑马灯写法


下一篇:2021-05-28_蓝桥杯嵌入式拓展板STM32G431--数码管