OpenEdv-开源电子网

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

求大佬帮忙看看,我这个串口接收的问题出在哪了,代码不复杂

[复制链接]

54

主题

347

帖子

0

精华

高级会员

Rank: 4

积分
948
金钱
948
注册时间
2022-4-20
在线时间
356 小时
发表于 2023-11-8 17:10:16 | 显示全部楼层 |阅读模式
11金钱
代码是复制的比较新的原子哥的hal库F103串口实验代码,增加了一个DMA接收功能。
整个程序的功能是:通过DMA接收串口助手发来的消息,然后把消息发回(非DMA模式)串口助手。

故障现象:只有第一次的消息可以被完整发送,后面的每次都会少发两个字节。但在调试时,通过打断点的方式观察,消息又会每次都被完整发送。

希望大佬帮忙看一看,谢谢。

具体代码如下:
主函数:

  1. int main(void)
  2. {
  3.     HAL_Init();                             /* 初始化HAL库 */
  4.     sys_stm32_clock_init(RCC_PLL_MUL9);     /* 设置时钟为72Mhz */
  5.     delay_init(72);                         /* 延时初始化 */
  6.     usart_init(115200);                     /* 串口初始化为115200 */
  7.     dma_init(DMA1_Channel5);                /* DMA接收初始化 */
  8.    
  9.     __HAL_UART_ENABLE_IT(&g_uart1_handle, UART_IT_IDLE);                        /* 开启USART1空闲中断 */
  10.     HAL_UART_Receive_DMA(&g_uart1_handle, g_usart_rx_buf, USART_REC_LEN);       /* 开启DMA接收,并且设定接收数组 */
  11.    
  12.     while (1)
  13.     {
  14.         if (g_usart_rx_sta != 0)
  15.         {
  16.             HAL_UART_Transmit_IT(&g_uart1_handle, g_usart_rx_buf, g_usart_rx_sta);
  17.             g_usart_rx_sta = 0;
  18.         }
  19.     }
  20. }
复制代码
串口初始化:
  1. void usart_init(uint32_t baudrate)
  2. {
  3.     /*UART 初始化设置*/
  4.     g_uart1_handle.Instance = USART_UX;                                       /* USART_UX */
  5.     g_uart1_handle.Init.BaudRate = baudrate;                                  /* 波特率 */
  6.     g_uart1_handle.Init.WordLength = UART_WORDLENGTH_8B;                      /* 字长为8位数据格式 */
  7.     g_uart1_handle.Init.StopBits = UART_STOPBITS_1;                           /* 一个停止位 */
  8.     g_uart1_handle.Init.Parity = UART_PARITY_NONE;                            /* 无奇偶校验位 */
  9.     g_uart1_handle.Init.HwFlowCtl = UART_HWCONTROL_NONE;                      /* 无硬件流控 */
  10.     g_uart1_handle.Init.Mode = UART_MODE_TX_RX;                               /* 收发模式 */
  11.     HAL_UART_Init(&g_uart1_handle);                                           /* HAL_UART_Init()会使能UART1 */
  12. }

  13. /**
  14. * @brief       UART底层初始化函数
  15. * [url=home.php?mod=space&uid=271674]@param[/url]       huart: UART句柄类型指针
  16. * [url=home.php?mod=space&uid=60778]@note[/url]        此函数会被HAL_UART_Init()调用
  17. *              完成时钟使能,引脚配置,中断配置
  18. * @retval      无
  19. */
  20. void HAL_UART_MspInit(UART_HandleTypeDef *huart)
  21. {
  22.     GPIO_InitTypeDef gpio_init_struct;

  23.     if (huart->Instance == USART_UX)                            /* 如果是串口1,进行串口1 MSP初始化 */
  24.     {
  25.         USART_TX_GPIO_CLK_ENABLE();                             /* 使能串口TX脚时钟 */
  26.         USART_RX_GPIO_CLK_ENABLE();                             /* 使能串口RX脚时钟 */
  27.         USART_UX_CLK_ENABLE();                                  /* 使能串口时钟 */

  28.         gpio_init_struct.Pin = USART_TX_GPIO_PIN;               /* 串口发送引脚号 */
  29.         gpio_init_struct.Mode = GPIO_MODE_AF_PP;                /* 复用推挽输出 */
  30.         gpio_init_struct.Pull = GPIO_PULLUP;                    /* 上拉 */
  31.         gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;          /* IO速度设置为高速 */
  32.         HAL_GPIO_Init(USART_TX_GPIO_PORT, &gpio_init_struct);
  33.                
  34.         gpio_init_struct.Pin = USART_RX_GPIO_PIN;               /* 串口RX脚 模式设置 */
  35.         gpio_init_struct.Mode = GPIO_MODE_AF_INPUT;   
  36.         HAL_GPIO_Init(USART_RX_GPIO_PORT, &gpio_init_struct);   /* 串口RX脚 必须设置成输入模式 */
  37.         
  38.         HAL_NVIC_EnableIRQ(USART_UX_IRQn);                      /* 使能USART1中断通道 */
  39.         HAL_NVIC_SetPriority(USART_UX_IRQn, 0, 0);              /* 组2,最低优先级:抢占优先级0,子优先级0 */
  40.     }
  41. }
