单片机的通信和RTOS实时操作系统

嵌入式学习路线和ROTS开发模式(1) 嵌入式基础, RTOS 阅读详情

一、串口概述

1、TTL(正逻辑)

TTL    输入    输出
 高电平2-5V2.4-5V
低电平0-0.8V0-0.4V

TTL属于板级通信,包括UART(串口A-A线、蓝牙模块、wifi模块)、IIC、SPI等。

2、UART和USART区别

UART:通用异步接收发送机(一般为通信方式)

USART:通用同步异步接收发送机(一般为stm32单片机的外设)

3、RS485(差分电平)

RS485
高电平2-6V
低电平(-2)-(-6)V
特点两线半双工、比特率10M、距离远(最远1.2km)、可以一对多

4、基本概念

(1)异步和同步

异步:没有时钟线,同步:有时钟线

(2)串口和并口

    串口(串行):单方向通信的数据线只有一条

并口(并行):单方向通信的数据线有多条

(3)单工、半双工和全双工

单工:可以单方向通信的线

半双工:可以双向通信的线(读写不能同时)

全双工:可以双向通信的线(读写可以同时)

(4)波特率和比特率、校验方法

①什么是波特率和比特率?

1、波特率和比特率(位/秒) :描述数据在信道中传输速度的两个相关但不同的指标。

2、区别:波特率表示信号变化的频率,即每秒传输的符号数量。比特率表示信息传输的速率,即每秒传输的二进制位(比特)数量。

②校验方法有哪些?

CRC校验

1、CRC(循环冗余校验):一种检错码,主要用于检测数字数据在传输或存储过程中是否因干扰、硬件故障等原因产生了意外错误。它不是加密算法,不用于保密,只用于验证数据是否完好无损。

2、主要原理:发送端和接收端事先约定一个除数。发送端对原始数据执行一个特定的除法运算,得到的余数就是CRC码。发送方将“原始数据 + CRC码”一起发送。接收方用同样的除数对收到的数据再做一次除法。如果余数为0,则认为数据极大概率正确;如果余数不为0,则肯定发生了错误,数据有误。

奇偶校验

1、奇偶校验:一种最简单的检错码,主要用于检测数字数据在传输或存储过程中是否发生了奇数个比特错误(如1位、3位等)。它不是加密算法,不用于保密,只用于验证数据是否完好无损。

2、主要原理:发送端和接收端事先约定使用奇校验还是偶校验。发送端在原始数据末尾添加一个比特(校验位),使整个数据(原始数据+校验位)中“1”的个数为奇数(奇校验)或偶数(偶校验)。接收方对收到的数据(包括校验位)重新统计“1”的个数。如果统计结果与约定的奇偶性一致,则认为数据基本正确(但无法发现偶数个错误);如果奇偶性不一致,则肯定发生了奇数个错误。

二、IIC协议

IIC协议:半双工同步串行通信,两线制(SDA、SCL),多主多从,有起始/停止信号和应答位。

1、通信说明

SDA:串行数据线 SCL(SCK):串行时钟线
主机:启动总线传输数据,产生时钟信号开启传输的器件
从机:任何被寻址的器件

2、收发数据

主机发送数据给从机

主机接收从机的数据

3、时序图编程之砖头的建造

(1)起始信号

(2)停止信号

(3)数据有效性

(4)应答信号

4、时序图编程之总工程的建设

(1)单片机的通信大楼

(2)写数据

(3)读数据

三、SPI协议

SPI协议:全双工同步串行通信,四线制,一主多从,通过CS选择从机,时钟沿同步收发,无应答机制,速度高于IIC。

1、通信说明

Master:主机 Slave:从机
SCLK:串行时钟线
MOSI:主机发送数据,从机接收数据
MISO:主机接收数据,从机发送数据
SS/CS:选择从机(片选),高电平时从机不工作

2、单机通信和多机通信

(1)单机通信

(2)多机通信

3、通信过程

➊同步:我发送一个数据给你,你就要发回一个数据给我。

➋单片机->SPI模块,单片机发送想要发的8位数据,SPI模块发回8位数据(无意义的数据)。

➌SPI模块->单片机,单片机发送任意的8位数据(为了让SPI模块发送8位数据),SPI模块发送8位数据。

4、CPHA、CPOL、SPCLK

◉ CPHA:时钟相位(决定何时读数据:MISO),CPHA=0 在SPCLK第一个跳变沿读数据,CPHA=1 在SPCLK第二个跳变沿读数据。

◉ CPOL:时钟极性(决定何时写数据:MOSI),CPOL=0 SPCLK低电平不工作,高电平工作
,CPOL=1 SPCLK高电平不工作,低电平工作。

◉ SPCLK:同步串行端口时钟。

5、工作模式

四种模式00 01 10 11

(1)模式0(00)

(2)模式3(11)

6、模拟SPI和硬件SPI

(1)模拟SPI

(2)硬件SPI

(3)硬件SPI编程

四、IIC应用模块和SPI应用模块

1、IIC模块:AT24C02

AT24C02(I²C EEPROM)

1)接口:I²C(2线,低速,地址寻址)。

2)容量:2Kb(256字节),小容量。

3)特点:字节级读写,适用于小数据存储,如配置参数。

2、SPI模块:W25Q128

