STM32
直播中

万物死

8年用户 1304经验值
擅长:MEMS/传感技术
私信 关注
[问答]

接收STM32串口中断+DMA不定长字符串的方法是什么

接收STM32中断+DMA不定长字符串的方法是什么

回帖(1)

贾玲

2021-12-9 10:44:57
重启网上很多讲故事+DMA接收不定长字符串的例子,有谁抄来抄去不加验证,没有没有代码,还有可能对解决方案源每天解释。空来整理一下,下面的代码经过本人验证。
接收定长字符串简单,直接在中断里将字符放到数组里即可。
但是接收不定长字符串就比较麻烦,因为你不知道什么时候一帧数据接收结束。通常有两种方法来接收不定长时间:超时判断,服务中断。今天我们先介绍第二种方法。
首先明确一个概念,什么是STM32的后续中断,下面是stm32数据手册1对中断的定义:

  '1'符号被完全由'1'组成的一个完整的数据帧,伴随着包含了下一个帧的开始位('1'的数据也包括了停止位的隐私)。
一串数据全部由1组构成,包括我们发送帧停止位都是1。发送完这一串的数据的最后一个字节后,紧跟着一个空闲中断图1是手册里面的解释。




问题解决思路:用DMA1的通道5接收串口数据,当一帧数据发送完,进入32的串口中断,在串口中断里面计算本次DMA传输接收了多少字符,同时置位标志位,在main()函数中查询标志位来处理数据,别忘了处理完之后将标志位清零。
下面是代码:
串口1初始化:


void usart1_config(u32 baud)
{
GPIO_InitTypeDef io;
USART_InitTypeDef temp;
//RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);


RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);


io.GPIO_Pin = GPIO_Pin_9;
io.GPIO_Mode = GPIO_Mode_AF_PP;//txd
io.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &io);


io.GPIO_Pin = GPIO_Pin_10;
io.GPIO_Mode = GPIO_Mode_IN_FLOATING;//rxd
io.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &io);




temp.USART_BaudRate = baud;
temp.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
temp.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
temp.USART_Parity = USART_Parity_No;
temp.USART_StopBits = USART_StopBits_1;
temp.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &temp);


#if(USART1_RX_INT_EN)//这个宏为1
{
        NVIC_InitTypeDef NVIC_InitStruct;
       
        NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStruct);


        USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//使能串口1的空闲中断,注意这里并没有使能接收中断!
}
#endif
USART_Cmd(USART1, ENABLE);
}


DMA初始化


void usart1_DMA_config(DMA_Channel_TypeDef* DMAy_Channelx,u32 memAddr,u32 periphAddr,u8 bufSize)
{
DMA_InitTypeDef  DMA_InitStructure;


RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1????
/* 初始化DMA */
DMA_InitStructure.DMA_PeripheralBaseAddr = periphAddr;//        DMA外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = memAddr;//                        DMA内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//                ·传输方向从外设到内存
DMA_InitStructure.DMA_BufferSize = bufSize;                                        //传输数据大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//传输一次后外设地址不增加
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//传输一次后内存地址增加
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//按照字节传送
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//按照字节传送
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//正常模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级中等,一个DMA通道工作的话可以不设置
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //关闭内存到内存
DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化


#if(USART1_RX_DMA_INT_EN )//这个宏定义没开
{
        NVIC_InitTypeDef NVIC_InitStruct;
       
        NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel5_IRQn;//USART1_RX-channel5
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStruct);
        DMA_ITConfig(DMAy_Channelx,DMA_IT_TC,ENABLE);
}
#endif
}


DMA的使能
包括两部分,DMA使能与串口的接收请求使能。


void usart1_DMA_enable(DMA_Channel_TypeDef* DMAy_Channelx,u8 bufSize)
{
USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);//DMA接收请求使能
DMA_Cmd(DMAy_Channelx, DISABLE);//先关闭才能写传送的数据
DMA_SetCurrDataCounter(DMAy_Channelx, bufSize);//写入传送数量,如果此值为0DMA开不了
DMA_Cmd(DMAy_Channelx, ENABLE);
}


串口中断函数


void USART1_IRQHandler(void)
{
    u16 clear;
        if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE))
        {
                clear = USART_ReceiveData(USART1);//通过先读状态寄存器再读数据寄存器将IDLE清除
                usart1IdleItFlag = 1;//接收完成标志位置1
                bufRemainByte = 50 - DMA_GetCurrDataCounter(DMA1_Channel5);//计算本次传送的字节数。总数以50为例子
                PBout(11) = ~PBout(11);//配合led观察接收完毕
        }
}


上面需要注意,清除IDLE标志位的次序,根据数据手册的说明:


IDLE:监测到总线空闲 (IDLE line detected)
当检测到总线空闲时,该位被硬件置位。如果USART_CR1中的IDLEIE为’1’,则产生中断。由
软件序列清除该位(先读USART_SR,然后读USART_DR)。


注意比对上面代码。


在main()函数里面查询标志位处理数据


int main()
{
ST_USART1_RCV_BUF rxbuf={{0},0,0,0};
extern u8 usart1IdleItFlag;
extern u8 bufRemainByte;


SysTick_Init(72);
NVIC_PriorityGroupConfig(2);
LED_Init();
usart1_config(9600);
usart1_DMA_config(DMA1_Channel5,(u32)rxbuf.buf,(u32)&USART1->DR,50);
usart1_DMA_enable(DMA1_Channel5,50);
while(1)
{
        if(usart1IdleItFlag)
        {
                usart1_send_string(rxbuf.buf,bufRemainByte);
                usart1_DMA_enable(DMA1_Channel5,50);//ʹÄÜÏÂÒ»´ÎDMA
                usart1IdleItFlag = 0;//一定不要忘记将标志位清零。
        }
}


以上代码别写结束,下面是测试:
下面是测试图





如上图,发送了8次“12345678”,进入中断8次,一共接收到64个字符。测试完毕。第一次发帖,不足
举报

更多回帖

发帖
×
20
完善资料,
赚取积分