5
16
261
中级会员
AI8051U环境配置和p0.0灯闪烁.mp4
2024-12-7 16:16 上传
点击文件名下载附件
23.58 MB, 下载次数: 64
使用道具 举报 送花
demo_led.zip
2024-12-7 17:28 上传
39.42 KB, 下载次数: 67
嫌弃每次下载都需要按下P3.2接地和断电按键? 没关系,现在可以使用USB配合官方库实现USB不断电下载! 配置流程: 1:实验演示最后面放视频。 2:下载所需文件: (1):官方库文件 (2):官方例程 深圳国芯人工智能有限公司-库函数里面的USB库文件,下载代码+例程,解压缩。 打开这个 还有前面的这个 把这两个文件复制到你的keil工程中 查询模式是等待代码执行完一圈之后才去执行,而中断模式是一有信号就马上执行,为了保证代码的完整运行,这里选择查询模式。 3:移植关键部分到工程 (1):添加头文件 双击第一张图片中的Source Group 1,选中.lib文件,并在代码中加入 #include "stc32_stc8_usb.h" //USB CDC 头文件 这句代码。 (2):USB初始化函数 (3):命令参数 这里需要和isp下载软件中的一致,是用作命令识别的,识别成功才会进入hid下载模式 这里选择默认即可。 (4):打开P_SW2和IE2寄存器(只修改某一个位) 如图,(1:使能访问XFR寄存器。 (2:使能USB中断和总中断 具体可看手册对应章节。 这部分代码直接移植官方例程的即可。 最后附上演示视频。 PS(又是学习的一天)
USB不断电下载.mp4
2024-12-7 17:31 上传
3.82 MB, 下载次数: 65
5:代码和附件(是前一天的代码基础上改的,其实没太多变化) PS:加油,继续学习!
demo_code.zip
2024-12-7 17:42 上传
88.08 KB, 下载次数: 60
GPIO.zip
2024-12-7 17:49 上传
91.58 KB, 下载次数: 65
Code_AfterClass_Test.zip
2024-12-7 17:54 上传
96.1 KB, 下载次数: 60
93.99 KB, 下载次数: 67
在开始今天的摘要之前,先来看一下前一节课——IO定时器中断可能会出现的一些错误。
#define u8 unsigned char
,(中文)、,(英文)
}
开启****显示发送的数据和数据分包显示就行了,这样也可以通过串口打印的时间来大致查看定时器的定时是否准确。如下图:
分成以下四个部分:
周期性任务介绍
文件的创建(.c和.h)
结构体的介绍
结构体数组的周期性任务调度 一、先来看第一点(周期性任务介绍): 周期性任务介绍:所谓的周期性任务就是每隔一定的时间就去执行一个任务,比如每隔300ms点亮一次LED
对应这里的任务一:很简单明了,只需要
通过设置变量来计数,每进一次1ms的定时器设置中断,这个变量就自加一次,再判断满足临界条件就可以执行任务了。以下列出部分代码,具体代码可以看附件。
//变量定义部分: u8 State1 = 0; //LED1初始状态为0 u8 State2 = 0; //LED2初始状态为0 u8 State3 = 0; //LED3初始状态为0 //u16 Count_300 = 0; //计数300ms变量 //u16 Count_600 = 0; //计数600ms变量 //u16 Count_900 = 0; //计数900ms变量 u16 Count_ms[3] = { 0, 0, 0 }; //三个计时变量 //函数定义 这部分是定时器的1ms中断代码 void Timer0_Init(void) //1毫秒@24.000MHz 24位自动重载 { TM0PS = 0x00; //设置定时器时钟预分频 ( 注意:并非所有系列都有此寄存器,详情请查看数据手册 ) AUXR &= 0x7F; //定时器时钟12T模式 TMOD &= 0xF0; //设置定时器模式 TL0 = 0x30; //设置定时初始值 TH0 = 0xF8; //设置定时初始值 TF0 = 0; //清除TF0标志 TR0 = 1; //定时器0开始计时 ET0 = 1; //使能定时器0中断 } //定时器1ms中断函数 void Timer0_Isr(void) interrupt 1 //1ms执行一次。 { for(i = 0; i < 3; i++) //可以使用for循环+数组 { //或者是注释部分的三个变量 Count_ms[i] ++; } // Count_300++; // Count_600++; // Count_900++; } //主函数部分代码:printf打印是为了串口显示灯的状态(没实验箱(悲伤),但是应该快有了 if(Count_300 >= 300) //300MS达到 { Count_300 = 0; State1 = !State1; P00 = State1; //LED1取反 // printf("P00 = %d\r\n",State1); } if(Count_600 >= 600) //600MS达到 { Count_600 = 0; State2 = !State2; P01 = State2; //LED2取反 // printf("P01 = %d\r\n",State2); } if(Count_900 >= 900) //900MS达到 { Count_900 = 0; State3 = !State3; P02 = State3; //LED3取反 // printf("P02 = %d\r\n",State3); }
然后这里贴出一张结果截图:
然后这里也可以用作是一些需要周期性定时的任务上,比如说需要1秒钟使得蜂鸣器响一下。使用这个的好处是不会干扰主函数的执行,不像nop延时那样在主函数中执行完才能执行下面的代码。
数组的定义和使用 可以理解成数组就是加长版的变量?或者说一次性定义多个变量吧,这样比较好理解。
数组是从0开始索引的,老师说过一句话,计算机人的数学是从0开始的,而非计算机人是从1开始的。类似1G等于1024M,而不是1000M.
数组的定义和赋初始值:例如 u16 Count_ms[3] = { 0, 0, 0 }; //三个计时变量
u16 Count_ms[3] = { 0, 0, 0 }; //三个计时变量
数组变量的修改(数组名称+索引+等号+数据:State[0] = 2; //给数组写入数据
State[0] = 2; //给数组写入数据
Keil小Tip:选中多行,按住shift+tab,可以向前缩进。
所以在中断函数代码中就可以这样修改
//中断服务函数可以这样编写,串口打印老配方、同时把点灯放在了里面,不占同主函数了: if(Count_ms[0] >= 300) { Count_ms[0] = 0; State1 = !State1; P00 = State1; //LED1取反 printf("P00 = %d\r\n",State1); } // printf("当前是State数组的第%d个数据%d\r\n", (int)i);
用数组点亮流水灯
b0 b7 低 高 LED0 - LED7 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 0 */
u8 State[9] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
//代码: if( Count_ms[0] >= 500 ) { Count_ms[0] = 0; P0 = ~State[num]; printf("P0数值:%x\r\n", P0); num ++; if(num > 7) { num = 0; } }
*周期性任务介绍:任务3:按键P32按一下,LED通过数组移动一下****这里不能使用while来判断是不是没有松开按键了,不然会一直卡再while循环里面,导致下面的代码无法执行
//对应这里就是当一直按住P32 num++ 及其后面的语句一直无法执行。 if(P32 == 0) { Delay10ms(); if(P32 == 0) { while(P32 == 0); num ++; printf("P0数值:%x\r\n", P0); } }
//对应的就是下面,实现的效果就是按键一直按下的,printf打印不会受到干扰,依旧可以执行下去。 if(Count_ms[1] >= 1000) { Count_ms[1] = 0; printf("Ai8051U\r\n"); } if(Count_ms[2] >= 10) //按键持续按下50ms也会判定为按下按键,然后按一下加一下,实现了按键检测和串口打印的互不干扰。 { Count_ms[2] = 0; if(P32 == 0) { Key_vol ++; if(Key_vol == 5) { num ++; } printf("num数值:%d\r\n", num); } else { Key_vol = 0; } }
模块化编程,方便移植!?反正是很有用处的,文件多的时候,总不可能一直都放在一个main.c里面吧,看着多累啊。
先建立一个用户文件夹:user
在keil里面新建.c和.h文件,然后保存,命名为config.c(.h)。
添加文件到keil工程里面。
写好必备的语句,像是一下main.c里面的头文件啥的都可以放过来,然后再main.c里面就引用 #include "config.h"。
#include "config.h"
//1.在.h文件里面: #ifndef __CONFIG_H #define __CONFIG_H #include "ai8051u.h" //调用头文件 #include "stc32_stc8_usb.h" //USB CDC 头文件 #include "intrins.h" //调用头文件 #define u8 unsigned char //8位无符号变量(0-255) #define u16 unsigned int //16位无符号变量(0-65535) void Sys_Init(void); //函数声明 void Timer0_Init(void); //1ms定时函数声明 #endif //2. 在.c文件里面 #include "config.h"
在锤子里面的C251选项把新建的子文件夹添加到工程的目录里面,不然是找不到的。
**编译,没有报错,恭喜你拥有了自己的自定义函数! **
接下来就是移植其他的一些配置代码了,然后这里就不说了,后面看附件吧。?
三、重中之重啊:结构体数组的周期性任务调度
老规则的PPT介绍
结构体还是很重要的,在c语言中,嵌入式编程中很常见的。
结构体可以理解成用户自定义的一个数据类型,然后使用也是数组一样,类型+名称+赋值
然后下面的代码都可以在官方提供的实验箱代码里面找到,具体是第27个(27-通过定时器周期性调度任务综合例程,简单实用的任务调度系统,推荐)整体下载链接
这里新建了一个Task.h和Task.c来编写结构体相关的代码。
task.h
typedef struct //固定搭配 { u8 Run; //任务状态:Run/Stop //1的时候执行 u16 TIMCount; //定时计数器 //自减到0,置位run u16 TRITime; //重载计数器 //重载给TIMCount void (*TaskHook) (void); //任务函数 //run到1,就执行的函数 } TASK_COMPONENTS; //结构体的名字
然后这里是结构体的使用:这里定义了一个结构体数组,对应上面的三个变量和一个函数
static TASK_COMPONENTS Task_Comps[]= { //状态 计数 周期 函数 {0, 300, 300, LED0_Blink}, /* task 1 Period: 300ms */ {0, 600, 600, LED1_Blink}, /* task 2 Period: 600ms */ {0, 900, 900, LED2_Blink}, /* task 3 Period: 900ms */ {0, 1000, 1000, Printf_1S }, /* task 4 Period: 1000ms */ {0, 10, 10, Key_Task }, /* task 5 Period: 10ms */ /* Add new task here */ };
然后记录一下关于如何使用
u8 Tasks_Max = sizeof(Task_Comps)/sizeof(Task_Comps[0]); //这里使用一个sizeof,通过判断字节数来看Task_Comps[]数组里面有多少个任务,这里是五个。后面的for循环需要用到。 //======================================================================== // 函数: 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++) //for循环执行每一个任务 { if(Task_Comps[i].TIMCount) /* If the time is not 0 */ //当不为0时 { Task_Comps[i].TIMCount--; /* Time counter decrement */ //自减 if(Task_Comps[i].TIMCount == 0) /* If time arrives *///为0 { /*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 *///设置标志位状态,run为1了,该去执行下面函数了。 } } } } //======================================================================== // 函数: Task_Pro_Handler_Callback // 描述: 任务处理回调函数. // 参数: None. // 返回: None. // 版本: V1.0, 2012-10-22 //======================================================================== 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 */ //判断run的状态 { Task_Comps[i].Run = 0; /* Flag clear 0 */ //也是重新设置为初始状态,run=0 Task_Comps[i].TaskHook(); /* Run task */ //去执行下面的函数 } } }
然后后面的事情就很简单了,就是使用部分了。
//task.c 这里设置了五个,对应300、600、900、1000、10ms的周期性定时任务。 static TASK_COMPONENTS Task_Comps[]= { //状态 计数 周期 函数 {0, 300, 300, LED0_Blink}, /* task 1 Period: 300ms */ {0, 600, 600, LED1_Blink}, /* task 2 Period: 600ms */ {0, 900, 900, LED2_Blink}, /* task 3 Period: 900ms */ {0, 1000, 1000, Printf_1S }, /* task 4 Period: 1000ms */ {0, 10, 10, Key_Task }, /* task 5 Period: 10ms */ /* Add new task here */ }; //然后是新建了一个IO.c和.h专门来存放关于IO操作的函数 #include "io.h" u8 State1 = 0; //LED1初始状态为0 u8 State2 = 0; //LED2初始状态为0 u8 State3 = 0; //LED3初始状态为0 u16 Key_Vol =0; //按键维持时间 void LED0_Blink(void) { State1 = !State1; P00 = State1; printf("P00 = %d\r\n",(int)State1); } void LED1_Blink(void) { State2 = !State2; P01 = State2; printf("P01 = %d\r\n",(int)State2); } void LED2_Blink(void) { State3 = !State3; P02 = State3; printf("P02 = %d\r\n",(int)State3); } void Printf_1S(void) { printf("AI8051U\r\n"); } void Key_Task(void) { if(P32 == 0) { Key_Vol ++; if(Key_Vol == 5) { //执行按键按下的任务 printf("按键单击\r\n"); } } else { Key_Vol = 0; } }
最后整理一下文件,重新编译,烧录,看看ISP串口打印效果。可以看到完美执行了前面设置的五个定时器周期性任务。完美??
嗷嗷,keil工程尽量英文命名,防止出现各种编译报错:
附件的说明和下载:
一次很棒的教程,学到了很多,感谢冲哥、STC、立创!?
也是首次使用markdown来编写文章?
很奇怪的感觉,写是写了,但是好像不能直接复制粘贴上来显示。有些格式好像不对?
附件:demo_code.zip 附件:demo_code.zip
本版积分规则 发表回复 回帖后跳转到最后一页
|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )
GMT+8, 2025-5-8 15:00 , Processed in 0.129676 second(s), 86 queries .
Powered by Discuz! X3.5
© 2001-2025 Discuz! Team.