复制代码
串口引脚定义及中断回调函数:
  1. #define USART_TX_GPIO_PORT                  GPIOA
  2. #define USART_TX_GPIO_PIN                   GPIO_PIN_9
  3. #define USART_TX_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */

  4. #define USART_RX_GPIO_PORT                  GPIOA
  5. #define USART_RX_GPIO_PIN                   GPIO_PIN_10
  6. #define USART_RX_GPIO_CLK_ENABLE()          do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0)   /* PA口时钟使能 */

  7. #define USART_UX                            USART1
  8. #define USART_UX_IRQn                       USART1_IRQn
  9. #define USART_UX_IRQHandler                 USART1_IRQHandler
  10. #define USART_UX_CLK_ENABLE()               do{ __HAL_RCC_USART1_CLK_ENABLE(); }while(0)  /* USART1 时钟使能 */

  11. //中断回调函数
  12. void USART_UX_IRQHandler(void)
  13. {
  14.     if (__HAL_UART_GET_FLAG(&g_uart1_handle,UART_FLAG_IDLE))
  15.     {
  16.         g_usart_rx_sta = USART_REC_LEN - (__HAL_DMA_GET_COUNTER(&g_dma_handle));
  17.         HAL_DMA_Abort(g_uart1_handle.hdmarx);                                       /* 传输完成以后关闭串口DMA */

  18.         HAL_UART_Receive_DMA(&g_uart1_handle, g_usart_rx_buf, USART_REC_LEN);       /* 开启DMA接收,并且设定接收数组 */
  19.         
  20.     }
  21.     HAL_UART_IRQHandler(&g_uart1_handle);
  22. }
复制代码
DMA配置:
  1. void dma_init(DMA_Channel_TypeDef* DMAx_CHx)
  2. {
  3.     if ((uint32_t)DMAx_CHx > (uint32_t)DMA1_Channel7)     /* 大于DMA1_Channel7, 则为DMA2的通道了 */
  4.     {
  5.         __HAL_RCC_DMA2_CLK_ENABLE();                      /* DMA2时钟使能 */
  6.     }
  7.     else
  8.     {
  9.         __HAL_RCC_DMA1_CLK_ENABLE();                      /* DMA1时钟使能 */
  10.     }
  11.    
  12.     __HAL_LINKDMA(&g_uart1_handle, hdmarx, g_dma_handle);           /* 将DMA与USART1联系起来(接收DMA) */
  13.    
  14.     /* Rx DMA配置 */
  15.     g_dma_handle.Instance = DMAx_CHx;                               /* USART1_RX使用的DMA通道为: DMA1_Channel5 */
  16.     g_dma_handle.Init.Direction = DMA_PERIPH_TO_MEMORY;             /* DIR = 0 , 外设到存储器模式 */
  17.     g_dma_handle.Init.PeriphInc = DMA_PINC_DISABLE;                 /* 外设非增量模式 */
  18.     g_dma_handle.Init.MemInc = DMA_MINC_ENABLE;                     /* 存储器增量模式 */
  19.     g_dma_handle.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;    /* 外设数据长度:8位 */
  20.     g_dma_handle.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;       /* 存储器数据长度:8位 */
  21.     g_dma_handle.Init.Mode = DMA_NORMAL;                            /* DMA模式:正常模式 */
  22.     g_dma_handle.Init.Priority = DMA_PRIORITY_MEDIUM;               /* 中等优先级 */

  23.     HAL_DMA_Init(&g_dma_handle);
  24. }
复制代码




又菜又爱指点
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

54

主题

347

帖子

0

精华

高级会员

Rank: 4

积分
948
金钱
948
注册时间
2022-4-20
在线时间
356 小时
 楼主| 发表于 2023-11-9 10:14:41 | 显示全部楼层
没找到问题出在哪,直接用寄存器重写了这个工程,现在是正常的了
又菜又爱指点
回复

使用道具 举报

28

主题

360

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1027
金钱
1027
注册时间
2021-2-4
在线时间
146 小时
发表于 2023-11-9 11:45:07 | 显示全部楼层
串口上总线都空闲了,才开启DMA吗?
我串口上没用过DMA,但是这个感觉就不太合理.
应该是一个字节就搬运一次吧,
不然串口收到的数据放DR寄存器里不读取出来不是会被覆盖吗
回复

使用道具 举报

54

主题

347

帖子

0

精华

高级会员

Rank: 4

积分
948
金钱
948
注册时间
2022-4-20
在线时间
356 小时
 楼主| 发表于 2023-11-10 16:23:59 | 显示全部楼层
电子控2021 发表于 2023-11-9 11:45
串口上总线都空闲了,才开启DMA吗?
我串口上没用过DMA,但是这个感觉就不太合理.
应该是一个字节就搬运一次 ...

在空闲中断里开DMA是为了重置CNDTR寄存器,这样下次搬数据往数组的第0字节开始搬
DMA是自动搬运的,不需要进中断一个一个搬,没数据来的时候产生空闲中断,相当于通知一下这部分数据搬完了
至于为什么出错,我还没搞明白,反正用寄存器直接重写了工程就没出现问题了
又菜又爱指点
回复

使用道具 举报

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

本版积分规则



关闭

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

正点原子公众号

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

GMT+8, 2024-11-23 19:45

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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