找回密码
 立即注册
楼主: du***

跟 冲哥教学视频学习STC——记录每一天的成长| 必须立即送强大的实验箱支持啊 !

[复制链接]
  • TA的每日心情
    开心
    半小时前
  • 签到天数: 152 天

    [LV.7]常住居民III

    6

    主题

    66

    回帖

    506

    积分

    高级会员

    积分
    506
     楼主| 发表于 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[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,x7F,0x6F};   //段码数组用来保存0~9 十个数字
    SEG_num[3] = {0xFE,0xFD,0xF7};  //位码数组用来表示第1位,第2位,第3位,如果是8位,就把[ ]元素长度改为10。
    show_Tab[3] = {};  //用来存储数码管每位要显示的数字,注意SEG_num[ ]和show_Tab[ ]数组长度要一致,因为后期要用到[ ]内的元素索引进行循环。

    在主程序或自定义函数中,可以重新对show_Tab[n]中的元素重新赋值,并把值传递给DIG_num[k]中的k值。
    如:
    show_Tab[1] = 4;   //注意show_Tab[1]内的值必须是0~9,因为是10进制显示,而数码管只能1位1位显示,如果想数码管显示123,必须让第1位显示1,第2位显示2,第3位显示3,可以把123通过除10的倍数和求余的方式得到1,2,3。
    P0 = DIG_num[show_Tab[1]] =DIG_num[4] = 0x66;  //这样实现了变量的传递。

    动态显示示例:数码管显示123
    show_Tab[0] = 1;         //(123/100)%10=1, 假如把123看成1个变量k,随着程序每运行1次k自加1,这样就可以让数码管随着程序运行动态显示递增数字了。这就是变量的传递,在程序中其实很多部分都是在说明变量的传递过程。只有变量我们程序才充满多样化,不会死板,有更多灵活性。

    show_Tab[1] = 2;         //(123/10)%10=2

    show_Tab[2] = 3;        //(123/1)%10=3

    int num = 0;
    for(num = 0; num<3; num++)
    {
       P1 = SEG_num[num];           //先给位码赋值
       P0 = DIG_num[show_Tab[num]];  //再给段码赋值
       delay_ms(2);
    }

    我自己本身有做笔记的习惯,看了视频一遍,自己再把其中的重点和知识点提炼出来,这样才能加深自己的理解,否则自己不知道视频内的重点是啥。如果一次看不懂,没关系,多看几遍就懂了。一定要有耐心!
    附上自己的笔记,供参考!

    数码管静态显示原理

    数码管静态显示原理

    数码管动态显示方法

    数码管动态显示方法
    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    4 小时前
  • 签到天数: 94 天

    [LV.6]常住居民II

    11

    主题

    139

    回帖

    263

    积分

    中级会员

    积分
    263
    发表于 2024-3-25 21:01:43 | 显示全部楼层
    已阅,顶起!
    回复 送花

    使用道具 举报

  • TA的每日心情
    开心
    4 小时前
  • 签到天数: 94 天

    [LV.6]常住居民II

    11

    主题

    139

    回帖

    263

    积分

    中级会员

    积分
    263
    发表于 2024-3-29 22:04:21 | 显示全部楼层
    继续加油啊
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    半小时前
  • 签到天数: 152 天

    [LV.7]常住居民III

    6

    主题

    66

    回帖

    506

    积分

    高级会员

    积分
    506
     楼主| 发表于 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
    TCON  TF1   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
    TMOD  T1_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-[TH0,TL0])/ (SYSCLK/(TM0PS+1))
    12T:定时器周期 = { (65536-[TH0,TL0])  / (SYSCLK/(TM0PS+1)) } X 12

    如果TM0PS不设置值,默认为0。
    同样的TH0和TL0,12T为1T的12倍,假如1T最大定时周期只能计到0.1s时,设为12T,定时周期可达到1.2s。

    定时器充当的作用:
    在主程序中设立中断程序,定时器中断,可实现硬件计时,或者使程序每隔一固定时间完成一项操作。
    替代长时间的delay,提高CPU的运行效率和处理速度,能及时的响应某个事件。

    会根据自己所需的定时周期来反推TH0,TL0的值。根据定时器周期计算公式反推。注意是1T还是12T。
    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    半小时前
  • 签到天数: 152 天

    [LV.7]常住居民III

    6

    主题

    66

    回帖

    506

    积分

    高级会员

    积分
    506
     楼主| 发表于 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工具也有定时器计算器,给我们省了很大的事,但是我们还是要弄懂这个原理。
    1. /***********************************************************************
    2.         第11集 定时器
    3.                   ___
    4.         主程序   /   
    5.           |          __/
    6.           |   __     定时器计时到了,先执行定时器中断程序,
    7.           |     \     执行完后再返回主程序继续执行。
    8.         主程序   \___
    9.        
    10. 1.定时器/计数器的核心部件是一个加法计数器,其本质是对脉冲进行计数。只是计数脉冲来源不同:
    11. 如果计数脉冲来自系统时钟,则为定时方式 ,此时定时器/计数器每12个时钟或者每1个时钟得到一个
    12. 计数脉冲,计数值加1;如果计数脉冲来自于单片机外部引脚,则为计数方式,每来1个脉冲加1。
    13. 2.STC32G支持24位定时器,8位预分频+16位自动重装载。
    14. 3.定时器/计数器寄存器说明:
    15. 符号        描述                                地址        B7                B6                B5                B4                B3                B2                B1                B0                复位值
    16. TCON        定时器控制寄存器        88H                TF1                TR1                TF0                TR0                IE1                IT1                IE0                IT0                0000,0000
    17. TMOD        定时器模式寄存器        89H                T1_GATE        T1_C/T        T1_M1        T1_M0        T0_GATE        T0_C/T        T0_M1        T0_M0        0000,0000
    18. AUXR        辅助寄存器1                        8EH                T0x12        T1x12 UART_M0x6        T2R                T2_C/T        T2x12        EXTRAM        S1BRT        0000,0001
    19. T4T3M        定时器4/3控制寄存器        DDH                T4R                T4_C/T        T4x12        T4CLKO        T3R                T3_C/T        T3x12        T3CLKO        0000,0000
    20. TH0,定时器0高8位寄存器,TL0,定时器0低8位寄存器,最大值65535。
    21. TH1,定时器1高8位寄存器,TL1,定时器1低8位寄存器,最大值65535。
    22. T2H,定时器2高8位寄存器,T2L,定时器2低8位寄存器,最大值65535。
    23. T3H,定时器3高8位寄存器,T2L,定时器3低8位寄存器,最大值65535。
    24. T4H,定时器4高8位寄存器,T4L,定时器4低8位寄存器,最大值65535。
    25. TM0PS, 8位定时器0时钟预分频寄存器,最大值255。
    26. TM1PS, 8位定时器0时钟预分频寄存器,最大值255。
    27. TM2PS, 8位定时器0时钟预分频寄存器,最大值255。
    28. TM3PS, 8位定时器0时钟预分频寄存器,最大值255。
    29. TM4PS, 8位定时器0时钟预分频寄存器,最大值255。
    30. T1_M1,T1_M0 = 00B,两位二进制用来配置寄存器工作模式。
    31. 00 —— 16位自动重装载
    32. 01 —— 16位不自动重装载
    33. 10 —— 8位自动重装载
    34. 11 —— 不可屏蔽中断的16位自动重装载
    35. 定时器T0,T1,T2,T3,T4并不是全部支持4种模式,请看下表:
    36. 定时器        模式0        模式1        模式2        模式3
    37.                  00B         01B         10B         11B
    38. T0                  √                  √                  √                 √
    39. T1                  √                  √                  √                 ×
    40. T2                  √                  ×                  ×                 ×
    41. T3                  √                  ×                  ×                 ×
    42. T4                  √                  ×                  ×                 ×
    43. T0,T1 16位自动重装载模式,定时周期计算公式:SYSclk为系统时针,TM0PS默认值为0,最大值为255
    44. 1T模式: 定时器周期 = [65536-[TH0,TL0]] / [SYSclk/(TM0PS+1)]
    45. 12T模式:定时器周期 = {[65536-[TH0,TL0]]/ [SYSclk/(TM0PS+1)]}*12
    46. 如何配置定时器,采用寄存器写入方式:
    47. 1.选择想要使用的定时器,如选择T0,自定义T0函数void Timer0_Init(void),把2~5寄存器写入内容放到函数内;
    48. 2.选择定时器时钟模式1T/12T,12T模式——AUXR &= 0x7F; 即AUXR &= 0111 1111,T0x12 =0为12T,置1则为1T,用与运行是保证T0x12为0不变。
    49.   1T模式——AUXR |= 0x80;即AUXR |= 1000 0000,T0x12 =1为1T,用或运行是保证T0x12为1不变。
    50. 3.配置TMOD寄存器T0工作模式,TMOD &= 0xF0; 即TMOD &= 1111 0000,低4位是T0,用与运行是保证T0低4位为0000不变。
    51. 4.设定8位预分频,最大值为0xFF,即255,假设定时周期为0.1s,根据定时周期计算公式反算得出TM0PS和[TH0,TL0],编写好TH0和TL0的值。
    52. 5.TF0 = 0;        //清除TF0标志,TF0是T0的中断请示位,定时器0溢出中断标志。中断服务程序中,硬件自动清零。
    53.   TR0 = 1;        //定时器0开始计时,只有T0_GATE = 0,TR0 = 1时,T0才开始计时
    54.   ET0 = 1;        //使能定时器0中断,是IE中断允许寄存器的B1位,ET0是T0的中断允许位,详见《STC32G芯片手册》第12.4.1节
    55. 6.新建void Timer0_Isr(void) interrupt 1 { //放中断执行程序  }中断函数,注意“interrupt 1”需要在手册5.9.2节中查询对应的中断号,
    56.   这里T0的中断号是“1”。
    57.   
    58.   以上,定时器T0的中断模式参数配置完成。
    59. ***本程序执行过程:主程序是P2.7上的LED亮5s,再熄灭5s。定时器是0.5s进1次中断,P2.0上的LED每隔0.5s取1次反,
    60.                 所以看到程序的运行效果是,P2.7和P2.0有同时点亮的情况,类似主程序与中断程序分别执行互不干扰。***
    61. ***********************************************************************/
    62. #include <STC32G.H>
    63. #include "comm/stc32_stc8_usb.h"
    64. #define MAIN_Fosc 24000000UL                        // 定义一个主时钟24MHz
    65. char *USER_DEVICEDESC = NULL;
    66. char *USER_PRODUCTDESC = NULL;
    67. char *USER_STCISPCMD = "@STCISP#";
    68. void sys_init();
    69. void delay_ms(u16 ms)
    70. {
    71.         u16 i;
    72.         do
    73.         {
    74.                 i= MAIN_Fosc/6000;
    75.                 while(--i);
    76.         }        while(--ms);
    77. }
    78. void Timer0_Init(void);                                        // 寄存器配置函数
    79. // 流水灯相关说明,P2.0~P2.7为共阳极连接,P4.5接三极管基极,为低电平时发射极与基极接通,
    80. // 电流饱合后,发射极到集电极接通,LED得到高电平,P2.0~P2.7输出低电平即可点灯。
    81. sbit ON_LED = P4^5;                // 点灯总开关,三极管是小电流控制大电流的开关
    82. sbit LED_1        = P2^0;                // 流水灯LED1
    83. sbit LED_2        = P2^1;                // 流水灯LED2
    84. sbit LED_3        = P2^2;                // 流水灯LED3
    85. sbit LED_4        = P2^3;                // 流水灯LED4
    86. sbit LED_5        = P2^4;                // 流水灯LED5
    87. sbit LED_6        = P2^5;                // 流水灯LED6
    88. sbit LED_7        = P2^6;                // 流水灯LED7
    89. sbit LED_8        = P2^7;                // 流水灯LED8
    90. void main()
    91. {
    92.         sys_init();                  //系统初始化
    93.     usb_init();                  //USB CDC 接口配置
    94.     EA = 1;                                 //打开总中断
    95.         ON_LED = 0 ;            //打开LED总开关
    96.         Timer0_Init();
    97.         while(1)
    98.         {
    99.                 LED_8 =0;
    100.                 delay_ms(5000);
    101.                 LED_8 =1;
    102.                 delay_ms(5000);
    103.         }
    104. }
    105. void Timer0_Isr(void) interrupt 1
    106. {
    107.         //放中断执行程序
    108.         ON_LED = 0;
    109.         LED_1 = !LED_1;
    110. }
    111. void Timer0_Init(void)                //0.5秒@24.000MHz
    112. {
    113.         AUXR &= 0x7F;                        //定时器时钟12T模式,这里用与运算是为了保证T0x12 = 0不变,7F=0111,1111
    114.         TMOD &= 0xF0;                        //设置定时器模式为16位自动重装载,F0=1111,0000,T0_GATE= 0,T0_C/T=0,T0_M1=0,T0_M0=0
    115.         TL0 = 0xFC;                                //设置定时初始值低8位
    116.         TH0 = 0x45;                                //设置定时初始值高8位
    117.         TM0PS = 0x14;                        //65536-{[SYSclk/(TM0PS+1)]*(0.1/12)}
    118.         TF0 = 0;                                //清除TF0标志
    119.         TR0 = 1;                                //定时器0开始计时
    120.         ET0 = 1;                                //使能定时器0中断
    121. }
    122. void sys_init()
    123. {
    124.     WTST = 0;  //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
    125.     EAXFR = 1; //扩展寄存器(XFR)访问使能
    126.     CKCON = 0; //提高访问XRAM速度
    127.     P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
    128.     P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
    129.     P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
    130.     P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
    131.     P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
    132.     P5M1 = 0x00;   P5M0 = 0x00;   //设置为准双向口
    133.     P6M1 = 0x00;   P6M0 = 0x00;   //设置为准双向口
    134.     P7M1 = 0x00;   P7M0 = 0x00;   //设置为准双向口
    135.     //====== USB 初始化 ======
    136.     P3M0 &= ~0x03;
    137.     P3M1 |= 0x03;
    138.    
    139.     IRC48MCR = 0x80;
    140.     while (!(IRC48MCR & 0x01));
    141.        
    142. //        USBCLK = 0x00;                                  //使用USB-HID需屏蔽此行
    143. //        USBCON = 0x90;                                  //使用USB-HID需屏蔽此行
    144. }
    复制代码


    定时器0工作模式流程图

    定时器0工作模式流程图

    定时周期计算公式

    定时周期计算公式

    定时器与计数器.xlsx

    175.89 KB, 下载次数: 23

    定时周期计算与寄存器功能说明

    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    半小时前
  • 签到天数: 152 天

    [LV.7]常住居民III

    6

    主题

    66

    回帖

    506

    积分

    高级会员

    积分
    506
     楼主| 发表于 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
    IE  A8H     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个计数脉冲后进入中断,则[TH0,TL0] = 65536 - n。


    通过数码管显示计数值有2种方法:
    1.中断法,通过进中断执行中断程序增加计数值,因数码管动态显示特性,需控制在20ms内,可进定时器刷新或在主程序循环执行,注意主程序运行时间要在20ms内。
    2.查询寄存器法,查询TH0,TL0寄存器的值,并将值赋给显示变量,通过中转方式显示。最大为65535。


    定时器与计数器学习大概就到这里了,通过冲哥和布丁橘子视频学习对定时器和计数器有一定了解,算是掌握了定时器和计数器的基本用法。



    好记性不如烂笔头,不是经常使用单片机的话,过一段时间真的会忘记,翻翻笔记就又想起来了。最后附上学习笔记!!!


    IMAG0122.jpg
    IMAG0127.jpg
    IMAG0124.jpg
    IMAG0125.jpg
    IMAG0126.jpg
    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    半小时前
  • 签到天数: 152 天

    [LV.7]常住居民III

    6

    主题

    66

    回帖

    506

    积分

    高级会员

    积分
    506
     楼主| 发表于 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;
    }

    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    4 小时前
  • 签到天数: 94 天

    [LV.6]常住居民II

    11

    主题

    139

    回帖

    263

    积分

    中级会员

    积分
    263
    发表于 2024-4-15 21:39:43 | 显示全部楼层
    已阅
    回复 送花

    使用道具 举报

  • TA的每日心情
    开心
    半小时前
  • 签到天数: 152 天

    [LV.7]常住居民III

    6

    主题

    66

    回帖

    506

    积分

    高级会员

    积分
    506
     楼主| 发表于 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文件
    1. <blockquote>#include "SEG_LED.h"
    复制代码
    .h文件
    1. #ifndef __SEG_LED_H
    2. #define __SEG_LED_H
    3. #include <STC32G.h>
    4. #include "comm/stc32_stc8_usb.h"
    5. /**************引脚定义**************/
    6. #define DIG         P0                                                // 定义数码管段码引脚
    7. #define SEG         P1                                                // 定义数码管位码引脚
    8. #define LED_ON  P45                                                // LED的电源开关引脚
    9. /**************变量声明**************/
    10. extern u8 SEG_show[3];                                        // 数码管的显示变量,外部变量,在此处.h文件中声明后,其他.c可调用这个.h文件进行变量引用
    11. extern u8 bdata LED_DATA;                                // LED的状态变量
    12. #define LED         LED_DATA                                // 8个LED的控制变量
    13. #define DIG0         SEG_show[0]                                // 数码管0的控制变量
    14. #define DIG1         SEG_show[1]                                // 数码管1的控制变量
    15. #define DIG2         SEG_show[2]                                // 数码管2的控制变量
    16. #define DIG3         SEG_show[3]                                // 数码管3的控制变量
    17. #define DIG4         SEG_show[4]                                // 数码管4的控制变量
    18. #define DIG5         SEG_show[5]                                // 数码管5的控制变量
    19. #define DIG6         SEG_show[6]                                // 数码管6的控制变量
    20. #define DIG7         SEG_show[7]                                // 数码管7的控制变量
    21. extern bit LED0;                                                // LED0的状态变量
    22. extern bit LED1;                                                // LED1的状态变量
    23. extern bit LED2;                                                // LED2的状态变量
    24. extern bit LED3;                                                // LED3的状态变量
    25. extern bit LED4;                                                // LED4的状态变量
    26. extern bit LED5;                                                // LED5的状态变量
    27. extern bit LED6;                                                // LED6的状态变量
    28. extern bit LED7;                                                // LED7的状态变量
    29. /**************函数声明**************/
    30. void SEG_LED_display(void);
    31. #endif
    复制代码


    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

  • TA的每日心情
    开心
    半小时前
  • 签到天数: 152 天

    [LV.7]常住居民III

    6

    主题

    66

    回帖

    506

    积分

    高级会员

    积分
    506
     楼主| 发表于 2024-4-17 10:07:14 | 显示全部楼层
    1. #include "SEG_LED.h"
    2. u8 DIG_number[11] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00};        // 数码管段码数组,显示0~9不带小数点,关闭显示
    3. u8 SEG_number[3]  = {0xF7,0xFD,0xFE};                                                                                        // 数码管位码数组,左边为第1位,共3位
    4. u8 SEG_show[3]    = {0,10,10};                                                                                                        // 数码管显示数组,用于把变量传递给DIG_number[]中的元素索引,如SEG_show[0]=‘0’,DIG_number[0]
    5. //bdata 可位寻址变量,代表这是1个8位变量,可对其中单独某位进行操作,注意位变量只有0或1两种值。
    6. u8 bdata LED_DATA = 0xFF;                                                                                                                // 8位可位寻址变量,加入bdata,表示LED_DATA是1个8位位地址变量,可以单独对LED_DATA的某一位进行操作
    7. //对位寻址变量每一位做sbit声明,如LED0表示LED_DATA的低1位,对LED0进行0或1赋值,相当于对LED_DATA^0赋值,这里相当于IO端口,P32 = P3^2是同样原理
    8. //LED_DATA只相当于1个8个槽的抽屉,每个槽只能放0或1,假设LED_DATA = 0xF0; 在后面可以用P2 = LED_DATA,等价于P2 = 0xF0;
    9. sbit LED0 = LED_DATA^0;                                                                                                                        // LED0定义,LED_DATA的低1位
    10. sbit LED1 = LED_DATA^1;                                                                                                                        // LED1定义,LED_DATA的低2位
    11. sbit LED2 = LED_DATA^2;                                                                                                                        // LED2定义,LED_DATA的低3位
    12. sbit LED3 = LED_DATA^3;                                                                                                                        // LED3定义,LED_DATA的低4位
    13. sbit LED4 = LED_DATA^4;                                                                                                                        // LED4定义,LED_DATA的高1位
    14. sbit LED5 = LED_DATA^5;                                                                                                                        // LED5定义,LED_DATA的高2位
    15. sbit LED6 = LED_DATA^6;                                                                                                                        // LED6定义,LED_DATA的高3位
    16. sbit LED7 = LED_DATA^7;                                                                                                                        // LED7定义,LED_DATA的高4位
    17. /******************************函数定义******************************/
    18. //********************************************************************
    19. //函数名称:void SEG_LED_display(void)
    20. //函数功能:数码管与LED刷新显示
    21. //入口参数:无
    22. //函数返回:无
    23. //当前版本:V1.0
    24. //修改日期:2024/04/01
    25. //当前作者:
    26. //其他备注:这个程序是数码管和LED共用1组IO口,怎么让数码管和IO口同时显示呢,是用1个叫循环刷新(分时复用)的方法。打的1个时间差,利用人眼在20ms内分不清LED有无连续显示。
    27. //          定时器每1ms进入1次中断,前3ms用于数码管刷新显示,第4ms用于LED显示,第5ms用于全部关闭显示,第6ms将num清0,每6ms为1个周期循环
    28. //********************************************************************
    29. void SEG_LED_display(void)
    30. {
    31.         static u8 num = 0;                                                                                                                        // 这里使用静态变量static,只在程序第1次运行时赋初值,防止num在函数二次调用时又从0开始
    32.         P0 = 0x00;                                                                                                                                        // 为什么这里要加入这行,是因为我的开发板P0口接到板载数码管上了,而P0口正常是高电平,会导致数码管点亮
    33.         if(num<=2)                                                                                                                                        // 定时器T0每1ms进入1次中断,num = 0,1,2时刷新3位数码管显示
    34.         {
    35.                 LED_ON = 1;                                                                                                                                // LED电源关闭,如果不关闭LED电源,LED也会同步显示
    36.                 SEG = SEG_number[num];                                                                                                        // 先数码管位码显示
    37.                 DIG = DIG_number[SEG_show[num]];                                                                                // 再数码管段码显示
    38.         }
    39.         else if(num <= 3)                                                                                                                        // num = 3时,刷新LED显示
    40.         {
    41.                 LED_ON = 0;                                                                                                                                // LED电源打开,刷新LED显示
    42.                 SEG = 0xFF;                                                                                                                                // 数码管位码关闭,关闭数码管显示,如果不关闭,数码管还会显示乱码
    43.                 DIG = LED_DATA;                                                                                                                        // 刷新LED显示,将LED_DATA的8位值传递给DIG,实际是在此处将LED_DATA的值写入给P口了
    44.         }
    45.         else                                                                                                                                                 // num = 4时,关闭LED显示,关闭数码管显示
    46.         {
    47.                 LED_ON = 1;
    48.                 SEG = 0xFF;
    49.                 DIG = 0xFF;
    50.         }
    51.         num++;                                                                                                                                                // num自加1
    52.         if(num >=5)                                                                                                                                        // num = 5时,num清0
    53.                 num = 0;
    54. }
    复制代码
    .c 代码没有上传成功,重新上传一下。
    永远相信美好的事情即将发生!
    回复 支持 反对 送花

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

    GMT+8, 2024-6-15 08:59 , Processed in 0.080231 second(s), 66 queries .

    Powered by Discuz! X3.5

    © 2001-2024 Discuz! Team.

    快速回复 返回顶部 返回列表