FreeRTOS中断中释放信号量

news2024/11/17 5:26:29

串口接收:中断程序中逆序打印字符串

 串口接收:逆序回环实验思路

注:任务优先级较高会自动的切换上下文进行运行

FreeRTOS中的顶半操作和底半操作

顶半操作和底半操作“这种叫法源自与Linux”在嵌入式开发中,为了和Linux操作系统做一个区分,可以称之为“中断延迟处理”,以下是中断中释放信号量的示意理解图,“顶半操作”实际上就是在中断函数中进行的操作,“中断”-----> 并不属于RTOS系统中自带的,但是中断该产生还是会产生,在实际的开发中我们要尽量的减少频繁的中断,对操作系统的影响,保证器实时性,“底半操作”

实际上就是回到OS中的操作,从顶半操作回到底半操作的需要跨越的屏障可以称之为“裸多屏障”,裸机和多任务之间的屏障,一般在没有主动上下文切换的情况先,任务的切换通常会有一定时间的延时,通常这种情况下我们需要调用主动上下文切换的API“portYIELD()”对上下文进行及时快速的切换以保证系统的实时性(“注:裸多屏障的切换要快”)。

【顶半操作(“中断程序部分”)的设计原则】

注:

1: 中断处理程序ISR,不能介入RTOS的业务

2:ISK运行完之前不可以交出CPU资源

3:尽可能短,越长实时响应性能越差

4:中断程序不参与调度

中断ISR ----------------顶半操作/底半操作--------------【立即处理/迅速完成】 需要大量的时间

在硬件中断中尽快的获取需要的数据给到RTOS进行处理

【串口花样回环实验】

硬件中断的基本原理 :硬件中断可以是由外部的设备进行触发,以下是STM32中提供的中断处理函数,采用弱定义的方式,一般的中断处理函数采用的是递归调用的方式进行处理,使用指针此处使用弱定义的方式可以方便程序开发任务的理解

函数的弱定义_weak

回调函数-----------------> 使用函数指针的方式实现函数的回调

中断中释放信号量失败??什么问题导致,如何在中断程序中释放信号量

在FreeRTOS中如何编写中断程序,中断程序编写原则

1:ISR程序应该尽量简短,迅速返回,不许等待

2:ISR中严禁使用具有阻塞性质的API

“信号量释放API vTaskSemaphoreGive”在FreeRTOS中具有阻塞性质

在FreeRTOS中使用中断专用的API,带FromISR的API

在FreeRTOS中API分为两种,一种是 普通的API一种是中断专用的API“中断专用的API专门用于解决中断问题” 。

关于FromISR API的形参:pxHigherPriorityTaskWoken

中断的API迅速从底半任务,转换为顶半任务,才可以快速的接收(中断程序释放信号量之后,底半任务迅速的运行起来,在这个时间内实现连续的中断串口的接收)

在pxHigherPriorityTaskWoken在调用之后有一个任务立即被运行起来,这个时候有一个中断专用的API会将这个值从0设置为1,如果没有任何任务因为中断中调用这个API,而导致状态发生变化那么它就会保持0这个值而不会设置为1,因此在定义和使用这个变量的时候需要将这个变量的初始化值设置为0,如果中断服务程序退出后,底半任务要立即运行起来就需要执行portYIELD_FROM_ISR 这个中断任务切换的上下文,“过程大概四60ms”

如果程序在中断之中没有主动调用portYIELD_FROM_ISR这个API,任务确实会从阻塞状态变为就绪状态,但是上下文的切换操作会在最近的一次TICK中断中完成,这个时间会有一个延迟,这个长度大概四在200-800ms左右,“根本的问题在于我们在进入中断之后是否需要及时的切换上下文”要看实际中处理的事情是否紧急。

尽量避免频繁的任务切换,“避免频繁的任务切换”,“避免频繁的任务切换”

串口回传数据代码第一部分

#include "main.h"
#include "cmsis_os.h"

#include "stdio.h"
#include "math.h"
#include "shell.h"

UART_HandleTypeDef huart1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);

// 信号量
SemaphoreHandle_t uart_rx_se;

// 串口发送的字节数据
uint8_t uart_rx_byte = 0;

// 缓冲区
uint8_t usart_rx_buf[100] = {0};
// 计数
uint8_t usart_rx_cnt = 0;

int fputc(int ch,FILE *f)
{
 while((USART1->SR & 0X40) == 0);

 USART1->DR = (uint8_t)ch;
	
 return ch;
}



