一、空闲中断基本框架
STM32的空闲中断(Idle Interrupt)通常用于在CPU空闲时执行任务,例如在低功耗模式下减少功耗等。当CPU完成当前任务后,会进入空闲状态,此时会触发空闲中断。在空闲中断中,可以执行一些需要在CPU空闲时执行的任务,例如读取传感器数据、更新LCD显示等。
要使用STM32的空闲中断,需要进行以下步骤:
1.配置NVIC优先级:将空闲中断的优先级设置为较低的值。
2.启用空闲中断:在代码中启用空闲中断,并设置空闲中断的处理函数。
以下是使用STM32 HAL库实现空闲中断的简单示例代码:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
//串口接收中断回调函数
}
int main(void)
{
HAL_Init(); //初始化HAL库
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //启用空闲中断
while (1)
{
//主程序逻辑
}
}
void HAL_UART_IdleCallback(UART_HandleTypeDef *huart)
{
//空闲中断处理函数
//更新LCD显示、读取传感器数据等任务
}
以上代码中,当串口接收完成时,会触发串口接收中断回调函数
(HAL_UART_RxCpltCallback)。
在主程序中,会一直循环执行,直到CPU进入空闲状态触发空闲中断。
空闲中断处理函数 (HAL_UART_IdleCallback),会在空闲中断触发时被调用,执行一些需要在CPU空闲时执行的任务。
二、空闲中断(接收/发送)循环缓冲区
本次的文章主要是记录学习超子老师的代码。
他的B站链接:https://space.bilibili.com/517828171?spm_id_from=333.337.0.0
文章来源:https://www.toymoban.com/news/detail-722586.html
1、 定义一个数据管理结构体,一个总的功能结构体
#define U1_TX_SIZE 2048
#define U1_RX_SIZE 2048
#define U1_RX_MAX 256
typedef struct{//记录数据的开始位置和结束位置
uint8_t *start;
uint8_t *end;
}LCB;
typedef struct{
uint32_t RxCounter;//记录存入缓冲区的数据量
uint32_t TxCounter;
uint32_t TxState;//发送状态
LCB RxLocation[10];
LCB TxLocation[10];
LCB *RxInPtr;
LCB *RxOutPtr;
LCB *RxEndPtr;
LCB *TxInPtr;
LCB *TxOutPtr;
LCB *TxEndPtr;
UART_HandleTypeDef uart;
}UCB;
2、然后对这个缓冲区进行一个初始化的工作,这个函数需要在(MX_USART1_UART_Init)进行调用。并且缓冲区初始化函数已经开启了空闲中断,开启了中断接收。
void U1_PtrInit(void)//发送和接收缓冲区初始化函数
{
uart1.RxInPtr = &uart1.RxLocation[0];//数据输入指针指向数据开始和结束位置管理数组的首地址
uart1.RxOutPtr = &uart1.RxLocation[0];
uart1.RxEndPtr = &uart1.RxLocation[9];//数据输入指针指向数据开始和结束位置管理数组的尾地址
uart1.RxCounter = 0;//存储到缓冲区的数据量
uart1.RxInPtr->start = U1_RxBuff;//让第一个数据开始指针指向缓冲区的首地址
uart1.TxInPtr = &uart1.TxLocation[0];
uart1.TxOutPtr = &uart1.TxLocation[0];
uart1.TxEndPtr = &uart1.TxLocation[9];
uart1.TxCounter = 0;
uart1.TxInPtr->start = U1_TxBuff;
__HAL_UART_ENABLE_IT(&uart1.uart, UART_IT_IDLE);//开启空闲中断
HAL_UART_Receive_IT(&uart1.uart,uart1.RxInPtr->start,U1_RX_MAX);//开始接收
}
3、假设,现在已经有数据接收了,那么我们需要实现串口中断函数(注意看代码里的注释)
void USART1_IRQHandler(void)//串口中断函数
{ //当串口中断发生后会进入到此函数,但是我们接收的数据小于RX_MAX,所以不会发生串口接收完成中断.
HAL_UART_IRQHandler(&uart1.uart);
//由于程序的规定是接收和发送小于256字节的数据,所以不会发生串口接收完成中断。如果接收到45个字节,那么我理论上是根据接收函数要接收256个,
//但是接收不到256个字节,所以再接收到45个个字节之后,串口线就处于空闲状态,也就是这个时候,空闲中断就发生了,也就是进入到USART1_IRQHandler函数
//由于HAL没有处理空闲中断的程序,所以我们需要模仿它处理接收完成中断的程序,实现一个处理空闲中断的程序
if(__HAL_UART_GET_FLAG(&uart1.uart, UART_FLAG_IDLE)){//判断是否是这个中断发生
__HAL_UART_CLEAR_IDLEFLAG(&uart1.uart);//清楚中断标志位
uart1.RxCounter += (U1_RX_MAX - uart1.uart.RxXferCount);//通过uart1.uart.RxXferCount(这个值表示还有多少个字节需要接收)我们用U1_RX_MAX减去就能得到接收的字节数量
HAL_UART_AbortReceive_IT(&uart1.uart);最后再调用终止接收中断函数
}
}
4、实现中断回调函数
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)//终止串口接收中断回调函数
{
if(huart->Instance == USART1){
uart1.RxInPtr->end = &U1_RxBuff[uart1.RxCounter - 1];//将数据结束处的地址赋值给uart1.RxInPtr->end
uart1.RxInPtr++;
if(uart1.RxInPtr == uart1.RxEndPtr){
uart1.RxInPtr = &uart1.RxLocation[0];
}
if((U1_RX_SIZE - uart1.RxCounter)<U1_RX_MAX){//再判断缓冲区剩下的空间是否能存储下U1_RX_MAX字节,不能则回滚
uart1.RxCounter = 0;
uart1.RxInPtr->start = U1_RxBuff;
}else{//可以放下就让下一个数据输入指针指向下一个可以存储的地址
uart1.RxInPtr->start = &U1_RxBuff[uart1.RxCounter];
}
HAL_UART_Receive_IT(&uart1.uart,uart1.RxInPtr->start,U1_RX_MAX);//然后再次开启中断接收函数
}
}
5、实现发送函数
void U1_Txdata(uint8_t *data, uint32_t data_len)//串口发送函数
{
if((U1_TX_SIZE - uart1.TxCounter )>=data_len){//判断发送数据是否可以存放在缓冲区内
uart1.TxInPtr->start = &U1_TxBuff[uart1.TxCounter];//通过uart1.TxCounter可以获取到缓冲区内的数据有多少,
//从而可以将下一个下标的地址给uart1.TxInPtr->star
}else{//说明缓冲区内的剩余空间已经不能放下我们所要发送的数据,所以要回滚到前面
uart1.TxCounter = 0;
uart1.TxInPtr->start = U1_TxBuff;//将缓冲区的地址给uart1.TxInPtr->star
}
memcpy(uart1.TxInPtr->start,data,data_len);//把data放在uart1.TxInPtr->start这个地方,长度是data_len
uart1.TxCounter += data_len;//缓冲区计数加上放入的长度data_len
uart1.TxInPtr->end = &U1_TxBuff[uart1.TxCounter - 1];//标记此次数据的结束位置,通过uart1.TxCounter可以计算出在缓冲区内的下标位置
uart1.TxInPtr++;//让数据输入指针加一,指向下一个的TxLocation数组的地址
if(uart1.TxInPtr == uart1.TxEndPtr){//判断数据输入指针是否到达10个的极限,如果是就会进行回滚,指向TxLocation数组的首地址
uart1.TxInPtr = &uart1.TxLocation[0];
}
}
6、实现发送中断回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) //串口发送中断回调函数
{
if(huart->Instance == USART1) {
if(huart->Instance == USART1){
uart1.TxState = 0;
}
}
}
7、在while(1)添加以下代码,这个代码实现的功能是:接收到数据后立马发送回去
if(uart1.RxOutPtr != uart1.RxInPtr){
U1_Txdata(uart1.RxOutPtr->start,uart1.RxOutPtr->end - uart1.RxOutPtr->start + 1);
uart1.RxOutPtr++;
if(uart1.RxOutPtr == uart1.RxEndPtr){
uart1.RxOutPtr = &uart1.RxLocation[0];
}
}
if((uart1.TxOutPtr != uart1.TxInPtr)&&(uart1.TxState==0)){
uart1.TxState = 1;
HAL_UART_Transmit_IT(&uart1.uart,uart1.TxOutPtr->start,uart1.TxOutPtr->end - uart1.TxOutPtr->start + 1);
uart1.TxOutPtr++;
if(uart1.TxOutPtr == uart1.TxEndPtr){
uart1.TxOutPtr = &uart1.TxLocation[0];
}
}
8、缓冲区管理结构图
9、下面是所以整理超子老师的代码文章来源地址https://www.toymoban.com/news/detail-722586.html
/***************在usart.h文件添加以下代码************************/
#ifndef __UART_H
#define __UART_H
#include "string.h"
#include "stdint.h"
#include "stm32f1xx_hal_uart.h"
#define U1_TX_SIZE 2048
#define U1_RX_SIZE 2048
#define U1_RX_MAX 256
typedef struct{//记录数据的开始位置和结束位置
uint8_t *start;
uint8_t *end;
}LCB;
typedef struct{
uint32_t RxCounter;//记录存入缓冲区的数据量
uint32_t TxCounter;
uint32_t TxState;//发送状态
LCB RxLocation[10];
LCB TxLocation[10];
LCB *RxInPtr;
LCB *RxOutPtr;
LCB *RxEndPtr;
LCB *TxInPtr;
LCB *TxOutPtr;
LCB *TxEndPtr;
UART_HandleTypeDef uart;
}UCB;
void MX_USART1_UART_Init(uint32_t bandrate);
void U1_PtrInit(void);
void U1_Txdata(uint8_t *data, uint32_t data_len);
extern UCB uart1;
#end
/***********************************************************/
//在usart.c文件里自行斟酌添加
/***************.C********************************************/
#include "stm32f1xx_hal.h"
#include "uart.h"
UCB uart1;
uint8_t U1_RxBuff[U1_RX_SIZE];
uint8_t U1_TxBuff[U1_TX_SIZE];
void MX_USART1_UART_Init(void)//串口功能初始化
{
uart1.uart.Instance = USART1;
uart1.uart.Init.BaudRate = 115200;
uart1.uart.Init.WordLength = UART_WORDLENGTH_8B;
uart1.uart.Init.StopBits = UART_STOPBITS_1;
uart1.uart.Init.Parity = UART_PARITY_NONE;
uart1.uart.Init.Mode = UART_MODE_TX_RX;
uart1.uart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
uart1.uart.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&uart1.uart) != HAL_OK)
{
Error_Handler();
}
U1_PtrInit();//发送和接收缓冲区初始化函数
}
void U1_PtrInit(void)//发送和接收缓冲区初始化函数
{
uart1.RxInPtr = &uart1.RxLocation[0];//数据输入指针指向数据开始和结束位置管理数组的首地址
uart1.RxOutPtr = &uart1.RxLocation[0];
uart1.RxEndPtr = &uart1.RxLocation[9];//数据输入指针指向数据开始和结束位置管理数组的尾地址
uart1.RxCounter = 0;//存储到缓冲区的数据量
uart1.RxInPtr->start = U1_RxBuff;//让第一个数据开始指针指向缓冲区的首地址
uart1.TxInPtr = &uart1.TxLocation[0];
uart1.TxOutPtr = &uart1.TxLocation[0];
uart1.TxEndPtr = &uart1.TxLocation[9];
uart1.TxCounter = 0;
uart1.TxInPtr->start = U1_TxBuff;
__HAL_UART_ENABLE_IT(&uart1.uart, UART_IT_IDLE);//开启空闲中断
HAL_UART_Receive_IT(&uart1.uart,uart1.RxInPtr->start,U1_RX_MAX);//开始接收
}
void U1_Txdata(uint8_t *data, uint32_t data_len)//串口发送函数
{
if((U1_TX_SIZE - uart1.TxCounter )>=data_len){//判断发送数据是否可以存放在缓冲区内
uart1.TxInPtr->start = &U1_TxBuff[uart1.TxCounter];//通过uart1.TxCounter可以获取到缓冲区内的数据有多少,
//从而可以将下一个下标的地址给uart1.TxInPtr->star
}else{//说明缓冲区内的剩余空间已经不能放下我们所要发送的数据,所以要回滚到前面
uart1.TxCounter = 0;
uart1.TxInPtr->start = U1_TxBuff;//将缓冲区的地址给uart1.TxInPtr->star
}
memcpy(uart1.TxInPtr->start,data,data_len);//把data放在uart1.TxInPtr->start这个地方,长度是data_len
uart1.TxCounter += data_len;//缓冲区计数加上放入的长度data_len
uart1.TxInPtr->end = &U1_TxBuff[uart1.TxCounter - 1];//标记此次数据的结束位置,通过uart1.TxCounter可以计算出在缓冲区内的下标位置
uart1.TxInPtr++;//让数据输入指针加一,指向下一个的TxLocation数组的地址
if(uart1.TxInPtr == uart1.TxEndPtr){//判断数据输入指针是否到达10个的极限,如果是就会进行回滚,指向TxLocation数组的首地址
uart1.TxInPtr = &uart1.TxLocation[0];
}
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)//串口底层硬件初始化
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)//串口底层硬件复位
{
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_DISABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
HAL_NVIC_DisableIRQ(USART1_IRQn);
}
}
void HAL_UART_AbortReceiveCpltCallback(UART_HandleTypeDef *huart)//终止串口接收中断回调函数
{
if(huart->Instance == USART1){
uart1.RxInPtr->end = &U1_RxBuff[uart1.RxCounter - 1];//将数据结束处的地址赋值给uart1.RxInPtr->end
uart1.RxInPtr++;
if(uart1.RxInPtr == uart1.RxEndPtr){
uart1.RxInPtr = &uart1.RxLocation[0];
}
if((U1_RX_SIZE - uart1.RxCounter)<U1_RX_MAX){//再判断缓冲区剩下的空间是否能存储下U1_RX_MAX字节,不能则回滚
uart1.RxCounter = 0;
uart1.RxInPtr->start = U1_RxBuff;
}else{//可以放下就让下一个数据输入指针指向下一个可以存储的地址
uart1.RxInPtr->start = &U1_RxBuff[uart1.RxCounter];
}
HAL_UART_Receive_IT(&uart1.uart,uart1.RxInPtr->start,U1_RX_MAX);//然后再次开启中断接收函数
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) //串口接收中断回调函数
{
if(huart->Instance == USART1) {
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) //串口发送中断回调函数
{
if(huart->Instance == USART1) {
if(huart->Instance == USART1){
uart1.TxState = 0;
}
}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {//串口接收数据出错回调函数
if(huart->Instance == USART1) {
}
}
/***************.C********************************************/
//在中断函数文件里添加
/***************.IT********************************************/
void USART1_IRQHandler(void)//串口中断函数
{ //当串口中断发生后会进入到此函数,但是我们接收的数据小于RX_MAX,所以不会发生串口接收完成中断.
HAL_UART_IRQHandler(&uart1.uart);
//由于程序的规定是接收和发送小于256字节的数据,所以不会发生串口接收完成中断。如果接收到45个字节,那么我理论上是根据接收函数要接收256个,
//但是接收不到256个字节,所以再接收到45个个字节之后,串口线就处于空闲状态,也就是这个时候,空闲中断就发生了,也就是进入到USART1_IRQHandler函数
//由于HAL没有处理空闲中断的程序,所以我们需要模仿它处理接收完成中断的程序,实现一个处理空闲中断的程序
if(__HAL_UART_GET_FLAG(&uart1.uart, UART_FLAG_IDLE)){//判断是否是这个中断发生
__HAL_UART_CLEAR_IDLEFLAG(&uart1.uart);//清楚中断标志位
uart1.RxCounter += (U1_RX_MAX - uart1.uart.RxXferCount);//通过uart1.uart.RxXferCount(这个值表示还有多少个字节需要接收)我们用U1_RX_MAX减去就能得到接收的字节数量
HAL_UART_AbortReceive_IT(&uart1.uart);最后再调用终止接收中断函数
}
}
/***************.IT********************************************/
//主函数里添加
if(uart1.RxOutPtr != uart1.RxInPtr){
U1_Txdata(uart1.RxOutPtr->start,uart1.RxOutPtr->end - uart1.RxOutPtr->start + 1);
uart1.RxOutPtr++;
if(uart1.RxOutPtr == uart1.RxEndPtr){
uart1.RxOutPtr = &uart1.RxLocation[0];
}
}
if((uart1.TxOutPtr != uart1.TxInPtr)&&(uart1.TxState==0)){
uart1.TxState = 1;
HAL_UART_Transmit_IT(&uart1.uart,uart1.TxOutPtr->start,uart1.TxOutPtr->end - uart1.TxOutPtr->start + 1);
uart1.TxOutPtr++;
if(uart1.TxOutPtr == uart1.TxEndPtr){
uart1.TxOutPtr = &uart1.TxLocation[0];
}
}
到了这里,关于【STM32的空闲中断(Idle Interrupt)】的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!