W25Q128(SPI Flash)

1)接口:SPI(4线,高速,指令寻址)。

2)容量:128Mb(16MB),大容量。

3)特点:页操作/扇区擦除,适合存储程序代码或大量数据。

3、核心区别

1)速度:SPI(W25Q128)远快于I²C(AT24C02)。

2)容量:W25Q128 容量是 AT24C02 的 6.5 万倍。

3)应用:AT24C02 存小参数,W25Q128 存代码/大数据。

五、RTOS实时操作系统

RTOS是一种能在严格时间限制内完成任务调度的嵌入式操作系统,强调确定性和可预测性。

RTOS(Real Time OS)即实时操作系统,根据各个任务的要求,进行资源(包括存储器、外设等)管理、消息管理、任务调度、异常处理等工作。

为什么使用RTOS?

a、嵌入式实时操作系统提高了系统的可靠性。

b、提高了开发效率,缩短了开发周期,例如官方设置提供网络协议栈、文件系统、图形界面(ucGUI、emWIN、QT.....)的支持。

c、嵌入式实时操作系统充分发挥了32位CPU的多任务的潜力。

(1)裸机开发的例子

(2)RTOS开发的例子

六、RTOS的任务状态

任务状态机制

七、资源保护

资源共享方法

何时选用

关中断、开中断

(临界区)

前提条件:

当访问共享资源的速度很快(读取或写入极少量数据时、需要和中断服务程序共享变量或数据结构、喂狗操作等)

关于可靠访问也得使用开关中断的方法:如FLASH写数据。就算FLASH访问比较耗时,都得使用开关中断的方法,保证数据的可靠写入和访问,因为数据是无价的。

影响:

由于使用该方法会影响中断延迟,所以极度不推荐使用该方法。

给调度器上锁、解锁

前提条件:

当访问共享资源如访问变量缓冲区数据,甚至变量值自加/自减。

影响:

给调度器上锁同样有缺点,会导致给调度器上锁的任务成为最高优先级。

这已经有悖于RTOS的初衷,所以同样不推荐使用该方法,当然该方法比关中断要好些,起码不会影响系统的中断延迟。

信号量

前提条件:

当所有的任务可以无限等待对共享资源(常见于硬件的访问,而该硬件的使用时候也比较耗时间)的访问时。

影响:

使用信号量可能导致优先级反转的问题,这样会破坏任务的预期顺序;然而其优点在于它的速度使用互斥型信号量要略快一些。

互斥型信号量

(互斥量、互斥锁)

前提条件:

这是访问共享资源的首选方法。特别是当某些任务对共享资源(常见于硬件的访问,而该硬件的使用时候也比较耗时间)访问有时间要求时。

影响:

RTOS的互斥型信号量具有一套内建的优先级继承机制,用来避免优先级反转的问题。然而正如上文提到的,该方法相对于普通信号量来说要稍慢一些,因为占有互斥型信号量的任务的优先级可能需要改变,这要占用CPU的时间。

1、临界区

(1)涉及到的内核控制函数

①函数taskENTER_CRITICAL:进入临界区,用于任务函数中,此函数本质上是一个宏。

②函数taskEXIT_CRITICAL:退出临界区,用于任务函数中,此函数本质上是一个宏。

③函数taskENTER_CRITICAL_FROM_ISR:进入临界区,用于中断服务函数中,此函数本质上是一个宏。

④函数taskEXIT_CRITICAL_FROM_ISR:退出临界区,用于中断服务函数中,此函数本质上是一个宏。

2、调度器

①函数vTaskSuspendAll:挂起任务调度器,调用此函数不需要关闭可屏蔽中断即可挂起任务调度器。

②函数xTaskResumeAll:此函数用于将任务调度器从挂起状态恢复。

3、信号量

①创建一个计数信号量

#include "semphr.h"
SemaphoreHandle_t xSemaphoreCreateCounting( UBaseType_t uxMaxCount, UBaseType_t uxInitialCount);

参数说明:

  • uxMaxCount:计数信号量的最大值,当达到这个值的时候,信号量不能再被释放。

  • uxInitialCount:创建计数信号量的初始值。

返回值:

  • 成功:返回一个计数信号量句柄,用于访问创建的计数信号量。

  • 失败:返回 NULL。

②删除一个信号量

vSemaphoreDelete()用于删除一个信号量,包括二值信号量、计数信号量、互斥量和递归互斥量。如果有任务阻塞在该信号量上,那么不要删除该信号量。

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore );

参数说明:xSemaphore信号量句柄。
返回值:无。

③信号量释放(V操作)

xSemaphoreGive() 是释放信号量的宏(实际调用消息队列发送函数),适用于二值信号量、计数信号量和互斥量,但不能用于递归互斥量,且禁止在中断中使用。

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore );

参数说明:xSemaphore信号量句柄。

返回值:

  • 成功:pdTRUE信号量被释放。

  • 失败:使用pdFALSE。 

④在中断释放信号量(V操作)

用于释放一个信号量,带中断保护。被释放的信号量可以是二进制信号量和计数信号量。

BaseType_t xSemaphoreGiveFromISR(SemaphoreHandle_t xSemaphore,signed BaseType_t *pxHigherPriorityTaskWoken)