/*
    define : 顶半操作,中断处理函数
    函数的回调,这个时候编译器会放弃原来的弱定义来进行新定义的实现

*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
	  // 这是一个类型的重定义,实际上是一个long类型
	  portBASE_TYPE taskWoken = 0;
      // 判断是否是当前串口处理实例
	  if(huart->Instance == USART1){
		    // 尽快的完成中断的处理回到RTOS中
			// xSemaphoreGive(uart_rx_se);
		    // 第一个参数是
		    xSemaphoreGiveFromISR(uart_rx_se,&taskWoken);
		  
		    HAL_UART_Receive_IT(&huart1,&uart_rx_byte,1);
		  
		    //立即调用这个中断处理函数
		    portYIELD_FROM_ISR(taskWoken);
	  }  

}


void usart_rx_entry(void *p){
			while(1){
				xSemaphoreTake(uart_rx_se,portMAX_DELAY);
				
				// 缓冲区中放入接收到的字节
				usart_rx_buf[usart_rx_cnt] = uart_rx_byte;
				
		//		printf("receive_byte:[%02X]\r\n",usart_rx_buf[usart_rx_cnt]);
				if(usart_rx_buf[usart_rx_cnt] == 0x0D ||usart_rx_buf[usart_rx_cnt] == 0x0A ){
					   usart_rx_buf[usart_rx_cnt] = 0;
					   // 方便下一次的接收设置为0
					   usart_rx_cnt = 0;
					   printf("string echo : %s\r\n",usart_rx_buf);
					   
				}else{
				   usart_rx_cnt ++;
				}

	}
}

// 任务辅助,判断程序是否死掉
void led_toggle_entry(void *p){
    while(1){
	  HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
	  vTaskDelay(1000);
	}
}

void task_create_entry(void *p){
   	TaskHandle_t tmp_handle;
	TaskHandle_t xHandle;
	TaskHandle_t xHandle2;
	
	vTaskDelay(5000);
	
	printf("start to create sem\r\n");
	uart_rx_se = xSemaphoreCreateBinary();
	
	printf("process is start");
	
	
	printf("TASK1 IS CREATE\r\n");
	xTaskCreate(usart_rx_entry,"usart_rx_entry",128,(void *)0,10,&xHandle);
	// 调用hal库中断方式接收串口的函数
	HAL_UART_Receive_IT(&huart1,&uart_rx_byte,1);
	
	printf("TASK2 IS CREATE\r\n");
	xTaskCreate(led_toggle_entry,"led_toggle_entry",128,(void *)0,10,&xHandle2);
    
	tmp_handle = xTaskGetHandle("task_create_task");
	vTaskDelete(tmp_handle);

}


int main(void)
{
  TaskHandle_t Handle;
	
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
	
//	shell_init();
	
   xTaskCreate(task_create_entry,"task_create_entry",128,(void*)0,12,&Handle);	
	

   // 开启任务调度
   vTaskStartScheduler();
	
  while (1)
  {
  }

}

以上接收串口数据的方式其实比较原始,效率不高,任务切花也是有代价的

RTOS中使用DMA接收串口数据前导

中断中接收字节改进后的函数参数

#include "main.h"
#include "cmsis_os.h"

#include "stdio.h"
#include "math.h"
#include "shell.h"

UART_HandleTypeDef huart1;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);

SemaphoreHandle_t uart_rx_sem;

uint8_t uart_rx_byte = 0;

uint8_t uart_rx_buf[100] = {0};
uint8_t uart_rx_cnt = 0;

int fputc(int ch,FILE *f)
{
 while((USART1->SR & 0X40) == 0);

 USART1->DR = (uint8_t)ch;
	
 return ch;
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
 portBASE_TYPE taskWoken = 0;
	
 if(huart->Instance == USART1)
 {
//	xSemaphoreGiveFromISR(uart_rx_sem,&taskWoken);
	 
	HAL_UART_Receive_IT(&huart1,&uart_rx_byte,1);
	
    uart_rx_buf[uart_rx_cnt] = uart_rx_byte;
	 
	//printf("rev_byte:[%02X]\r\n",uart_rx_buf[uart_rx_cnt]);
	 
	if(uart_rx_buf[uart_rx_cnt] == 0X0D || uart_rx_buf[uart_rx_cnt] == 0X0A)
	{
	 uart_rx_buf[uart_rx_cnt] = 0;
	 uart_rx_cnt = 0;
		
//	 printf("string echo : %s\r\n",uart_rx_buf);
	 xSemaphoreGiveFromISR(uart_rx_sem,&taskWoken);	
	}
	else{
	   	 uart_rx_cnt ++;	 
	}
	portYIELD_FROM_ISR(taskWoken);
 }
}

void uart_rx_entry(void *p)
{
 while(1)
 {
  xSemaphoreTake(uart_rx_sem,portMAX_DELAY);
	 
  // 打印缓冲区中的数据
 printf("string echo : %s\r\n",uart_rx_buf);
 }
}

void led_toggle_entry(void *p)
{
 while(1)
 {
	HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_8);
	 
	vTaskDelay(1000);
 }
}

void task_create_entry(void *p)
{	
 TaskHandle_t tmp_handle;	
 TaskHandle_t xHandle;
 TaskHandle_t xHandle2;
	
 vTaskDelay(5000);
	
 //=====================================
 printf("start to create sems\r\n");
 uart_rx_sem = xSemaphoreCreateBinary();	
	
 printf("start to create tasks\r\n");	
	
 printf("create 1st task\r\n");
	
 xTaskCreate(uart_rx_entry,"uart_rx_task",128,(void *)0,10,&xHandle);
	
 HAL_UART_Receive_IT(&huart1,&uart_rx_byte,1);
	
 printf("create 2nd task\r\n");
	
 xTaskCreate(led_toggle_entry,"led_toggle_task",128,(void *)0,10,&xHandle);
	
 //=====================================	

 tmp_handle = xTaskGetHandle("task_create_task");
	
 vTaskDelete(tmp_handle);
}

int main(void)
{
	TaskHandle_t xHandle;
	
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
	
	//shell_init();
	
	xTaskCreate(task_create_entry,"task_create_task",128,(void *)0,12,&xHandle);
	
	vTaskStartScheduler();
	
  while (1)
  {
  }

}

嵌入式软件的开发原则:

1:提高任务的运行效率

2:批量的处理是提高效率的最好办法,而不是少量的处理

硬件中断不是RTOS体系中的东西DMA是数据转运,可以让CPU解放出来“缓解CPU的压力”

传输的模式有M2M,M2P模式..............................

使用DMA优化串口接收实验

......后续继续更新

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1697767.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode 1631. 最小体力消耗路径 二分+BFS、并查集、Dijkstra算法

最小体力消耗路径 题目与水位上升的泳池中游泳类似 二分查找BFS 首先,采用二分查找,确定一个体力值,再从左上角,进行BFS,查看能否到达右下角,如果不行,二分查找就往大的数字进行查找&#xff…

终端安全管理系统、天锐DLP(数据泄露防护系统)| 数据透明加密保护,防止外泄!

终端作为企业员工日常办公、数据处理和信息交流的关键工具,承载着企业运营的核心信息资产。一旦终端安全受到威胁,企业的敏感数据将面临泄露风险,业务流程可能遭受中断,甚至整个企业的运营稳定性都会受到严重影响。 因此&#xff…

Java——认识Java

一、介绍 1、起源 Java 是由 Sun Microsystems 于 1995 年推出的一种面向对象的编程语言和计算平台。由詹姆斯高斯林(James Gosling,后来被称为Java之父)和他的同事们共同研发。后来,Sun 公司被 Oracle(甲骨文&#…

【list】list库介绍 + 简化模拟实现

本节博客先对list进行用法介绍,再在库的基础上简化其内容和形式,简单进行模拟实现,有需要借鉴即可。 目录 1.list介绍1.1 list概述1.2相关接口的介绍 2.简化模拟实现3.各部分的细节详述3.1结点3.2迭代器细节1:迭代器用原生指针还是…

【动态规划】斐波那契数列模型(C++)

目录 1137.第N个泰波那契数 解法(动态规划) 算法流程 1. 状态表⽰: 2. 状态转移⽅程: 3. 初始化: 4. 填表顺序: 5. 返回值: C算法代码 优化: 滚动数组 测试: …

电脑提示请重新安装软件MSVCP140.dll的几种解决方法分享

在日常使用电脑的过程中,我们常常会遇到一些错误提示,其中之一就是找不到msvcp140.dll文件,导致软件无法正常启动运行。这个问题可能是由于缺少相应的依赖库或者版本不匹配引起的。下面我将介绍5种解决方法,帮助大家解决这个问题。…

0524_网络编程8

思维导图:

Java基础的语法---StringBuilder

StringBuilder 构造方法 StringBuilder():创建一个空的StringBuilder实例。 StringBuilder(String str):创建一个StringBuilder实例,并将其初始化为指定的字符串内容。 StringBuilder(int a): 创建一个StringBuilder实例…

数据结构--《二叉树》

二叉树 1、什么是二叉树 二叉树(Binar Tree)是n(n>0)个结点的优先集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两颗互不相交的、分别称为根结点的左子树和右子树的二叉树构成。 这里给张图,能更直观的感受二叉树&#xff1…

AJAX初级

AJAX的概念: 使用浏览器的 XMLHttpRequest 对象 与服务器通信 浏览器网页中,使用 AJAX技术(XHR对象)发起获取省份列表数据的请求,服务器代码响应准备好的省份列表数据给前端,前端拿到数据数组以后&#xf…

手把手教学,一站式教你实现服务器(Ubuntu)Anaconda多用户共享

背景:书接上回,一站式安装Ubuntu及配置服务器手把手教学,一站式安装ubuntu及配置服务器-CSDN博客 在安装及配置好服务器后,因为课题组可能涉及多个用户共用一台服务器,为了防止服务器上代码误删和Anaconda环境管理混乱…

js之图表使用

今天为了给大家演示图表的使用,今天展示下切换图形的修改属性快速修改 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><script src"./js/jquery-3.7.1.js"></script><script src…

Android 使用 adb 列出设备上所有危险权限

步骤1&#xff1a;确定 Android SDK 位置 打开 Android Studio 的设置&#xff0c;并来到 Languages & Frameworks › Android SDK 处&#xff1a; 这里可以看到 Android SDK 目录的位置&#xff1a; 例如&#xff1a;/Users/admin/Library/Android/sdk。 复制这个路径&am…

RAG概述(一):RAG架构的演进

目录 概述 RAG核心步骤 Indexing索引 Retrieval检索 Generation生成​​​​​​​ Native RAG Advanced RAG Modular RAG 参考 概述 RAG&#xff1a;Retrieval-Augmented Generation 检索增强生成。 RAG通过结合LLMs的内在知识和外部数据库的非参数化数据&#xff…

区间合并-leetcode合并石头的最低成本-XMUOJ元素共鸣:深层次的唤醒

题目 思路 话不多说&#xff0c;直接上代码 附上INT_MAX和INT_MIN 【C】详解 INT_MAX 和 INT_MIN&#xff08;INT_MAX 和 INT_MIN是什么&#xff1f;它们的用途是什么&#xff1f;如何防止溢出&#xff1f;&#xff09;_c int max-CSDN博客 代码 /* leetcode合并石头的最低…

未授权访问:Hadoop 未授权访问漏洞

目录 1、漏洞原理 2、环境搭建 3、未授权访问 4、通过REST API命令执行 防御手段 今天继续学习各种未授权访问的知识和相关的实操实验&#xff0c;一共有好多篇&#xff0c;内容主要是参考先知社区的一位大佬的关于未授权访问的好文章&#xff0c;还有其他大佬总结好的文章…

【机器学习数据可视化-07】波士顿房价预测数据分析

波士顿房价预测&#xff1a;基于数据可视化的深入探索 一、引言   在当今社会&#xff0c;房地产市场作为经济的重要支柱之一&#xff0c;其走势与波动直接影响着国家经济的稳定和人民生活的品质。波士顿&#xff0c;这座历史悠久且充满活力的城市&#xff0c;其房地产市场一…

ElasticSearch学习篇12_《检索技术核心20讲》基础篇

背景 学习极客实践课程《检索技术核心20讲》https://time.geekbang.org/column/article/215243 课程分为基础篇、进阶篇、系统案例篇 主要记录企业课程学习过程课程大纲关键点&#xff0c;以文档形式记录笔记。 内容 检索技术&#xff1a;它是更底层的通用技术&#xff0c…

如何用bet快速创建文件夹多个同级文件夹,多层子文件夹

第一种用txt编辑&#xff0c;保存格式改为bat 运行即可 md用来创建文件夹 md空格文件夹名字 或者 md空格文件夹名字\子文件夹名字 第一个创建一个文件夹&#xff0c;或者多个同级文件夹用空格隔开或者用,英文逗号隔开 md 00 md 00 md 11 md 22 md 33 或者 md 00 1…

Python 中别再用 ‘+‘ 拼接字符串了!

当我开始学习 Python 时&#xff0c;使用加号来连接字符串非常直观和容易&#xff0c;就像许多其他编程语言&#xff08;比如Java&#xff09;一样。 然而&#xff0c;很快我意识到许多开发者似乎更喜欢使用.join()方法而不是。 在本文中&#xff0c;我将介绍这两种方法之间的…