文章目录
- 【蓝桥杯-单片机】基础模块:数码管
- 01 数码管原理图
- 什么是位选和段选
- 共阳极数码管和共阴极数码管的区分
- (1)共阳极数码管(Common Anode):
- (2)共阴极数码管(Common Cathode):
- 02 代码
- (1)数码管静态显示
- (2)数码管动态显示
- ①定时器生成步骤
- ②中断配置方法
- 补充知识:8051的中断源
- (1)8051的5个中断源
- (2)中断函数写法
- (3)中断触发控制寄存器IE (左边是高位)
- (4)中断优先级控制寄存器IP
- (5)TIMER控制寄存器TCON
【蓝桥杯-单片机】基础模块:数码管
01 数码管原理图
从图2可知,位选和段选最终都接到了D0.7口;从图1可知,对应D0.7的是P0口。因此P0口是数据口,数据应该往P0口写。
但是位选和段选都是写入P0口。P0=0xA4,如何设置为段选?利用锁存器实现。
令P2.6=1,写入段选数据;令P2.6=0,关闭,数据存进去了。
令P2.7=1,写入位选数据;令P2.7=0,关闭,数据存进去了。
//数码管显示函数
void Seg_Disp(unsigned char wela,dula)
{P0 = 0x00; //消影P2_6 = 1;P2_6 = 0; P0 = Seg_Wela[wela];P2_7 = 1;P2_7 = 0; P0 = Seg_Dula[dula];P2_6 = 1;P2_6 = 0;
}
什么是位选和段选
-
位选(Digit Select):
位选是指在多位数码管中选择哪一位进行显示。例如,4位数码管就有4个位选引脚,通过控制这些引脚的电平状态,可以选择显示哪一位的数字。位选的常见方式是通过一个计时器或者控制芯片,轮流地激活每一位,以达到依次显示多个数字的效果。
位选引脚一般用W0、W1、W2等表示,其中W0对应最低位,W1对应次低位,以此类推。 -
段选(Segment Select):
段选是指选择数码管的哪一段(LED)亮起来,以显示对应数字的哪一部分。例如,7段数码管的每一段可以表示数字0-9中的一部分。段选通常使用BCD码(二进制编码十进制)或其他编码方式,将要显示的数字转化为对应的段选信号。
段选引脚一般用a、b、c、d、e、f、g等表示,对应数码管的7个LED段。 -
DP是数码管中的一种特殊段,它代表小数点(Decimal Point)。在一些数码管中,除了表示数字0到9的七个段(a到g),还包括一个用于显示小数点的额外段,即DP段。
这里的h即DP。
共阳极数码管和共阴极数码管的区分
共阳极数码管和共阴极数码管是两种常见的数码管类型,它们在工作原理和控制方式上有一些区别:
(1)共阳极数码管(Common Anode):
在共阳极数码管中,所有的LED段的阳极(正极)都是连接在一起的,而每个LED段的阴极(负极)则分别连接到不同的引脚。
控制时,通过给某一段的阴极引脚接通电流,同时将共阳极引脚设置为高电平,该段就会被激活,显示相应的数字或字符。
(2)共阴极数码管(Common Cathode):
在共阴极数码管中,所有的LED段的阴极(负极)都是连接在一起的,而每个LED段的阳极(正极)则分别连接到不同的引脚。
控制时,通过给某一段的阳极引脚接通电流,同时将共阴极引脚设置为低电平,该段就会被激活,显示相应的数字或字符。
主要区别在于共阳极数码管是通过通电阴极(负极)来激活LED段,而共阴极数码管是通过通电阳极(正极)来激活LED段。
例如:假设以下是一个共阴极数码管(共阴极引脚设置为低电平激活),如果我要显示数字2,对8个段选线的电平设置(从h->a)为10100100
,转换为16进制为0xA4
。即设置段选为0xA4就可以让数码管显示数字2
。
02 代码
(1)数码管静态显示
普通代码(直接在while循环里写)
P0 = 0x00; //消影P2_6 = 1;P2_6 = 0; P0 = 0xfe;//访客(数据)来了P2_7 = 1;//位码门打开P2_7 = 0;//位码门关闭,防止别人进来P0 = 0x5b;//访客(数据)来了P2_6 = 1;//段码门打开P2_6 = 0;//段码门关闭,防止别人进来(这是用锁存器实现的)
模块化编程:
/* 变量声明区 */
unsigned char Seg_Wela[6] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
unsigned char Seg_Dula[11] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
//数码管显示函数
void Seg_Disp(unsigned char wela,dula)
{P0 = 0x00; //消影P2_6 = 1;P2_6 = 0; P0 = Seg_Wela[wela];P2_7 = 1;P2_7 = 0; P0 = Seg_Dula[dula];P2_6 = 1;P2_6 = 0;
}
这边的代码和蓝桥杯的底层代码是有一点不一样的,但是思路都是一样的,要先学会简单的。
这些代码再蓝桥杯比赛的时候需要凭借肌肉记忆敲出来,不能现场推,会来不及。
(2)数码管动态显示
动态数码管:每位数码管显示不同的数字,若直接重复调用一个数码管显示的函数,会因为程序执行逻辑是由上至下依次执行,会造成后面的数据影响前面的显示效果
- 一种解决方案,显示后面直接delay。这种做法不建议,程序写多了会卡。
- 用定时器
特性 | 定时器(Timer) | 延时函数(Delay) |
---|---|---|
作用 | 用于计算程序执行时间、生成精确的时间延迟、定时触发事件等。 | 用于在程序中创建一段固定的时间延迟 |
工作原理 | 以某个频率递增或递减的计数器 | 通过在程序中插入循环,等待一段时间 |
使用方法 | 配置计数方向、预分频器、计数模式等参数 | 确定循环次数,基于系统时钟频率和所需延时时间 |
应用 | 生成精确的时间间隔、PWM控制、周期性任务调度等 | 简单的等待、初始化延时等 |
总结:定时器提供了更为精确的时间控制,适用于需要准确时间测量和控制的场景。延时函数是一种简单的时间控制方法,适用于一些简单的等待或初始化场景,但在需要高精度时间控制的情况下不够准确。
①定时器生成步骤
- 打开STC-ISP烧录软件,找到定时器计算器
- 系统频率12MHz定时长度1毫秒定时器模式十六位定时器时钟127T
- 复制C代码到工程中
- 删除AUXR &=Ox7F;
//定时器初始化函数
void Timer0Init(void) //1毫秒@12.000MHz
{//AUXR &= 0x7F; //定时器时钟12T模式,没有这个定义,删掉TMOD &= 0xF0; //设置定时器模式TL0 = 0x18; //设置定时初值TH0 = 0xFC; //设置定时初值TF0 = 0; //清除TF0标志TR0 = 1; //定时器0开始计时ET0 = 1;//加上EA = 1;//加上
}
在嵌入式系统和单片机编程中,ET0 和 EA 是与定时器/计数器相关的控制寄存器。
- ET0(Timer 0 溢出中断允许位):
ET0 位是 8051 单片机中的特殊功能寄存器(SFR),用于控制定时器/计数器 0 的溢出中断是否允许。
当 ET0 位被设置为 1 时,允许定时器/计数器 0 溢出时触发中断。溢出中断是在定时器/计数器达到最大计数值后溢出到 0 时触发的。 - EA(全局中断允许位):
EA 位同样是 8051 单片机中的特殊功能寄存器,用于控制全局中断是否允许。
当 EA 位被设置为 1 时,允许所有中断(包括外部中断、定时器中断等)生效。如果 EA 位被清零,则禁止所有中断,即使各个中断的特定中断允许位(如 ET0)被设置为 1,相应中断也不会触发。
这两个寄存器通常与定时器/计数器模块一起使用,以实现在定时器计数达到某个值时触发中断的功能。在使用定时器中断时,通常需要设置 ET0 位来启用定时器溢出中断,并设置 EA 位来启用全局中断。
②中断配置方法
- 在生成的定时器初始化函数内增加中断打开命令ET0=1、EA=1
- 书写中断服务函数(Timer0Server)
- 在服务函数内初始化计数值
- 在主程序内添加定时器0初始化函数
//中断服务函数
void Timer0Server() interrupt 1
{TL0 = 0x18; //设置定时初始值TH0 = 0xFC; //设置定时初始值Seg_Pos++;if(Seg_Pos == 6) Seg_Pos = 0;Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]);}
完整代码
/* 头文件声明区 */
#include <REGX52.H>
#include <intrins.h>/* 变量声明区 */
unsigned char Led_Data = 0xFE;//用于循环位移的LED变量 初始值为LED1亮
unsigned char Seg_Wela[6] = {0xfe,0xfd,0xfb,0xf7,0xef,0xdf};
unsigned char Seg_Dula[11] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
unsigned char Seg_Pos;
unsigned char Seg_Buf[6] = {5,2,1,10,10,10};/* 五百毫秒延时函数 */
void Delay500ms() //@12.000MHz
{unsigned char i, j, k;i = 4;j = 205;k = 187;do{do{while (--k);} while (--j);} while (--i);
}/**
* @函数名 自定义延时函数
* @函数功能 延时任意毫秒
* @入口参数 延时时间
* @返回值 无
*/
void Delay(unsigned int xms) //@12.000MHz
{unsigned char i, j;while(xms--){i = 2;j = 239;do{while (--j);} while (--i);}
}//数码管显示函数
void Seg_Disp(unsigned char wela,dula)
{P0 = 0x00; //消影P2_6 = 1;P2_6 = 0; P0 = Seg_Wela[wela];P2_7 = 1;P2_7 = 0; P0 = Seg_Dula[dula];P2_6 = 1;P2_6 = 0;
}//定时器初始化函数
void Timer0Init(void) //1毫秒@12.000MHz
{TMOD &= 0xF0; //设置定时器模式TMOD |= 0x01; //设置定时器模式TL0 = 0x18; //设置定时初始值TH0 = 0xFC; //设置定时初始值TF0 = 0; //清除TF0标志TR0 = 1; //定时器0开始计时ET0 = 1;EA = 1;
}//中断服务函数
void Timer0Server() interrupt 1
{TL0 = 0x18; //设置定时初始值TH0 = 0xFC; //设置定时初始值Seg_Pos++;if(Seg_Pos == 6) Seg_Pos = 0;Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]);}/* Main */
void main()
{Timer0Init();while(1){}
}
- 头文件和变量声明
#include <REGX52.H> 和 #include <intrins.h> 分别包含 8051 单片机的头文件和一些内联汇编函数的头文件。
Led_Data 是用于循环位移的 LED 变量。
Seg_Wela 和 Seg_Dula 是用于控制共阴数码管的位选和段选的数组。
Seg_Pos 用于表示当前数码管显示的位置。
Seg_Buf 是用于存储数码管显示的数字。 - 延时函数:
Delay500ms() 实现了一个 500 毫秒的延时函数,通过嵌套循环实现延时。
Delay(unsigned int xms) 是一个自定义延时函数,根据传入的毫秒数进行延时。 - 数码管显示函数:
Seg_Disp(unsigned char wela, dula) 用于控制数码管的显示。通过设置 P0 端口和 P2.6、P2.7 引脚的状态来控制位选和段选。 - 定时器初始化函数和中断服务函数:
Timer0Init() 初始化定时器0,配置为工作在方式1,用于产生1毫秒的定时中断。
Timer0Server() 是定时器0的中断服务函数,通过定时中断实现LED循环位移和数码管显示的刷新。 - Main 函数:
main() 函数中初始化了定时器,并进入一个无限循环,程序主要通过定时器中断服务函数进行 LED 和数码管的显示控制。
补充知识:8051的中断源
以下内容搬运自博客:http://blog.chinaunix.net/uid-20629402-id-1608165.html
(1)8051的5个中断源
(2)中断函数写法
返回值 函数名 interrupt n { …… }
n对应中断源编号。
例如:
//中断服务函数
void Timer0Server() interrupt 1
{TL0 = 0x18; //设置定时初始值TH0 = 0xFC; //设置定时初始值Seg_Pos++;if(Seg_Pos == 6) Seg_Pos = 0;Seg_Disp(Seg_Pos,Seg_Buf[Seg_Pos]);}
interrupt 1表示这个是5个中断源中Timer0的中断服务函数!
(3)中断触发控制寄存器IE (左边是高位)
- EX0:响应外部/INT0的中断
- ET0:响应TIMER0的中断
- EX1、ET1:对应/INT1与TIMER1
- ES:对应UART
- ET2:响应TIMER2溢出或捕捉的中断(仅对8052)
- EA:中断使能。EA=1时才允许中断。
(4)中断优先级控制寄存器IP
分别对应各中断的优先级。仅分0(低)、1(高)两级,同级的中断还是看中断源编号进行优先级排序。
(5)TIMER控制寄存器TCON
- TF1与TF0分别是硬件去置位的,当Timer1/Counter1溢出时,TF1会被置为1,而当处理器去执行中断服务时,它又被硬件置0。(当然TF0管的就是Timer0/Counter0了)
- TR1与TR0由软件置位,管的是Timer/Counter的激活。(如在程序里写TR0=1,就是说Counter0开始计数,当然如果这个Timer/Counter被设置为Counter的工作方式的话)
- IE1与IE0由硬件置位,与TF1、TF0等同,只是IE1与IE0管的是外部中断。
- IT1与IT0由软件置位,设为1时,对应的外部中断为负缘触发,设0时为低准位触发。