你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32使用DMA加串口空闲中断接收数据

[复制链接]
jinyi7016 发布时间:2017-12-25 22:47
本帖最后由 jinyi7016 于 2017-12-25 22:51 编辑

    STM32中,需要用串口接收数据,是使用串口中断来接收数据。但是用这种方法的话,就要频繁进入串口中断,然后处理,效率就比较低。于是就想到用DMA来接收串口数据,这个STM32也是支持的。但是关键的一点,怎么知道数据接收完毕了呢?如果接收的数据长度固定,那就好办,直接设置DMA的接收数据个数就行了。但是如果长度不固定了,那应该怎么办了?
         这个时候,就要用到STM32在串口中提供的另一个好用的东西了,就是串口空闲中断。在STM32的串口控制器中,设置了有串口空闲中断,即如果串口空闲,又开启了串口空闲中断的话,就触发串口空闲中断,然后程序就会跳到串口中断去执行。有了这个,是不是可以判断什么时候串口数据接收完毕了呢?因为串口数据接收完毕后,串口总线肯定是会空闲的嘛,那这个中断肯定是会触发的了。


无标题.png

还有一个问题,这串口空闲中断是只要串口空闲就会产生吗?其实不是的,串口空闲中断要触发的话,是要RXNE位被置位后,串口总线空闲才会触发的。所以我们不用担心,串口数据发送完毕后,会不会触发串口空闲中断了。

无标题.png

1、  配置串口。包括设置串口的引脚配置,串口的配置,串口中断的配置,串口的接收DMA的配置
void USART_init(void){   GPIO_InitTypeDef   GPIO_InitStructure;    USART_InitTypeDef  USART_InitStructure;   
NVIC_InitTypeDef   NVIC_InitStructure;     //开启时钟   
RCC_APB2PeriphClockCmd(USART_RCC,ENABLE);    //配置TX端口   
GPIO_InitStructure.GPIO_Pin = GPIO_USART_TX;   
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;   
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   
GPIO_Init(GPIO_USART_TYPE,&GPIO_InitStructure);    //配置RX端口  
  GPIO_InitStructure.GPIO_Pin = GPIO_USART_RX;   
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;   
GPIO_Init(GPIO_USART_TYPE,&GPIO_InitStructure);     //配置串口模式   
USART_InitStructure.USART_BaudRate = 115200;   
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;
   USART_Init(USART1,&USART_InitStructure);       //中断配置   
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;   
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;   
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;   
NVIC_Init(&NVIC_InitStructure);     /* 若总线空闲,产生中断 */   
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);     /*开启串口DMA接收*/   
USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);       USART_Cmd(USART1,ENABLE);
}

         代码比较简单,一看就明白了,这就是使用库函数开发的好处,代码易懂。这里,关键的是要开启总线空闲中断,并且开启串口DMA接收。注意,不要开启串口接收中断,不然接收数据就会一直产生中断了。
2、  DMA配置
DMA配置,要先查看串口接收是使用的哪个DMA的哪个通道,对于USART1_RX使用的是DMA1的5通道。

无标题.png

         然后就是代码配置DMA了。
void DMA_init(void)
{  
DMA_InitTypeDef    DMA_Initstructure;
//   NVIC_InitTypeDef   NVIC_Initstructure;      /*开启DMA时钟*/   
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  //   /* Enable the DMA1 Interrupt *///   NVIC_Initstructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;       //通道设置为串口1中断//   
NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;   
//中断响应优先级0//   NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority=1;
//   NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;        
//打开中断//   NVIC_Init(&NVIC_Initstructure);    /*DMA配置*/  
DMA_Initstructure.DMA_PeripheralBaseAddr =  (u32)(&USART1->DR);
DMA_Initstructure.DMA_MemoryBaseAddr     = (u32)receive_data;  
DMA_Initstructure.DMA_DIR = DMA_DIR_PeripheralSRC;   
DMA_Initstructure.DMA_BufferSize = 128;   
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_High;  
DMA_Initstructure.DMA_M2M = DMA_M2M_Disable;   
DMA_Init(DMA1_Channel5,&DMA_Initstructure);     //启动DMA   
DMA_Cmd(DMA1_Channel5,ENABLE);    //开启DMA发送发成中断   
//DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
}
         因为这里,不需要用到DMA中断,所以DMA中断就不要使能了。因此DMA中断配置也就不需要了。这里,关键的是要设置DMA_DIR为DMA_DIR_PeripheralSRC,表示数据是从外设到内存。这里设定的DMA_Mode是普通模式,即数据传输就只能一次。
