- 在上次写完第10届的定时器实现动态数码管的相关问题后,发现没有写如何通过定时器实现动态数码管的,这次进行详细说明。同时还有以前我的代码还存在一个不影响运行的错误。
源代码
main.c
#include <STC15F2K60S2.H>
#include <iic.h>
#include <smg.h>
bit AD_flag = 1;//电压读取标准
bit x = 1;//从参数界面进入计数界面的标志
bit led_flag;//L1五秒计时标志
bit L1_flag;//L1点亮标志
bit flag;//led灯状态更改标志
unsigned char mode = 0;//模式切换标志
unsigned char AD = 0;//记录电压的数字量
unsigned char AD_temp = 0;//用于辅助计数判断
unsigned char V = 0;//电压参数,已经放大10倍
unsigned int N = 0;//储存计数值
unsigned char count = 0;//无效按键记录
//按键消抖必要延时
void Delay20ms(void)
{
unsigned char data i, j;
i = 234;
j = 115;
do
{
while (--j);
} while (--i);
}
//按键扫描函数
void KeyScan()
{
P32 = 0;P33 = 1;
if(P35 == 0 ) //S13
{
Delay20ms();
if(P35 ==0)
{
if(mode == 2)
{
count = 0;
N = 0;
}
else
count++;
while(P35 ==0);
}
}
if(P34 == 0) //S17
{
Delay20ms();
if(P34 ==0)
{
if(mode == 1)
{
count = 0;
if(V > 0)V = V - 5;
else if(V == 0)V = 50;
}
else
count++;
while(P34 ==0);
}
}
P32 = 1;P33 = 0;
if(P35 == 0) //S12
{
Delay20ms();
if(P35 ==0)
{
count = 0;
if(++mode == 3)mode = 0;
if(mode == 2)x = 1;
while(P35 ==0);
}
}
if(P34 == 0 ) //S16
{
Delay20ms();
if(P34 ==0)
{
if(mode == 1)
{
count = 0;
if(V < 50)V = V + 5;
else if(V == 50)V = 0;
}
else
count++;
while(P34 ==0);
}
}
}
//界面更改函数
void Display()
{
switch(mode)
{
case 0:
Mode0(AD);
break;
case 1:
Mode1(V);
break;
case 2:
Mode2(N);
break;
}
}
//计数处理函数
void Data_Processing()
{
if(AD_temp * 10 / 51 >= V && V > AD * 10 / 51)
{
N++;
AD_temp = 0;
}
}
//改变某个led灯状态而不改变其他led灯状态
void ledx(unsigned char pos,n)
{
if(n)
P0 = P0 | (0x01 << pos - 1);
else
P0 = P0 & ~(0x01 << pos - 1);
SelectHC573(4);
}
//led灯状态处理函数
void Led()
{
if(AD * 10 / 51 < V && led_flag == 0)
{
led_flag = 1;
}
if(L1_flag)
{
led_flag = 0;
ledx(1,0);
}
if(AD * 10 / 51 > V)
{
L1_flag = 0;
ledx(1,1);
}
if(N % 2 == 1)ledx(2,0);
else ledx(2,1);
if(count >= 3)ledx(3,0);
else ledx(3,1);
}
//主函数
void main()
{
Init_System();
V = AT24C02_Read();
while(1)
{
//70毫秒读取一次电压
if(AD_flag)
{
AD_flag = 0;
AD_temp = AD;
AD = PCF8591_Read();
Data_Processing();
}
//从参数界面进入计数界面将参数存入E2PROM
if(mode == 2 && x == 1)
{
x = 0;
AT24C02_Write(V);
}
KeyScan();//键盘扫描
Display();//数码管界面更改
//led灯状态更改
if(flag)
{
flag = 0;
Led();
}
}
}
//定时器0中断服务函数
void Service_Timer0() interrupt 1
{
static unsigned int i = 0,i1 = 0,i2 = 0,i3 = 0;
TH0 = (65536 - 1000) / 256;
TL0 = (65536 - 1000) % 256;
SMG_Byte(i,Seg_Buf[i]);
if(++i == 8)i = 0;
if(++i1 == 70)
{
i1 = 0;
AD_flag = 1;
}
if(led_flag)
{
if(++i2 == 5000)
{
i2 = 0;
L1_flag = 1;
}
}
if(++i3 == 10)
{
i3 = 0;
flag = 1;
}
}
smg.c
#include <STC15F2K60S2.H>
//不带小数点的段码表
code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0x88, //A
0x83, //b
0xc6, //C
0xa1, //d
0x86, //E
0x8e //F
};
//带小数点的段码表
code unsigned char Seg_Point[] =
{
0x40, //0
0x79, //1
0x24, //2
0x30, //3
0x19, //4
0x12, //5
0x02, //6
0x78, //7
0x00, //8
0x10, //9
};
//数码管显示值从这里读取
unsigned char Seg_Buf[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
//锁存器选择函数
void SelectHC573(unsigned char n)
{
switch(n)
{
case 4:
P2 = (P2 & 0x1f) | 0x80;
break;
case 5:
P2 = (P2 & 0x1f) | 0xa0;
break;
case 6:
P2 = (P2 & 0x1f) | 0xc0;
break;
case 7:
P2 = (P2 & 0x1f) | 0xe0;
break;
}
P2 = (P2 & 0x1f) | 0x00;
}
//初始化无关设备和定时器0
void Init_System()
{
P0 = 0x00;
SelectHC573(5);
P0 = 0xff;
SelectHC573(4);
TMOD = 0x01;
TH0 = (65536 - 1000) / 256;
TL0 = (65536 - 1000) % 256;
EA = 1;
ET0 = 1;
TR0 = 1;
}
//数码管段选和位选
void SMG_Byte(unsigned char pos,value)
{
P0 = 0xff;
SelectHC573(7);
P0 = 0x01 << pos;
SelectHC573(6);
P0 = value;
SelectHC573(7);
P0 = 0xff;
}
//电压界面
void Mode0(unsigned char AD)
{
Seg_Buf[0] = 0xc1;
Seg_Buf[1] = 0xff;
Seg_Buf[2] = 0xff;
Seg_Buf[3] = 0xff;
Seg_Buf[4] = 0xff;
Seg_Buf[5] = Seg_Point[AD / 51];
Seg_Buf[6] = Seg_Table[AD * 10 / 51 % 10];
Seg_Buf[7] = Seg_Table[AD * 100 / 51 % 10];
}
//电压参数界面,已经放大10倍
void Mode1(unsigned char V)
{
Seg_Buf[0] = 0x8c;
Seg_Buf[1] = 0xff;
Seg_Buf[2] = 0xff;
Seg_Buf[3] = 0xff;
Seg_Buf[4] = 0xff;
Seg_Buf[5] = Seg_Point[V / 10];
Seg_Buf[6] = Seg_Table[V % 10];
Seg_Buf[7] = Seg_Table[0];
}
//计数界面
void Mode2(unsigned int n)
{
Seg_Buf[0] = 0xc8;
if(n >= 1000000)Seg_Buf[1] = Seg_Table[n / 1000000];
else Seg_Buf[1] = 0xff;
if(n >= 100000)Seg_Buf[2] = Seg_Table[n / 100000 % 10];
else Seg_Buf[2] = 0xff;
if(n >= 10000)Seg_Buf[3] = Seg_Table[n / 10000 % 10];
else Seg_Buf[3] = 0xff;
if(n >= 1000)Seg_Buf[4] = Seg_Table[n / 1000 % 10];
else Seg_Buf[4] = 0xff;
if(n >= 100)Seg_Buf[5] = Seg_Table[n / 100 % 10];
else Seg_Buf[5] = 0xff;
if(n >= 10)Seg_Buf[6] = Seg_Table[n / 10 % 10];
else Seg_Buf[6] = 0xff;
Seg_Buf[7] = Seg_Table[n % 10];
}
smg.h
#ifndef _smg_h_
#define _smg_h_
unsigned char Seg_Buf[];
void SelectHC573(unsigned char n);
void Init_System();
void SMG_Byte(unsigned char pos,value);
void Mode0(unsigned char AD);
void Mode1(unsigned char V);
void Mode2(unsigned int n);
#endif
iic.c
#include <STC15F2K60S2.H>
#include<intrins.h>
sbit sda = P2^1;
sbit scl = P2^0;
#define DELAY_TIME 5
//
static void I2C_Delay(unsigned char n)
{
do
{
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
_nop_();_nop_();_nop_();_nop_();_nop_();
}
while(n--);
}
//
void I2CStart(void)
{
sda = 1;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 0;
I2C_Delay(DELAY_TIME);
scl = 0;
}
//
void I2CStop(void)
{
sda = 0;
scl = 1;
I2C_Delay(DELAY_TIME);
sda = 1;
I2C_Delay(DELAY_TIME);
}
//
void I2CSendByte(unsigned char byt)
{
unsigned char i;
for(i=0; i<8; i++){
scl = 0;
I2C_Delay(DELAY_TIME);
if(byt & 0x80){
sda = 1;
}
else{
sda = 0;
}
I2C_Delay(DELAY_TIME);
scl = 1;
byt <<= 1;
I2C_Delay(DELAY_TIME);
}
scl = 0;
}
//
unsigned char I2CReceiveByte(void)
{
unsigned char da;
unsigned char i;
for(i=0;i<8;i++){
scl = 1;
I2C_Delay(DELAY_TIME);
da <<= 1;
if(sda)
da |= 0x01;
scl = 0;
I2C_Delay(DELAY_TIME);
}
return da;
}
//
unsigned char I2CWaitAck(void)
{
unsigned char ackbit;
scl = 1;
I2C_Delay(DELAY_TIME);
ackbit = sda;
scl = 0;
I2C_Delay(DELAY_TIME);
return ackbit;
}
//
void I2CSendAck(unsigned char ackbit)
{
scl = 0;
sda = ackbit;
I2C_Delay(DELAY_TIME);
scl = 1;
I2C_Delay(DELAY_TIME);
scl = 0;
sda = 1;
I2C_Delay(DELAY_TIME);
}
void AT24C02_Write(unsigned char dat)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(0x00);
I2CWaitAck();
I2CSendByte(dat);
I2CWaitAck();
I2CStop();
}
unsigned char AT24C02_Read()
{
unsigned char temp = 0;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(0x00);
I2CWaitAck();
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
temp = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
unsigned char PCF8591_Read()
{
unsigned char temp = 0;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(0x03);
I2CWaitAck();
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
temp = I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return temp;
}
iic.h
#ifndef _iic_h_
#define _iic_h_
void AT24C02_Write(unsigned char dat);
unsigned char AT24C02_Read();
unsigned char PCF8591_Read();
#endif
定时器实现动态数码管
用到的所有代码
#include <STC15F2K60S2.H>
//不带小数点的段码表
code unsigned char Seg_Table[] =
{
0xc0, //0
0xf9, //1
0xa4, //2
0xb0, //3
0x99, //4
0x92, //5
0x82, //6
0xf8, //7
0x80, //8
0x90, //9
0x88, //A
0x83, //b
0xc6, //C
0xa1, //d
0x86, //E
0x8e //F
};
//带小数点的段码表
code unsigned char Seg_Point[] =
{
0x40, //0
0x79, //1
0x24, //2
0x30, //3
0x19, //4
0x12, //5
0x02, //6
0x78, //7
0x00, //8
0x10, //9
};
//数码管显示值从这里读取
unsigned char Seg_Buf[] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff};
//锁存器选择函数
void SelectHC573(unsigned char n)
{
switch(n)
{
case 4:
P2 = (P2 & 0x1f) | 0x80;
break;
case 5:
P2 = (P2 & 0x1f) | 0xa0;
break;
case 6:
P2 = (P2 & 0x1f) | 0xc0;
break;
case 7:
P2 = (P2 & 0x1f) | 0xe0;
break;
}
P2 = (P2 & 0x1f) | 0x00;
}
//初始化无关设备和定时器0
void Init_System()
{
P0 = 0x00;
SelectHC573(5);
P0 = 0xff;
SelectHC573(4);
TMOD = 0x01;
TH0 = (65536 - 1000) / 256;
TL0 = (65536 - 1000) % 256;
EA = 1;
ET0 = 1;
TR0 = 1;
}
//数码管段选和位选
void SMG_Byte(unsigned char pos,value)
{
P0 = 0xff;
SelectHC573(7);
P0 = 0x01 << pos;
SelectHC573(6);
P0 = value;
SelectHC573(7);
P0 = 0xff;
}
//电压界面
void Mode0(unsigned char AD)
{
Seg_Buf[0] = 0xc1;
Seg_Buf[1] = 0xff;
Seg_Buf[2] = 0xff;
Seg_Buf[3] = 0xff;
Seg_Buf[4] = 0xff;
Seg_Buf[5] = Seg_Point[AD / 51];
Seg_Buf[6] = Seg_Table[AD * 10 / 51 % 10];
Seg_Buf[7] = Seg_Table[AD * 100 / 51 % 10];
}
void Service_Timer0() interrupt 1
{
static unsigned int i = 0;
TH0 = (65536 - 1000) / 256;
TL0 = (65536 - 1000) % 256;
SMG_Byte(i,Seg_Buf[i]);
if(++i == 8)i = 0;
}
- 首先是三个数组,其中要说的是Seg_Buf数组,数码管显示的值就是从这里面进行读取。我们只要在每次切换界面时重新对Seg_Buf数组进行赋值就可以实现界面切换。
- SelectHC573函数
调用这个函数在每次打开对应的锁存器后就会立马关闭。因此要先更改P0端口的值在执行该函数。 - Init_System函数没什么可讲的,提一下SMG_Byte函数中最后一行的P0 = 0xff是为解决用定时器实现动态数码管引出来的问题,同时也会引出另一问题,具体如何解决的在我的第10届代码中已经做了详细解释。
- 中断服务函数
先讲一下我以前的代码的不对之处,我以前都是对i进行如下定义的
unsigned int i;
运行起来并不会有任何错误,但是经过同学提醒发现这里不符合C语言的语法。因为这样定义在C语言的语法看来会导致i的初值并不确定并且每次执行完中断服务函数i值内存都会被释放,无法完成计数的功能。但是实际运行却又完成了其计数功能。 最后到处查找也没有找到原因。只能归结于编译器默认将其定义为静态变量且赋值为0。
所以其正确定义应该如下,将其定义为静态变量且赋值为0。因为静态变量的只有在程序结束才会被释放。
static unsigned int i = 0;
最后就是在中断服务函数中调用数码管的段选位选函数,每一毫秒进入一次中断服务函数,就根据i值点亮一个数码管再对i值进行0-8循环更改。
- 因此只需要在主函数中调用不同的Mode()函数对Seg_Buf进行单独赋值就可以实现数码管的动态显示。