信号量-二值信号量
信号与量(二值信号量)
- 信号与量
1. 信号用于 通知
2. 量 用于 表示资源的个数
根据"量"的不同,可分为二状态信号量 和 计数型信号量 - 共享资源(全局变量)同一时间不被并发调用
二值信号量(拿卡-还卡场景)
释放Give与取走Take信号量(主语是我程序)
- Create:系统创建一个二值信号量,即创建’一张卡’
- Take:当前程序取走这一个资源
if (xSemaphoreTake(myBinarySem01Handle, portMAX_DELAY) == pdTRUE) {
// 信号量获取成功,打印 hello
printf("task01\r\n");
}
- Give:当前程序释放这一个资源
if (xSemaphoreGive(myBinaryHandle) == pdTRUE)
printf("二值信号量放入成功\r\n");
else
printf("二值信号量放入失败\r\n");
}
场景一任务同步:数据采集 优先于 数据上传
- 创建顺序是01 02 03 04
- 两个任务:任务二先于任务一执行
- 即任务二是采集数据,而任务一是上传数据,保证上传的数据都是最新的
采集上传OneNET平台:1:温度 + 2:浓度–>> 3:上传数据
void StartTask01_ONENET(void const * argument)
{
/* USER CODE BEGIN StartTask01_ONENET */
/* Infinite loop */
for(;;)
{
if (xSemaphoreTake(myBinarySem01Handle, portMAX_DELAY) == pdTRUE &&
xSemaphoreTake(myBinarySem02Handle, portMAX_DELAY) == pdTRUE) {
// 两个信号量都获取成功,开始上传数据
printf(" Tem = %.2f°C, CO2= %.2f ppm\r\n", temperature, co2Concentration);
printf("\r\n");
}
osDelay(1);
}
场景二资源互斥:同一时间下,全局变量只能被同一人拿到
OnePrinter打印机设备gloabal_value或者
- 只有一台打印机设备
- 任务一:打印数学资料 使用打印机
- 任务二:打印语文资料 使用打印机
(类似于两个人同时拉肚子,抢厕所)
使用打印机,打印数学试卷
使用打印机,打印英语试卷
场景三事件通知:外部中断触发,相当于一个标志位
外部中断通过flag或者信号量通知另一个任务
- 二值信号量用于 :中断事件发送时,唤醒任务
1. take:需要一个二值信号量.相当于减1
2. give:生成一个二值信号量,相当于加1
串口中断业务逻辑代码:唤醒任务一
任务一
串口相关
串口重定向
#include "stdio.h"
//重定向printf
int fputc(int ch,FILE *f)
{
uint8_t temp[1]={ch};
HAL_UART_Transmit(&huart1,temp,1,2);
return ch;
}
串口中断Rx与Tx中断处理函数
- 业务代码 和 开启串口中断代码在uart.c里全部实现
uart.c里:开启uart_X中断
// 定义接收缓冲区和变量
#define BUFFER_SIZE 4 // 统一接收缓冲区大小
uint8_t rx_buffer[BUFFER_SIZE]; // 接收缓冲区
uint8_t KEY1_Value = 0; // KEY1状态值
uint8_t KEY2_Value = 0; // KEY2状态值
//开uart1中断
if (HAL_UART_Receive_IT(&huart1, rx_buffer, BUFFER_SIZE) != HAL_OK) {
Error_Handler();
}
找到所需中断回调函数
uart.c里:编写对应业务流程
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
// 判断接收到的数据
if(rx_buffer[0] == 'K' && rx_buffer[1] == 'E' && rx_buffer[2] == 'Y')
{
if(rx_buffer[3] == '1')
{
KEY1_Value = 1;
printf("KEY1 Pressed\r\n"); // 可选的调试输出
}
else if(rx_buffer[3] == '2')
{
KEY2_Value = 1;
printf("KEY2 Pressed\r\n"); // 可选的调试输出
}
}
// 重新启动接收
HAL_UART_Receive_IT(&huart1, rx_buffer, 4);
}
}
其他问题
为什么信号量是是队列的一种?
freertos的main.c里while循环业务失效?
- 在FreeRTOS中,==主函数(main)在启动调度器后就不会再执行while循环了。==如果你想实现一个业务流程,应该创建一个新的任务。
在哪里创建二值信号量?
- 二值信量是在CubeMX里卡片自动生成的,一般位于MX_FREERTOS_Init(void) 代码里
freertos二值信号量初始默认是什么状态?
- 默认情况下,当创建成功二值信号量时,第一次使用默认是不可用状态,并且必须通过xSemaphoreGive(xBinarySemaphore)使信号量初始为可用状态
为什么串口中断里释放二值信号量导致系统卡死?
/* USER CODE BEGIN 1 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
// 判断接收到的数据
if(rx_buffer[0] == 'K' && rx_buffer[1] == 'E' && rx_buffer[2] == 'Y')
{
if(rx_buffer[3] == '1')
{
KEY1_Value = 1;
// printf("KEY1 Pressed\r\n"); // 可选的调试输出
//xSemaphoreGive(myBinarySem01Handle);
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(myBinarySem01Handle, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken == pdTRUE) {
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
// 重新启动接收
HAL_UART_Receive_IT(&huart1, rx_buffer, 4);
}
}