韦东山FreeRTOS快速入门课程学习笔记

FreeRTOS快速入门课程学习笔记



前言

本文是基于韦东山老师的FreeRTOS的课程笔记,持续更新中。。。。


提示:以下是本篇文章正文内容,下面案例可供参考

一、_单片机_RTOS_架构的概念

1.RTOS有什么用

在这里插入图片描述在这里插入图片描述
妈妈在喂饭的时候,同事感觉没人理我,当妈妈在回复同事的时候,小孩感觉没人理我
在这里插入图片描述
在这里插入图片描述
以上就是裸机开发的流程,执行完一个任务才能去执行另一个任务
接下来是基于RTOS的开发
在这里插入图片描述
任务之间穿插执行,只要速度够快剧看不出来有短暂延迟,近似实现了同时执行两个任务
在这里插入图片描述
优先级高的事件先处理,优先级相同的事件交叉处理

2.深入理解CPU架构

在这里插入图片描述

在这里插入图片描述

二、嵌入式基本数据结构

1.堆的概念

所谓堆就是一块空闲的内存,你也可以来管理这块内存,从这块内存中来取出一部分,用完之后再把它释放回去。
就比如说我们举一个例子

char heap_buf[1024];
int pos=0;

void *my_malloc(int size)
{
   int old_pos=pos;
   pos+=size;
   return &heap_buf[old_pos];
}

我们说堆就是一块空闲的内存,那么我们可以定义一个数组heap_buf[1024],它就是一块空闲的内存,我只要在他上面来实现内存的分配和释放,那么它就是一个堆。

我们来实现一个比较简单的函数*my_malloc(int size),分配了size大小的空间,返回这个空间的首地址
在这里插入图片描述
当我们在这块空闲的内存是实现malloc函数的时候,这块空闲的内存就被称为堆,我们使用一个整数来表示空闲内存的位置,使用这种最简单的方法的话,我们没用办法实现free函数,在这里我们先写出一个free函数

void my_free(void *buf)
{
  /*err*/
}

假装我们实现的free函数可以使用了

int main (void)
{
  char ch=65;  //char ch ='A';
  int i;
  char *buf=*my_malloc(100);

  unsigned char uch=200;

  for(i=o;i<26;i++)
  buf[i]= 'A'+i;
}

在这里插入图片描述
我们可以先调试一下,先编译,我们需要设置一下,点击debug这里使用模拟器,然后点击调试
在这里插入图片描述
先打断点,可以看到,在执行my_malloc(int size)之前,buf是空的。
进入
my_malloc(int size)一路执行
在这里插入图片描述
它会返回这个buf中的某一个地址
这个数组的地址在这里
在这里插入图片描述
我们可以看到这个heap_buf的地址是0x20000004,当我们第一次分配的时候肯定是返回他的首地址,我们现在可以继续执行,多执行几次,我们来看看这个buf的内容
在这里插入图片描述
看到没有,这里面已经添加了好几个数据

2.栈的概念

对于栈它是一个幕后英雄,在我们编写程序的时候,你感受不到栈的存在,我们写一个简单的程序

void c_fun(void)
{
}

void b_fun(void)
{
}

void a_fun(int val)
{
 int a=8;
 a+=val;

 b_fun();
 c_fun();
 return a;
}

