OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5807|回复: 9

STM32F1接受大数据量存在丢数据

[复制链接]

7

主题

16

帖子

0

精华

初级会员

Rank: 2

积分
90
金钱
90
注册时间
2015-1-6
在线时间
10 小时
发表于 2020-12-2 18:31:33 | 显示全部楼层 |阅读模式
1金钱
本帖最后由 124756828 于 2020-12-2 18:33 编辑

开发板:STM32F103问题描述:用UART空闲中断+DMA接收数据会存在丢数据的现象
具体现象:用PC串口工具每隔10ms发送640byte数据,

如果如下代码接收数据在main函数中,只做数据接收长度累加,那么接受不会有问题
但是我一旦在USART2_IRQHandler中做一系列的动作,比如把接收的数据copy到ringbuffer中,就会丢数据,求助
1)代码初始化,波特率设置为921600
  1. uint8_t hw_uart_init(uint32_t baud_rate)
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStructure;
  4.     USART_InitTypeDef USART_InitStructure;
  5.     NVIC_InitTypeDef NVIC_InitStructure;

  6.     /* Enable USART2,GPIOA,DMA1 RCC clock */
  7.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  8.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
  9.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

  10.     USART_DeInit(USART2);
  11.     USART_Cmd(USART2, DISABLE);
  12.     /* Initialize the GPIOA0,GPIOA1,GPIOA2,GPIOA3 */
  13.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;
  14.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  15.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
  16.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  17.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_3;
  18.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  19.     GPIO_Init(GPIOA, &GPIO_InitStructure);

  20.     /* Data format :1:8:1, no parity check, hardware flow control */
  21.     USART_InitStructure.USART_BaudRate = baud_rate;
  22.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;
  23.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
  24.     USART_InitStructure.USART_Parity = USART_Parity_No;
  25.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_RTS_CTS;
  26.     USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

  27.     /* Enable USART interrupts, mainly for idle interrupts */
  28.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
  29.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=BT_PREE_PRIO;
  30.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = BT_SUB_PRIO;
  31.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  32.     NVIC_Init(&NVIC_InitStructure);

  33.     /* Initializes USART2 to enable USART,USART idle interrupts, and USART RX DMA */
  34.     USART_Init(USART2, &USART_InitStructure);
  35.     USART_ITConfig(USART2, USART_IT_IDLE, ENABLE);
  36.     USART_DMACmd(USART2,USART_DMAReq_Rx,ENABLE);
  37.     USART_Cmd(USART2, ENABLE);

  38.     /* Initializes DMA and enables it */
  39.     hw_memset(&DMA_UART2,0,sizeof(DMA_InitTypeDef));
  40.     DMA_DeInit(DMA1_Channel6);
  41.     DMA_UART2.DMA_PeripheralBaseAddr = (uint32_t)&USART2->DR;
  42.     DMA_UART2.DMA_MemoryBaseAddr = (uint32_t)bt_dma_rx_buf;
  43.     DMA_UART2.DMA_DIR = DMA_DIR_PeripheralSRC;
  44.     DMA_UART2.DMA_BufferSize = BT_DMA_BUF_SIZE;
  45.     DMA_UART2.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  46.     DMA_UART2.DMA_MemoryInc = DMA_MemoryInc_Enable;
  47.     DMA_UART2.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
  48.     DMA_UART2.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  49.     DMA_UART2.DMA_Mode = DMA_Mode_Normal;
  50.     DMA_UART2.DMA_Priority = DMA_Priority_VeryHigh;
  51.     DMA_UART2.DMA_M2M = DMA_M2M_Disable;
  52.     DMA_Init(DMA1_Channel6, &DMA_UART2);

  53.     DMA_Cmd(DMA1_Channel6, ENABLE);

  54.     return BT_ERR_OK;

  55. }
复制代码
2)UART空闲中断接受数据
  1. void USART2_IRQHandler(void)
  2. {
  3.    
  4.     if(USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
  5.     {
  6.             DMA_Cmd(DMA1_Channel6, DISABLE);
  7.         /* Without this, the interrupt cannot be cleared and continues into the interrupt */
  8.         USART_ReceiveData(USART2);

  9.         /* Clear the interrupt and reset DMA */
  10.         USART_ClearITPendingBit(USART2,USART_IT_IDLE);
  11.         recv2_len += BT_DMA_BUF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel6);

  12.                  uart2_dma_enable(DMA1_Channel6);
  13.     }
  14. }
复制代码
  1. void uart2_dma_enable(DMA_Channel_TypeDef*DMA_CHx)
  2. {
  3.     //DMA_Cmd(DMA_CHx, DISABLE );
  4.     DMA_UART2.DMA_MemoryBaseAddr = (uint32_t)bt_dma_rx_buf;
  5.     DMA_UART2.DMA_BufferSize = BT_DMA_BUF_SIZE;
  6.     DMA_Init(DMA1_Channel6, &DMA_UART2);
  7.     DMA_Cmd(DMA_CHx, ENABLE);
  8. }
复制代码


最佳答案

