- 打卡等级:常住居民I
- 打卡总天数:72
- 最近打卡:2026-03-31 00:08:41
中级会员
- 积分
- 260
|
发表于 2026-1-27 01:24:01
|
显示全部楼层
Jellyfish从89C51到AI8051学习贴第九天!
今天继续学习《8051U深度入门到32位51大型实战教学视频》第八集 定时器周期性任务调度。
0x01 学习重点
- 如何通过1个定时器进行多个任务序列调度。
- C语言数组使用。
- C语言for循环的使用。
- 新版本的按键抖动判断逻辑。
- C语言的.c和.h文件的建立与使用。
- C语言的结构体使用。
0x02 学习心得
-
C 语言 for 循环的使用
//基础语法
for (初始化; 条件判断; 更新) {
// 循环体
}
//死循环
for(;;) { // 等同于 while(1)
// 运行任务
}
//软件阻塞延时(不精确,受时钟频率影响
void Delay(uint32_t count) {
for(; count > 0; count--); // 简单的递减延时
}
-
C 语言数组使用
//静态初始化
uint8_t buffer[4] = {0xAA, 0xBB, 0xCC, 0xDD};
//查表法优化(节省 CPU 计算量):
//例如控制 LED 的呼吸灯亮度曲线,预先计算好放入数组:
const uint8_t PWM_Table[] = {0, 10, 50, 150, 255}; // 存放在 Flash 中,节省 RAM
//数组越界检查:单片机没有操作系统保护内存,数组越界会导致死机或改写其他变量的值。
for(uint8_t i = 0; i < sizeof(buffer); i++) { // 使用 sizeof 动态计算长度
Send_UART(buffer[i]);
}
-
C 语言的结构体(Struct)
//基本定义与封装:
//结构体可以将不同类型的数据(如任务开关、周期、函数指针)打包在一起。
typedef struct {
uint8_t ID; // 任务 ID
uint16_t Interval; // 执行间隔
void (*TaskPtr)(void); // 函数指针,指向具体要执行的代码
} Task_TypeDef;
//结构体指针与硬件寄存器映射
GPIO_A->ODR = 0x01; // GPIO_A 是一个结构体指针,指向特定的内存地址
//位域(Bit-fields)优化内存(嵌入式黑科技)
struct Status {
uint8_t isRunning : 1; // 只占 1 个 bit
uint8_t errorCode : 7; // 占 7 个 bit
}; // 整个结构体仅占 1 字节
-
通过定时器中断函数对数组的多个成员累加,在主函数判断实现多任务序列调度。
unsigned int led_count[3]={0,0,0};
int length = sizeof(led_count) / sizeof(led_count[0]);
unsigned char LED_state[3] = {0,0,0};
void Timer0_Isr(void) interrupt 1
{
//中断函数实现
int i;
for (i=0;i<length;i++){
led_count[i]++;
}
if(led_count[0]>=3){
led_count[0]=0;
LED_state[0]=!LED_state[0];
P00=LED_state[0];
printf("现在LED00的状态变更为:%d\n",(int)LED_state[0]);
}
if(led_count[1]>=6){
led_count[1]=0;
LED_state[1]=!LED_state[1];
P01=LED_state[1];
printf("现在LED01的状态变更为:%d\n",(int)LED_state[1]);
}
if(led_count[2]>=9){
led_count[2]=0;
LED_state[2]=!LED_state[2];
P02=LED_state[2];
printf("现在LED02的状态变更为:%d\n",(int)LED_state[2]);
}
}
void Timer0_Init(void) //100毫秒@24.000MHz
{
TM0PS = 0x03; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xB0; //设置定时初始值
TH0 = 0x3C; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
-
通过按键间隔时间判断是否按下,累加判断成功次数,实现按键抖动判断不影响其他任务执行。
此时LED灯正常闪烁!

#include "STC32.h"
#include "stc32_stc8_usb.h"
unsigned int time_count[4]={0,0,0,0};
int length = sizeof(time_count) / sizeof(time_count[0]);
unsigned char LED_state[3] = {0,0,0};
unsigned int key_count=0;
unsigned int key_pressed_flag = 0; // 定义标志位
void Timer0_Isr(void) interrupt 1
{
//中断函数实现
int i;
for (i=0;i<length;i++){
time_count[i]++;
}
if(time_count[0]>=30){
time_count[0]=0;
LED_state[0]=!LED_state[0];
P00=LED_state[0];
//printf("现在LED00的状态变更为:%d\n",(int)LED_state[0]);
}
if(time_count[1]>=60){
time_count[1]=0;
LED_state[1]=!LED_state[1];
P01=LED_state[1];
//printf("现在LED01的状态变更为:%d\n",(int)LED_state[1]);
}
if(time_count[2]>=90){
time_count[2]=0;
LED_state[2]=!LED_state[2];
P02=LED_state[2];
//printf("现在LED02的状态变更为:%d\n",(int)LED_state[2]);
}
if(time_count[3]>=1){
time_count[3]=0;
if(P14==0){
if(key_pressed_flag == 0) { // 如果之前还没处理过这个按键
key_count++;
if(key_count>=10){
key_pressed_flag = 1; // 只立个 Fla
}
}
}else{
key_count=0;
key_pressed_flag = 0; // 允许下一次按下处理
}
}
}
void Timer0_Init(void) //10毫秒@24.000MHz
{
TM0PS = 0x00; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 )
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0xE0; //设置定时初始值
TH0 = 0xB1; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void main()
{
WTST = 0;
CKCON = 0;
P0M0 = 0xff;
P0M1 = 0x00; //我的外围LED电路是IO口接到LED的阳极,配置成推挽输出提供更大电流。
//P0M0 = 0x00; P0M1 = 0x00; //实测准双向模式无法点亮我的LED,估计是串联了PR20 101(100Ω)的排阻。
P1M0 &= ~0x30; P1M1 &= ~0x30; //我把按键接到了P1.4、P1.5分别代表按键1和按键2。
P2M0 = 0x00; P2M1 = 0x00;
P2=0; //这个P2的0、1、2口接了个74hc138控制数码管。
P_SW2 |= 0x80;
usb_init(); //USB CDC 接口配置
Timer0_Init();
EA = 1;
P0=0;
while (1)
{
if (bUsbOutReady)
{
USB_SendData(UsbOutBuffer,OutNumber); //发送数据缓冲区,长度(接收数据原样返回, 用于测试)
printf("\nSTC深圳国芯人工智能有限公司 %s\n","yyds");
usb_OUT_done();
}
if(key_pressed_flag==1) {
printf("按键逻辑触发已经超过%d此判断按键按下",(int)key_count);
key_pressed_flag = 2;
}
}
}
-
通过的结构体使用,高效使用定时器
typedef struct
{
u8 Run; //任务状态:Run/Stop
u16 TIMCount; //定时计数器
u16 TRITime; //重载计数器
void (*TaskHook) (void); //任务函数
} TASK_COMPONENTS;
static TASK_COMPONENTS Task_Comps[]=
{
//状态 计数 周期 函数
{0, 300, 300, LED0_Blink}, /* task 1 Period: 300ms */
{0, 600, 600, LED1_Blink}, /* task 1 Period: 600ms */
{0, 900, 900, LED2_Blink}, /* task 1 Period: 600ms */
{0, 10, 10, KEY_Task}, /* task 1 Period: 600ms */
};
u8 Tasks_Max = sizeof(Task_Comps)/sizeof(Task_Comps[0]);
//========================================================================
// 函数: Task_Handler_Callback
// 描述: 任务标记回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
//========================================================================
//在中断函数中执行这个函数代码,实现数量累加
void Task_Marks_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps[i].TIMCount) /* If the time is not 0 */
{
Task_Comps[i].TIMCount--; /* Time counter decrement */
if(Task_Comps[i].TIMCount == 0) /* If time arrives */
{
/*Resume the timer value and try again */
Task_Comps[i].TIMCount = Task_Comps[i].TRITime;
Task_Comps[i].Run = 1; /* The task can be run */
}
}
}
}
//========================================================================
// 函数: Task_Pro_Handler_Callback
// 描述: 任务处理回调函数.
// 参数: None.
// 返回: None.
// 版本: V1.0, 2012-10-22
//========================================================================
//这个函数在main函数里面持续执行,当判断发现有任务时间到了就开始执行
void Task_Pro_Handler_Callback(void)
{
u8 i;
for(i=0; i<Tasks_Max; i++)
{
if(Task_Comps[i].Run) /* If task can be run */
{
Task_Comps[i].Run = 0; /* Flag clear 0 */
Task_Comps[i].TaskHook(); /* Run task */
}
}
}
0x03 待学习重点
-
89C51的经验有多少可以迁移到STC32G上(已完成)
目前看感觉在前学过的那些东西已经跟不上时代了,现在重新系统性的学习AI8051。
-
如何将传统51单片机项目迁移到STC32G平台上(已完成)
通过AiCube项目创建助手功能选择IO口、中断等功能类型,一键生成初始keil工程,然后把核心逻辑代码复制过来就能搞定!
-
硬件USB控制器可以玩出什么花样?(持续学习 持续探索)
- USB转双串口(其实是不是通过软件模拟可以实现USB转N串口?加上蓝牙模块可以实现无线串口调试了,再加上MAX232打上RJ45就可以用来配置交换机了。
-
在用户代码中嵌入 USB-CDC 代码,实现一直修改代码一直USB不停电下载。(已完成)
-
定时器一次只能定时一个此,如果有很多个任务怎么办?(已完成)
初步猜测可能性:1、在定时器函数内部增加if/else判断某个全局变量的值确定要执行哪一段语句。2、使用多个定时器。
答案:想的差不多,方法如下
- 通过数组(包括结构体数组),定时器中断函数对数组的多个成员累加,在主函数判断实现多任务序列调度。
-
如果我的函数执行的很慢,定时器等待的时间很短,会不会出现问题?
|
|