FreeRTOS源码探析之--任务调度相关
FreeRTOS可以运行多任务,在于其内核的任务调度功能,本篇介绍任务调度的基本思路与部分源码分析。
1 裸机编程与RTOS 的区别1.1 裸机程序基本框架1234567891011121314151617/*主函数*/int main(){ init();//一些初始化 /*死循环*/ while(1) { do_something_1();//执行一些逻辑 do_something_2(); }//循环执行}/*中断服务函数*/IRQ_Handler(){ set_flag();//简短的标记操作}
单片机裸机编程的思路比较简单,就是一个死循环,程序依次执行while(1)中的各条语句,循环往复即可,需要处理某些紧急事件时,通过中断服务函数来打断while(1)的执行。
裸机编程虽然简单,但只能在一个循环中执行各种裸机,第一项功能执行完后才能执行第二项功能,就好比有多个人在轮流干活,CPU的利用率不高,不能处理并行逻辑。
1.2 RTOS程序基本框架12 ...
FreeRTOS源码探析之--事件标志组
事件是一种实现任务间通信的机制,主要用于实现多任务间的同步,但事件通信只能是事件类型的通信,无数据传输。
1 基础概述1.1 基本作用事件标志的作用类似于全局型的flag,多个标志组合在一起构成事件标志组,这里先分析一下事件标志组于全局flag的区别:
使用事件标志组可以让 FreeRTOS内核有效地管理任务,而全局变量是无法做到的,任务的超时等机制需要用户自己去实现
使用了全局变量就要防止多任务的访问冲突,而使用事件标志组则处理好了这个问题,用户无需担心
使用事件标志组可以有效地解决中断服务程序和任务之间的同步问题
1.2 运行原理
如上图:
任务1在一个循环中等待事件的到来,等待时处于阻塞状态,即任务挂起状态。阻塞的时间可用设置。当有事件位被置位时,执行处理事件。
任务1的处理事件可以触发事件(给自身的任务触发事件),但这种方式触发后,只能等程序再次运行到xEventGroupWaitBits才能处理
任务2或其它任务可以触发事件(这是比较常用的用法),这样,在任务2触发事件后,任务1可以从阻塞态变为就绪态,如果任务1的优先级较高,则任务1开始运行,执行处理事件,实现任务 ...
FreeRTOS源码探析之--软件定时器
软件定时器是FreeRTOS中的一个重要模块,使用软件定时器可以方便的实现一些与超时或周期性相关的功能,本篇从FreeRTOS的源码入手,来分析FreeRTOS软件定时器的运行机理。
1 基础知识1.1 软件定时器与硬件定时器的区别硬件定时器
每次在定时时间到达之后就会自动触发一个中断,用户在中断服务函数中处理信息
硬件定时器的精度一般很高,可以达到纳秒级别
硬件定时器是芯片本身提供的定时功能
软件定时器
指定时间到达后要调用回调函数(也称超时函数),用户在回调函数中处理信息
硬件定时器的定时精度与系统时钟的周期有关,一般系统利用SysTick作为软件定时器的基础时钟,系统节拍配置为FreeRTOSConfig.h中的configTICK_RATE_HZ,默认是1000,那么系统的时钟节拍周期就为1ms
软件定时器是由操作系统提供的一类系统接口
注意:软件定时器回调函数的上下文是任务,回调函数要快进快出,且回调函数中不能有任何阻塞任务运行的情况,如vTaskDelay()以及其它能阻塞任务运行的函数。
1.2 软件定时器的两种工作模式FreeRTOS提供的软件定时器支持单次 ...
图解环形链表--创建、循环赋值与删除
C语言中,链表是一种数据结构,相比较数组的连续存储,链表是一种将内存分散(当前也可以连续)的数据节点通过指针的方式连接在一起,此外,链表不仅可以存储简单的数据类型,还可以存储结构体,只要定义好自己的链表结构体即可。
链表,从名字上来看是一条数据链,一般的链表其末尾节点是没有指向的,但当把链表的末尾节点指定为指向头节点时,则构成了一个环形链表。
链表结构体首先定义环形链表的节点的形式,即一个结构体,简单为例,该结构体内只有一个float数据,以及指向下一个节点的指针,如下:
123456//链表节点结构体 typedef struct stData{ float data; struct stData *pNext;}stData;
环形链表的使用需要定义3个指针:1个是环形链表的指针(分配后就是固定值),1个头指针和1个尾指针(在向链表写入新数据时这两个指针不不断的改变节点的指向)。
环形链表首先需要初始化,为其分配内存空间,初始化后,链表内的数据全部初始化为0,且头指针和尾指针都先与环形链表指向同一地址。
1234567stData *pList = NU ...
python读取excel进行数据处理并保存为新的excel结果
一般需要数据处理时我们会使用excel表格,并可使用其自带的求和、排序等功能对数据进行处理,但对于某些复杂的处理,我们可以使用python工具来读取excel数据,并通过python编程,来实现自己所需要的数据处理结果和数据保存方式。
下面以一组学生成绩数据为例,计算每行的总分和平均分,并将最终结果连同学号保存到新的excel中,最终效果如下,左边是原始数据excel文件,右边为数据处理后生成的excel结果文件:
代码代码如下,已经加了详细注释,需要注意的是,程序中data = ori.iloc[0:,1:5]这句中:
0: 指定行的范围:表示行数据从0到最后一行,这样就不需要具体指定最后一行是多少行,另外,这里的0实际是excel中的第2行,可能是因为默认把excel的第1行当作是数据的表头,就跳过了吧
1:5 指定列的范围:这里其实是左闭右开,即1到4,即excel中的第2列到5列(注意0才是第1列)
为了确定读取的是否正确,可以先打印出部分读取的数据确认一下,如先读取5行。
12345678910111213141516171819202122232425262 ...
探索变量与指针的关系--从内存中观察
C语言中,指针是很重要的一个功能,但想要用好指针却不是一件容易的事,本篇从内存存储的数据出发,通过对比变量与指针在内存中究竟是怎么存储的,来对指针有一个直观的认识。
首先明确一个概念:变量,存储的是数值,而指针,是一种特殊的变量,它存储的是地址形式的数值。
下面编写一个测试程序来验证一下,该程序定义了一些变量与指针,另外,还使用malloc函数来动态申请内存(普通变量是存储在栈区,动态申请的变量在堆区,通过打印其内存地址可以看出差别)。
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455#include <stdio.h>#include <stdlib.h>int main(){ int a = 0xFF01; //变量,存储的是数值 (0xFF01=65281) char b = 23; int *p1 = &a; //指针,存储的是地址(一种特殊的数值) short *p3; char ...
嵌入式中的合作开发--函数指针
在嵌入式软件开发中,一个项目往往需要多人协作完成。
比如A需要完成项目的整体逻辑功能,而整个逻辑功能包含许多具体的小功能,但A又没有时间或能力来实现这些小功能,这时可以让B来协助实现函数内部的功能。
通常的思路是,B写好某个函数后,A直接通过B声明的函数调用即可。但这会有一些问题,例如B写好函数之后,A只能使用B声明的函数名来使用,假如B声明的函数名的命名规则很不符合A的口味,A用起来就很不爽,哈哈。那这样怎么办呢?聪明的A可以自己再声明一个自己喜欢的函数名,并通过函数指针传递的功能来获得B的功能。
下面来讲一下具体实现:
声明函数指针-A负责比如A需要一个求和功能的函数,但他没时间写,他可以自己先声明一个函数指针:
12//a中声明一个指针函数,其函数内部的功能需要另一个人b来完成 int (*mysum)(int, int)=0;
这个看起来和普通的函数声明很像,都有函数名,返回值类型与参数类型,但该函数名前有一个星号,表示它是函数指针,另外其函数实体可以先初始化为0。
函数指针赋值-A负责这一步相当于函数指针初始化,也相当于函数注册,就是将A声明的函数指针,通过指针赋值的 ...
Linux与Windows间文件互传之TFTP方式
Linux端安装tftp服务
安装xinetd,tftp 和tftpd
12sudo apt-get install xinetdsudo apt-get install tftp tftpd
配置/etc/xinetd.d/tftp文件
新建一个文件:
1#sudo vim /etc/xinetd.d/tftp
写入如下内容
12345678910111213service tftp{ socket_type = dgram protocol = udp wait = yes user = root server = /usr/sbin/in.tftpd server_args = -s /var/tftpboot/ disable = no per_source = 11 cps = 100 2 ...
嵌入式开发,各类存储方式知多少?
ROM & RAM
ROM(Read Only Memory)
ROM在系统停止供电的时候仍然可以保持数据,如光盘CD-ROM,可以保存文件,且只能读取。
RAM(Random Access Memory)
RAM通常都是在掉电之后就丢失数据,如计算机的内存条就是RAM,关机后内存数据被自动清空。
PROM & EPROM && EEROMROM又可分为几类
PROM
可编程的ROM,但只能写入一次(有点像光盘,只能刻录一次),这种是早期的产品,现在已经不用了。
EPROM
通过紫外光的照射擦出原先的程序,现在也不用了。
EEPROM
电可擦除,如学习单片机时用到的AT24C02芯片,大小为2K,用于保存少量掉电不丢失的数据,单片机通过IIC来读写这个EEPROM的内容。
SRAM & DRAMRAM又可分为两大类
SRAM(Static RAM)
SRAM即静态RAM,读写速度非常快,但价格也较贵,用于CPU的一级缓冲,二级缓冲。
单片机中也有SRAM,如STM32F103VET6具有64K的SRAM,用于程序运行时变 ...
MPU6050姿态解算2-欧拉角&旋转矩阵
IMU姿态解算IMU,即惯性测量单元,一般包含三轴陀螺仪与三轴加速度计。之前的文章MPU6050姿态解算1-DMP方式已将对MPU6050这款IMU作了简单的介绍,并通过其内部的DMP处理单元直接得到姿态解算的四元数结果。本篇将通过软件解算的方式,利用欧拉角与旋转矩阵来对陀螺仪与加速度计的原始数据进行姿态求解,并将两种姿态进行互补融合,最终得到IMU的实时姿态。
本篇的姿态解算选用的旋转顺序为ZYX,即IMU坐标系初始时刻与大地坐标系重合,然后依次绕自己的Z、Y、X轴进行旋转,这里先自定义一下每次的旋转名称和符号:
绕IMU的Z轴旋转:航向角yaw, 转动 y 角度
绕IMU的Y轴旋转:俯仰角pitch,转动 p 角度
绕IMU的X轴旋转:横滚角row, 转动 r 角度
三次旋转的示意图如下:
另外,横滚roll,俯仰pitch,偏航yaw的实际含义如下图:
旋转矩阵旋转矩阵的知识请先参阅3维旋转矩阵推导与助记以及3维旋转矩阵推导与助记-补充篇,这里只列出本篇需要用到的3个旋转矩阵,注意这3个旋转矩阵是坐标变换的旋转矩阵。
欧拉角旋转欧拉角旋转的知识请先参阅欧拉角旋 ...