STM32控制数码管实现秒表计数
旨在通过STM32来实现控制四位共阳极的数码管实现秒表计数的效果,并使用按键进行控制进行控制暂停,中断,清0的功能。
主要的硬件
STM32F103C8T6最小系统板,3641BS四位共阳极的数码管(其余的原理差不多),按键三个,以及杜邦线若干
设计思路
因为在51单片机中实现对数码管进行点亮主要就是通过进行位选中以及段选中之后进行控制数码管的点亮。同理:使用F103的最小系统板进行直接使用IO口的输出高低电平来实现控制数码管的高低电平实现点亮的作用.然后通过按键来实现控制定时器的开始与暂停的操作。
四位共阳极数码管
下面是我去淘宝网上找的驱动原理的图
驱动原理:在这个图中是可以发现除了共阳级的部分,其余的引脚都是公用的,也就是在单元(多一个小数点显示);按发光二极管单元连接方式分为共阳极数码管和共阴极数码管。共阳数码管是指将所有发光二极管的阳极接到一起形成公共阳极(COM)的数码管。共阳数码管在应用时应将公共极COM接到+5V,当某一字段发光二极管的阴极为低电平时,相应字段就点亮。当某一字段的阴极为高电平时,相应字段就不亮。共阴数码管是指将所有发光二极管的阴极接到一起形成公共阴极(COM)的数码管。共阴数码管在应用时应将公共极COM接到地线GND上,当某一字段发光二极管的阳极为高电平时,相应字段就点亮。当某一字段的阳极为低电平时,相应字段就不亮。
我们此时就是实现GPIO口输出低电平去驱动他,来实现亮灭的转换
数码管和按键的初始化
使用的是数码管的PA口来控制0-7来控制数码管的段显示,使用的是8-11来控制数码管的位选。具体怎么连线就是根据驱动原理的图的第几个引脚1,2,3,4号的控制数码管的来实现与8-11号引脚链接,其余的段选引脚分别与0-7号链接。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3 |GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6 |GPIO_Pin_7 |GPIO_Pin_8 | GPIO_Pin_9 |GPIO_Pin_10 |GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_SetBits(GPIOA,GPIO_Pin_0 |GPIO_Pin_1 |GPIO_Pin_2 |GPIO_Pin_3 |GPIO_Pin_4 |GPIO_Pin_5 |GPIO_Pin_6 |GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9 |GPIO_Pin_10 |GPIO_Pin_11 |GPIO_Pin_12);
//按键初始化以及获取按键值
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;//按键需要选择上拉输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1|GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&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(GPIOC,&GPIO_InitStructure);
GPIO_SetBits(GPIOB,ENABLE);
GPIO_SetBits(GPIOC,ENABLE);
}
/*
这个定义方式就是unsigned char 的意思,但是在单片机中把char当作了数字输入
所以也可以改称这种写法
*/
uint8_t Key_GetNum(void)
{
uint8_t KeyNum=0;
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)==0);
Delay_ms(20);
KeyNum=1;
}
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_14)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_14)==0);
Delay_ms(20);
KeyNum=14;
}
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)==0)
{
Delay_ms(20);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_10)==0);
Delay_ms(20);
KeyNum=10;
}
return KeyNum;
}
数码管的驱动函数部分
void DisPlayNum(int n)
{
if (n < 9999)
{
unsigned int i;
unsigned int s;
s = n;
i=0;
/*
ÒòΪÊǹ²Ñô¼«ËùÒÔÖ±½ÓµÍµçƽ¾ÍÈ«²¿³õʼ»¯ÁË
²ÉÓõÄÊÇÑ»·É¨ÃèµÄ·½Ê½½øÐÐ
*/
for (i=0;i<4;i++)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_All);
switch (i)
{
case 0:
GPIO_SetBits(GPIOA,GPIO_Pin_11 );
break;
case 1:
GPIO_SetBits(GPIOA,GPIO_Pin_10);
break;
case 2:
GPIO_SetBits(GPIOA,GPIO_Pin_9);
break;
case 3:
GPIO_SetBits(GPIOA,GPIO_Pin_8);
break;
}
//GPIO_SetBits(GPIOA,GPIO_Pin_6|GPIO_Pin_5|GPIO_Pin_4|GPIO_Pin_3|GPIO_Pin_2|GPIO_Pin_1|GPIO_Pin_0);
switch (s % 10)
{
case 0:
GPIO_SetBits(GPIOA,GPIO_Pin_7|GPIO_Pin_6);
break;
case 1:
GPIO_SetBits(GPIOA,GPIO_Pin_7|GPIO_Pin_6|GPIO_Pin_5|GPIO_Pin_4|GPIO_Pin_3|GPIO_Pin_0);
break;
case 2:
GPIO_SetBits(GPIOA,GPIO_Pin_7|GPIO_Pin_5|GPIO_Pin_2);
break;
case 3:
GPIO_SetBits(GPIOA,GPIO_Pin_7|GPIO_Pin_5|GPIO_Pin_4);
break;
case 4:
GPIO_SetBits(GPIOA,GPIO_Pin_7|GPIO_Pin_4|GPIO_Pin_3|GPIO_Pin_0);
break;
case 5:
GPIO_SetBits(GPIOA,GPIO_Pin_7|GPIO_Pin_4|GPIO_Pin_1);
break;
case 6:
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_7);
break;
case 7:
GPIO_SetBits(GPIOA,GPIO_Pin_7|GPIO_Pin_6|GPIO_Pin_5|GPIO_Pin_4|GPIO_Pin_3);
break;
case 8:
GPIO_SetBits(GPIOA,GPIO_Pin_7);
break;
case 9:
GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_7);
break;
}
s = s / 10;
Delay_ms(1);
if (s==0)
break;
}
}
}
定时器
由于是是要实现秒表计时的效果,所以就是使用定时器来实现秒表的计时自增功能
定时器的初始化部分
void Timer_Init(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=1000-1;
TIM_TimeBaseInitStructure.TIM_Prescaler=720-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,ENABLE);//开启定时器2
}
定时器的中断部分
/*´每100US进一次中断实现自增*/
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
{
if(++TimeCheck>100)
{
TimeCheck=0;
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
主函数部分
由于项目内容较少,所以直接把关于按键处理函数写在了主函数中的WHILe循环中。
int main(void)
{
unsigned char mode=0;
Seg_Init();
Key_Init();
Timer_Init();
while(1)
{
key_num=Key_GetNum();//¶ÁÈ¡°´¼ü
switch(key_num)
{
case 1://¿ªÊ¼°´¼ü
TIM_Cmd(TIM2, ENABLE);
mode=1;//¿ªÊ¼
break;
case 10://ÔÝÍ£°´¼ü
TIM_Cmd(TIM2, DISABLE);
mode=2;//ÔÝÍ£
break;
case 14://Çå³ý°´¼ü
TIM_Cmd(TIM2, DISABLE);
TimeCheck=0;
count=0;
mode=0;//Çå³ýÏÔʾ
break;
}
if(mode==1)//¿ªÊ¼
{
if(TimeCheck>=10)//ÿ0.1sÔö¼Ó1
{
TimeCheck=0;
if(++count>9999)count=9999;//×î´ó999.9s
}
}
DisPlayNum(count);
}
}
存在的部分问题
驱动原理的图照成共阴极的了。不过原理差不多。这个扫描耗费资源,对于怎么个位上的DP位的小数点点亮的控制不太方便,没有使用数组进行IO口便捷,希望有大佬也给我讲解下相关的经验。谢谢