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 代码没有上传成功,重新上传一下。
页: 1 2 [3] 4 5
查看完整版本: 跟 冲哥教学视频学习STC——记录每一天的成长| 必须立即送强大的实验箱支持啊 !