一、原理图
二、数码管电路
控制数码管其实与控制LED的原理相似,数码管的一位可以看作八段LED组成。因此控制数码管,则可以从控制段选和控制位选两方面入手。Y6C控制段选,Y7C控制位选。
我的板子上面所配备的数码管是共阳数码管。
数码管静态显示:
数码管的静态显示是指段选是一致的,位选可以独立控制,通过位选来控制哪几个数码管亮,显示的数字始终是一样的。
例如我要让数码管的第一位显示数字2且没有小数点,步骤如下:
位选:应该使com1为高电平1,其他为0,P0二进制代码为0000 0001,写成十六进制即为0x01
段选:abdeg为低电平0,其他为高电平1。P0二进制代码为1010 0100,写成十六进制即为0xa4
数码管动态显示:
数码管的动态显示是指一位一位的操作操作每个数码管,多个数码管交替显示,数码管的动态显示是利用人眼的视觉暂留效果,使人看到多个数码管同时显示的效果。
数码管消隐/数码管鬼影:
使用数码管的时候遇到这样的问题:不应该亮的的位隐约有数据,不该亮的段隐约有亮光等情况,
- 延时
- 关闭所有段选(0xff),改变好位选后,再打开段即可
- 关闭所有位选(0x00),赋值过程都做好后,再重新打开即可
编码原理
code unsigned char T_DU[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};// 段码
code unsigned char T_WE[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//位码
编码定义方法与C语言中数组定义方法类似,不同的是多加了code关键字。code即编码的意思,需要注意的是,单片机C语言中定义数组时是占用内存空间的,而定义编码时是直接分配到程序空间中,编译后,编码占用的是程序存储空间,而非内存空间。在调用数组时,T_DU后的[]中数字从0开始,对应后面{}中的第一个元素。
三、定时器中断(初步学习)
1.中断源
52单片机共有6个中断源
中断源 | 默认中断级别 | 序号(C语言) | 入口地址(汇编语言) |
---|---|---|---|
INT0,外部中断0 | 最高 | 0 | 0003H |
T0,定时器/计数器0中断 | 第二 | 1 | 000BH |
INT1,外部中断1 | 第三 | 2 | 0013H |
T1,定时器/计数器1中断 | 第四 | 3 | 001BH |
TI/RI,串行口中断 | 第五 | 4 | 0023H |
T2,定时器/计数器2中断 | 最低 | 5 | 002BH |
2.单片机的定时器系统
51单片机内部有2个16位可编程的定时器/计数器,即定时器T0和定时器T1,52单片机内部多一个T2定时器/计数器,它们既有定时功能也有计数功能,通过设置与它们相关的特殊功能寄存器,可以选择启用定时功能或计数功能。
3.定时器中断函数部分
stc-isp→定时器计算器→选择定时长度、正确的频率、定时器、模式、时钟等
//定时器
void Timer0Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
//定时器中断服务函数
void isr_timer_0() interrupt 1 using 工作组
{
//……
}
//主函数
void main()
{
Timer0Init();
EA=1;打开总中断
ET0=1;打开定时器0中断
while (1)
{
//……
}
}
“using工作组”是指这个中断服务函数使用单片机内存中4组工作寄存器中的哪一组,C51编译器在编译程序时会自动分配工作组,因此最后一句话通常省略,但以后遇到这样的代码要知道是什么意思。
在中断服务函数程序中一般不要写过多的函数处理语句,如果语句过多,中断服务函数代码还未执行完毕,而下一次中断又来临,这样我们会丢失这次中断。一般我们遵循的原则是:能在主程序中完成的功能就不在中断函数中写,若非要在中断函数中完成,那么一定要高效、简洁。
四、关于变量需不需要赋初值
unsigned char i;
如果是全局变量,i的值为0
如果是在函数内部定义的局部变量,i的值就是随机的。
局部变量的内存是从栈里分配的,系统会从栈里找到一个int那么大的内存分配给i。 因为这个栈里的东西没有清零,所以这段内存里的内容什么不知道是什么情况。 所以,局部变量使用前必须初始化。
在 C 语言中,static 关键字不仅可以用来修饰变量,还可以用来修饰函数。
在使用 static 关键字修饰变量时,我们称此变量为静态变量。
静态变量的存储方式与全局变量一样,都是静态存储方式。但这里需要特别说明的是,静态变量属于静态存储方式,属于静态存储方式的变量却不一定就是静态变量。例如,全局变量虽然属于静态存储方式,但并不是静态变量,它必须由 static 加以定义后才能成为静态全局变量。
有时候,我们希望函数中局部变量的值在函数调用结束之后不会消失,而仍然保留其原值。即它所占用的存储单元不释放,在下一次调用该函数时,其局部变量的值仍然存在,也就是上一次函数调用结束时的值。
这时候,我们就应该将该局部变量用关键字 static 声明为“静态局部变量”。
静态局部变量一般的使用场景,如下所示:
- 需要保留函数上一次调用结束时的值。
- 如果初始化后,变量只会被引用而不会改变其值,则这时用静态局部变量比较方便,以免每次调用时重新赋值。
- 在静态数据区,内存中所有的字节默认值都是 0x00。静态变量与全局变量也一样,它们都存储在静态数据区中,因此其变量的值默认也为 0。
五、代码实现
数码管八位都显示0
#include <STC15F2K60S2.h>
//主函数
void main()
{
//消隐
P2=((P2&0x1f)|0xe0);
P0=0xff;
P2&=0x1f;
//位选
P2=((P2&0x1f)|0xc0);
P0=0xff;
P2&=0x1f;
//段选
P2=((P2&0x1f)|0xe0);
P0=0xc0;
P2&=0x1f;
}
数码管动态扫描
软件延时实现
#include <intrins.h>
#include <STC15F2K60S2.h>
void Delay2ms();//软件延时函数
code unsigned char T_DU[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};// 段码
code unsigned char T_WE[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//位码
//主函数
void main()
{
unsigned char i;
while (1)
{
//消隐
P2=((P2&0x1f)|0xe0);
P0=0xff;
P2&=0x1f;
//位选
P2=((P2&0x1f)|0xc0);
P0=T_WE[i];
P2&=0x1f;
//段选
P2=((P2&0x1f)|0xe0);
P0=T_DU[i];
P2&=0x1f;
i++;
if (i==8)
i=0;
Delay2ms();
}
}
//软件延时函数
void Delay2ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 22;
j = 128;
do
{
while (--j);
} while (--i);
}
定时器中断实现
#include <STC15F2K60S2.h>
void Timer0Init(void);
code unsigned char T_DU[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};// 段码
code unsigned char T_WE[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//位码
//主函数
void main()
{
Timer0Init();
EA=1;//打开总中断
ET0=1;//打开定时器0中断
while(1);
}
//定时器
void Timer0Init(void) //2毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x9A; //设置定时初值
TH0 = 0xA9; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
//定时器中断服务函数
void isr_timer_0() interrupt 1
{
static unsigned char i;
//消隐
P2=((P2&0x1f)|0xe0);
P0=0xff;
P2&=0x1f;
//位选
P2=((P2&0x1f)|0xc0);
P0=T_WE[i];
P2&=0x1f;
//段选
P2=((P2&0x1f)|0xe0);
P0=T_DU[i];
P2&=0x1f;
i++;
if (i==8)
i=0;
}
多位数码显示 软件延时实现
/*
数码管显示 ---- -123
unsigned char定义的不能大于255
例如如果要显示999 那就用int
小数点0xbf
*/
#include <intrins.h>
#include <STC15F2K60S2.h>
void Delay2ms();//软件延时函数
void display();//显示函数
code unsigned char tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf};
unsigned char dspbuf[8]={10,10,10,10,10,0,0,0};//显示缓冲区
unsigned char dspcom=0;
unsigned char x=123;
//主函数
void main()
{
while (1)
{
//刷新显示缓冲区
dspbuf[5]=x/100;
dspbuf[6]=x%100/10;
dspbuf[7]=x%10;
display();
Delay2ms();
}
}
//软件延时函数
void Delay2ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 22;
j = 128;
do
{
while (--j);
} while (--i);
}
//显示函数
void display()
{
//消隐
P2=((P2&0x1f)|0xe0);
P0=0xff;
P2&=0x1f;
//位选
P2=((P2&0x1f)|0xc0);
P0=(1<<dspcom);
P2&=0x1f;
//段选
P2=((P2&0x1f)|0xe0);
P0=tab[dspbuf[dspcom]];
P2&=0x1f;
if(++dspcom==8)
{
dspcom=0;
}
}
关于小数点的实现
图1:这种情况与显示数字原理一致,只需在tab[]数组里添加显示小数点的元素码。再对刷新显示缓冲区部分代码进行改变即可。
图2:这种情况由于小数点和数字在同一位显示,并且要求数字改变时小数点不能动,所以不能在数组里对段码进行修改。通过以下代码可以实现。思想是知道小数点的位置,通过if判断,此位小数点常亮。
#include <intrins.h>
#include <STC15F2K60S2.h>
void Delay1ms();//软件延时函数
void display();//显示函数
code unsigned char tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf};
unsigned char dspbuf[8]={10,10,10,10,10,0,0,0};//显示缓冲区
unsigned char dspcom=0;
unsigned char x=123;
//主函数
void main()
{
P2=0xA0;P06=0;P2=0x00;
P2=0x80;P0=0xff;P2=0x00;
while (1)
{
//刷新显示缓冲区
dspbuf[5]=x/100;
dspbuf[6]=x%100/10;
dspbuf[7]=x%10;
display();
Delay1ms();
}
}
//软件延时函数
void Delay1ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
_nop_();
i = 11;
j = 190;
do
{
while (--j);
} while (--i);
}
//显示函数
void display()
{
P2=((P2&0x1f)|0xe0);
P0=0xff;
P2&=0x1f;
P2=((P2&0x1f)|0xe0);
if (dspcom==6)
{
P0=((tab[dspbuf[dspcom]])&0x7f);//该位小数点常亮
}
else
{
P0=tab[dspbuf[dspcom]];
}
P2&=0x1f;
P2=((P2&0x1f)|0xc0);
P0=(1<<dspcom);
P2&=0x1f;
if(++dspcom==8)
{
dspcom=0;
}
}
数码管显示计时 含小数点 每500ms增加0.1
#include <intrins.h>
#include <STC15F2K60S2.h>
void Timer0Init(void);//定时器
void Delay500ms();//软件延时函数
void display();//显示函数
code unsigned char tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf};
unsigned char dspbuf[8]={0,0,0,0,0,0,0,0};//显示缓冲区
unsigned char dspcom=0;
unsigned char x=0;
//主函数
void main()
{
P2=0xA0;P06=0;P2=0x00;
P2=0x80;P0=0xff;P2=0x00;
Timer0Init();
EA=1;//打开总中断
ET0=1;//打开定时器0中断
while (1)
{
//刷新显示缓冲区
dspbuf[5]=x/100;
dspbuf[6]=x%100/10;
dspbuf[7]=x%10;
x+=1;
Delay500ms();
}
}
//定时器
void Timer0Init(void) //1毫秒@11.0592MHz
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xCD; //设置定时初值
TH0 = 0xD4; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
}
//软件延时函数
void Delay500ms() //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 22;
j = 3;
k = 227;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//显示函数
void display()
{
P2=((P2&0x1f)|0xe0);
P0=0xff;
P2&=0x1f;
P2=((P2&0x1f)|0xe0);
if (dspcom==6)
{
P0=((tab[dspbuf[dspcom]])&0x7f);//该位小数点常亮
}
else
{
P0=tab[dspbuf[dspcom]];
}
P2&=0x1f;
P2=((P2&0x1f)|0xc0);
P0=(1<<dspcom);
P2&=0x1f;
if(++dspcom==8)
{
dspcom=0;
}
}
//中断服务函数
void isr_timer_0() interrupt 1
{
display();
}