参数说明:

  • xSemaphore:信号量句柄。

  • pxHigherPriorityTaskWoken:在中断中使用 xSemaphoreGiveFromISR() 释放信号量时,若唤醒的任务优先级 ≥ 当前被中断的任务,则参数 pxHigherPriorityTaskWoken 会被设为 pdTRUE,提示需在中断退出前执行上下文切换。从 FreeRTOS V7.3.0 起,该参数可为 NULL(表示不需要此提示)。

返回值:

  • 成功:pdTRUE信号量被释放。

  • 失败:使用pdFALSE。 

⑤获取一个信号量,可以是二值信号量、计数信号量、互斥量。(P操作)

xSemaphoreTake() 用于获取二值信号量、计数信号量或互斥量(递归互斥量除外),本质是调用 xQueueGenericReceive() 的宏,且不能用于中断,中断场景必须使用其保护版本 xQueueReceiveFromISR()。

BaseType_t xSemaphoreTake( SemaphoreHandle_t xSemaphore, TickType_t xTicksToWait );

参数说明:

  • xSemaphore:信号量句柄。

  • xBlockTime :等待信号量的超时时间 xTicksToWait 以 tick 为单位;若 INCLUDE_vTaskSuspend 为 1 且该参数设为 portMAX_DELAY,则任务将无限期阻塞(无超时)。

返回值:

  • 成功:返回pdTRUE。

  • 失败:返回errQUEUE_EMPTY(指定的超时时间内获取失败)。

从该宏定义可以看出释放信号量本质是一次消息出队操作。当任务试图获取信号量时,若信号量无效,任务将在用户指定的阻塞时间(xBlockTime)内等待;若在此期间信号量变为有效,或等待超时,任务都会自动从阻塞态转移为就绪态。

⑥在中断获取一个信号量,可以是二值信号量、计数信号量(P操作)

xSemaphoreTakeFromISR() 是 xSemaphoreTake() 的中断安全版本,无阻塞机制,只能用于二值信号量和计数信号量,不能用于互斥量。原因是互斥量的优先级继承机制在中断中毫无意义,且互斥量本身不允许在中断中使用。

BaseType_t xSemaphoreTakeFromISR(SemaphoreHandle_t xSemaphore, signed BaseType_t *pxHigherPriorityTaskWoken)

功能描述:在中断中获一个信号量(其实很少在中断中获取信号量)。可以是二值信号量、计数信号量。

参数说明:

  • xSemaphore:信号量句柄。

  • pxHigherPriorityTaskWoken:在中断中使用 xSemaphoreTakeFromISR() 时,若唤醒了更高优先级的任务,参数 pxHigherPriorityTaskWoken 会置为 pdTRUE,提示需要在中断退出前进行上下文切换。从 FreeRTOS V7.3.0 起,该参数可为 NULL(表示不需要此提示)。

返回值:

  • 成功:返回 pdTRUE。

  • 失败:返回 errQUEUE_EMPTY(失败因为信号量不可用)。

⑦创建一个二值信号量

二值信号量适合用于同步(因无优先级继承,简单高效),互斥信号量适合用于互斥访问(因有优先级继承,可避免优先级翻转)。二者虽类似,但优先级继承机制是关键区分点。

SemaphoreHandle_t vSemaphoreCreateBinary( void );

参数说明:无

返回值:

  • 成功:返回一个二值信号量句柄,用于访问创建的二值信号量。

  • 失败:返回 NULL。

⑧优先级翻转

优先级翻转(Priority Inversion)是一个实时操作系统中典型的多任务调度问题。

  • 现象:当高优先级任务因等待被低优先级任务占用的共享资源(如互斥量)而被阻塞时,中优先级任务抢占CPU执行,导致高优先级任务迟迟无法运行,其优先级形同虚设。

  • 后果:系统调度行为反常,仿佛高、低优先级发生了“翻转”,严重时会破坏任务的实时性。

  • 解决方案:使用支持优先级继承机制(如FreeRTOS的互斥信号量)的锁,可有效防止此问题。

4、互斥信号量

①互斥量创建

SemaphoreHandle_t xSemaphoreCreateMutex( void )

参数说明:无

返回值:

  • 成功:信号量句柄。

  • 失败:NULL。

②互斥量删除

void vSemaphoreDelete( SemaphoreHandle_t xSemaphore )

参数说明:信号量句柄

返回值:无

③互斥量获取

xSemaphoreTake(SemaphoreHandle_t xSemaphore,TickType_t xBlockTime)

参数说明:

  • xSemaphore:信号量句柄。

  • xBlockTime:等待信号量的超时时间 xTicksToWait 以 tick 为单位;若 INCLUDE_vTaskSuspend 为 1 且该参数设为 portMAX_DELAY,则任务将无限期阻塞(无超时)。

返回值:

  • 成功:返回pdTRUE。

  • 失败:返回errQUEUE_EMPTY(指定的超时时间内获取失败)。

④互斥量释放

BaseType_t xSemaphoreGive( SemaphoreHandle_t xSemaphore )

参数说明:xSemaphore信号量句柄。

返回值:

  • 成功:pdTRUE。

  • 失败:pdFALSE。

⑤死锁

死锁是指两个或多个任务互相等待对方释放自己所需的资源,导致所有相关任务都无法继续执行的一种僵局。