int main (void)
{
  char ch=65;  //char ch ='A';
  int i;
  char *buf=*my_malloc(100);

  unsigned char uch=200;

  for(i=o;i<26;i++)
  buf[i]= 'A'+i;

  a_fun(46);

可以看到main函数调用a函数,a函数调用b函数和c函数,在整个过程中我们都没有感觉到栈的存在,但是我来问几个问题:
1.返回地址保存在哪里?
在这里插入图片描述
main函数怎么调用到函数a,我得记录一下函数a的返回地址。所以说第一步它吧返回地址保存在某个寄存器里面LR(LinkRegister),后面我们讲到ARM架构时,你会知道这些寄存器的,可以用来保存一些词,你看,main函数调用函数a之前,它会先把这条语句的地址保存起来,LR等于(return 0)这句话的地址,然后调用函数a;同理,函数a要调用函数b,它也要先把c_fun()这个返回地址保存下来,LR等于这句话的地址,然后调用函数b_fun().
那么问题来了,LR会被覆盖吗?
在这个场景里面,如果你不继续做处理的话,LR寄存器里面值肯定会被覆盖,那我们怎么处理呢,简单!!
在函数a内部它要把LR的值保存起来,存入栈中,我把LR的值也就是语句1的地址保存在栈中,后面我再来调用函数b的时候,我就不怕这个寄存器被覆盖了;同样道理函数b也会在函数b的内部把返回地址保存在栈中,到这里,第一个问题就解决了,返回地址保存在哪里?保存在栈里
在这里插入图片描述

什么是栈,栈就是一块空闲的内存,就比如说打开我们的代码
在这里插入图片描述
在调用main函数之前,它要使用汇编代码设置这个栈,SP就是栈寄存器,让他指向某一块空闲的内存,这样就可以了,以后你就可以调用c函数了,整个使用栈的过程是怎样的呢,我们来演示一下。

我们刚才已经总结了一个c函数要做什么事情,在c函数的入口,也就是开始前面那一部分代码
,它会做什么事情呢,我们先把要做的事情给完善一下。c函数开头,你看它要把LR等等寄存器保存在栈中,还要把局部变量等保存在栈中,那么它肯定得划分出栈呀。第一,划分栈,有LR寄存器和局部变量,划分出来的栈用来保存这些LR寄存器还有局部变量。第二,那显然是LR等寄存器存入栈。第三,执行代码。
就比如说代码里面假设它有a=8的话,我不是为这个a在栈里面划分了空间吗,我就把8这个值写到那个栈里面去。
在这里插入图片描述
让SP指向起始位置,设置好栈之后就可以去调用main函数了,它怎么调用main函数呢,它使用BL指令来调用main函数。我们知道它调用main函数,main函数中会调用a函数,a函数中又会调用到b函数。在这个过程中,它在汇编文件里面使用BL指令来调用main函数,BL会把下一条指令也就是main函数的返回地址保存在LR寄存器里面。

所以,第一步BL main会做两件事情,让LR等于返回地址,还有执行main函数,main函数它就会划分出自己的栈,假设它划分出一块空间,SP=SP-N,这N字节的内存就是main函数的栈,在里面它会保存LR等寄存器和局部变量。
第二步,调用函数a,让LR等于函数a的返回地址,执行函数a。在函数a的开头它做的事也是类似的,一开始的时候它也会划分出M字节的空间,让SP=SP-M,这M字节的空间就是函数a的栈,在里面它会保存LR等寄存器和局部变量。
第三步,函数b做的事同理。
在这里插入图片描述
假设函数b执行完了,它会返回到哪里。他就会函数b的栈中把LR的值给取出来,跳过去执行

二、FreeRTOS源码怎么用?

1.从官方源码精简出第一个FreeRTOS程序

1.1 FreeRTOS目录结构
在这里插入图片描述
1.2 核心文件
FreeRTOS/Source/tasks.c
FreeRTOS/Source/list.c
其他文件的作用也一起列表如下:
在这里插入图片描述
1.3 移植时涉及的文件
移植FreeRTOS时涉及的文件放在FreeRTOS/Source/portable/[compiler]/[architecture]目录下,
比如:RVDS/ARM_CM3,这表示cortexM3架构在RVDS或Keil工具上的移植文件。
里面有2个文件:

port.c
portmacro.h

1.4 内存管理
文件在FreeRTOS/Source/portable/MemMang下,它也是放在portable目录下,表示你可以提供自己的函数。

源码中默认提供了5个文件,对应内存管理的5种方法。
在这里插入图片描述
1.5 USER目录下是预先配置好的、没有编译错误的工程。目的是让你可以基于它进行修改,以适配你的单板。

这些Demo还可以继续精简:

main函数中只需要保留2个函数:
prvSetupHardware()
vTaskStartScheduler()
如下图所示
在这里插入图片描述
1.6 数据类型和编程规范
1.6.1 数据类型
每个移植的版本都含有自己的portmacro.h头文件,里面定义了2个数据类型:

TickType_t:
FreeRTOS配置了一个周期性的时钟中断:Tick Interrupt;
每发生一次中断,中断次数累加,这被称为tick count;
tick count这个变量的类型就是TickType_t;
TickType_t可以是16位的,也可以是32位的;
FreeRTOSConfig.h中定义configUSE_16_BIT_TICKS时,TickType_t就是uint16_t;
否则TickType_t就是uint32_t;
对于32位架构,建议把TickType_t配置为uint32_t;
BaseType_t:
这是该架构最高效的数据类型
32位架构中,它就是uint32_t
16位架构中,它就是uint16_t
8位架构中,它就是uint8_t
BaseType_t通常用作简单的返回值的类型,还有逻辑值,比如pdTRUE/pdFALSE
1.6.2 变量名
变量名有前缀:
在这里插入图片描述
1.6.3 函数名
函数名的前缀有2部分:返回值类型、在哪个文件定义。
在这里插入图片描述
1.6.4 宏的名
宏的名字是大小,可以添加小写的前缀。前缀是用来表示:宏在哪个文件中定义。
在这里插入图片描述
通用的宏定义如下:
在这里插入图片描述

2.修改官方源码增加串口打印

  • 17
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值