重启网上很多讲故事+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个字符。测试完毕。
第一次发帖,不足
重启网上很多讲故事+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个字符。测试完毕。
第一次发帖,不足
举报