发生死锁的四个必要情况(缺一不可):

  • 互斥:资源不能被共享,一次只能一个任务占用。

  • 持有并等待:任务已持有至少一个资源,又在等待其他任务持有的资源。

  • 不可剥夺:资源只能由持有者主动释放,其他任务无法强行夺取。

  • 循环等待:形成“任务A等B、B等C、C等A”的环形依赖链。

典型场景:任务A锁住资源1,等待资源2;任务B锁住资源2,等待资源1,双方永远等待。

5、事件标志组

①事件标志组创建

#include "event_groups.h"
EventGroupHandle_t xEventGroupCreate(void)

参数说明:无

返回值:

  • 成功:返回事件标志组的句柄。

  • 失败:返回NULL。

②事件标志组删除

void vEventGroupDelete( EventGroupHandle_t xEventGroup )

参数说明:xEventGroup事件标志组的句柄

返回值:无

③事件标志组指定的位的置位

EventBits_t xEventGroupSetBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet )

参数说明:

  • xEventGroup:事件标志组的句柄

  • uxBitsToSet:参数用于指定事件标志组中需要被置位(设为1)的事件位。其值是一个位掩码,每一位对应一个标志位,通过按位或组合可实现多个位同时置位(如 0x09 表示同时置位 bit3 和 bit0)。

返回值:返回调用xEventGroupSetBits()时事件组中的值。

④在中断中执行事件标志组置位

BaseType_t xEventGroupSetBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToSet, BaseType_t *pxHigherPriorityTaskWoken )

参数说明:

  • xEventGroup:事件标志组的句柄

  • uxBitsToSet:参数用于指定事件标志组中需要被置位(设为1)的事件位。其值是一个位掩码,每一位对应一个标志位,通过按位或组合可实现多个位同时置位(如 0x09 表示同时置位 bit3 和 bit0)。

  • pxHigherPriorityTaskWoken:在使用之前必须初始化成 pdFALSE。

在中断中使用 xEventGroupSetBitsFromISR() 设置事件位时,若因此唤醒了更高优先级的任务,则参数 pxHigherPriorityTaskWoken 会被设为 pdTRUE,提示需要在中断退出前进行任务切换。从 FreeRTOS V7.3.0 起,该参数可为 NULL(表示不需要此提示)。

返回值:

  • 成功:返回pdPASS。

  • 失败:返回pdFAIL。

⑤等待事件标志组

EventBits_t xEventGroupWaitBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait )

参数说明:

  • xEventGroup:事件标志组的句柄

  • uxBitsToWaitFor:指定要等待的一个或多个事件位(按位或组合)。

  • xClearOnExit:等待成功后,是否自动清除 uxBitsToWaitFor 指定的位。

  • xWaitForAllBits:

    • pdTRUE:等待所有指定位都置位(逻辑与)。

    • pdFALSE:等待任意一个指定位置位(逻辑或)。

  • xTicksToWait:最大等待超时时间(tick),可用 portTICK_PERIOD_MS 换算为毫秒。

返回值:返回事件组的实际状态(所有被置位的位),可能包含额外位或仅部分指定位,需要用户自行按位和 uxBitsToWaitFor 来判断等待条件是否真正满足。

⑥事件标志组指定的位清零

EventBits_t xEventGroupClearBits( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )

参数说明:

  • xEventGroup:事件标志组的句柄

  • uxBitsToClear:指定事件标志组中需要被清除(设为0)的事件位。其值是一个位掩码,每一位对应一个标志位,通过按位或组合可实现多个位同时清除(如 0x09 表示同时清除 bit3 和 bit0)。

返回值:事件标志组还没有清除指定位之前的值。

⑦在中断中执行事件标志组指定的位清零

BaseType_t xEventGroupClearBitsFromISR( EventGroupHandle_t xEventGroup, const EventBits_t uxBitsToClear )

参数说明:

  • xEventGroup:事件标志组的句柄

  • uxBitsToClear:指定事件标志组中需要被清除(设为0)的事件位。其值是一个位掩码,每一位对应一个标志位,通过按位或组合可实现多个位同时清除(如 0x09 表示同时清除 bit3 和 bit0)。

返回值:

  • 成功:返回pdPASS。

  • 失败:返回pdFAIL。

⚠️注意事项:若使用xEventGroupSetBitsFromISR、xEventGroupClearBitsFromISR等函数,官方要求3个宏定义生效。

FreeRTOSConfig.h文件

#define configUSE_TRACE_FACILITY 1
#define configUSE_TIMERS 1

FreeRTOS.h文件

#ifndef INCLUDE_xTimerPendFunctionCall
  #define INCLUDE_xTimerPendFunctionCall 1
#endif

6、消息队列

①消息队列创建函数

QueueHandle_t xQueueCreate(UBaseType_t uxQueueLength, UBaseType_t uxItemSize);

功能描述:用于创建一个新的队列。

参数:

  • uxQueueLength:队列能够存储的最大消息单元数目,即队列长度。

  • uxItemSize:队列中消息单元的大小,以字节为单位,该大小设置非常重要,否则得到的数据不完整。

返回值:

  • 成功:返回一个队列句柄,用于访问创建的队列;

  • 失败:返回NULL,可能原因是创建队列需要的 RAM 无法分配成功。

