[技术问答] 关于华大MCU DMA收发串口问题

[复制链接]
9308|26
 楼主| nczywq 发表于 2021-10-1 16:08 | 显示全部楼层 |阅读模式
我参考usart例程中的DMA收发方式,修改成了DMA只收数据,然后用作finsh的通信端口USART1,完全正常,然后我又使能了一个USART4做为485收发,参考UART1的代码拷贝了一个,关键代码如下:
  1. #define USART4_CH                                 (M4_USART4)
  2. #define USART4_BAUDRATE   (115200ul)
  3. #define USART4_RX_PORT    (PortB)
  4. #define USART4_RX_PIN     (Pin04)
  5. #define USART4_RX_FUNC    (Func_Usart4_Rx)
  6. #define USART4_TX_PORT    (PortB)
  7. #define USART4_TX_PIN     (Pin05)
  8. #define USART4_TX_FUNC    (Func_Usart4_Tx)
  9. #define USART4_EI_NUM     (INT_USART4_EI)
  10. #define USART4_EI_IRQn    (Int003_IRQn)
  11. extern uint8_t Uart4_Rx;                                //串口4接收缓存
  12. extern uint8_t Uart4_Buffer[];        //串口4接收到的数据
  13. extern uint8_t Uart4_Count;
  14. void Uart4_Init(void)
  15. {
  16.         en_result_t enRet = Ok;
  17.     stc_irq_regi_conf_t stcIrqRegiCfg;
  18.     uint32_t u32Fcg1Periph = PWC_FCG1_PERIPH_USART1 | PWC_FCG1_PERIPH_USART2 | \
  19.                              PWC_FCG1_PERIPH_USART3 | PWC_FCG1_PERIPH_USART4;
  20.                 /* Enable peripheral clock */
  21.     PWC_Fcg1PeriphClockCmd(u32Fcg1Periph, Enable);
  22.        
  23.     const stc_usart_uart_init_t stcInitCfg = {
  24.         UsartIntClkCkNoOutput,
  25.         UsartClkDiv_1,
  26.         UsartDataBits8,
  27.         UsartDataLsbFirst,
  28.         UsartOneStopBit,
  29.         UsartParityNone,
  30.         UsartSamleBit8,
  31.         UsartStartBitFallEdge,
  32.         UsartRtsEnable,
  33.     };
  34.                  /* Initialize USART IO */
  35.     PORT_SetFunc(USART4_RX_PORT, USART4_RX_PIN, USART4_RX_FUNC, Disable);
  36.     PORT_SetFunc(USART4_TX_PORT, USART4_TX_PIN, USART4_TX_FUNC, Disable);

  37.     /* Initialize USART */
  38.     enRet = USART_UART_Init(USART4_CH, &stcInitCfg);
  39.     if (enRet != Ok)
  40.     {
  41.         while (1)
  42.         {
  43.         }
  44.     }
  45.     else
  46.     {
  47.     }

  48.     /* Set baudrate */
  49.     enRet = USART_SetBaudrate(USART4_CH, USART4_BAUDRATE);
  50.     if (enRet != Ok)
  51.     {
  52.         while (1)
  53.         {
  54.         }
  55.     }
  56.     else
  57.     {
  58.     }

  59.     /* Set USART RX error IRQ */
  60.                 MEM_ZERO_STRUCT(stcIrqRegiCfg);
  61.     stcIrqRegiCfg.enIRQn = USART4_EI_IRQn;
  62.     stcIrqRegiCfg.pfnCallback = &Usart4ErrIrqCallback;
  63.     stcIrqRegiCfg.enIntSrc = USART4_EI_NUM;
  64.     enIrqRegistration(&stcIrqRegiCfg);
  65.     NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
  66.     NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
  67.     NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);

  68.     /*Enable TX && RX && RX interrupt function*/
  69.     USART_FuncCmd(USART4_CH, UsartTx, Enable);
  70.     USART_FuncCmd(USART4_CH, UsartRx, Enable);
  71.     USART_FuncCmd(USART4_CH, UsartRxInt, Enable);
  72. }
