之前使用数码管做一些东西,由于外设比较少就直接用单片机的IO口控制数码管了。我用的3641BS四位共阳数码管,一共12个引脚,全部使用单片机IO。数码管引脚图如下:
写程序的思路是位选置1,段选为0的地方点亮显示。一般动态显示需要用到延时用于视觉暂留,以前用delay函数空等待,这样只能放在main函数的while循环里面,如果while函数中有程序占用时间过长会产生闪烁的感觉。放在定时器中断里面也会出问题。于是就取消delay函数,把动态扫描程序放在定时器中断中,利用定时器来切换数码管显示位置,方便很多。
main.c
#include "led.h"
#include "delay.h"
#include "sys.h"
//ALIENTEK miniSTM32开发板实验1
//跑马灯实验
//技术支持:www.openedv.com
//广州市星翼电子科技有限公司
//数码管显示位数
#define DIS_NUM 3 //修改此数值可设置显示位数 只可减小此值
//共阳数码管段码表
// 0 1 2 3 4 5 6 7 8 9 A B C D E F 全灭
u8 smg_code[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff};
//数码管位选表,最后一位表示全部不选择
uint8_t numb[]={0x08,0x04,0x02,0x01,0x00};
u8 dis_pos=0; //数码管显示位置
u16 time=0; //秒表时间
/*******************************************************************************
* Function Name : Write_data
* Description : 数码管写段选
* Input : data :要发送的段选数据
* Output : None
* Return : None
*******************************************************************************/
void Write_data(u8 data)
{
if((data & 0x80) >> 7) H=1;
else H=0;
if((data & 0x40) >> 6) G=1;
else G=0;
if((data & 0x20) >> 5) F=1;
else F=0;
if((data & 0x10) >> 4) E=1;
else E=0;
if((data & 0x08) >> 3) D=1;
else D=0;
if((data & 0x04) >> 2) C=1;
else C=0;
if((data & 0x02) >> 1) B=1;
else B=0;
if((data & 0x01) ) A=1;
else A=0;
}
/*******************************************************************************
* Function Name : dis_bit
* Description : 数码管显示位置,用于选择某个要显示的数码管
* Input : pos : 数码管位置
* Output : None
* Return : None
*******************************************************************************/
void dis_bit(u8 pos)
{
//以下屏蔽的代码也可用
/* if((pos&0x01)) {
DIG1=1;
DIG2=0;
DIG3=0;
DIG4=0;
}
else if((pos&0x02)) {
DIG1=0;
DIG2=1;
DIG3=0;
DIG4=0;
}
else if((pos&0x04)) {
DIG1=0;
DIG2=0;
DIG3=1;
DIG4=0;
}
else if((pos&0x08)) {
DIG1=0;
DIG2=0;
DIG3=0;
DIG4=1;
}
else if(pos==0x00)
{
DIG1=0;
DIG2=0;
DIG3=0;
DIG4=1;
}*/
switch(pos)
{
case 0x01:
DIG1=1;
DIG2=0;
DIG3=0;
DIG4=0;
break;
case 0x02:
DIG1=0;
DIG2=1;
DIG3=0;
DIG4=0;
break;
case 0x04:
DIG1=0;
DIG2=0;
DIG3=1;
DIG4=0;
break;
case 0x08:
DIG1=0;
DIG2=0;
DIG3=0;
DIG4=1;
break;
case 0x00:
DIG1=0;
DIG2=0;
DIG3=0;
DIG4=0;
break;
}
}
/*******************************************************************************
* Function Name : dis_num
* Description : 数码管显示数据
* Input : num:要显示的数据
* Output : None
* Return : None
*******************************************************************************/
void dis_num(u16 num)
{
u8 tab[4]; //定义一个数组,用于存放各个数码管显示的数据
tab[3]=smg_code[num%10000/1000];
tab[2]=smg_code[num%1000/100];
tab[1]=smg_code[num%100/10];
tab[0]=smg_code[num%10]; //分离数据
Write_data(smg_code[16]); //段选全不亮,消影
Write_data(tab[dis_pos]); //送段选
dis_bit(numb[dis_pos]); //送位选
}
/*******************************************************************************
* Function Name : TIM3_Int_Init
* Description : 定时器3初始化函数
* Input : arr:自动重装值。 psc:时钟预分频数
* Output : None
* Return : None
* 定时时间=(arr+1)*(psc+1)/72MHz(单位us)
*******************************************************************************/
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx
}
int main(void)
{
delay_init(); //延时函数初始化
LED_Init(); //初始化与LED连接的硬件接口
TIM3_Int_Init(49,7199); //定时5ms 时间太长数码管会闪烁
while(1)
{
LED1=!LED1; //LED闪烁
delay_ms(1000);
}
}
/*******************************************************************************
* Function Name : TIM3_IRQHandler
* Description : 定时器3中断处理函数
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void TIM3_IRQHandler(void)
{
static u8 cnt=0; //定义一个变量用于秒表计数 要定义成static类型,否则 ++cnt>200条件进不去
if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
if(++cnt>200) //计时1秒时间到
{
cnt=0;
time++; //秒钟加1
if(time>9999)time=0;
}
dis_num(time); //显示数据
dis_pos++; //数码管位置变化1位
if(dis_pos>DIS_NUM) dis_pos=0; //限定数码管显示位数,可改小DIS_NUM得到想要的位数
}
}
数码管控制io口配置文件led.h
#ifndef __LED_H
#define __LED_H
#include "sys.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK战舰STM32开发板
//LED驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/2
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//
#define LED0 PAout(8) // PA8
#define LED1 PDout(2) // PD2
//1E 2D 3H 4C 5G 6DIG4 7B 8DIG3 9DIG2 10F 11A 12DIG1
//B7 B9 C0 C2 A0 A2 A3 A1 A3 C1 C13 B8
#define A PAout(0) // 段选a
#define B PAout(1) // 段选b
#define C PAout(2) // 段选c
#define D PAout(3) // 段选d
#define E PAout(4) // 段选e
#define F PAout(5) // 段选f
#define G PAout(6) // 段选g
#define H PAout(7) // 段选h
#define DIG1 PCout(3) // 位选1
#define DIG2 PCout(2) // 位选2
#define DIG3 PCout(1) // 位选3
#define DIG4 PCout(0) // 位选4
void LED_Init(void);//初始化
#endif
实物效果,前面两位数码管显示不完整是因为手机拍照的原因。
数码管未点亮时工作电流
数码管正常工作时最大电流