②消息队列静态创建函数

QueueHandle_t xQueueCreateStatic(UBaseType_t uxQueueLength, UBaseType_t uxItemSize, uint8_t *pucQueueStorageBuffer, StaticQueue_t *pxQueueBuffer );

功能描述:用于创建一个新的队列。

参数:

  • uxQueueLength:队列能够存储的最大消息单元数目,即队列长度。

  • uxItemSize:队列中消息单元的大小,以字节为单位。

  • pucQueueStorageBuffer:指针,指向一个 uint8_t 类型的数组,数组的大小至少有uxQueueLength* uxItemSize 个字节。当 uxItemSize 为 0 时,pucQueueStorageBuffer 可以为 NULL。

  • pxQueueBuffer:指针,指向 StaticQueue_t 类型的变量,该变量用于存储队列的数据结构。

返回值:

  • 成功:返回一个队列句柄,用于访问创建的队列;

  • 失败:返回NULL,可能原因是创建队列需要的 RAM 无法分配成功。

③动态和静态创建函数

什么是动态创建函数和静态创建函数呢?这里我们额外了解一下它们的区别。

1. 动态创建(xQueueCreate())

  • 内存来源:队列的控制块与存储区(消息缓冲区)均自动从堆(pvPortMalloc)中动态分配。

  • 优点:使用简单,无需关心内存布局。

  • 缺点:可能导致堆碎片,且无法在编译期确定内存占用。

2. 静态创建(xQueueCreateStatic())

  • 内存来源:队列的控制块与存储区需由用户预先提供(通常为全局数组或静态变量)。

  • 优点:确定性高,无动态内存分配失败风险,适合对可靠性要求严格的系统(如汽车、医疗)。

  • 缺点:需手动计算并分配内存,使用稍繁琐。

总结:

  • 动态创建:内核自动用 pvPortMalloc() 帮你申请内存。

  • 静态创建:你必须自己提前准备好内存,然后传给内核。

一句话记住:动态 = 内核替你 malloc;静态 = 你自己给内存。

④用于向队列尾部发送一个队列消息

BaseType_t xQueueSend(QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait);

参数说明:

  • xQueue:队列句柄。

  • pvItemToQueue:指针,指向要发送到队列尾部的队列消息。

  • xTicksToWait:指定当队列满时,任务等待队列出现空闲的最大超时时间。若设为 0 则立即返回;单位为 tick,可用 portTICK_PERIOD_MS 换算为毫秒;当 INCLUDE_vTaskSuspend 为 1 且设为 portMAX_DELAY 时,任务将无限期挂起等待(无超时)。

返回值:

  • 成功:返回 pdTRUE。

  • 失败:返回 errQUEUE_FULL。

⑤在中断服务程序中用于向队列尾部发送一个消息

BaseType_t xQueueSendFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);

参数说明:

  • xQueue:队列句柄。

  • pvItemToQueue:指针,指向要发送到队列尾部的消息。

  • pxHigherPriorityTaskWoken:在中断中使用队列发送函数时,若入队操作唤醒了更高优先级的任务,则参数 pxHigherPriorityTaskWoken 会被设为 pdTRUE,提示需要在中断退出前执行上下文切换。从 FreeRTOS V7.3.0 起,该参数可为 NULL(表示不需要此提示)。

返回值:

  • 成功:返回 pdTRUE。

  • 失败:返回 errQUEUE_FULL。

⑥向队列队首发送一个消息

BaseType_t xQueueSendToFront( QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait );

参数说明:

  • xQueue:队列句柄。

  • pvItemToQueue:指针,指向要发送到队列尾部的消息。

  • xTicksToWait:指定当队列满时,任务等待队列出现空闲的最大超时时间。若设为 0 则立即返回;单位为 tick,可用 portTICK_PERIOD_MS 换算为毫秒;当 INCLUDE_vTaskSuspend 为 1 且设为 portMAX_DELAY 时,任务将无限期挂起等待(无超时)。

返回值:

  • 成功:返回 pdTRUE。

  • 失败:返回 errQUEUE_FULL。

⑦在中断服务程序中向消息队列队首发送一个消息

BaseType_t xQueueSendToFrontFromISR(QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t *pxHigherPriorityTaskWoken);

参数说明:

  • xQueue:队列句柄。

  • pvItemToQueue:指针,指向要发送到队首的消息。

  • pxHigherPriorityTaskWoken:在中断中使用队列发送函数时,若入队操作唤醒了更高优先级的任务,则参数 pxHigherPriorityTaskWoken 会被设为 pdTRUE,提示需要在中断退出前执行上下文切换。从 FreeRTOS V7.3.0 起,该参数可为 NULL(表示不需要此提示)。

返回值:

  • 成功:返回 pdTRUE。

  • 失败:返回 errQUEUE_FULL。

⑧从一个队列中接收消息,并把接收的消息从队列中删除

BaseType_t xQueueReceive(QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait);

参数说明:

  • xQueue:队列句柄。

  • pvBuffer:指针,指向接收到要保存的数据。

  • xTicksToWait:指定当队列满时,任务等待队列出现空闲的最大超时时间。若设为 0 则立即返回;单位为 tick,可用 portTICK_PERIOD_MS 换算为毫秒;当 INCLUDE_vTaskSuspend 为 1 且设为 portMAX_DELAY 时,任务将无限期挂起等待(无超时)。