下面是DMA初始化函数
  1. /* DMAC */
  2. #define DMA1CH1_UNIT                        (M4_DMA1)
  3. #define DMA1_CH1                            (DmaCh1)
  4. #define DMA1CH1_TRG_SEL                     (EVT_USART4_RI)



  5. /* DMA block transfer complete interrupt */
  6. #define DMA1CH1_BTC_INT_NUM                 (INT_DMA1_BTC1)
  7. #define DMA1CH1_BTC_INT_IRQn                (Int004_IRQn)
  8. void Dma1Ch1Init(void)
  9. {
  10.         stc_dma_config_t stcDmaInit;
  11.     stc_irq_regi_conf_t stcIrqRegiCfg;
  12.                 MEM_ZERO_STRUCT(stcDmaInit);

  13.     /* Enable peripheral clock */
  14.     PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_DMA1 | PWC_FCG0_PERIPH_DMA2,Enable);

  15.     /* Enable DMA. */
  16.     DMA_Cmd(DMA1CH1_UNIT,Enable);

  17.     /* Initialize DMA. */
  18.     MEM_ZERO_STRUCT(stcDmaInit);
  19.     stcDmaInit.u16BlockSize = 1u; /* 1 block */
  20.     stcDmaInit.u32SrcAddr = ((uint32_t)(&USART4_CH->DR)+2ul); /* Set source address. */
  21.     stcDmaInit.u32DesAddr = (uint32_t)(&Uart4_Rx);     /* Set destination address. */
  22.     stcDmaInit.stcDmaChCfg.enSrcInc = AddressFix;  /* Set source address mode. */
  23.     stcDmaInit.stcDmaChCfg.enDesInc = AddressFix;  /* Set destination address mode. */
  24.     stcDmaInit.stcDmaChCfg.enIntEn = Enable;       /* Enable interrupt. */
  25.     stcDmaInit.stcDmaChCfg.enTrnWidth = Dma8Bit;   /* Set data width 8bit. */
  26.     DMA_InitChannel(DMA1CH1_UNIT, DMA1_CH1, &stcDmaInit);
  27.                
  28.                

  29.     /* Enable the specified DMA channel. */
  30.     DMA_ChannelCmd(DMA1CH1_UNIT, DMA1_CH1, Enable);

  31.     /* Clear DMA flag. */
  32.     DMA_ClearIrqFlag(DMA1CH1_UNIT, DMA1_CH1, TrnCpltIrq);

  33.     /* Enable peripheral circuit trigger function. */
  34.     PWC_Fcg0PeriphClockCmd(PWC_FCG0_PERIPH_AOS,Enable);
  35.                 ;

  36.     /* Set DMA trigger source. */
  37.     DMA_SetTriggerSrc(DMA1CH1_UNIT, DMA1_CH1, DMA1CH1_TRG_SEL);

  38.     /* Set DMA block transfer complete IRQ */
  39.                 MEM_ZERO_STRUCT(stcIrqRegiCfg);
  40.     stcIrqRegiCfg.enIRQn = DMA1CH1_BTC_INT_IRQn;
  41.     stcIrqRegiCfg.pfnCallback = &Dma1ch1BtcIrqCallback;
  42.     stcIrqRegiCfg.enIntSrc = DMA1CH1_BTC_INT_NUM;
  43.     enIrqRegistration(&stcIrqRegiCfg);
  44.     NVIC_SetPriority(stcIrqRegiCfg.enIRQn, DDL_IRQ_PRIORITY_DEFAULT);
  45.     NVIC_ClearPendingIRQ(stcIrqRegiCfg.enIRQn);
  46.     NVIC_EnableIRQ(stcIrqRegiCfg.enIRQn);
  47. }
  48. static void Dma1ch1BtcIrqCallback(void)
  49. {
  50.                 RS485CS(1); //准备发送
  51.     DMA_ClearIrqFlag(DMA1CH1_UNIT, DMA1_CH1, BlkTrnCpltIrq);
  52.                 Uart4_Buffer[Uart4_Count++] = Uart4_Rx;
  53.                 USART_SendData(USART4_CH,(uint16_t)Uart4_Rx);
  54.                 while (Reset == USART_GetStatus(USART4_CH, UsartTxComplete));
  55.                 RS485CS(0); //准备发送
  56.        
  57.         /*        if(Uart4_Count >= 10)
  58.                 {
  59.                         RS485CS(1); //准备发送
  60.                         for(int i = 0 ; i < 10 ; i += 1)
  61.                         {
  62.                                 USART_SendData(USART4_CH,(uint16_t)Uart4_Buffer[i]);
  63.                                 while (Reset == USART_GetStatus(USART4_CH, UsartTxComplete));
  64.        
  65.                         }
  66.                         Uart4_Count = 0 ;
  67.                         RS485CS(0); //准备发送
  68.                 }*/
  69. }
串口1使用的Int001_IRQn
DMA1CH0使用的Int002_IRQn
串口4使用的Int003_IRQn
DMA1CH1使用的Int004_IRQn

