【AVR Meag128 单片机原理与接口技术实验】SPI方式控制数码管

实验名称:利用定时器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

猜你喜欢

转载自blog.csdn.net/weixin_39589455/article/details/89575244