返回值:

  • 成功:返回 pdTRUE。

  • 失败:返回 pdFALSE。

若接收完消息,不想删除,可以使用xQueuePeek函数。

⑨在中断中从一个队列中接收消息,并从队列中删除该消息

BaseType_t xQueueReceiveFromISR(QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxHigherPriorityTaskWoken);

参数说明:

  • xQueue:队列句柄。

  • pvBuffer:pxHigherPriorityTaskWoken

  • pxHigherPriorityTaskWoken:在中断中使用队列发送函数时,若入队操作唤醒了更高优先级的任务,则参数 pxHigherPriorityTaskWoken 会被设为 pdTRUE,提示需要在中断退出前执行上下文切换。从 FreeRTOS V7.3.0 起,该参数可为 NULL(表示不需要此提示)。

返回值:

  • 成功:返回 pdTRUE。

  • 失败:返回 pdFALSE。

若接收完消息,不想删除,可以使用xQueuePeekFromISR函数。

⑩pdMS_TO_TICKS函数使用

宏pdMS_TO_TICKS用于将毫秒转成节拍数,FreeRTOS V8.1.0及以上版本才有这个宏。

const TickType_t xTicksToWait = pdMS_TO_TICKS( 100 );

八、时钟树

STM32F4系列时钟树

裸机编程(Bare-Metal)和实时操作系统(RTOS)对比 在单片机开发中,裸机编程(Bare-Metal)和实时操作系统(RTOS)是两种常见的设计方法,它们在架构、资源管理、开发复杂度等方面存在显著差异。裸机编程是单片机开发的“基本功”,适合资源敏感的小型项目;RTOS则是应对复杂系统的“利器”,通过牺牲部分资源换取开发效率和可靠性。开发者需根据项目需求、硬件资源和团队经验权衡选择。 阅读详情

相关推荐

Microchip 系列:SAM L 系列 (基于 ARM Cortex-M0+)_(18).嵌入式实时操作系统(RTOS)集成

嵌入式实时操作系统(RTOS)是一种专门设计用于管理嵌入式系统资源的软件。它能够提供多任务调度、任务间通信、资源管理等功能,使得开发者可以更高效地管理复杂的应用程序。在 SAM L 系列单片机上集成 RTOS,可以显著提高系统的实时响应能力和稳定性。在 SAM L 系列单片机上集成 FreeRTOS 可以显著提高系统的实时响应能力和稳定性。通过合理配置 FreeRTOS,管理任务、同步机制和中断处理,开发者可以更高效地开发复杂的嵌入式应用程序。

2401_87715305的博客 695

单片机和RTOS

一段代码+运行位置+运行环境在计算机科学中,任务(Task)指的是一个需要计算机系统执行的工作单元或操作。任务可以是一个指令序列、函数、进程、线程或任何需要计算机执行的可定义的工作。任务是计算机系统中的基本单位,通过将大型任务分解为更小的子任务,可以简化系统的设计和管理。每个任务通常有特定的输入、处理逻辑和输出。任务可以并行执行(在多个处理器或核心上同时执行)或顺序执行(按照特定的顺序依次执行)。在操作系统中,任务通常是指进程或线程,它们是并发执行的实体。每个任务都有自己的代码、数据和运行环境。

m0_73731708的博客 4767

STMicroelectronics 系列:STM32F3 系列_(8).STM32F3系列实时操作系统的支持

通过上述示例,我们可以看到STM32F3系列单片机在使用FreeRTOS时的多种配置和使用方法。FreeRTOS不仅提供了丰富的任务管理功能,还支持多种同步和通信机制,使得开发复杂嵌入式应用变得更加容易。合理配置和使用FreeRTOS,可以有效提高系统的响应速度和可靠性。希望这些示例能够帮助你在STM32F3系列单片机上更好地使用FreeRTOS。

2401_87715305的博客 1076

一、单片机_RTOS_架构

RTOS的概念,单片机架构,两者之间的关系

西西弗的博客 2332

STMicroelectronics 系列:STM32L0 系列_(13).STM32L0的实时操作系统支持

FreeRTOS是一个免费的、开源的实时操作系统,适用于小型嵌入式系统。轻量级:占用资源少,适合资源受限的嵌入式系统。可移植性:支持多种硬件平台,包括ARM Cortex-M系列。可配置性:用户可以根据需求配置不同的功能模块。任务调度:支持多种任务调度算法,确保任务按优先级执行。中断管理:有效管理中断,确保中断处理的及时性和可靠性。FreeRTOS提供了一系列API,用于任务创建、任务调度、信号量、互斥量、队列等操作,使得开发者可以方便地编写多任务应用程序。

2401_87715305的博客 1211

掌握51单片机:Small RTOS操作系统实战指南

在现代嵌入式系统中,51单片机因其简单易用和功能强大而广泛应用于许多领域。为了满足实时性的要求,引入实时操作系统(RTOS)是非常重要的。RTOS的使用能够在提高系统稳定性和可维护性的同时,增强任务调度的灵活性和资源利用的效率。实时操作系统是专门为了处理实时任务而设计的操作系统,它保证了系统能够及时响应外部事件并完成特定操作。RTOS对于那些对时间敏感的应用至关重要,如工业控制、医疗设备和汽车电子等领域,能够确保系统的稳定性和可靠性。