3、 串口中断程序编写
这个就是关键的地方了。在这里,需要做什么了。需要对DMA设置下。当进入这个中断的时候,串口接收的数据,已经在内存的数组中了。通过读取DMA的计数值,就可以知道接收到了多少个数据。然后再把DMA给diable掉,重新设置接收数据长度,在开启DMA,接收下一次串口数据。为什么要这么做了,因为在STM32手册中有如下说明:

无标题.png

另外还有一点,串口空闲中断触发后,硬件会自动将串口空闲中断标志位给置1,我们是需要将给标志位给置0的,不然又要进中断了,这个在手册中也有说明。

无标题.png

         代码就如下了:

void USART1_IRQHandler(void)
{  
  unsigned char num=0;   
if(USART_GetITStatus(USART1,USART_IT_IDLE) == SET)   
{       num = USART1->SR;      
num = USART1->DR; //清USART_IT_IDLE标志      
DMA_Cmd(DMA1_Channel5,DISABLE);    //关闭DMA      
num = 128 -  DMA_GetCurrDataCounter(DMA1_Channel5);      //得到真正接收数据个数        
receive_data[num] = '\0';       DMA1_Channel5->CNDTR=128;       //重新设置接收数据个数         
DMA_Cmd(DMA1_Channel5,ENABLE);  //开启DMA     
  receive_flag = 1;           //接收数据标志位置1   
}
}
         关键的一点,就是要读取SR,DR,将USART_IT_IDLE标志给清掉,然后DMA设置要注意下。
         在主函数中,使用下面代码测试:
         int main(){    periph_init();    printf("hello world\n");    while(1)    {       while(receive_flag == 0);       receive_flag = 0;       printf("%s",receive_data);    }}
         当串口接收数据后,中断程序会使receive_flag为1,然后就跳出while循环。打印接收到的数据。

    测试结果:

无标题.png

发送什么,就接收什么。
还测试了下,在波特率460800下,都还是能正常的工作的。





收藏 3 评论7 发布时间:2017-12-25 22:47

举报

7个回答
秾秾电 回答时间:2018-1-24 14:18:35
请问楼主是用哪款STM32,我想移植到STM32F030上,在修改一些寄存器。
秾秾电 回答时间:2018-1-24 14:21:26
我对照了手册看了一下,应该是STM32F103吧
yyz1111 回答时间:2018-7-5 10:34:15
楼主你好!我现在正在调试,为什么我的程序没有进入串口中断呢
zts329547875 回答时间:2018-7-5 12:44:36
标记一下,回头再看
ZDPHPN 回答时间:2018-7-10 17:11:54
我再搞一个现在使用的串口DMA收发的(收使用空闲中断的)

RS232.rar

下载

1.69 KB, 下载次数: 89

wwwheihei 回答时间:2018-7-11 14:57:15
如果一帧数据中几个字节间隔有点大,是不是会误判呢
tinaxiayuwei 回答时间:2018-7-21 17:00:42
yyz1111 发表于 2018-7-5 10:34
楼主你好!我现在正在调试,为什么我的程序没有进入串口中断呢

我的也是中断进不去,我用的是串口3  DMA空闲中断进不去,初始化代码一样的

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版