完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1.1 前后台系统:
早期嵌入式开发没有嵌入式操作系统的概念 ,直接操作裸机,在裸机上写程序,比如用51单片机基本就没有操作系统的概念。通常把程序分为两部分:前台系统和后台系统。 简单的小系统通常是前后台系统,这样的程序包括一个死循环和若干个中断服务程序:应用程序是一个无限循环,循环中调用API函数完成所需的操作,这个大循环就叫做后台系统。中断服务程序用于处理系统的异步事件,也就是前台系统。前台是中断级,后台是任务级。 1.2 RTOS系统: RTOS全称为:Real Time OS,就是实时操作系统,强调的是:实时性。实时操作系统又分为硬实时和软实时。硬实时要求在规定的时间内必须完成操作 ,硬实时系统不允许超时,在软实时里面处理过程超时的后果就没有那么严格。 在实时操作系统中,我们可以把要实现的功能划分为多个任务,每个任务负责实现其中的一部分,每个任务都是一个很简单的程序,通常是一个死循环。 RTOS操作系统:FreeRTOS,UCOS,RTX,RT-Thread,DJYOS等。 RTOS操作系统的核心内容在于:实时内核。 可剥夺型内核: RTOS的内核负责管理所有的任务,内核决定了运行哪个任务,何时停止当前任务切换到其他任务,这个是内核的多任务管理能力。多任务管理给人的感觉就好像芯片有多个CPU,多任务管理实现了CPU资源的最大化利用,多任务管理有助于实现程序的模块化开发,能够实现复杂的实时应用。 RTOS中的经典代表作:FreeRTOS,而FreeOS的内核是可剥夺型的,所以我们简单提一下什么是可剥夺型内核,关于FreeRTOS内核的详细内容我们会在后续的视频中专门讲解的。 可剥夺内核顾名思义就是可以剥夺其他任务的CPU使用权,它总是运行就绪任务中的优先级最高的那个任务。 2.1 FreeRTOS系统简介: FreeRTOS是一个可裁剪、可剥夺型的多任务内核,而且没有任务数限制。FreeRTOS提供了实时操作系统所需的所有功能,包括资源管理、同步、任务通信等。 FreeRTOS是用C和汇编来写的,其中绝大部分都是用C语言编写的,只有极少数的与处理器密切相关的部分代码才是用汇编写的,FreeRTOS结构简洁,可读性很强!最主要的是非常适合初次接触嵌入式实时操作系统学生、嵌入式系统开发人员和爱好者学习。 为什么学FreeRTOS 1、因为FreeROTS开源。 2、FreeRTOS免费 3、FreeRTOS是很多第三方组件钦定的系统! 2.2 FreeRTOS相关资料查找: 1、FreeRTOS官网:http://www.freertos.org/。 2、开源电子网:www.openedv.com。 3、其他论坛。 FreeRTOS 移植 2.2.1 向工程中添加相应文件 1、添加FreeRTOS 源码 在基础工程中新建一个名为FreeRTOS 的文件夹,如图2.2.1.1 所示: 创建FreeRTOS 文件夹以后就可以将FreeRTOS 的源码添加到这个文件夹中,添加完以后如图2.2.1.2 所示: 在1.3.2 小节中详细的讲解过portable 文件夹,我们只需要留下keil、MemMang 和RVDS这三个文件夹,其他的都可以删除掉,完成以后如图2.2.1.3 所示: 2、向工程分组中添加文件 打开基础工程,新建分组FreeRTOS_CORE 和FreeRTOS_PORTABLE,然后向这两个分组中添加文件,如图2.2.1.4 所示: 分组FreeRTOS_CORE 中的文件在什么地方就不说了,打开FreeRTOS 源码一目了然。重点来说说FreeRTOS_PORTABLE 分组中的port.c 和heap_4.c 是怎么来的,port.c 是RVDS 文件夹下的ARM_CM3 中的文件,因为STM32F103 是Cortex-M3 内核的,因此要选择ARM_CM3中的port.c 文件。heap_4.c 是MemMang 文件夹中的,前面说了MemMang 是跟内存管理相关 的,里面有5 个c 文件:heap_1.c、heap_2.c、heap_3.c、heap_4.c 和heap_5.c。这5 个c 文件是五种不同的内存管理方法,就像从北京到上海你可以坐火车、坐飞机,如果心情好的话也可以走路,反正有很多种方法,只要能到上海就行。这里也一样的,这5 个文件都可以用来作为FreeRTOS 的内存管理文件,只是它们的实现原理不同,各有利弊。这里我们选择heap_4.c,至于原因,后面会有一章节专门来讲解FreeRTOS 的内存管理,到时候大家就知道原因了。这里就先选择heap_4.c,毕竟本章的重点是FreeRTOS 的移植。 3、添加相应的头文件路径 添加完FreeRTOS 源码中的C 文件以后还要添加FreeRTOS 源码的头文件路径,头文件路径如图2.2.1.5 所示: 头文件路径添加完成以后编译一下,看看有没有什么错误,结果会发现提示打不开“FreeRTOSConfig.h”这个文件,如图2.2.1.6 所示: 这是因为缺少FreeRTOSConfig.h 文件,这个文件在哪里找呢?你可以自己创建,显然这不是一个明智的做法。我们可以找找FreeRTOS 的官方移植工程中会不会有这个文件,打开FreeRTOS 针对STM32F103 的移植工程文件,文件夹是CORTEX_STM32F103_Keil,打开以后如图2.2.1.7 所示: 果然!官方的移植工程中有这个文件,我们可以使用这个文件,但是建议大家使用我们例程中的FreeRTOSConf.h 文件,这个文件是FreeRTOS 的系统配置文件,不同的平台其配置不同,但是我们提供的例程中的这个文件肯定是针对ALIENTEK 开发板配置正确的。这个文件复制到什么地方大家可以自行决定,这里我为了方便放到了FreeRTOS 源码中的include 文件夹下。FreeRTOSConfig.h 是何方神圣?看名字就知道,他是FreeRTOS 的配置文件,一般的操作系统都有裁剪、配置功能,而这些裁剪及配置都是通过一个文件来完成的,基本都是通过宏定义来完成对系统的配置和裁剪的,关于FreeRTOS 的配置文件FreeRTOSConfig.h 后面也会有一章节来详细的讲解。到这里我们再编译一次,没有错误!如图2.2.1.8 所示: 如果还有错误的话大家自行根据错误类型查找和修改错误! 2.2.2 修改SYSTEM 文件 SYSTEM 文件夹里面的文件一开始是针对UCOS 而编写的,所以如果使用FreeRTOS 的话就需要做相应的修改。本来打算让SYSTEM 文件夹也支持FreeRTOS,但是这样的话会导致SYSTEM 里面的文件太过于复杂,这样非常不利于初学者学习,所以这里就专门针对FreeRTOS修改了SYSTEM 里面的文件。 1、修改sys.h 文件 sys.h 文件修改很简单,在sys.h 文件里面用宏SYSTEM_SUPPORT_OS 来定义是否使用OS,我们使用了FreeRTOS,所以应该将宏SYSTEM_SUPPORT_OS 改为1。 //0,不支持os //1,支持os #define SYSTEM_SUPPORT_OS 1 //定义系统文件夹是否支持OS 2、修改usart.c 文件 usart.c 文件修改也很简单,usart.c 文件有两部分要修改,一个是添加FreeRTOS.h 头文件,默认是添加的UCOS 中的includes.h 头文件,修改以后如下: //如果使用os,则包括下面的头文件即可. #if SYSTEM_SUPPORT_OS #include "FreeRTOS.h" //os 使用 #endif 另外一个就是USART1 的中断服务函数,在使用UCOS 的时候进出中断的时候需要添加OSIntEnter()和OSIntExit(),使用FreeRTOS 的话就不需要了,所以将这两行代码删除掉,修改以后如下: void USART1_IRQHandler(void) //串口1 中断服务程序 { u8 Res; if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) { Res =USART_ReceiveData(USART1); //读取接收到的数据 if((USART_RX_STA&0x8000)==0) //接收未完成 { if(USART_RX_STA&0x4000) //接收到了0x0d { if(Res!=0x0a)USART_RX_STA=0; //接收错误,重新开始 else USART_RX_STA|=0x8000; //接收完成了 } else //还没收到0X0D { if(Res==0x0d)USART_RX_STA|=0x4000; else { USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ; USART_RX_STA++; if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0; } } } } } 3、修改delay.c 文件 delay.c 文件修改的就比较大了,因为涉及到FreeRTOS 的系统时钟,delay.c 文件里面有4个函数,先来看一下函数SysTick_Handler(),此函数是滴答定时器的中断服务函数,代码如下: extern void xPortSysTickHandler(void); //systick 中断服务函数,使用OS 时用到 void SysTick_Handler(void) { if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行 { xPortSysTickHandler(); } } FreeRTOS 的心跳就是由滴答定时器产生的,根据FreeRTOS 的系统时钟节拍设置好滴答定时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用FreeRTOS 的API 函数xPortSysTickHandler()。delay_init()是用来初始化滴答定时器和延时函数,代码如下: //初始化延迟函数 //SYSTICK 的时钟固定为AHB 时钟,基础例程里面SYSTICK 时钟频率为AHB/8 //这里为了兼容FreeRTOS,所以将SYSTICK 的时钟频率改为AHB 的频率! //SYSCLK:系统时钟频率 void delay_init() { u32 reload; SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟 HCLK fac_us=SystemCoreClock/1000000; //不论是否使用OS,fac_us 都需要使用 reload=SystemCoreClock/1000000; //每秒钟的计数次数 单位为M reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ 设定溢出 //时间reload 为24 位寄存器,最大值: //16777216,在72M 下,约合0.233s 左右 fac_ms=1000/configTICK_RATE_HZ; //代表OS 可以延时的最少单位 SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK 中断 SysTick->LOAD=reload; //每1/configTICK_RATE_HZ 秒中断 //一次 SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK } 前面我们说了FreeRTOS 的系统时钟是由滴答定时器提供的,那么肯定要根据FreeRTOS 的系统时钟节拍来初始化滴答定时器了,delay_init()就是来完成这个功能的。FreeRTOS 的系统时钟节拍由宏configTICK_RATE_HZ 来设置,这个值我们可以自由设置,但是一旦设置好以后我们就要根据这个值来初始化滴答定时器,其实就是设置滴答定时器的中断周期。在基础例程中滴答定时器的时钟频率设置的是AHB 的1/8,这里为了兼容FreeRTOS 将滴答定时器的时钟频率改为了AHB,也就是72MHz!这一点一定要注意!接下来的三个函数都是延时的,代码如下: //延时nus //nus:要延时的us 数. //nus:0~204522252(最大值即2^32/fac_us@fac_us=168) void delay_us(u32 nus) { u32 ticks; u32 told,tnow,tcnt=0; u32 reload=SysTick->LOAD; //LOAD 的值 ticks=nus*fac_us; //需要的节拍数 told=SysTick->VAL; //刚进入时的计数器值 while(1) { tnow=SysTick->VAL; if(tnow!=told) { //这里注意一下SYSTICK 是一个递减的计数器就可以了. if(tnow told=tnow; if(tcnt>=ticks)break; //时间超过/等于要延迟的时间,则退出. } }; } //延时nms,会引起任务调度 //nms:要延时的ms 数 //nms:0~65535 void delay_ms(u32 nms) { if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行 { if(nms>=fac_ms) //延时的时间大于OS 的最少时间周期 { vTaskDelay(nms/fac_ms); //FreeRTOS 延时 } nms%=fac_ms; //OS 已经无法提供这么小的延时了, //采用普通方式延时 } delay_us((u32)(nms*1000)); //普通方式延时 } //延时nms,不会引起任务调度 //nms:要延时的ms 数 void delay_xms(u32 nms) { u32 i; for(i=0;i delay_us()是us 级延时函数,delay_ms 和delay_xms()都是ms 级的延时函数,delay_us()和delay_xms()不会导致任务切换。delay_ms()其实就是对FreeRTOS 中的延时函数vTaskDelay()的简单封装,所以在使用delay_ms()的时候就会导致任务切换。delay.c 修改完成以后编译一下,会提示如图2.2.2.1 所示错误: 图2.2.2.1 的错误提示表示在port.c、delay.c 和stm32f10x_it.c 中三个重复定义的函数:SysTick_Handler()、SVC_Handler()和PendSV_Handler(),这三个函数分别为滴答定时器中断服务函数、SVC 中断服务函数和PendSV 中断服务函数,将stm32f10x_it.c 中的三个函数屏蔽掉,如图2.2.2.2 所示: 再次编译代码,应该没有错误了,如果还是错误的话自行根据错误类型修改!至此,SYSTEM文件夹就修改完成了。 2.3 移植验证实验 2.3.1 实验程序设计 1、实验目的 编写简单的FreeRTOS 应用代码,测试FreeRTOS 的移植是否成功。鉴于大家还没正式学习FreeRTOS,可以直接将本实验代码复制粘贴到自己的移植工程中。 2、实验设计 本实验设计四个任务:start_task()、led0_task ()、led1_task ()和float_task(),这四个任务的任务功能如下: start_task():用来创建其他三个任务。 led0_task ():控制LED0 的闪烁,提示系统正在运行。 led1_task ():控制LED1 的闪烁。 float_task():简单的浮点测试任务,用于测试STM32F4 的FPU 是否工作正常。 3、实验工程 FreeRTOS 实验2-1 FreeRTOS 移植实验。 4、实验程序与分析 任务设置 #include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "FreeRTOS.h" #include "task.h" /************************************************ ALIENTEK 战舰STM32F103 开发板 FreeRTOS 实验2-1 FreeRTOS 移植实验-库函数版本 技术支持:www.openedv.com 淘宝店铺:http://eboard.taobao.com 关注微信公众平台微信号:"正点原子",免费获取STM32 资料。 广州市星翼电子科技有限公司 作者:正点原子 @ALIENTEK ************************************************/ #define START_TASK_PRIO 1 //任务优先级 #define START_STK_SIZE 128 //任务堆栈大小 TaskHandle_t StartTask_Handler; //任务句柄 void start_task(void *pvParameters); //任务函数 #define LED0_TASK_PRIO 2 //任务优先级 #define LED0_STK_SIZE 50 //任务堆栈大小 TaskHandle_t LED0Task_Handler; //任务句柄 void led0_task(void *p_arg); //任务函数 #define LED1_TASK_PRIO 3 //任务优先级 #define LED1_STK_SIZE 50 //任务堆栈大小 TaskHandle_t LED1Task_Handler; //任务句柄 void led1_task(void *p_arg); //任务函数 ● main()函数 int main(void) { NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置系统中断优先级分组4 delay_init(); //延时函数初始化 uart_init(115200); //初始化串口 LED_Init(); //初始化LED //创建开始任务 xTaskCreate((TaskFunction_t )start_task, //任务函数 (const char* )"start_task", //任务名称 (uint16_t )START_STK_SIZE, //任务堆栈大小 (void* )NULL, //传递给任务函数的参数 (UBaseType_t )START_TASK_PRIO, //任务优先级 (TaskHandle_t* )&StartTask_Handler); //任务句柄 vTaskStartScheduler(); //开启任务调度 } ● 任务函数 //开始任务任务函数 void start_task(void *pvParameters) { taskENTER_CRITICAL(); //进入临界区 //创建LED0 任务 xTaskCreate((TaskFunction_t )led0_task, (const char* )"led0_task", (uint16_t )LED0_STK_SIZE, (void* )NULL, (UBaseType_t )LED0_TASK_PRIO, (TaskHandle_t* )&LED0Task_Handler); //创建LED1 任务 xTaskCreate((TaskFunction_t )led1_task, (const char* )"led1_task", (uint16_t )LED1_STK_SIZE, (void* )NULL, (UBaseType_t )LED1_TASK_PRIO, (TaskHandle_t* )&LED1Task_Handler); vTaskDelete(StartTask_Handler); //删除开始任务 taskEXIT_CRITICAL(); //退出临界区 } //LED0 任务函数 void led0_task(void *pvParameters) { while(1) { LED0=~LED0; vTaskDelay(500); } } //LED1 任务函数 void led1_task(void *pvParameters) { while(1) { LED1=0; vTaskDelay(200); LED1=1; vTaskDelay(800); } } 测试代码中创建了3 个任务:LED0 测试任务、LED1 测试任务和浮点测试任务,它们的任务函数分别为:led0_task()、led1_task()。led0_task()和led1_task()任务很简单,就是让LED0 和LED1 周期性闪烁。由于我们只是用测试代码来测试FreeRTOS 是否移植成功的,所以关于具体的函数的调用方法这些不要深究,后面会有详细的讲解! 2.3.2 实验程序运行结果分析 编译并下载代码到STM32F103 开发板中,下载进去以后会看到LED0 和LED1 开始闪烁, LED0 均匀闪烁,那是因为我们在LED0 的任务代码中设置好的LED0 亮500ms,灭500ms。 LED1 亮的时间短,灭的时间长,这是因为在LED1 的任务代码中设置好的亮200ms,灭800ms。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1609 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1540 浏览 1 评论
970 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
681 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1587 浏览 2 评论
1861浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
643浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
515浏览 3评论
528浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
503浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-20 14:40 , Processed in 0.784567 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号