weixin_42601134的博客 1374

单片机开发的两种类型

最近学了一下ROS操作系统的工作原理和使用方法,想到了对于单片机来说的操作系统RTOS开发的一些概念,觉得有必要在细说一下单片机开发的两种方式。

weixin_46813864的博客 1648

89C2051单片机与RTX51 Tiny实时操作系统应用案例

89C2051单片机,作为经典的8051系列微控制器之一,具有紧凑的设计和丰富的功能,广泛应用于嵌入式系统和小型控制系统。它的核心是8位微处理器,带有2KB的ROM用于存储代码,以及128字节的RAM用于数据存储和寄存器。此外,89C2051内置2个定时器/计数器,具备串行通信接口,支持多种外围设备连接,便于实现各种控制功能。RTX51 Tiny 是一个为8051系列单片机设计的抢占式多任务实时操作系统内核。

weixin_31776191的博客 535

一文理清嵌入式、51、32、ARM和RTOS、linux的关系

资源丰富(Flash可达2MB,RAM数百KB)、主频高(MHz至数百MHz)。:应用处理器(MPU),支持Linux/Android(如树莓派、手机SoC)。是顶层概念,包含硬件(51、STM32、ARM)和软件(RTOS、Linux)。:裸机程序、实时操作系统(RTOS)或通用操作系统(如Linux)。资源占用小(几KB内存),适合MCU(如STM32、ESP32)。:资源受限(CPU、内存、功耗)、实时性要求、专用性强。:微控制器(MCU)、微处理器(MPU)、传感器、外设。

AaMJT的博客 1754

51单片机网络开发板实用教程

TCP/IP协议栈是一种用于计算机网络通信的协议族,它包括一系列协议,这些协议共同工作以确保数据从一个网络节点可靠地传输到另一个网络节点。协议栈被划分为四个层次,每一层负责不同的通信任务:应用层:这是最高层次,负责提供特定的应用程序,如HTTP、FTP、SMTP等,它们定义了数据的格式和信息交换的机制。传输层:传输层有两个主要协议,TCP(传输控制协议)和UDP(用户数据报协议)。TCP提供面向连接的、可靠的数据传输服务,而UDP则提供无连接的、不可靠的数据报服务。网络互连层。

weixin_28931507的博客 1406

vsomeip3 双机通信文件配置

验证环境 vsomeip地址Android运行命令,红色的地方需要和配置文件一样Linux运行命令,红色的地方需要和配置文件一样Linux service配置文件 ,红色的地方需要更改Android client配置文件,红色的地方需要更改,蓝色的地方可不要 ...

m0_37132481的博客 5222

单片机 -> RTOS -> Linux

编排|入门小站素材来源 | 掘金参考地址:https://juejin.cn/post/6938385978004340744对于嵌入式而言,大部分人的进阶路线:单片机 -> RTOS -> Linux。下面,针对有单片机、RTOS基础的同学,分享一份入门 Linux 的基础内容。Linux 基础操作系统操作系统Operating System简称OS,是软件的一部分,它是...

张巧龙的博客 1294

【Qt串口调试助手】1.1 - Qt5编写串口调试助手,Qt串口编程