查看完整内容[请看2#楼]

首先,中断里拷贝数据到ringbuf是正常处理,这个操作耗时很短,没有问题;其次这个数据量和波特率很小,对于103来说毫无压力。参考以建议: 1、DMA接收应该设置为“DMA_Mode_Circular”(连续模式),非单次模式,如果数据连续来,就可能丢失数据; 2、使用上串口空闲中断、DMA接收半满中断、DMA接收完成中断,三者接收处理函数,缺一不可; 3、如果主函数(线程)任务量比较大,需增加DMA 接收buf和ringbuf大小。 参考F0/F1 D ...
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

2

主题

685

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3448
金钱
3448
注册时间
2017-7-4
在线时间
869 小时
发表于 2020-12-2 18:31:34 | 显示全部楼层
首先,中断里拷贝数据到ringbuf是正常处理,这个操作耗时很短,没有问题;其次这个数据量和波特率很小,对于103来说毫无压力。参考以建议:
1、DMA接收应该设置为“DMA_Mode_Circular”(连续模式),非单次模式,如果数据连续来,就可能丢失数据;
2、使用上串口空闲中断、DMA接收半满中断、DMA接收完成中断,三者接收处理函数,缺一不可;
3、如果主函数(线程)任务量比较大,需增加DMA 接收buf和ringbuf大小。
参考F0/F1 DMA收&发实现,实测1.5Mbps波特率不翻车(串口助手连续发,不延时):
实现过程:https://acuity.blog.csdn.net/article/details/108367512
仓库代码:https://github.com/Prry/stm32-uart-dma
回复

使用道具 举报

7

主题

16

帖子

0

精华

初级会员

Rank: 2

积分
90
金钱
90
注册时间
2015-1-6
在线时间
10 小时
 楼主| 发表于 2020-12-2 18:55:34 | 显示全部楼层
自己顶一下
回复

使用道具 举报

3

主题

1155

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
7464
金钱
7464
注册时间
2015-1-15
在线时间
1368 小时
发表于 2020-12-2 19:06:48 | 显示全部楼层
自己想不通原因嘛,数据拷贝不需要时间嘛,所以丢失正常的,要么增大发送间隔时间,要么中断内做数据拷贝,放到外部任务中处理

一分耕耘一分收获。
回复

使用道具 举报

3

主题

312

帖子

0

精华

高级会员

Rank: 4

积分
907
金钱
907
注册时间
2011-10-19
在线时间
196 小时
发表于 2020-12-2 23:41:05 | 显示全部楼层
  • 确认发送间隔是否准确的10ms,因为通用系统的实时性不强,精度往往是毫秒级的;
  • 一帧数据的发送时长:1000ms * 640 / (921600bps / 10bit) = 6.944444444444445ms,差不多7ms,所以帧间隔约3ms不算大也不算小,但也不应该在中断里做过多操作。应该给dma分配多个缓存,每次接收时切换使用不同的缓存区,在中断外处理接收到的数据。


回复

使用道具 举报

2

主题

141

帖子

0

精华

高级会员

Rank: 4

积分
679
金钱
679
注册时间
2020-11-21
在线时间
67 小时
发表于 2020-12-3 08:10:22 | 显示全部楼层
相当于你在进行数据处理的时候,又有新的数据进来了,没时间处理新来的数据,就会导致数据丢失。
回复

使用道具 举报

3

主题

821

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3369
金钱
3369
注册时间
2011-11-10
在线时间
207 小时
发表于 2020-12-3 09:04:30 | 显示全部楼层
中断本来就是处理异常情况,你处理要快要赶紧回来,不建议做太多活。任务留在主函数处理,这是最好的方法和习惯。
回复

使用道具 举报

0

主题

71

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
360
金钱
360
注册时间
2017-10-10
在线时间
131 小时
发表于 2020-12-3 09:54:18 | 显示全部楼层
本帖最后由 YukawaManabu 于 2020-12-3 09:56 编辑

正常来说,你在中断里顶多做做标志位的赋值操作就够了,剩下的数据处理不要放中断里,放到外部去做,或者有操作系统的话用消息队列或者信号量是最好的,你波特率高,又大量数据放中断去做。。。不丢数据才怪
回复

使用道具 举报

3

主题

1907

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
4106
金钱
4106
注册时间
2018-8-14
在线时间
696 小时
发表于 2020-12-3 11:39:51 | 显示全部楼层
这数据量不算大, 以103的处理能力同时收三组都不应该有问题
回复

使用道具 举报

7

主题

16

帖子

0

精华

初级会员

Rank: 2

积分
90
金钱
90
注册时间
2015-1-6
在线时间
10 小时
 楼主| 发表于 2021-2-26 08:27:44 | 显示全部楼层
Acuity 发表于 2020-12-2 18:31
首先,中断里拷贝数据到ringbuf是正常处理,这个操作耗时很短,没有问题;其次这个数据量和波特率很小,对 ...

1.5Mbps一般不会翻车,蓝牙的数据量大概在1s 320Mbps左右
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-8 21:20

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表