USART1,用在finsh就完全正常的,可以正常收发;
USART4,就是无法收到电脑发送的数据,没有进入中断,但是单片机发数据电脑能收到。
我是从STM32转到华大MCU的,这是第一次使用华大MCU做的板子,也是第一次使用rt-thread,这是公司第一次使用国产的芯片和rt-thread,这次公司的测量项目,我直接从,芯片选型,全部转为国产,到ORCAD画图,allegro画PCB,打样,自己做SMT,经历了1个多月,现在板子终于出成品了,就开始了漫长的代码移植,STM32我是一直在MAC上开发的,遗憾的是华大没有官方的GCC移植代码,我发现IAR里面有GCC,的S和linker,我自己就手动写了,makefile,直到编译,解决了无数个坑,最后编译终于没有问题了,可是华大的没有openocd的cfg文件,我用仿真器下载不上去,由于项目没有时间了,暂时放弃了linux和MAC下的编译了,回到Keil了,如果论坛里有人要我GCC makefile文件,留言,我把源码发给你,可以一直研究openocd 怎么把编译后的文件下载到MCU。
项目完成后,我会把我STM32转向华大的MCU,做的教学视频,从ORCAD画原理图,到allegro布线,到打样,到做钢网,到贴片。到移植官方的代码,到移植rt-thread,最后在到移植finsh.
现在就是遇到了,单片机收不到电脑数据,可能是我不熟悉华大MCU的原因,还希望论坛大佬,能指点我一下。谢谢大家

 楼主| nczywq 发表于 2021-10-5 15:27 | 显示全部楼层
,哎,搞了几天了,还是没有进展。
zchong 发表于 2021-10-8 07:47 来自手机 | 显示全部楼层
慢慢来,仔细核对代码
 楼主| nczywq 发表于 2021-10-8 14:33 | 显示全部楼层
zchong 发表于 2021-10-8 07:47
慢慢来,仔细核对代码

我使用逻辑分析仪分析了我发的数据,发现是能够正常解码的,我看了华大官方的管脚,PB4/NJTRST,这个脚,带多功能,虽然写了fun_grp2,但是直接改代码,是不能收到数据的,我在家又鬼使神差的跳线方式到B3/JTDO_TRACESWO,还是不行,我当时就怀疑我板子设计是不是有问题,原理图,PCB布局全部核对了一次,发送接收线,差分走线,走的没问题。我就直接怀疑了是不是管脚有多功能的时候,不能在华大官方的代码上改端口,我尝试把接收口改到PB9,板子上飞线到PB9,发现居然能正常收到数据了,我只能说华大官方的也很坑,不知道,有没有这方面的说明,还是他们自己都不知道这个问题?哎,板子要重新设计了,还是官方例子,115200波特率,改成57600没问题,再改就会报,参数错误,真的很不想去研究误码率,可是官方代码又不兼容其他波特率,,,国产之路,用着真的心累

评论

@wubangmi :我一个月前才在网上买的你们的MCU,做板子,加焊接,加移植代码,一共才一个多月,9月中旬才开始搞代码的,所以对你们的MCU还是有很多不熟悉的地方,这个管脚被用成了JTAG,也完全是靠以前的经验得出来的,- - ,你们这个管脚配置这个功能,比ST的强太多了,他们的管脚是固定了那几个口,用着真好  发表于 2021-10-11 14:22
PB4/NJTRST,你都知道这是个复用的IO口,你要先把这个口子设置成GPIO功能,再设置为你需要的UART口,不然他还是JTAG的NJRST功能。 PORT_DebugPortSetting(TDO_SWO|TDI|TRST,Disable);这个函数是吧JTAG的几个口子配置成IO的,你先调用一次这玩意  发表于 2021-10-9 17:12
asmine 发表于 2021-10-9 16:27 | 显示全部楼层
finish指的是什么
 楼主| nczywq 发表于 2021-10-10 10:28 来自手机 | 显示全部楼层
asmine 发表于 2021-10-9 16:27
finish指的是什么

就是shell窗口,像ubuntu一样,用命令行控制板子
两只袜子 发表于 2021-10-10 17:17 来自手机 | 显示全部楼层
唉,都是st缺货惹的祸
martinhu 发表于 2021-10-11 09:18 | 显示全部楼层
nczywq 发表于 2021-10-8 14:33
我使用逻辑分析仪分析了我发的数据,发现是能够正常解码的,我看了华大官方的管脚,PB4/NJTRST,这个脚, ...