这章来学习串口数据流操作。仿照成品制作一个自己用Qt编写的串口助手。 1. 布局UI界面 创建QWidget工程,布局UI界面:拖入2个 Plain Text Edit,作为串口数据的接收显示和发送框。 拖入几个 Combo Box 、Label 和 Push Button,使用两个Widget进行布局。如下所示。 将接收区的 Plain Text Edit,属性勾选上 readOnly,使其运行时不会被人为的输入操作影响。 加入一些弹簧,使布局美观。(有必要可以加设置不能调整..

Mark_md的博客 9273

51c嵌入式~RTOS~合集1

操作系统的定义:一个计算机系统可以大致分为四个部分:硬件(Hardware),操作系统(operating system),应用程序(application programs),使用者(user)。硬件包含了CPU,存储空间,输入输出设备等为整个系统提供了基础的计算资源。操作系统是一个控制程序,作为硬件和应用程序之间的桥梁,主要是和硬件打交道,负责协调分配计算资源和内存资源给不同的应用程序使用,并防止系统出现故障。

whaosoft~aiotの开发板商城 1249

解决Linux下Questasim中代码字体非常小

Questasim的source窗口里代码字体非常小 你是不是也遇到这样的问题,在使用Vmware时,Questasim的代码字体非常小,小到完全看不见,连断点都没办法标记。 下面我给出我摸索好久的解决办法,分享给大家。 1.首先,版本是Questasim 10.7c,在Vmware16中打开,情况如下: 不多逼逼,直接上图开搞: 给textFontV2设置值:monospace 12 normal roman 这里一定要注意啦,点完apply之后,不要点ok,直接关闭窗口。 ...

SummerXRT的博客 7814

《QGIS空间数据处理与高级制图》【目录】

《QGIS空间数据处理与高级制图》是一本面向GIS技术人员和学生的进阶手册,重点提升数据处理效率和专业制图标准化水平。全书涵盖空间数据预处理、矢量编辑、栅格处理、坐标系统应用和符号化设计等核心内容。书中详细介绍了批量数据转换、拓扑检查、遥感影像处理、自定义坐标系创建等实用技能,并提供了模型构建器和PyQGIS脚本实现自动化操作。通过大量实战案例和问题排查指南,帮助读者从基础使用进阶到专业应用,适合测绘、规划、环保等行业的GIS从业者参考使用。

LuckyHanMo的博客 372

为什么 51 单片机仍然需要一套完整的实时操作系统?

8051 单片机是否需要实时操作系统?本文从裸机开发的优势与局限出发,分析任务调度、时间管理、中断协同、任务间通信及资源管理的实际价值,并讨论传统 8051 平台对轻量化设计和实时性验证的特殊要求。结合 HRTOS 4.0 的内核功能、开发组件、硬件测试与文档体系,探讨 8051 实时操作系统的工程意义与适用场景。

qq_36826123的博客 386

【OpenClaw】Edict 三省六部制部署与启动

文章摘要 本文聚焦Edict系统的本地部署与启动,提供三种安装方式:Docker快速体验、macOS/Linux原生安装和Windows生产仿真。核心目标是让用户能够独立完成系统启动和基础问题排查,确保看板、后端服务、同步脚本和网关正常运行。文章强调环境检查的重要性,并给出具体验收标准:浏览器能访问看板页面(http://127.0.0.1:7891)和接口返回JSON数据。同时提供一键启动/停止脚本和常见问题处理建议,帮助用户快速验证部署结果。配套系列教程涵盖部署启动、使用实战、二次开发和封装版本使用等内

Mr数据杨 2901

stm32单片机学习路线

1.了解ARM体系结构和Cortex-M核:STM32是基于ARM Cortex-M核的单片机,了解ARM体系结构和Cortex-M核的特性和架构可以帮助你更好地理解STM32的工作原理和特点。2.学习配置和使用STM32的外设:掌握如何配置和使用STM32的外设,包括定时器、串口、I2C、SPI等,以及各种通信协议的实现,如UART通信、I2C通信等。对于单片机开发来说,了解电子电路的基础知识是非常重要的,包括基本的电路原理、电阻、电容、电感等元件的作用和特性。

teach2004的博客 852

agent实战专栏|日程管理Agent:智能时间管家

在快节奏的现代生活中,时间管理已成为每个人面临的核心挑战。根据全球知名调研机构 Gallup 的统计数据,现代职场人平均每天仅能有效专注工作 2 小时 23 分钟,剩余时间被会议、邮件、碎片化事务大量占据。微软的另一项研究显示,知识工作者平均每天需要处理 200 多条日程相关的信息,包括会议邀请、日历变更、提醒通知等。传统的日历工具(如 Google Calendar、Outlook Calendar、Apple Calendar)虽然在可视化展示和基础提醒方面表现出色,但在智能化方面仍存在明显短板:日程管

2201_76125393的博客 52

【AI智能体】Claude Code 高级编程技巧实战项目详解

Claude Code 高级编程技巧实战项目详解

congge 1万+

如何提升嵌入式开发技术

嵌入式开发技术是一种专门的技能,需要经验和特定的知识。以下是一些提升嵌入式开发技术的建议: 学习编程语言:掌握嵌入式开发所需的编程语言是至关重要的。 C 和 C++ 是嵌入式开发中最常用的编程语言,因此应该重点学习这些语言。 学习微控制器和单片机:微控制器和单片机是嵌入式系统的核心组件。学习如何编程、配置和调试这些设备是提升嵌入式开发技能的关键。 深入了解操作系统:嵌入式系统通常需要运行实时...

weixin_42602726的博客 440

《PCI Express体系结构导读1》-- PCI简介

请先阅读完一遍此书再来看文章哦,这样效率更高~

HoneyIC的博客 151

将uc/OS系统移植到STM32f103c8t6(基于HAL库)

参考文献 关于重定向printf出错 Error[Pe020]: identifier “FILE” is undefined 解决方案

yeye3333的博客 712

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

【职场进阶路】从单片机到Linux:一位工程师的嵌入式技术栈构建心法

本文分享了一位工程师从51单片机到Linux系统的嵌入式技术栈构建历程。通过51单片机的基础训练、STM32的进阶开发、RTOS的任务调度实践,最终掌握Linux嵌入式系统的核心技能。文章详细解析了每个阶段的关键技术拐点和学习心法,为开发者提供了一条清晰的嵌入式学习路线图。

weixin_30443075的博客 353

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

嵌入式领域划分及对应的网络通信方法介绍

本文介绍了嵌入式系统的三大细分领域及其网络通信实现方法:1)单片机裸机开发使用M系列MCU,通过外接WIFI/4G模块实现联网;2)实时操作系统开发采用M/R系列MCU或ESP32,基于FreeRTOS和lwIP协议栈;3)系统级开发使用A系列MPU,在Linux系统上通过Socket接口实现网络通信。文章详细阐述了不同领域的硬件特点、操作系统选择及网络协议实现方式,特别说明了lwIP协议栈在嵌入式网络中的关键作用。

人生的意义在于新的经历 591

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

上一篇: 人脸识别的环境配置
下一篇: C语言指针、运算符与类型转换
橙橙笔记
博客等级 码龄4年 13粉丝 42原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值