实验名称:利用定时器T1实现定时1S的实验
功能描述:利用定时器T1实现1S定时,数码管显示秒数。
实验目的:学会使用定时器T1进行定时。
学会使用数码管显示数据。
学习定时器中断。
实验说明:MCU–M128
内部8M晶振
如果数码管刷新频率不够高的话将会出现闪烁的现象。
/************************************************
文件:main.c
用途:
注意:内部8M晶振
************************************************/
#include "config.h"
volatile unsigned int countnum=0;
void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCNT1H = 0x8F; //setup
TCNT1L = 0x81;
OCR1AH = 0x70;
OCR1AL = 0x7F;
OCR1BH = 0x70;
OCR1BL = 0x7F;
OCR1CH = 0x70;
OCR1CL = 0x7F;
ICR1H = 0x70;
ICR1L = 0x7F;
TCCR1A = 0x00;
TCCR1B = 0x04; //start Timer
}
#pragma interrupt_handler timer1_ovf_isr:15
void timer1_ovf_isr(void)
{
TCNT1H = 0x8F; //reload counter high value
TCNT1L = 0x81; //reload counter low value
countnum++;
if(countnum>9999) countnum=0;
}
void init_devices(void)
{
CLI(); //disable all interrupts
timer1_init();
TIMSK = 0x04; //timer interrupt sources
SEI(); //re-enable interrupts
}
void main(void)
{
init_devices();
HC_595_init();
while(1)
{
Seg7_Led_display(countnum);
//Seg7_Led_float(25.13);
}
}
首先,countnum用以在中断中计数1~9999(因为有四个数码管)
其数字进入的是Seg7_Led_display()
函数如下:
/************************************************
文件:hc595.c
用途:
注意:内部8M晶振
************************************************/
#include "config.h"
const unsigned char Seg7_Data[]={0x3F,0x06,0x5B,0x4F,0x66, //0,1,2,3,4
0x6D,0x7D,0x07,0x7F,0x6F, //5,6,7,8,9
0x77,0x7C,0x39,0x5E,0x79,0x71,0x00}; //a,b,c,d,e,f
volatile unsigned char Seg7_Led_Buf[4],point=0,point_pos=0;//point是小数点标志1代表有小数点point_pos表示小数点位置
/*************************************************************************
** 函数名称:HC595初始化
** 功能描述:
** 输 入:
** 输出 :
** 全局变量:
** 调用模块:
** 说明:
** 注意:
**************************************************************************/
void HC_595_init(void)
{
OE_DDR |= (1<<OE);
OE_PORT &= (1<<OE);
PORTB = 0x0F;
spi_init();
Seg7_Led_Buf[0]=16;
Seg7_Led_Buf[1]=16;
Seg7_Led_Buf[2]=16;
Seg7_Led_Buf[3]=16;
}
/*************************************************************************
** 函数名称:HC595送数据
** 功能描述:
** 输 入:
** 输出 :
** 全局变量:
** 调用模块:
** 说明:
** 注意:
**************************************************************************/
void HC_595_OUT(unsigned char data)
{
SS_L();
SPI_MasterTransmit(data);
SS_H();
}
/*************************************************************************
** 函数名称:HC595刷新显示
** 功能描述:
** 输 入:
** 输出 :
** 全局变量:
** 调用模块:
** 说明:
** 注意:
**************************************************************************/
void Seg7_Led_Update(void)
{
HC_595_OUT(Seg7_Data[Seg7_Led_Buf[0]]);
Seg7_Bit0_En();
delay_nus(60);
Seg7_Bit0_Dis();
HC_595_OUT(Seg7_Data[Seg7_Led_Buf[1]]);
if((point==1)&&(point_pos==1))
HC_595_OUT((Seg7_Data[Seg7_Led_Buf[1]])|(1<<dp));
Seg7_Bit1_En();
delay_nus(60);
Seg7_Bit1_Dis();
HC_595_OUT(Seg7_Data[Seg7_Led_Buf[2]]);
if((point==1)&&(point_pos==2))
HC_595_OUT((Seg7_Data[Seg7_Led_Buf[2]])|(1<<dp));
Seg7_Bit2_En();
delay_nus(60);
Seg7_Bit2_Dis();
HC_595_OUT(Seg7_Data[Seg7_Led_Buf[3]]);
if((point==1)&&(point_pos==3))
HC_595_OUT((Seg7_Data[Seg7_Led_Buf[3]])|(1<<dp));
Seg7_Bit3_En();
delay_nus(60);
Seg7_Bit3_Dis();
}
/*************************************************************************
** 函数名称:Hc595显示整形数据
** 功能描述:
** 输 入:0~9999
** 输出 :
** 全局变量:
** 调用模块:
** 说明:
** 注意:
**************************************************************************/
void Seg7_Led_display(unsigned int data)
{
if(data>9999) //错误处理,超出显示范围则全亮
{
HC_595_OUT(0xFF);
Seg7_Bitselect_PORT|=((1<<Seg7_Bit0)|(1<<Seg7_Bit1)|(1<<Seg7_Bit2)|(1<<Seg7_Bit3));
}
else if(data>999)
{
Seg7_Led_Buf[3]=data/1000;
Seg7_Led_Buf[2]=(data%1000)/100;
Seg7_Led_Buf[1]=(data%100)/10;
Seg7_Led_Buf[0]=data%10;
Seg7_Led_Update();
}
else if(data>99)
{
Seg7_Led_Buf[3]=16;
Seg7_Led_Buf[2]=(data%1000)/100;
Seg7_Led_Buf[1]=(data%100)/10;
Seg7_Led_Buf[0]=data%10;
Seg7_Led_Update();
}
else if(data>9)
{
Seg7_Led_Buf[3]=16;
Seg7_Led_Buf[2]=16;
Seg7_Led_Buf[1]=(data%100)/10;
Seg7_Led_Buf[0]=data%10;
Seg7_Led_Update();
}
else
{
Seg7_Led_Buf[3]=16;
Seg7_Led_Buf[2]=16;
Seg7_Led_Buf[1]=16;
Seg7_Led_Buf[0]=data%10;
Seg7_Led_Update();
}
}
/*************************************************************************
** 函数名称:HC595显示浮点数据
** 功能描述:
** 输 入:
** 输出 :
** 全局变量:
** 调用模块:
** 说明:
** 注意:
**************************************************************************/
void Seg7_Led_float(float data)
{
unsigned int temp;
/*
重要说明:data+=0.00001;其中0.00001为容错值
解决float数据类型在计算机内部存储的误差问题,可以解决显示问题
但是会引入新的计算误差,如果精度要求大于0.00001建议更改容错值或者将此处注释掉
*/
data+=0.00001;
point=1;
if(data>999) //错误处理,超出显示范围则全亮
{
HC_595_OUT(0xFF);
Seg7_Bitselect_PORT|=((1<<Seg7_Bit0)|(1<<Seg7_Bit1)|(1<<Seg7_Bit2)|(1<<Seg7_Bit3));
}
else if(data>99)
{
temp=data*10;
point_pos=1;
Seg7_Led_Buf[3]=temp/1000;
Seg7_Led_Buf[2]=(temp%1000)/100;
Seg7_Led_Buf[1]=(temp%100)/10;
Seg7_Led_Buf[0]=temp%10;
Seg7_Led_Update();
}
else if(data>9)
{
temp=data*100;
point_pos=2;
Seg7_Led_Buf[3]=temp/1000;
Seg7_Led_Buf[2]=(temp%1000)/100;
Seg7_Led_Buf[1]=(temp%100)/10;
Seg7_Led_Buf[0]=temp%10;
Seg7_Led_Update();
}
else
{
temp=data*1000;
point_pos=3;
Seg7_Led_Buf[3]=temp/1000;
Seg7_Led_Buf[2]=(temp%1000)/100;
Seg7_Led_Buf[1]=(temp%100)/10;
Seg7_Led_Buf[0]=temp%10;
Seg7_Led_Update();
}
point=0;
}
Seg7_Led_Buf[]
字符被提取存入这个数组中,随后执行刷新数码管的Seg7_Led_Update();
函数用以显示数字:
这个函数是关键:
void Seg7_Led_Update(void)
{
HC_595_OUT(Seg7_Data[Seg7_Led_Buf[0]]);
Seg7_Bit0_En();
delay_nus(60);
Seg7_Bit0_Dis();
HC_595_OUT(Seg7_Data[Seg7_Led_Buf[1]]);
if((point==1)&&(point_pos==1))
HC_595_OUT((Seg7_Data[Seg7_Led_Buf[1]])|(1<<dp));
Seg7_Bit1_En();
delay_nus(60);
Seg7_Bit1_Dis();
HC_595_OUT(Seg7_Data[Seg7_Led_Buf[2]]);
if((point==1)&&(point_pos==2))
HC_595_OUT((Seg7_Data[Seg7_Led_Buf[2]])|(1<<dp));
Seg7_Bit2_En();
delay_nus(60);
Seg7_Bit2_Dis();
HC_595_OUT(Seg7_Data[Seg7_Led_Buf[3]]);
if((point==1)&&(point_pos==3))
HC_595_OUT((Seg7_Data[Seg7_Led_Buf[3]])|(1<<dp));
Seg7_Bit3_En();
delay_nus(60);
Seg7_Bit3_Dis();
}
比如,HC_595_OUT(Seg7_Data[Seg7_Led_Buf[0]]);
中,Seg7_Data[Seg7_Led_Buf[0]]
将数字转换成段选码(控制数码管显示什么数字的),随后进入HC_595_OUT(Seg7_Data[Seg7_Led_Buf[0]]);
中:
这个函数将段选的数据送给spi的寄存器:
void HC_595_OUT(unsigned char data)
{
SS_L();
SPI_MasterTransmit(data);
SS_H();
}
这里用以上原理图来解释上面的函数,首先,PB0电平先拉低再置高的目的:使74HC595在上升沿的时候将数据锁到O0-O7数据输出口。
SPI_MasterTransmit(data);这段函数出自spi.c:
/************************************************
文件:spi.c
用途:SPI驱动
************************************************/
#include "config.h"
/*************************************************************************
** 函数名称: spi_init(void)
** 功能描述: SPI初始化
** 输 入:
** 输出 :
** 全局变量: 无
** 调用模块:
** 说明:
** 注意:
**************************************************************************/
void spi_init(void)
{
DDRB |= (1<<MOSI)|(1<<SCK)|(1<<SS);//设置MOSI,SCK输出
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0)|(1<<SPR1);//使能SPI,主机模式
}
/*************************************************************************
** 函数名称: SPI_MasterTransmit(char Data)
** 功能描述: SPI主机发送数据
** 输 入: Data 需要通过SPI传输的数据
** 输出 :
** 全局变量: 无
** 调用模块:
** 说明:
** 注意:
**************************************************************************/
void SPI_MasterTransmit(char Data)
{
/* 启动数据传输 */
SPDR = Data;
/* 等待传输结束 */
while(!(SPSR & (1<<SPIF)))
;
}
void SPI_MasterTransmit(char Data)
的作用:将刚刚的段选码数据存入SPDR寄存器,然后由以上一点所述,将数据锁到O0-O7数据输出口后,数据输出则为数码管数字的具体显示。(简而言之,就是,74H595作用是将spi的串行通信的数据变成并行)
最后,是位选的选择:
比如,第一个数码管的,宏定义:
#define Seg7_Bit0_En() {Seg7_Bitselect_DDR|=(1<<Seg7_Bit0);Seg7_Bitselect_PORT|=(1<<Seg7_Bit0);}
#define Seg7_Bit0_Dis() {Seg7_Bitselect_DDR|=(1<<Seg7_Bit0);Seg7_Bitselect_PORT&=~(1<<Seg7_Bit0);}
即置高PB4-PB7任意一个则会使三极管导通,从而使数码管工作。
有疑问可以留言,欢迎交流哦。
源码附上:
https://download.csdn.net/download/weixin_39589455/11146950