dumon
发表于 2024-3-24 22:28:45
第9集 下 数码管的动态显示
在上节课中,学习了数码管的静态显示,因为数码管是共阴极或共阳极接在一起的,这样做的目的是为了节省IO,如果以4位8段数码管来取例,我们单片机的I/O是不足够使用的。
所以用的共阳或共阴的方式把LED接起来,这样能省许多IO,如8位8段数码管我们最多只需16个就足够了。这样随之带来的1个问题是,数码管1位1位轮流显示是没问题,但是同时显示的话就会出现错码。这里冲哥视频里给出的方法是利用人眼的视觉停留,将数码管字符显示循环起来,每1位切换时间在2ms内,这样8位的总时间也可管控在20ms内,我们人眼是分辩不出来从第1位显示到第8位的字符有停顿的,感觉就像同时出现一样,但是如果你把切换时间延长到200ms,就可以明显看到字符切换停顿了。
冲哥程序内是用数组来快捷显示数字字符
DIG_num = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,x7F,0x6F}; //段码数组用来保存0~9 十个数字
SEG_num = {0xFE,0xFD,0xF7};//位码数组用来表示第1位,第2位,第3位,如果是8位,就把[ ]元素长度改为10。
show_Tab = {};//用来存储数码管每位要显示的数字,注意SEG_num[ ]和show_Tab[ ]数组长度要一致,因为后期要用到[ ]内的元素索引进行循环。
在主程序或自定义函数中,可以重新对show_Tab中的元素重新赋值,并把值传递给DIG_num中的k值。
如:
show_Tab = 4; //注意show_Tab内的值必须是0~9,因为是10进制显示,而数码管只能1位1位显示,如果想数码管显示123,必须让第1位显示1,第2位显示2,第3位显示3,可以把123通过除10的倍数和求余的方式得到1,2,3。
P0 = DIG_num] =DIG_num = 0x66;//这样实现了变量的传递。
动态显示示例:数码管显示123
show_Tab = 1; //(123/100)%10=1, 假如把123看成1个变量k,随着程序每运行1次k自加1,这样就可以让数码管随着程序运行动态显示递增数字了。这就是变量的传递,在程序中其实很多部分都是在说明变量的传递过程。只有变量我们程序才充满多样化,不会死板,有更多灵活性。
show_Tab = 2; //(123/10)%10=2
show_Tab = 3; //(123/1)%10=3
int num = 0;
for(num = 0; num<3; num++)
{
P1 = SEG_num; //先给位码赋值
P0 = DIG_num];//再给段码赋值
delay_ms(2);
}
我自己本身有做笔记的习惯,看了视频一遍,自己再把其中的重点和知识点提炼出来,这样才能加深自己的理解,否则自己不知道视频内的重点是啥。如果一次看不懂,没关系,多看几遍就懂了。一定要有耐心!
附上自己的笔记,供参考!
电子爱好者2024
发表于 2024-3-25 21:01:43
已阅,顶起!
电子爱好者2024
发表于 2024-3-29 22:04:21
继续加油啊
dumon
发表于 2024-4-1 22:13:27
第11集 定时器的使用
1.定时器的本质是一个加法计数器!对脉冲进行计数。
计数脉冲来自系统时钟——定时器,有12T和1T两种模式,12T是12个时钟才计数1,1T是1个时钟就计数1。
计数脉冲来自单片机外部引脚——计数器。
2.STC32G单片机有5个定时器T0,T1,T2,T3,T4。芯片支持定时器数量需查阅相应芯片手册。
计时模式与计数模式设定寄存器——T0~T1:TMOD T2:AUXR T3~T4:T4T3M
3.要学会使用寄存器来配置定时器/计数器
B7 B6 B5 B4 B3 B2 B1 B0
TCONTF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0
TF1:T1溢出中断标志
TR1:T1运行控制位,T1_GATE = 0,TR1=1时允许T1开始计数
TF0:T0溢出中断标志
TR0:T0运行控制位,T0_GATE = 0,TR0=1时允许T0开始计数
一般不直接对TCON进行操作,而是分开以‘位’来做操作。
TH0,TH1,TH2,TH3,TH4:定时器T0/T1/T2/T3/T4高4位
TL0, TL1, TL2,TL3, TL4:定时器T0/T1/T2/T3/T4低4位
B7 B6 B5 B4 B3 B2 B1 B0
TMODT1_GATE T1_C/T T1_M1 T1_M0 T0_GATE T0_C/T T0_M1 T0_M0
T1_GATE:控制定时器T1,置1时只有在INT1脚为高及TR1=1时才能打开定时器1
T0_GATE:T0_GATE= 1 & INT0=1 & TR0 =1时才能打开定时器0
T1_C/T:T1_C/T = 0时,T1为定时器,置1时为计数器,对应外部端口为P3.5
T0_C/T:T0_C/T = 0时,T0为定时器,置1时为计数器,对应外部端口为P3.4
T1_M1/T1_M0:定时器T1工作模式选择,共有3种模式
T0_M1/T0_M0:定时器T0工作模式选择,共有4种模式
B7 B6 B5 B4 B3 B2 B1 B0
AUXR T0x12 T1x12 UART_M0x6 T2R T2_C/T T2x12 EXTRAM S1BRT
T0x12:置0时为12分频,置1时为不分频
T1x12:置0时为12分频,置1时为不分频
T0_M1 T0_M0 说明 模式
0 0 16位自动重装载 模式0
0 1 16位不自动重装载 模式1
1 0 8位自动重装载 模式2
1 1 不可屏蔽中断的16位自动重装载 模式3
定时器支持工作模式
模式0 模式1 模式2 模式3
T0 V V V V
T1 V V V X
T2 V X X X
T3 V X X X
T4 V X X X
STC32G为8位预分频+16位定时器:16位表示为2的16次方=65536
TM0PS/TM1PS/TM2PS/TM3PS/TM4PS:为8位二进制数,范围为0x00~0xFF,十进制为0~255。
定时器定时周期计算公式:
1T:定时器周期 = (65536-)/ (SYSCLK/(TM0PS+1))
12T:定时器周期 = { (65536-)/ (SYSCLK/(TM0PS+1)) } X 12
如果TM0PS不设置值,默认为0。
同样的TH0和TL0,12T为1T的12倍,假如1T最大定时周期只能计到0.1s时,设为12T,定时周期可达到1.2s。
定时器充当的作用:
在主程序中设立中断程序,定时器中断,可实现硬件计时,或者使程序每隔一固定时间完成一项操作。
替代长时间的delay,提高CPU的运行效率和处理速度,能及时的响应某个事件。
会根据自己所需的定时周期来反推TH0,TL0的值。根据定时器周期计算公式反推。注意是1T还是12T。
dumon
发表于 2024-4-2 15:20:54
定时器相关函数:定时器初始化函数与定时器中断函数。
定时器初始化函数主要是用于对定时器T0/T1/T2/T3/T4进行相关寄存器的配置,如指定是定时功能还是计数功能,12T/1T时钟模式,定时器TH0,TL0的初始值等。
16位很好理解,就是2的16次方,即65536,假设TH0和TL0最大存储组合为0xFFFF,即65535,当TH0和TL0等于65535时表示定时器要溢出了,如果是8位定时器,就只用到TL0,最大只能到255。
所以大家要知道这个16位和8位指的是什么,就是定时器TH0和TL0的最大值。
在定时器寄存器配置这里,我琢磨出几个点,因为TMOD是T0和T1共用寄存器嘛,冲哥视频里用了与运算,和或运算。
如:
AUXR |= 0x80; //定时器时钟1T模式,这里用或运算是为了保证T0x12 = 1不变,7F=1000,0000.
TMOD &= 0xF0; //设置定时器模式为16位自动重装载,F0=1111,0000,T0_GATE= 0,T0_C/T=0,T0_M1=0,T0_M0=0,
这里大家琢磨下,如果我想保持T0为1T模式不变,我就必须用或的方式,因为这位为1,如果在前面的程序中有写入AUXR = 0x08,是不是可以保证第1位仍为0不变,不会受到后面的干扰。
那如果想保持某位为0不变,就适用与门,因为与门是全1得1,只要有1个0,那这位就是始终为0了。请看TMOD &= 0xF0,保证低4位为0,即T0的工作模式不被打扰。
与门适合那种保持某位为0状态,因为更新后无论是1或0,这位始终为0。
或门适合保持某位为1状态,因为这位只要是1就不受影响。
这个在后面都可以用到。
为什么这里要讲下与,或,非门呢,因为在视频中有介绍到手册“定时器0模式”,用1张图说明了定时器的工作原理,就必须掌握与或非门才能更好的理解定时器工作流程,因为寄存器的某1位在这里面充当的开关的作用,在为0或置1时会导致定时器走向不同的路线。
具体大家看下我上传的图片,里面都要说明。这里也更好理解为什么T0_GATE = 1 & INT0 = 1 &TR0 =1时才能打开定时器0/计数器0了。大家看着我的注释一起把流程走一遍,就会有一种豁然开朗的感觉。在这个图里也能知道到底要配置哪些寄存器与参数了。
放上一个程序,供大家参考下。同时EXCEL文档内有定时周期计算公式,方便大家学习,当然STC-ISP工具也有定时器计算器,给我们省了很大的事,但是我们还是要弄懂这个原理。
/***********************************************************************
第11集 定时器
___
主程序 /
| __/
| __ 定时器计时到了,先执行定时器中断程序,
| \ 执行完后再返回主程序继续执行。
主程序 \___
1.定时器/计数器的核心部件是一个加法计数器,其本质是对脉冲进行计数。只是计数脉冲来源不同:
如果计数脉冲来自系统时钟,则为定时方式 ,此时定时器/计数器每12个时钟或者每1个时钟得到一个
计数脉冲,计数值加1;如果计数脉冲来自于单片机外部引脚,则为计数方式,每来1个脉冲加1。
2.STC32G支持24位定时器,8位预分频+16位自动重装载。
3.定时器/计数器寄存器说明:
符号 描述 地址 B7 B6 B5 B4 B3 B2 B1 B0 复位值
TCON 定时器控制寄存器 88H TF1 TR1 TF0 TR0 IE1 IT1 IE0 IT0 0000,0000
TMOD 定时器模式寄存器 89H T1_GATE T1_C/T T1_M1 T1_M0 T0_GATE T0_C/T T0_M1 T0_M0 0000,0000
AUXR 辅助寄存器1 8EH T0x12 T1x12 UART_M0x6 T2R T2_C/T T2x12 EXTRAM S1BRT 0000,0001
T4T3M 定时器4/3控制寄存器 DDH T4R T4_C/T T4x12 T4CLKO T3R T3_C/T T3x12 T3CLKO 0000,0000
TH0,定时器0高8位寄存器,TL0,定时器0低8位寄存器,最大值65535。
TH1,定时器1高8位寄存器,TL1,定时器1低8位寄存器,最大值65535。
T2H,定时器2高8位寄存器,T2L,定时器2低8位寄存器,最大值65535。
T3H,定时器3高8位寄存器,T2L,定时器3低8位寄存器,最大值65535。
T4H,定时器4高8位寄存器,T4L,定时器4低8位寄存器,最大值65535。
TM0PS, 8位定时器0时钟预分频寄存器,最大值255。
TM1PS, 8位定时器0时钟预分频寄存器,最大值255。
TM2PS, 8位定时器0时钟预分频寄存器,最大值255。
TM3PS, 8位定时器0时钟预分频寄存器,最大值255。
TM4PS, 8位定时器0时钟预分频寄存器,最大值255。
T1_M1,T1_M0 = 00B,两位二进制用来配置寄存器工作模式。
00 —— 16位自动重装载
01 —— 16位不自动重装载
10 —— 8位自动重装载
11 —— 不可屏蔽中断的16位自动重装载
定时器T0,T1,T2,T3,T4并不是全部支持4种模式,请看下表:
定时器 模式0 模式1 模式2 模式3
00B 01B 10B 11B
T0 √ √ √ √
T1 √ √ √ ×
T2 √ × × ×
T3 √ × × ×
T4 √ × × ×
T0,T1 16位自动重装载模式,定时周期计算公式:SYSclk为系统时针,TM0PS默认值为0,最大值为255
1T模式: 定时器周期 = ] /
12T模式:定时器周期 = {]/ }*12
如何配置定时器,采用寄存器写入方式:
1.选择想要使用的定时器,如选择T0,自定义T0函数void Timer0_Init(void),把2~5寄存器写入内容放到函数内;
2.选择定时器时钟模式1T/12T,12T模式——AUXR &= 0x7F; 即AUXR &= 0111 1111,T0x12 =0为12T,置1则为1T,用与运行是保证T0x12为0不变。
1T模式——AUXR |= 0x80;即AUXR |= 1000 0000,T0x12 =1为1T,用或运行是保证T0x12为1不变。
3.配置TMOD寄存器T0工作模式,TMOD &= 0xF0; 即TMOD &= 1111 0000,低4位是T0,用与运行是保证T0低4位为0000不变。
4.设定8位预分频,最大值为0xFF,即255,假设定时周期为0.1s,根据定时周期计算公式反算得出TM0PS和,编写好TH0和TL0的值。
5.TF0 = 0; //清除TF0标志,TF0是T0的中断请示位,定时器0溢出中断标志。中断服务程序中,硬件自动清零。
TR0 = 1; //定时器0开始计时,只有T0_GATE = 0,TR0 = 1时,T0才开始计时
ET0 = 1; //使能定时器0中断,是IE中断允许寄存器的B1位,ET0是T0的中断允许位,详见《STC32G芯片手册》第12.4.1节
6.新建void Timer0_Isr(void) interrupt 1 { //放中断执行程序}中断函数,注意“interrupt 1”需要在手册5.9.2节中查询对应的中断号,
这里T0的中断号是“1”。
以上,定时器T0的中断模式参数配置完成。
***本程序执行过程:主程序是P2.7上的LED亮5s,再熄灭5s。定时器是0.5s进1次中断,P2.0上的LED每隔0.5s取1次反,
所以看到程序的运行效果是,P2.7和P2.0有同时点亮的情况,类似主程序与中断程序分别执行互不干扰。***
***********************************************************************/
#include <STC32G.H>
#include "comm/stc32_stc8_usb.h"
#define MAIN_Fosc 24000000UL // 定义一个主时钟24MHz
char *USER_DEVICEDESC = NULL;
char *USER_PRODUCTDESC = NULL;
char *USER_STCISPCMD = "@STCISP#";
void sys_init();
void delay_ms(u16 ms)
{
u16 i;
do
{
i= MAIN_Fosc/6000;
while(--i);
} while(--ms);
}
void Timer0_Init(void); // 寄存器配置函数
// 流水灯相关说明,P2.0~P2.7为共阳极连接,P4.5接三极管基极,为低电平时发射极与基极接通,
// 电流饱合后,发射极到集电极接通,LED得到高电平,P2.0~P2.7输出低电平即可点灯。
sbit ON_LED = P4^5; // 点灯总开关,三极管是小电流控制大电流的开关
sbit LED_1 = P2^0; // 流水灯LED1
sbit LED_2 = P2^1; // 流水灯LED2
sbit LED_3 = P2^2; // 流水灯LED3
sbit LED_4 = P2^3; // 流水灯LED4
sbit LED_5 = P2^4; // 流水灯LED5
sbit LED_6 = P2^5; // 流水灯LED6
sbit LED_7 = P2^6; // 流水灯LED7
sbit LED_8 = P2^7; // 流水灯LED8
void main()
{
sys_init(); //系统初始化
usb_init(); //USB CDC 接口配置
EA = 1; //打开总中断
ON_LED = 0 ; //打开LED总开关
Timer0_Init();
while(1)
{
LED_8 =0;
delay_ms(5000);
LED_8 =1;
delay_ms(5000);
}
}
void Timer0_Isr(void) interrupt 1
{
//放中断执行程序
ON_LED = 0;
LED_1 = !LED_1;
}
void Timer0_Init(void) //0.5秒@24.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式,这里用与运算是为了保证T0x12 = 0不变,7F=0111,1111
TMOD &= 0xF0; //设置定时器模式为16位自动重装载,F0=1111,0000,T0_GATE= 0,T0_C/T=0,T0_M1=0,T0_M0=0
TL0 = 0xFC; //设置定时初始值低8位
TH0 = 0x45; //设置定时初始值高8位
TM0PS = 0x14; //65536-{*(0.1/12)}
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void sys_init()
{
WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口
P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口
P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口
P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口
P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口
P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口
P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口
P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口
//====== USB 初始化 ======
P3M0 &= ~0x03;
P3M1 |= 0x03;
IRC48MCR = 0x80;
while (!(IRC48MCR & 0x01));
// USBCLK = 0x00; //使用USB-HID需屏蔽此行
// USBCON = 0x90; //使用USB-HID需屏蔽此行
}
dumon
发表于 2024-4-3 09:57:49
定时器如果想进中断,一定要记得开放所有中断:EA = 1;
如何让定时器定时更长?
1.可将TM0PS 8位预分频寄存器数值增加,最大为255。
以1T时钟,SYSCLK = 24MHz 为例,
TM0PS = 0 时, 最大定时周期 = 65536/(24000000/1) = 0.0027s
TM0PS = 255 时, 最大定时周期 = 65536/(24000000/(255+1)) = 0.6990s
可以看出分母除以了256,整体放大了256倍。这是其中1个方法。
2.1T时钟改为12T时钟,12个脉冲计数1次,放大12位。
12T: 最大定时周期 = (65536/(24000000/1))*12 = 0.0327s。
3.定时器内增加1个累加计数器,假设定时器定时为1ms@24MHz,在定时器中断程序内每隔1ms计数器变量自加1,等这个变量自加到我们想要的数值时,这个时间相当于变相延长。
举例:
void Time0_Isr(void)interrupt 1 // 每隔1ms@24Mhz进入中断
{
static u16 num = 0; // 注意标记为静态变更,只在程序第1次时赋初值。
num++;
if(num >= 2000)
{
// 执行中断分程序
/*中断程序放在此处*/
num = 0 ; // 计数变量清0
}
}
IE中断允许寄存器,地址为A8H,
bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0
IEA8H EA ELVD EADC ES ET1 EX1 ET0 EX0
不建议直接对IE进行写操作,而是对IE上的位进行单独操作,如想让T0允许中断,就把ET0 = 1; 想让T1允许中断,就把ET1 = 1;
计数器的用法:
如想每计数1个脉冲进入中断,因为TH0,TL0是65536溢出,那么65536 -1 = 65535 = 0xFFFF,即得到TH0 = 0xFF,TL0 = 0xFF。这样可以得到1个公式,如果想n个计数脉冲后进入中断,则 = 65536 - n。
通过数码管显示计数值有2种方法:
1.中断法,通过进中断执行中断程序增加计数值,因数码管动态显示特性,需控制在20ms内,可进定时器刷新或在主程序循环执行,注意主程序运行时间要在20ms内。
2.查询寄存器法,查询TH0,TL0寄存器的值,并将值赋给显示变量,通过中转方式显示。最大为65535。
定时器与计数器学习大概就到这里了,通过冲哥和布丁橘子视频学习对定时器和计数器有一定了解,算是掌握了定时器和计数器的基本用法。
好记性不如烂笔头,不是经常使用单片机的话,过一段时间真的会忘记,翻翻笔记就又想起来了。最后附上学习笔记!!!
dumon
发表于 2024-4-15 20:15:00
第13集简易多任务处理
1.重点理清程序的思路
2.应用模块化的编程
一个功能对应一个.c和.h文件,引脚定义都放在.h文件下,引脚使用宏定义方式#define,便于后期维护,不要在程序中直接对P4等进行操作。
.h文件中放置函数声明,.c文件中放置函数定义,在main主程序中进行函数调用。
步骤一,新建XX.h文件,并添加如下内容:
#ifndef __XX_H
#define __XX_H
#include "STC32G.h"//你想要引用的头文件
... ...
#endif
步骤二,新建XX.c文件,并设置引用头文件:
#include "XX.h"
步骤三,设置添加include 引用路径,将XX.c文件添加到工程里,在main.c程序中添加XX.h头文件。
小应用:
template增加自定义关键词,特别注意增加了不会立刻看到,需要”重启keil软件才能看到“。
修饰符”extern“的使用说明:
修饰符extern用在变量或者函数的声明前,用来说明”此变量或函数是在别处定义的,要在此处引用。
举例1:
文件a.c 需要引用b.c 中的变量int v,在a.c中声明extern int v,就可以引用变量v
举例2:
文件a.c 需要引用b.c 中的变量int v,在b.h中声明extern int v,然后在a.c中调用b.h就可以引用变量v。
特别注意!!!extern修饰的变量不能赋初值,不能赋初值,不能赋初值。
bdata位寻址变量的使用——8位:
a.c a.h
u8 bdata LED = 0x00; extern u8 bdata LED;
sbit LED0 = LED^0; extern bit LED0;
sbit LED1 = LED^1; extern bit LED1;
sbit LED2 = LED^2; extern bit LED2;
sbit LED3 = LED^3; extern bit LED3;
sbit LED4 = LED^4; extern bit LED4;
sbit LED5 = LED^5; extern bit LED5;
sbit LED6 = LED^6; extern bit LED6;
sbit LED7 = LED^7; extern bit LED7;
这里是利用举例2的方式,在.h文件中声明extern变量,那么在别的.c文件中通过调用a.h可以使用a.h的变量。
static 静态变量 : 程序只在初次运行时赋初值,后面不再赋初值,变量只会赋值一次,这就是静态变量。
举例:
u8 i = 0; 每次都会从0开始
static u8 i = 0;只有第1次为0,后面不会再重0开始,除非在后面语句手动写i = 0,才会从0开始。
大家可以看下这2段程序,为定时器T0中断函数,没有static修饰的是不是永远为0,不会移灯显示,而有static修饰的则会有流水点灯
void Tim0_isr() interrupt 3
{
u8 i = 0;
if(i<8)
{
P1 = ~(1<<i);
i++;
}
}
void Tim0_isr() interrupt 3
{
static u8 i = 0;
if(i<8)
{
P1 = ~(1<<i);
i++;
}
if(i==8)
i = 0;
}
电子爱好者2024
发表于 2024-4-15 21:39:43
已阅
dumon
发表于 2024-4-17 10:04:43
第13集 多任务处理
应用小技巧:
选择某块复制,光标停在你想要复制的位置,按下“shift+Alt”不松,再拖动鼠标选取内容,ctrl+c复制,ctrl+v粘贴,注意粘贴不会带有回车,需要提高预留出你想要粘贴的位置。
拖动鼠标形成一条垂直线时,可插入字符串内容键盘,可键盘输入。这个需要实际操作一下就会了。
新理念——分时复用技术(循环刷新)
视频中,实验箱LED流水灯与数码管共用1组P60~P67端口,怎么让流水灯与数码管同时显示?——打时间差
8位数码管是8个段码与8个位码,段码是用P6口,位码是用P7口,8位流水线是用P6口做为低电平,P4.0作为共阳极VCC。
设定定时器T0每1ms进入一次中断程序,增加1个num变量,每进1次中断程序,num自加1,
当0<num<7时,共计8个毫秒,这8个毫秒用于刷新8位数码管显示,在这8个毫秒内流水线的P4.0是关闭的,这样只会刷新数码管,而不会更新流水灯。
当num = 8时,关闭数码管的位码端口P7,打开LED的共阳极P4.0,刷新LED的显示,而不会更新数码管的显示。
当num >8时,则关闭P7和P4.0,表示数码管和LED全部熄灭无显示,并将num清0。
这样以10个ms为1个小周期,可以周而复始刷新数码管和LED,使两者不冲突。
以上的关键是:数码管与LED要分别再有1个独立的开关可控制,虽然两者都共用P6口,但数码管有P7可控制开关,LED有P4.0控制开关,这才是两者不冲突显示的原因。
这个大家要理解一下,实际去操作一下,如果数码管和LED没有单独的开关,就会造成并联显示,因为两者共用P6口,只要P6口有输出高低电平,两者都会同时显示。
因为我手上暂时没有实验箱,我是用的自制开发板,只有3位数码管,大家可以参考一下,总的工作模式和原理是相同的。
.c文件
<blockquote>#include "SEG_LED.h".h文件
#ifndef __SEG_LED_H
#define __SEG_LED_H
#include <STC32G.h>
#include "comm/stc32_stc8_usb.h"
/**************引脚定义**************/
#define DIG P0 // 定义数码管段码引脚
#define SEG P1 // 定义数码管位码引脚
#define LED_ONP45 // LED的电源开关引脚
/**************变量声明**************/
extern u8 SEG_show; // 数码管的显示变量,外部变量,在此处.h文件中声明后,其他.c可调用这个.h文件进行变量引用
extern u8 bdata LED_DATA; // LED的状态变量
#define LED LED_DATA // 8个LED的控制变量
#define DIG0 SEG_show // 数码管0的控制变量
#define DIG1 SEG_show // 数码管1的控制变量
#define DIG2 SEG_show // 数码管2的控制变量
#define DIG3 SEG_show // 数码管3的控制变量
#define DIG4 SEG_show // 数码管4的控制变量
#define DIG5 SEG_show // 数码管5的控制变量
#define DIG6 SEG_show // 数码管6的控制变量
#define DIG7 SEG_show // 数码管7的控制变量
extern bit LED0; // LED0的状态变量
extern bit LED1; // LED1的状态变量
extern bit LED2; // LED2的状态变量
extern bit LED3; // LED3的状态变量
extern bit LED4; // LED4的状态变量
extern bit LED5; // LED5的状态变量
extern bit LED6; // LED6的状态变量
extern bit LED7; // LED7的状态变量
/**************函数声明**************/
void SEG_LED_display(void);
#endif
dumon
发表于 2024-4-17 10:07:14
#include "SEG_LED.h"
u8 DIG_number = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00}; // 数码管段码数组,显示0~9不带小数点,关闭显示
u8 SEG_number= {0xF7,0xFD,0xFE}; // 数码管位码数组,左边为第1位,共3位
u8 SEG_show = {0,10,10}; // 数码管显示数组,用于把变量传递给DIG_number[]中的元素索引,如SEG_show=‘0’,DIG_number
//bdata 可位寻址变量,代表这是1个8位变量,可对其中单独某位进行操作,注意位变量只有0或1两种值。
u8 bdata LED_DATA = 0xFF; // 8位可位寻址变量,加入bdata,表示LED_DATA是1个8位位地址变量,可以单独对LED_DATA的某一位进行操作
//对位寻址变量每一位做sbit声明,如LED0表示LED_DATA的低1位,对LED0进行0或1赋值,相当于对LED_DATA^0赋值,这里相当于IO端口,P32 = P3^2是同样原理
//LED_DATA只相当于1个8个槽的抽屉,每个槽只能放0或1,假设LED_DATA = 0xF0; 在后面可以用P2 = LED_DATA,等价于P2 = 0xF0;
sbit LED0 = LED_DATA^0; // LED0定义,LED_DATA的低1位
sbit LED1 = LED_DATA^1; // LED1定义,LED_DATA的低2位
sbit LED2 = LED_DATA^2; // LED2定义,LED_DATA的低3位
sbit LED3 = LED_DATA^3; // LED3定义,LED_DATA的低4位
sbit LED4 = LED_DATA^4; // LED4定义,LED_DATA的高1位
sbit LED5 = LED_DATA^5; // LED5定义,LED_DATA的高2位
sbit LED6 = LED_DATA^6; // LED6定义,LED_DATA的高3位
sbit LED7 = LED_DATA^7; // LED7定义,LED_DATA的高4位
/******************************函数定义******************************/
//********************************************************************
//函数名称:void SEG_LED_display(void)
//函数功能:数码管与LED刷新显示
//入口参数:无
//函数返回:无
//当前版本:V1.0
//修改日期:2024/04/01
//当前作者:
//其他备注:这个程序是数码管和LED共用1组IO口,怎么让数码管和IO口同时显示呢,是用1个叫循环刷新(分时复用)的方法。打的1个时间差,利用人眼在20ms内分不清LED有无连续显示。
// 定时器每1ms进入1次中断,前3ms用于数码管刷新显示,第4ms用于LED显示,第5ms用于全部关闭显示,第6ms将num清0,每6ms为1个周期循环
//********************************************************************
void SEG_LED_display(void)
{
static u8 num = 0; // 这里使用静态变量static,只在程序第1次运行时赋初值,防止num在函数二次调用时又从0开始
P0 = 0x00; // 为什么这里要加入这行,是因为我的开发板P0口接到板载数码管上了,而P0口正常是高电平,会导致数码管点亮
if(num<=2) // 定时器T0每1ms进入1次中断,num = 0,1,2时刷新3位数码管显示
{
LED_ON = 1; // LED电源关闭,如果不关闭LED电源,LED也会同步显示
SEG = SEG_number; // 先数码管位码显示
DIG = DIG_number]; // 再数码管段码显示
}
else if(num <= 3) // num = 3时,刷新LED显示
{
LED_ON = 0; // LED电源打开,刷新LED显示
SEG = 0xFF; // 数码管位码关闭,关闭数码管显示,如果不关闭,数码管还会显示乱码
DIG = LED_DATA; // 刷新LED显示,将LED_DATA的8位值传递给DIG,实际是在此处将LED_DATA的值写入给P口了
}
else // num = 4时,关闭LED显示,关闭数码管显示
{
LED_ON = 1;
SEG = 0xFF;
DIG = 0xFF;
}
num++; // num自加1
if(num >=5) // num = 5时,num清0
num = 0;
}
.c 代码没有上传成功,重新上传一下。