PB4/NJTRST,B3/JTDO_TRACESWO,这两个都是JTAG接口的特殊功能脚,上电后默认是JTAG功能,不是GPIO,所以不能像其他IO一样设置func序号就可以,需要先调用GPIO的PSPCR寄存器disable特殊功能(GPIO的库函数有封装),然后再初始化IO。
 楼主| nczywq 发表于 2021-10-11 13:45 | 显示全部楼层
martinhu 发表于 2021-10-11 09:18
PB4/NJTRST,B3/JTDO_TRACESWO,这两个都是JTAG接口的特殊功能脚,上电后默认是JTAG功能,不是GPIO,所以 ...

非常谢谢你,函数我已经找到,因为我使用的是swd,所以我使用了下面的代码来关闭了相关端口的功能
  1. PORT_DebugPortSetting(TDO_SWO,Disable);                        //关闭DEBUG端口功能
  2.         PORT_DebugPortSetting(TDI,Disable);                                //关闭DEBUG端口功能
  3.         PORT_DebugPortSetting(TRST,Disable);                        //关闭DEBUG端口功能

,再次感谢
martinhu 发表于 2021-10-11 15:20 | 显示全部楼层
nczywq 发表于 2021-10-11 13:45
非常谢谢你,函数我已经找到,因为我使用的是swd,所以我使用了下面的代码来关闭了相关端口的功能

,再 ...

“115200波特率,改成57600没问题,再改就会报,参数错误,”
至于波特率的问题,你要注意串口的时钟频率,还有波特率寄存器是8bit的,所以如果时钟频率太高,而波特率低的话,会导致波特率寄存器溢出,在库函数里面会返回错误,如果是调用库函数设置,可以查看返回值判断,建议使用的时候结合使用的波特率,用UART自带的时钟分频,将时钟分频到与波特率合适的频率
 楼主| nczywq 发表于 2021-10-11 15:37 | 显示全部楼层
martinhu 发表于 2021-10-11 15:20
“115200波特率,改成57600没问题,再改就会报,参数错误,”
至于波特率的问题,你要注意串口的时钟频率 ...

这个分频我做好了,总线我分成了50MHZ,串口再分成4,12.5mhz,的时候,9600到115200,测试都还蛮稳定的
 楼主| nczywq 发表于 2021-10-13 11:36 | 显示全部楼层
martinhu 发表于 2021-10-11 15:20
“115200波特率,改成57600没问题,再改就会报,参数错误,”
至于波特率的问题,你要注意串口的时钟频率 ...

哥,你们这个MCU定时器中断是怎么回事,我串口收到数据,打开计时,只要在时间内,再次收到数据我清空了TMR0_UNIT->CNTBR = 0ul ;TIM0CHB的计数值,但是这个值很飘忽不定啊,我清空了,他还是按照上一次的中断,提前进入中断了,导致我这数据还没收完,就提前收到结束信号了

评论

@wubangmi :感谢,感觉这款MCU越调越顺利了  发表于 2021-10-19 13:51
@nczywq :华大的UART例程里timerout和UART配合超时中断的例程  发表于 2021-10-14 16:39
@martinhu :串口还可以空闲中断?因为ModbusRTU结束符号是3个字符时间,我就用的定时器,来计算的结束符,不知道你讲的串口空闲中断是怎么用的?我TIM0我停止后再清除计数,也不行,并没有重新计数  发表于 2021-10-14 15:19
你用手动清零?timer0和uart可以自动做空闲中断吧? 如果手动清零,试试先停止再清零。  发表于 2021-10-14 11:30
labasi 发表于 2021-11-4 10:52 | 显示全部楼层
为什么要手动清零呢
wowu 发表于 2021-11-4 11:44 | 显示全部楼层
什么叫空闲中断呢
xiaoqizi 发表于 2021-11-4 11:46 | 显示全部楼层
不支持低速吗

评论

都支持的  发表于 2021-11-15 16:37
木木guainv 发表于 2021-11-4 11:59 | 显示全部楼层
计数值为什么会变化呢

评论

计数值读取到的并不太准确,好像不是实时的  发表于 2021-11-15 16:38
paotangsan 发表于 2021-11-4 12:01 | 显示全部楼层
都有多少分频呀

评论

分频,你在华大库文件里面,有一个结构体里面写了有多少分频的  发表于 2021-11-15 16:38
 楼主| nczywq 发表于 2021-11-15 16:36 | 显示全部楼层
labasi 发表于 2021-11-4 10:52
为什么要手动清零呢

因为我是想收到数据后,重置定时器,所 以想用手动
您需要登录后才可以回帖 登录 | 注册

本版积分规则

16

主题

83

帖子

3

粉丝
快速回复 在线客服 返回列表 返回顶部