写在前面,这是第一次写文章,用来记录做过的一些小东西!代码写得很烂,大佬勿喷嘿嘿嘿
一、定时器功能介绍
1.一个2位LED数码显示秒表,显示时间为00-99秒,每秒自动加一;
2.设计开始键、暂停键和复位键,在暂停的情况下也能复位。
二、硬件电路
1.主控芯片
因为功能比较简单不需要很多资源,所以在本次设计中采用的是STM32C8T6这款芯片。
2.数码管驱动电路
在本次设计中利用两个一位共阳数码管来显示数字0-9。通过IO输出高低电平来控制不同段,从而显示不同数字。
3.按键电路
利用三个按键,分别实现“开始”,“暂停”,“复位”功能,比较简单。
三、软件设计
1.数码管驱动代码
因为动态显示有点问题,所以最终选择了静态显示,既分别控制两个一位的共阳数码管。采用switch-case语句将数字0-9十种情况封装起来,然后利用for语句进行依次显示。(数码管的接线就不写了,接的很乱。可以根据自己的接线情况,修改switch-case里面的代码即可)
下面函数主要用来控制数码管的公共端(其实也可以不用)
#include "stm32f10x.h" // Device header
void LED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_6|GPIO_Pin_10);
}
数码管显示代码如下:
#include "stm32f10x.h" // Device header
#include "LED.h"
#include "Timer.h"
u8 j=0;
u8 k=0;
// SM1_Display()控制十位
void SM1_Display(u8 i)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
switch(i)
{
case 0:
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_3);
break;
case 1:
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5);
break;
case 2:
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5);
GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_7);
break;
case 3:
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_3|GPIO_Pin_5|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_4);
break;
case 4:
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5);
break;
case 5:
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_5|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_4);
break;
case 6:
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_0);
break;
case 7:
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_0|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5);
break;
case 8:
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_7);
break;
case 9:
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_5|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
break;
}
}
// SM2_Display()控制个位
void SM2_Display(u8 i)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
switch(i)
{
case 0:
GPIO_ResetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
GPIO_ResetBits(GPIOA,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11);
GPIO_SetBits(GPIOA,GPIO_Pin_12);
break;
case 1:
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_11);
GPIO_SetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_7);
GPIO_SetBits(GPIOA,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_12|GPIO_Pin_15);
break;
case 2:
GPIO_ResetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_12|GPIO_Pin_8|GPIO_Pin_9);
GPIO_SetBits(GPIOA,GPIO_Pin_11);
GPIO_SetBits(GPIOB,GPIO_Pin_7);
break;
case 3:
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_9|GPIO_Pin_11|GPIO_Pin_12);
GPIO_SetBits(GPIOA,GPIO_Pin_8);
GPIO_SetBits(GPIOB,GPIO_Pin_7);
break;
case 4:
GPIO_ResetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);
GPIO_ResetBits(GPIOA,GPIO_Pin_11|GPIO_Pin_12);
GPIO_SetBits(GPIOB,GPIO_Pin_5);
GPIO_SetBits(GPIOA,GPIO_Pin_8|GPIO_Pin_9);
break;
case 5:
GPIO_ResetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_7);
GPIO_ResetBits(GPIOA,GPIO_Pin_9|GPIO_Pin_11|GPIO_Pin_12);
GPIO_SetBits(GPIOB,GPIO_Pin_6);
GPIO_SetBits(GPIOA,GPIO_Pin_8);
break;
case 6:
GPIO_ResetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_7);
GPIO_ResetBits(GPIOA,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11|GPIO_Pin_12);
GPIO_SetBits(GPIOB,GPIO_Pin_6);
break;
case 7:
GPIO_ResetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_11);
GPIO_SetBits(GPIOA,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_12);
GPIO_SetBits(GPIOB,GPIO_Pin_7);
break;
case 8:
GPIO_ResetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11|GPIO_Pin_12);
GPIO_ResetBits(GPIOB,GPIO_Pin_7);
break;
case 9:
GPIO_ResetBits(GPIOB,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);
GPIO_ResetBits(GPIOA,GPIO_Pin_9|GPIO_Pin_11|GPIO_Pin_12);
GPIO_SetBits(GPIOA,GPIO_Pin_8);
break;
}
}
2.按键检测代码
按键配置代码如下:
//Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14 | GPIO_Pin_13| GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
3.外部中断代码
这一部分主要是用来实现“开始”,“暂停”功能。通过读取按键的状态判断是否按下,当按键按下时,进入中断函数,执行相应功能。
中断配置代码如下:
//EXTI.c
//Start_Init函数用来配置开始按键对应的中断
void Start_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line14;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}
//Stop_Init函数用来配置暂停按键对应的外部中断
void Stop_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource13);
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line13;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
}
当开始按键/暂停按键按下时,进入中断函数。中断函数代码如下:
void EXTI15_10_IRQHandler(void)
{
Key_Init();
//判断中断触发路线是EXTI_Line13还是EXTI_Line14,即判断是开始按键按下还是暂停按键按下
if (EXTI_GetITStatus(EXTI_Line14) == SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0)
{
按键消抖
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_14) == 0);
Delay_ms(20);
//打开定时器,开始计时
Timer_Init();
}
//清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line14);
}
if (EXTI_GetITStatus(EXTI_Line13) == SET)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 0)
{
//按键消抖
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13) == 0);
Delay_ms(20);
//关闭定时器,暂停计时
Timer_Stop();
}
//清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line13);
}
}
4.定时器中断代码
这一部分主要是用来实现计时,既每秒刷新一次数码管的数字1,2,3......99,从而实现计时。为了实现这一功能,利用STM32定时器定时中断的功能。STM32的定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。本项目中设定值为1s,既每1s进入中断函数一次,执行刷新数码管的函数,从而实现计时的功能。实现该功能定时器部分代码如下:
//Timer.c
#include "stm32f10x.h" // Device header
//Timer_Init函数的作用是配置定时器,当开始按键按下时,定时器打开,计时开始。
//1.利用库函数,配置定时器
void Timer_Init(void)
{
//step.1开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//step.2选择时基单元的时钟
TIM_InternalClockConfig(TIM2);
//step.3配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
//计数模式
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
//根据公式“定时频率=72M/(PSC+1)/(ARR+1)”,改变这两个值可实现不同时间的计时
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
//使能中断
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//2.利用库函数,配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
//3.启动定时器
TIM_Cmd(TIM2, ENABLE);
}
//Timer_Stop函数的作用是关闭时钟,当暂停按键按下时,定时器关闭,计时停止。
void Timer_Stop(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000- 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, DISABLE);
}
中断函数程序如下:
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
// SM2_Display()函数控制个位
SM2_Display(j);
j=j+1;
//SM1_Display()函数控制十位
SM1_Display(k);
//判断是否需要进位
if(j==10)
{
j=0;
k=k+1;
//判断是否计到99秒
if(k==10&j==0)
{
LED_Init();
Timer_Stop();
GPIO_SetBits(GPIOB,GPIO_Pin_8);
}
}
}
//清除中断标志位
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
5.主函数
主函数的作用主要是初始化各个模块,以及实现复位按键功能。代码如下:
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Key.h"
#include "SM.h"
#include "LED.h"
#include "EXTI.h"
#include "Timer.h"
extern u8 j;
extern u8 k;
int main(void)
{
//初始化
Start_Init();
Stop_Init();
LED_Init();
Key_Init();
SM1_Display(0);
SM2_Display(0);
//复位功能
while(1)
{
if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0)
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == 0);
Delay_ms(20);
j=0;
k=0;
SM1_Display(j);
SM2_Display(k);
Timer_Stop();
}
}
}
四、实物
五、总结
虽然这个计时器很简单,实现起来不是很复杂,但是通过做这个对中断和定时器真得有了更深的理解。
PS:定时器和中断部分的代码可以看江科大的教程,非常详细!
有需要完整代码的自取(数码管的驱动可以根据自己的接线情况,把switch语句修改一下即可):
http://链接:https://pan.baidu.com/s/1ChlnnRm-CGGoKuXaeLL9AQ 提取码:86ry