- 打卡等级:常住居民II
- 打卡总天数:82
- 最近打卡:2025-06-29 01:06:23
已绑定手机
高级会员
- 积分
- 673
|
发表于 2025-4-16 22:37:03
|
显示全部楼层
《8051U深度入门到32位51大型实战教学视频》--学习打卡8
第八集:定时器周期性调度任务
笔记:
1、周期性任务介绍
通过任务代码实现过程的讲解,融入了Ai8051U定时器/计数器知识和中断机制的讲解,以及编辑中的技巧、C语言知识点数组和for语句、按键防抖、工具软件的使用等知识点的讲解。
1.1 定义变量实现任务一,用了多个变量分别计数的方法实现一个定时器服务多个任务。
代码如下:
//擎天柱核心板-Ai8051U-32K64,定时器周期性调度任务实验程序。
//任务一,方法一:多个变量分别计数的方法实现一个定时器服务多个任务。
//http://112.74.51.126/forum.php?mod=attachment&aid=OTQzMjB8OTIxZmUxYjl8MTc1MTE5MzI5MXwwfA%3D%3D
//用到2025年4月8日发布的USB库(CDC):stc_usb_cdc_32g.LIB。
#include "ai8051u.h"
#include "ai_usb.h"
u16 Count_300 = 0;
u16 Count_600 = 0;
u16 Count_900 = 0;
u16 Count_ms[3] = {0, 0, 0};
u8 i;
void Delay20ms(void) //@40.000MHz
{
unsigned long edata i;
_nop_(); _nop_();
i = 199998UL;
while (i--);
}
void Timer0_Isr(void) interrupt 1
{
Count_300++;
Count_600++;
Count_900++;
}
void Timer0_Init(void) //1毫秒@40.000MHz
{
TM0PS = 0x00; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xFB; //设置定时初始值
TH0 = 0xF2; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void main(void)
{
EAXFR = 1; //允许访问扩展的特殊寄存器,XFR。(32 位模式请使用这句,注释下一句)。
// P_SW2 |= 0x80; //允许访问扩展的特殊寄存器,XFR。(8 位模式请使用这句,注释上一句)。
WTST = 0; //设置取程序代码等待时间:赋值为0表示不等待,程序以最快速度运行。
CKCON = 0; //设置访问片内的xdata速度:赋值为0表示用最快速度访问,不增加额外的等待时间。
P2M0 = 0x00; P2M1 = 0x00; //设置P2口为准双向口模式。
P3M0 = 0x00; P3M1 = 0x00; //设置P3口为准双向口模式。
usb_init(); //调用USB初始化函数。
Delay20ms();
Timer0_Init(); //调用T0初始化函数。
Delay20ms();
EA = 1; //打开总中断。
{int i = 1; for (i = 1; i <= 300; i++) Delay20ms();} //等待AIapp-ISP下载软件中串口自动打开。
printf_usb("程序开始执行!\r\n");
while (1)
{
//任务一,方法一:多个变量分别计数的方法实现一个定时器服务多个任务。
if (Count_300 >= 300)
{
Count_300 = 0;
P20 = ~P20;
}
if (Count_600 >= 600)
{
Count_600 = 0;
P21 = ~P21;
}
if (Count_900 >= 900)
{
Count_900 = 0;
P22 = ~P22;
}
if(bUsbOutReady) //bUsbOutReady 标志位是用于标明 USB 主机发送完成。
{
USB_SendData(UsbOutBuffer, OutNumber);
printf_usb("\r\n国芯MCU收到数\xfd据:%X\r\n", UsbOutBuffer[0]);
P2 = UsbOutBuffer[0];
LED40_SetPort(2, P2); //使用AIapp-ISP软件菜单“调试仿真接口”中的“擎天柱-LED-DIP40”进行模拟显示P2口。
usb_OUT_done(); //本包接收的数据处理完毕,准备接收下一包数据。
}
}
}
1.2 定义数组实现任务一,用数组计数的方法实现一个定时器服务多个任务。
代码如下:
//擎天柱核心板-Ai8051U-32K64,定时器周期性调度任务实验程序。
//
//任务一,方法二:数组计数的方法实现一个定时器服务多个任务。
//用到2025年4月8日发布的USB库(CDC):stc_usb_cdc_32g.LIB。
#include "ai8051u.h"
#include "ai_usb.h"
u16 Count_300 = 0;
u16 Count_600 = 0;
u16 Count_900 = 0;
u16 Count_ms[3] = {0, 0, 0};
u8 j;
void Delay20ms(void) //@40.000MHz
{
unsigned long edata i;
_nop_(); _nop_();
i = 199998UL;
while (i--);
}
void Timer0_Isr(void) interrupt 1
{
for (j = 0; j < 3; j++)
{
Count_ms[j]++;
}
if (Count_ms[0] >= 300)
{
P20 = ~P20;
Count_ms[0] = 0;
}
if (Count_ms[1] >= 600)
{
P21 = ~P21;
Count_ms[1] = 0;
}
if (Count_ms[2] >= 900)
{
P22 = ~P22;
Count_ms[2] = 0;
}
}
void Timer0_Init(void) //1毫秒@40.000MHz
{
TM0PS = 0x00; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xFB; //设置定时初始值
TH0 = 0xF2; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void main(void)
{
EAXFR = 1; //允许访问扩展的特殊寄存器,XFR。(32 位模式请使用这句,注释下一句)。
// P_SW2 |= 0x80; //允许访问扩展的特殊寄存器,XFR。(8 位模式请使用这句,注释上一句)。
WTST = 0; //设置取程序代码等待时间:赋值为0表示不等待,程序以最快速度运行。
CKCON = 0; //设置访问片内的xdata速度:赋值为0表示用最快速度访问,不增加额外的等待时间。
P2M0 = 0x00; P2M1 = 0x00; //设置P2口为准双向口模式。
P3M0 = 0x00; P3M1 = 0x00; //设置P3口为准双向口模式。
usb_init(); //调用USB初始化函数。
Delay20ms();
Timer0_Init(); //调用T0初始化函数。
Delay20ms();
EA = 1; //打开总中断。
{int i = 1; for (i = 1; i <= 300; i++) Delay20ms();} //等待AIapp-ISP下载软件中串口自动打开。
printf_usb("程序开始执行!\r\n");
while (1)
{
//任务一,方法二:数组计数的方法实现一个定时器服务多个任务。
//此处不需要写代码。
if(bUsbOutReady) //bUsbOutReady 标志位是用于标明 USB 主机发送完成。
{
USB_SendData(UsbOutBuffer, OutNumber);
printf_usb("\r\n国芯MCU收到数\xfd据:%X\r\n", UsbOutBuffer[0]);
P2 = UsbOutBuffer[0];
LED40_SetPort(2, P2); //使用AIapp-ISP软件菜单“调试仿真接口”中的“擎天柱-LED-DIP40”进行模拟显示P2口。
usb_OUT_done(); //本包接收的数据处理完毕,准备接收下一包数据。
}
}
}
2.3 定义数组实现任务2,有循环数组中存在的值来点亮LED,实现流水灯。
代码如下:
//擎天柱核心板-Ai8051U-32K64,定时器周期性调度任务实验程序。
//
//任务2:数组点亮LED,实现流水灯。
//用到2025年4月8日发布的USB库(CDC):stc_usb_cdc_32g.LIB。
#include "ai8051u.h"
#include "ai_usb.h"
u16 Count_300 = 0;
u16 Count_600 = 0;
u16 Count_900 = 0;
u16 Count_ms[3] = {0, 0, 0};
u8 State[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
u8 i;
u8 num = 0;
void Delay20ms(void) //@40.000MHz
{
unsigned long edata i;
_nop_(); _nop_();
i = 199998UL;
while (i--);
}
void Timer0_Isr(void) interrupt 1
{
for (i = 0; i < 3; i++)
{
Count_ms++;
}
}
void Timer0_Init(void) //1毫秒@40.000MHz
{
TM0PS = 0x00; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xFB; //设置定时初始值
TH0 = 0xF2; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void main(void)
{
EAXFR = 1; //允许访问扩展的特殊寄存器,XFR。(32 位模式请使用这句,注释下一句)。
// P_SW2 |= 0x80; //允许访问扩展的特殊寄存器,XFR。(8 位模式请使用这句,注释上一句)。
WTST = 0; //设置取程序代码等待时间:赋值为0表示不等待,程序以最快速度运行。
CKCON = 0; //设置访问片内的xdata速度:赋值为0表示用最快速度访问,不增加额外的等待时间。
P2M0 = 0x00; P2M1 = 0x00; //设置P2口为准双向口模式。
P3M0 = 0x00; P3M1 = 0x00; //设置P3口为准双向口模式。
usb_init(); //调用USB初始化函数。
Delay20ms();
Timer0_Init(); //调用T0初始化函数。
Delay20ms();
EA = 1; //打开总中断。
{int i = 1; for (i = 1; i <= 300; i++) Delay20ms();} //等待AIapp-ISP下载软件中串口自动打开。
printf_usb("程序开始执行!\r\n");
while (1)
{
//任务2:数组点亮LED,实现流水灯。
if (Count_ms[0] >= 500)
{
Count_ms[0] = 0;
P2 = ~State[num];
num++;
if (num > 7)
{
num = 0;
}
}
if(bUsbOutReady) //bUsbOutReady 标志位是用于标明 USB 主机发送完成。
{
USB_SendData(UsbOutBuffer, OutNumber);
printf_usb("\r\n国芯MCU收到数\xfd据:%X\r\n", UsbOutBuffer[0]);
P2 = UsbOutBuffer[0];
LED40_SetPort(2, P2); //使用AIapp-ISP软件菜单“调试仿真接口”中的“擎天柱-LED-DIP40”进行模拟显示P2口。
usb_OUT_done(); //本包接收的数据处理完毕,准备接收下一包数据。
}
}
}
2.4 任务3:按键1按一下,LED通过数组移动一下。
用新的实现方式实现了按键防抖,改进了以前方法,堵上一个漏洞。
代码如下:
//擎天柱核心板-Ai8051U-32K64,定时器周期性调度任务实验程序。
//
//任务3:按键按一下,LED通过数组移动一下。
//用到2025年4月8日发布的USB库(CDC):stc_usb_cdc_32g.LIB。
#include "ai8051u.h"
#include "ai_usb.h"
u16 Count_300 = 0;
u16 Count_600 = 0;
u16 Count_900 = 0;
u16 Count_ms[3] = {0, 0, 0};
u8 State[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
u8 i;
u8 num = 0;
u16 Key_Val = 0;
void Delay20ms(void) //@40.000MHz
{
unsigned long edata i;
_nop_(); _nop_();
i = 199998UL;
while (i--);
}
void Timer0_Isr(void) interrupt 1
{
for (i = 0; i < 3; i++)
{
Count_ms++;
}
}
void Timer0_Init(void) //1毫秒@40.000MHz
{
TM0PS = 0x00; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xFB; //设置定时初始值
TH0 = 0xF2; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void main(void)
{
EAXFR = 1; //允许访问扩展的特殊寄存器,XFR。(32 位模式请使用这句,注释下一句)。
// P_SW2 |= 0x80; //允许访问扩展的特殊寄存器,XFR。(8 位模式请使用这句,注释上一句)。
WTST = 0; //设置取程序代码等待时间:赋值为0表示不等待,程序以最快速度运行。
CKCON = 0; //设置访问片内的xdata速度:赋值为0表示用最快速度访问,不增加额外的等待时间。
P2M0 = 0x00; P2M1 = 0x00; //设置P2口为准双向口模式。
P3M0 = 0x00; P3M1 = 0x00; //设置P3口为准双向口模式。
usb_init(); //调用USB初始化函数。
Delay20ms();
Timer0_Init(); //调用T0初始化函数。
Delay20ms();
EA = 1; //打开总中断。
{int i = 1; for (i = 1; i <= 300; i++) Delay20ms();} //等待AIapp-ISP下载软件中串口自动打开。
printf_usb("程序开始执行!\r\n");
while (1)
{
//任务3:按键按一下,LED通过数组移动一下。
P2 = ~State[num];
if (Count_ms[0] >= 1000)
{
Count_ms[0] = 0;
printf_usb("国芯MCU--Ai8051U\r\n");
}
if (Count_ms[1] >= 5)
{
Count_ms[1] = 0;
if (0 == P32) //判断P32按钮是否按下。
{
Key_Val++;
if (5 == Key_Val)
{
num = (num > 7) ? 0 : ++num;
printf_usb("Ai8051U--%d--%d\r\n", num, Key_Val);
}
}
else
{
Key_Val = 0;
}
}
if(bUsbOutReady) //bUsbOutReady 标志位是用于标明 USB 主机发送完成。
{
USB_SendData(UsbOutBuffer, OutNumber);
printf_usb("\r\n国芯MCU收到数\xfd据:%X\r\n", UsbOutBuffer[0]);
P2 = UsbOutBuffer[0];
LED40_SetPort(2, P2); //使用AIapp-ISP软件菜单“调试仿真接口”中的“擎天柱-LED-DIP40”进行模拟显示P2口。
usb_OUT_done(); //本包接收的数据处理完毕,准备接收下一包数据。
}
}
}
2、文件的创建(.c和.h)
此部分讲解了多文件项目中如何创建新文件并添加到项目中,项目中如何在其它文件中引用别的文件,以及如何将代码拆分归类到不同文件。
2.1 新建新的“.c和.h”后缀的文件,并添加到项目中。两个文件同时建立,名称上只有后缀不同。一般一个.c和一个.h文件归类为执行一个外设或者一个任务或功能。这样可以让代码看起来简洁明了。
2.2 按照如下规则编写.c和.h文件。
新建的xxx.c和xxx.h文件是一组,代表一个功能块。xxx用实际名称全部代换。
(a)xxx.h格式:
#ifndef __XXX_H
#define __XXX_H
调用头文件
函数声明...
#endif
(b)xxx.c格式
#include “xxx.h”
函数定义
添加文件一定要记得引用路径和添加到工程里。
2.3 在项目其它文件的头部引用.h文件。
格式为:
#include "xxx.h"
3、结构体的介绍
结构体定义由关键字 struct 和结构体名组成,结构体名可以根据需要自行定义。
struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:
struct tag {
member-list;
member-list;
member-list;
...
} variable-list;
tag 是结构体标签。
member-list 是标准的变量定义,比如 int i; 或者 float f;,或者其他有效的变量定义。
variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
更多详细信息,请参考网址:https://www.runoob.com/cprogramming/c-structures.html
4、结构体数组的周期性任务调度
4.1 定义结构体,实现任务一,用结构体封装了与定时器有关的成员变量,用来保存对应的数据和函数实现的指向。
定义示例如下:
typedef struct
{
u8 Run; //任务状态:Run/Stop
u16 TIMCount; //定时计数器
u16 TRITime; //重载计数器
void (*TaskHook) (void); //任务函数
} TASK_COMPONENTS;
static TASK_COMPONENTS Task_Comps[] =
{
//状态 计数 周期 函数
{0, 1, 1, 执行功能},
{0, 10, 10, 执行功能},
};
4.2 用结构体实现任务一的代码。
代码如下:
http://112.74.51.126/forum.php?mod=attachment&aid=OTQzMjB8OTIxZmUxYjl8MTc1MTE5MzI5MXwwfA%3D%3D
http://112.74.51.126/forum.php?mod=attachment&aid=OTQzMTd8MDgzYzRmY2Z8MTc1MTE5MzI5MXwwfA%3D%3D
http://112.74.51.126/forum.php?mod=attachment&aid=OTQzMTZ8NjRiN2FmNTV8MTc1MTE5MzI5MXwwfA%3D%3D
http://112.74.51.126/forum.php?mod=attachment&aid=OTQzMjJ8Yzc0ZTE3MWN8MTc1MTE5MzI5MXwwfA%3D%3D
http://112.74.51.126/forum.php?mod=attachment&aid=OTQzMjF8OGE3YTc4MGV8MTc1MTE5MzI5MXwwfA%3D%3D
http://112.74.51.126/forum.php?mod=attachment&aid=OTQzMTl8YWJmYjIwNjF8MTc1MTE5MzI5MXwwfA%3D%3D
http://112.74.51.126/forum.php?mod=attachment&aid=OTQzMTh8ZjI1NzlmMzd8MTc1MTE5MzI5MXwwfA%3D%3D
心得感悟:
通过对周期性任务实现方法的学习,加深了对定时器/计数器功能的理解,同时也加深了对中断机制的理解。
在学习过程中,也学习到了一些代码编辑技巧以及出现问题如何解决的方法。
视频教程中讲解了3个任务,通过对这3个任务的学习,更能体会到条条大路通罗马的感觉,以及积累经验后才能找到相对比较优的路径的感觉,同时不断探索才可能发现新的路径。
总结:
通过学习本期视频,学到如下知识点:
1、定时器/计数器功能和中断机制相结合,能简化代码,提高程序性能,模拟出多任务的“假象”。
2、学习了数组的定义、使用。
3、学习了for循环的使用。
4、学习了在代码中如何注多行。
5、学习了新的按键防抖代码如何编写。
6、学习了项目文件中如何新建和引入.c和.h文件以及编写文件规则。
7、学习了结构体的定义、使用。
后记:
2025年4月16日,开始学习第八集:定时器周期性调度任务。
|
|