AI8051U 学习第8课 : 定时器周期性调度任务
这一节课超长,讲授的知识点也多, 学习起来比较有难度。周期性调度本身并不难理解, 倒是涉及到的C语言的核心特性, 需要花一些时间熟悉。
涉及C语言核心特性:
1. 数组
2. 结构体
3. 代码模块化
为了加深理解, 尽快掌握这些特性,最好的做法就是实践。 课程比较长, 我将按顺序, 逐步将昨天的作业(功德箱),加入新的特性来实现。
1. 使用数组。下面是使用数组代替两个定时器的代码。
复制代码 2. 引入结构体和状态机, 避免使用 while 导致程序阻塞。
使用结构体还涉及了指针的使用问题,调用按键指针,以确保数据一致性。 之前双倍功德时显示两个灯,导致了潜在逻辑问题, 为了简化代码, 修改为单倍功德时,显示 LED 1 , 双倍功德时,显示LED 2; 另外为了检验已经避免了按键按下时导致阻塞问题,增加了LED 3 1HZ 闪烁。代码如下:
- #include "ai8051u.h" //调用头文件
- #include "stc32_stc8_usb.h" //调用头文件
- #include "stdlib.h"
-
- //注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,本节课程的其余内容(USB不停电下载)均通用!
-
- char* USER_DEVICEDESC = NULL;
- char* USER_PRODUCTDESC = NULL;
- char* USER_STCISPCMD = "@STCISP#";
-
- unsigned int gd = 0; // 功德总数
-
- unsigned char gdx = 1; // 单次功德点数
-
- int led_delay[] = { 0, 0, 500 }; // led灯熄灭倒计时
-
- // 按钮状态
- typedef enum
- {
- KEY_STATE_IDLE,
- KEY_STATE_PRESS,
- KEY_STATE_CONFIRM,
- KEY_STATE_RELEASE
- } KeyState;
-
- // 按钮结构体
- typedef struct
- {
- unsigned char pin; // 按钮引脚电压 0 / 1
- KeyState state;
- unsigned int debounce_cnt;
- unsigned char press_flag;
- unsigned char change_flag;
- } KeyStruct;
-
- KeyStruct key32 = {1, KEY_STATE_IDLE, 0, 0, 0};
- KeyStruct key33 = {1, KEY_STATE_IDLE, 0, 0, 0};
-
- // 每毫秒扫描一次按钮状态变化
- // 找到遵循<抖动、按下、抖动、稳定>物理规律的按钮状态改变
- void KeyScan(KeyStruct* key, unsigned char current_pin)
- {
- switch(key->state)
- {
- case KEY_STATE_IDLE:
- if(current_pin == 0) // 检测到按钮按下动作
- {
- key->state = KEY_STATE_PRESS;
- key->debounce_cnt = 0;
- }
- break;
- case KEY_STATE_PRESS:
- key->debounce_cnt++;
- if(key->debounce_cnt >= 20)
- {
- if(current_pin == 0) // 确实被按下,已停止抖动
- {
- key->state = KEY_STATE_CONFIRM;
- key->press_flag = 1;
- key->change_flag = 1;
- }
- else // 是抖动,按钮弹回原位
- {
- key->state = KEY_STATE_IDLE;
- }
- key->debounce_cnt = 0; // 不管是哪种情况, 消抖计数都归零
- }
- break;
- case KEY_STATE_CONFIRM:
- if(current_pin == 1) // 检测到按钮释放
- {
- key->state = KEY_STATE_RELEASE;
- key->debounce_cnt = 0;
- }
- break;
- case KEY_STATE_RELEASE:
- key->debounce_cnt++;
- if(key->debounce_cnt >= 20) // 20ms 释放消抖
- {
- if(current_pin == 1) // 确认按钮释放
- {
- key->state = KEY_STATE_IDLE;
- key->press_flag = 0;
- }
- else
- {
- key->state = KEY_STATE_CONFIRM; // 按钮弹回
- }
- key->debounce_cnt = 0;
- }
- break;
- }
- }
-
- // 检查按键事件
- unsigned char Key_GetEvent(KeyStruct* key)
- {
- if(key->change_flag)
- {
- key->change_flag = 0; // 有按键事件
- return 1;
- }
- return 0; // 无按键事件
- }
-
- void Delay(unsigned int ms) //@24.000MHz
- {
- unsigned char data i, j;
-
- while(ms--)
- {
- i = 24;
- j = 85;
- do
- {
- while(--j);
- }
- while(--i);
- }
- }
-
- void Timer0_Init(void) //1毫秒@24.000MHz
- {
- TM0PS = 0x00; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
- AUXR &= 0x7F; //定时器时钟12T模式
- TMOD &= 0xF0; //设置定时器模式
- TL0 = 0x30; //设置定时初始值
- TH0 = 0xF8; //设置定时初始值
- TF0 = 0; //清除TF0标志
- TR0 = 1; //定时器0开始计时
- ET0 = 1; //使能定时器0中断
- }
-
- void main(void)
- {
- //P_SW2 |= 0x80; //B7位写1,使能访问XFR 等同于 EAFXR = 1;
- WTST = 0;
- EAXFR = 1;
- CKCON = 0;
-
- 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_init(); //USB CDC 接口配置
-
- IE2 |= 0x80; //使能USB中断
- EA = 1; //IE |= 0X80;
-
- while(DeviceState != DEVSTATE_CONFIGURED); //等待USB完成配置
-
- Timer0_Init();
-
- // 初始化LED状态
- P20 = 1; P21 = 1; P22 = 1;
-
- while(1)
- {
-
- if(Key_GetEvent(&key33))
- {
-
- // 每次按下,都切换功德点数
- gdx = gdx == 1 ? 2 : 1;
-
- switch((unsigned int) gdx)
- {
- case 1:
- printf("单倍功德时间\r\n");
- break;
- case 2:
- printf("双倍功德时间\r\n");
- break;
- }
-
- }
-
-
- if(Key_GetEvent(&key32))
- {
-
- gd += gdx; // 增加功德总数
- printf("功德+%d 当前功德:%d \r\n", gdx, gd);
-
- switch(gdx)
- {
- case 1:
- // 单倍功德,点亮1颗灯
- P20 = 0;
- led_delay[0] = 1000; // 重置等待时间为 1000 毫秒
- break;
- case 2:
- // 2倍功德,点亮第2颗灯,更直观
- P21 = 0;
- led_delay[1] = 2000; // 重置等待时间为 2000 毫秒
- break;
- }
-
- }
-
-
- // 重置时间归零后灭灯
- if(led_delay[0] <= 0)
- {
- P20 = 1;
- }
-
- // 重置时间归零后灭灯
- if(led_delay[1] <= 0)
- {
- P21 = 1;
- }
-
-
- // P22 闪烁,以便观察是否有阻塞
- if(led_delay[2] <= 0)
- {
- led_delay[2] = 500;
- P22 = ~P22;
- }
-
-
- // }
-
- if(bUsbOutReady)
- {
- //USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
-
- usb_OUT_done();
- }
-
- }
- }
-
- void Timer0_Isr(void) interrupt 1
- {
- // 时间递减
- if(led_delay[0] > 0)
- {
- led_delay[0]--;
- }
- if(led_delay[1] > 0)
- {
- led_delay[1]--;
- }
- if(led_delay[2] > 0)
- {
- led_delay[2]--;
- }
-
- KeyScan(&key32, P32);
- KeyScan(&key33, P33);
- }
复制代码 3. 引入模块化编程,在原程序中拆分出2个子模块: config 和 button 。 config 作为工程初始化的标准配置, button 作为无阻塞按钮事件的实现。
主程序代码清爽了:
- #include "config.h"
-
- //注意:擎天柱的LED端口在P2,且没有三极管的电源控制,所以只要控制P2端口即可,本节课程的其余内容(USB不停电下载)均通用!
- char* USER_DEVICEDESC = NULL;
- char* USER_PRODUCTDESC = NULL;
- char* USER_STCISPCMD = "@STCISP#";
-
- unsigned int gd = 0; // 功德总数
-
- unsigned char gdx = 1; // 单次功德点数
-
- int led_delay[] = { 0, 0, 500 }; // led灯熄灭倒计时
-
- // 按钮定义
- KeyStruct key32 = {1, KEY_STATE_IDLE, 0, 0, 0};
- KeyStruct key33 = {1, KEY_STATE_IDLE, 0, 0, 0};
-
- void main(void)
- {
- sys_init();
- Timer0_Init();
-
- // 初始化LED状态
- P20 = 1; P21 = 1; P22 = 1;
-
- while(1)
- {
-
- if(Key_GetEvent(&key33))
- {
-
- // 每次按下,都切换功德点数
- gdx = gdx == 1 ? 2 : 1;
-
- switch((unsigned int) gdx)
- {
- case 1:
- printf("单倍功德时间\r\n");
- break;
- case 2:
- printf("双倍功德时间\r\n");
- break;
- }
-
- }
-
-
- if(Key_GetEvent(&key32))
- {
-
- gd += gdx; // 增加功德总数
- printf("功德+%d 当前功德:%d \r\n", gdx, gd);
-
- switch(gdx)
- {
- case 1:
- // 单倍功德,点亮1颗灯
- P20 = 0;
- led_delay[0] = 1000; // 重置等待时间为 1000 毫秒
- break;
- case 2:
- // 2倍功德,点亮第2颗灯,更直观
- P21 = 0;
- led_delay[1] = 2000; // 重置等待时间为 2000 毫秒
- break;
- }
-
- }
-
-
- // 重置时间归零后灭灯
- if(led_delay[0] <= 0)
- {
- P20 = 1;
- }
-
- // 重置时间归零后灭灯
- if(led_delay[1] <= 0)
- {
- P21 = 1;
- }
-
- // P22 闪烁,以便观察是否有阻塞
- if(led_delay[2] <= 0)
- {
- led_delay[2] = 500;
- P22 = ~P22;
- }
-
-
- if(bUsbOutReady)
- {
- //USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
-
- usb_OUT_done();
- }
-
- }
- }
-
- // 定时器中断回调函数
- void Timer0_Isr(void) interrupt 1
- {
- // 时间递减
- if(led_delay[0] > 0)
- {
- led_delay[0]--;
- }
- if(led_delay[1] > 0)
- {
- led_delay[1]--;
- }
- if(led_delay[2] > 0)
- {
- led_delay[2]--;
- }
-
- KeyScan(&key32, P32);
- KeyScan(&key33, P33);
- }
复制代码
上机操作视频
|