串口传输大量数据时 总线空闲中断+DMA传输 为CPU减负
在使用串口接受数据时,如果用while在主函数等待,或者用if在主函数判断,传输少量的标志位可以,程序效率很低,如果用DMA+单字节串口接受中断,这样效率高了,但是当我们需要不间断传输大量数据时,单片机频繁进入中断,有时会影响main()函数的运行,如果采用串口总线空闲触发中断+DMA传输,可以完美的解决这两个问题。
USART_SR寄存器的IDLE位
代码如下:
/********************************************************************************
* @file LSYY_Usart_Api.c
* @author 绿水颖颖
* @version V1.0
* @date 2017.7.26
* @brief 传输大量数据时 空闲中断+DMA传输 为CPU减负
******************************************************************************
* @attention
*
*
********************************************************************************/
#include "LSYY_Usart_Api.h"
#include "LSYY_usart.h"
#include "LSYY_led.h"
/*内存池*/
uint8_t DMA_Rece_Buf[USART_RX_BUFF_SIZE];
/*
*函数名` : void USURT1_Config(void)
*描述 :配置USART1
*输入 :无
*输出 :无
*/
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure ;
USART_InitTypeDef USART_InitStructure ;
//开启串口的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK ,ENABLE);
//开启GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK,ENABLE);
//配置串口1的寄存器
//配置串口1所对应的GPIO口
//配置模式 复用推挽输出模式
GPIO_InitStructure.GPIO_Pin=DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP ;
//配置输出速率
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
//初始化GPIO口
GPIO_Init(DEBUG_USART_TX_GPIO_PORT,&GPIO_InitStructure);
//配置串口1GPIO口的模式
//GPIOA10
//GPIO_InitStructure.GPIO_Pin=DEBUG_USART_RX_GPIO_PORT_PIN;
GPIO_InitStructure.GPIO_Pin=DEBUG_USART_RX_GPIO_PIN;
//配置串口模式
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_IN_FLOATING ;
//初始化GPIOA
GPIO_Init(DEBUG_USART_RX_GPIO_PORT,&GPIO_InitStructure);
//配置串口
//设置波特率
USART_InitStructure.USART_BaudRate =DEBUG_USART_BAUDRATE ;
//设置传输字节
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
//设置停止标志位
USART_InitStructure.USART_StopBits=USART_StopBits_1;
//配置奇偶校检位,不设置
USART_InitStructure.USART_Parity=USART_Parity_No;
//配置硬件控制流,不配置
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None ;
//配置串口模式
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx ;
//初始化串口1
USART_Init(DEBUG_USARTx ,&USART_InitStructure);
/****************************************************注意**************************************************/
/*开启空闲中断*/
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
/*开启DMA传输数据*/
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
//使能串口
USART_Cmd(DEBUG_USARTx ,ENABLE);
USART_ClearFlag(USART1, USART_FLAG_TC);
}
/**
* @brief 串口空闲中断(IDLE)无
* @param 无
* @retval 无
**/
void USART_IDLE_NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 配置中断源:IDLE */
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
/* 配置抢占优先级 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 配置子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
/* 使能中断通道 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//初始化
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief USART1 DMA配置
* @param 无
* @retval 无
**/
void USART_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
//相应的DMA配置
DMA_DeInit(DMA1_Channel5); //将DMA的通道5寄存器重设为缺省值 串口1对应的是DMA通道5
//DMA外设ADC基地址
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;
//DMA内存基地址
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)DMA_Rece_Buf;
//数据传输方向,从外设读取发送到内存
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_BufferSize = USART_RX_BUFF_SIZE;
//外设地址寄存器不变
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//内存地址寄存器递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//数据宽度为8位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
//数据宽度为8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
//工作在正常缓存模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
//DMA通道 x拥有中优先级
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
//DMA通道x没有设置为内存到内存传输
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
//根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
//正式驱动DMA传输
DMA_Cmd(DMA1_Channel5, ENABLE);
}
/**
* @brief 串口DMA配置复位函数 恢复DMA指针
* @param 无
* @retval 无
**/
void DeInit_USART_DMA(DMA_Channel_TypeDef*DMA_CHx)
{
//串口DMA失能
DMA_Cmd(DMA_CHx,DISABLE);
/*DMA传输数值重装*/
DMA_SetCurrDataCounter(DMA_CHx,USART_RX_BUFF_SIZE);
//串口DMA使能
DMA_Cmd(DMA_CHx,ENABLE);
}
/**
* @brief 串口发送一个数组、字符串
* @param buf 字符串数组地址 len 数组字符串长度
* @retval 无
**/
void Usart1_Send(u8 *buf,u8 len)
{
u8 t;
//循环发送数据
for(t=0;t<len;t++)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
USART_SendData(USART1,buf[t]);
}
while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
}
/**
* @brief 串口中断
* @param 无
* @retval 无
**/
/*用于计算不同长度数据的长度*/
uint32_t DMA_Buf_Cnt;
void USART1_IRQHandler(void)
{
/*串口空闲标志位置1*/
if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)
{
/* 必须得写,不然IDLE位清除不了 */
USART_ReceiveData(USART1);
/*计算出本帧数据长度*/
DMA_Buf_Cnt=USART_RX_BUFF_SIZE-DMA_GetCurrDataCounter(DMA1_Channel5);
/*测试程序*/
printf("Length:%d\r\n",DMA_Buf_Cnt);
printf("Data:\r\n");
Usart1_Send((uint8_t *)DMA_Rece_Buf,DMA_Buf_Cnt);
printf("\r\n \r\n");
//清除中断标志位
USART_ClearITPendingBit(USART1,USART_IT_IDLE);
/*DMA使能*/
DeInit_USART_DMA(DMA1_Channel5);
}
}
/**
* @brief 测试函数
* @param 无
* @retval 无
**/
void USART_DMA_Test(void)
{
USART_IDLE_NVIC_Configuration();
USART_DMA_Config();
}
/**
* @brief 主函数
* @param 无
* @retval 无
**/
int main()
{
All_Init();
USART_DMA_Test();
while(1)
{
/*所有要处理任务*/
// TaskProcess();
}
}
串口上位机截图
注意内容:
!IDLE位清零必须进行一个读USART_SR寄存器操作,接着进行一个读取USART_DR寄存器操作
本人菜鸟,正在学习中,有问题还请多多交流指正1558194318@qq.com,如有侵权立即删除。