返回列表 发新帖我要提问本帖赏金: 120.00元(功能说明)

[应用方案] 【又换MCU】越来越好用系列,新塘031 串口PDMA通信。

[复制链接]
 楼主| 呐咯密密 发表于 2021-6-28 15:10 | 显示全部楼层 |阅读模式
<
@21小跑堂 #申请原创#
前言
老规矩,发帖先吐槽!!!
从入职新公司后,一个项目我折腾了两年,看了一下自己的帖子,都是泪,每次以为开发完了,就会出事故,这次保守点,用新塘一定没问题,一定会成功量产,一定不要重新来过。这一个项目我从ST换到GD,中间穿插灵动微,GD断货后又开始寻找新的替代,一开始是盯到了瑞萨,这是上头给的方案,直接给我整吐了,瑞萨是真的难用啊,于是给我备选恩智浦的PLC系列,也是老大难。最后联系到今天的主角新塘。在上周选用了NUC029,下载资料,搭建环境,串口测试,SPI测试,然后突然发现这玩意没有DMA,白瞎了这么长时间啊,样板都做好了,结果出幺蛾子。然后在换M031,这次深思熟虑,认为不会有问题了才开搞。作为一个半成熟的程序猿,肯定不能helloword起手了,直接上UART吧。
事前BB,好用!
环境搭建这里就跳过了,没啥用,我还是用KEIL 5 开发,自行下载个PACK包安装就好了。点此前往新塘官网。搜索自己的MCU型号,打开页面,在资源中有文档和软件
2058560d966836a3e4.png
在文档中下载数据手册等文档,在软件中下载例程和工具,软件中最实用的是以下几个软件:
3634060d966f49696e.png
从上到下依次是:官方例程库,Nu_link驱动,外设引脚配置软件,时钟配置软件。
外设引脚配置软件用于快速配置引脚以及复用,该软件只能配置引脚及其功能,不能配置外设等功能呢,例如串口的相关配置,这些事实现不了的。
时钟配置软件仅用于配置系统时钟以及各外设时钟。这两个软件支持导出.c代码。可复制粘贴到自己的工程。
这两个软件都是非常简单的,这里就不赘述了。
但是有一个时钟配置软件有BUG,以我用的M031SE3AE为例,外部时钟最大可使用32M,但是软件中最大只支持24M,希望官方可以修复。
1286160d9688bde262.png
2417260d968b0ca3af.png
开始代码

下载官方的例程,固件库代码在文件夹:D:\M031_Series_BSP_CMSIS_V3.03.000\SampleCode\StdDriver,寄存器代码在D:\M031_Series_BSP_CMSIS_V3.03.000\SampleCode\RegBased
这里采用固件库的方式开发,方便快捷。
时钟初始化:
  1. void SYS_Init(void)
  2. {

  3.     /*---------------------------------------------------------------------------------------------------------*/
  4.     /* Init System Clock                                                                                       */
  5.     /*---------------------------------------------------------------------------------------------------------*/
  6.     /* Unlock protected registers */
  7.     SYS_UnlockReg();

  8.     /* Enable HIRC clock (Internal RC 48MHz) */
  9.     CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

  10.     /* Wait for HIRC clock ready */
  11.     CLK_WaitClockReady(CLK_STATUS_HIRCSTB_Msk);

  12.     /* Select HCLK clock source as HIRC and HCLK source divider as 1 */
  13.     CLK_SetHCLK(CLK_CLKSEL0_HCLKSEL_HIRC, CLK_CLKDIV0_HCLK(1));

  14.     /* Set both PCLK0 and PCLK1 as HCLK */
  15.     CLK->PCLKDIV = CLK_PCLKDIV_APB0DIV_DIV1 | CLK_PCLKDIV_APB1DIV_DIV1;

  16.     /* Select IP clock source */
  17.     /* Select UART0 clock source is HIRC */
  18.     CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL1_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));
  19.     /* Select UART1 clock source is HIRC */
  20.     CLK_SetModuleClock(UART1_MODULE, CLK_CLKSEL1_UART1SEL_HIRC, CLK_CLKDIV0_UART1(1));

  21.     /* Enable UART0 peripheral clock */
  22.     CLK_EnableModuleClock(UART0_MODULE);
  23.     /* Enable UART1 peripheral clock */
  24.     CLK_EnableModuleClock(UART1_MODULE);
  25.     /* Enable PDMA module clock */
  26.     CLK_EnableModuleClock(PDMA_MODULE);

  27.     /* Update System Core Clock */
  28.     /* User can use SystemCoreClockUpdate() to calculate PllClock, SystemCoreClock and CycylesPerUs automatically. */
  29.     SystemCoreClockUpdate();

  30.     /*---------------------------------------------------------------------------------------------------------*/
  31.     /* Init I/O Multi-function                                                                                 */
  32.     /*---------------------------------------------------------------------------------------------------------*/

  33.     /* Set PB multi-function pins for UART0 RXD=PB.12 and TXD=PB.13 */
  34.     SYS->GPB_MFPH = (SYS->GPB_MFPH & ~(SYS_GPB_MFPH_PB12MFP_Msk | SYS_GPB_MFPH_PB13MFP_Msk))    |       \
  35.                     (SYS_GPB_MFPH_PB12MFP_UART0_RXD | SYS_GPB_MFPH_PB13MFP_UART0_TXD);

  36.     /* Set PB multi-function pins for UART1 RXD(PB.2) and TXD(PB.3) */
  37.     SYS->GPB_MFPL = (SYS->GPB_MFPL & ~(SYS_GPB_MFPL_PB2MFP_Msk | SYS_GPB_MFPL_PB3MFP_Msk))    |       \
  38.                     (SYS_GPB_MFPL_PB2MFP_UART1_RXD | SYS_GPB_MFPL_PB3MFP_UART1_TXD);

  39.     /* Lock protected registers */
  40.     SYS_LockReg();
  41. }
在初始化时钟之前需要确认自己的外部晶振的频率,然后在system_M031Series.h文件的第38行修改宏定义。
3234460d96a5f8c9c2.png
在初始化时钟时会将需要的外设时钟一起初始化,这里初始化了UART0和UART1的时钟以及PDMA的时钟。

初始化UART
由于时钟已经配置,在初始化UART的配置时会显得特别简单。如果你的UART没有特殊要求,两行代码即可完成UART的初始化。
  1. void UART0_Init()
  2. {
  3.     /*---------------------------------------------------------------------------------------------------------*/
  4.     /* Init UART                                                                                               */
  5.     /*---------------------------------------------------------------------------------------------------------*/
  6.     /* Reset UART0 */
  7.     SYS_ResetModule(UART0_RST);

  8.     /* Configure UART0 and set UART0 baud rate */
  9.     UART_Open(UART0, 115200);
  10. }

  11. void UART1_Init()
  12. {
  13.     /*---------------------------------------------------------------------------------------------------------*/
  14.     /* Init UART                                                                                               */
  15.     /*---------------------------------------------------------------------------------------------------------*/
  16.     /* Reset UART1 */
  17.     SYS_ResetModule(UART1_RST);

  18.     /* Configure UART1 and set UART1 Baudrate */
  19.     UART_Open(UART1, 2500000);
  20.         
  21.                  /* Enable Interrupt and install the call back function */
  22.         NVIC_EnableIRQ(UART13_IRQn);
  23.     UART_EnableInt(UART1, UART_INTEN_RDAIEN_Msk);
  24.   
  25. }
串口0用于printf的调试。串口1 是我需要与其他串口设备通信的接口。除了我在串口1设置了串口接收中断以外,初始化一个串口仅仅需要两个函数,非常方便,这里使用的串口默认配置:一个停止位,无校验位,8位数据,如果需要修改可自行进入函数修改。
在配置串口1的接收中断时遇到了问题:调用NVIC_EnableIRQ()函数初始化中断线时,参数我填的是UART1_IRQn,无报错,编译可通过,但是测试没现象,于是进入debug页面,发现中断函数并未被编译。
3651260d96e4cc399e.png
尝试了很多方法都不能进行编译,后来去看UART1_IRQn的定义,发现这个宏定义下面还有一个UART13_IRQn。于是明白过来了,
UART0UART2共用一个中断函数UART02_IRQHandler(),
UART1和3共用中断函数UART13_IRQHandler()
这里区别于其他家的库,不能用UART1_IRQn,需要用UART13_IRQn。
1925060d96e8ed3398.png
PDMA配置

  1. void PDMA_UART_TxTest(void)
  2. {
  3.     /* UART Tx PDMA channel configuration */
  4.     /* Set transfer width (8 bits) and transfer count */
  5.     PDMA_SetTransferCnt(PDMA, UART_TX_DMA_CH, PDMA_WIDTH_8, UART_TEST_LENGTH);

  6.     /* Set source/destination address and attributes */
  7.     PDMA_SetTransferAddr(PDMA, UART_TX_DMA_CH, (uint32_t)SrcArray, PDMA_SAR_INC, (uint32_t)&UART1->DAT, PDMA_DAR_FIX);

  8.     /* Set request source; set basic mode. */
  9.     PDMA_SetTransferMode(PDMA, UART_TX_DMA_CH, PDMA_UART1_TX, FALSE, 0);

  10.     /* Single request type */
  11.     PDMA_SetBurstType(PDMA, UART_TX_DMA_CH, PDMA_REQ_SINGLE, 0);

  12.     /* Disable table interrupt */
  13.     PDMA_DisableInt(PDMA,UART_TX_DMA_CH, PDMA_INT_TEMPTY );
  14. }
这里有几个需要自行修改的地方,PDMA_SetTransferCnt(PDMA, UART_TX_DMA_CH, PDMA_WIDTH_8, UART_TEST_LENGTH);修改PDMA_WIDTH_8为修改数据宽度,这里默认8位,UART_TEST_LENGTH为发送长度,我这里设置为11个。PDMA_SetTransferAddr(PDMA, UART_TX_DMA_CH, (uint32_t)SrcArray, PDMA_SAR_INC, (uint32_t)&UART1->DAT, PDMA_DAR_FIX);  SrcArray为数组的地址。因为我是发送的DMA,这里配置为内存到外设,如果是接收DMA则做以下设置:    PDMA_SetTransferAddr(PDMA, UART_RX_DMA_CH, (uint32_t)&UART1->DAT, PDMA_SAR_FIX, (uint32_t)DestArray, PDMA_DAR_INC);


配置完后再主函数打开DMA:
  1. SYS_ResetModule(PDMA_RST);

  2.         PDMA_Open(PDMA, (1 << UART_TX_DMA_CH));


中断函数配置及启动DMA
  1. void UART13_IRQHandler(void)
  2. {
  3.                 uint8_t res;
  4. //        uint32_t u32IntSts = UART1->ISR;
  5.         PB1 = 0;

  6.         res = UART_READ(UART1);//读UART_DAT寄存器自动清除中断标志
  7.         if(res == 0x1A)
  8.         {
  9.                 UART_DISABLE_INT(UART1, UART_INTEN_TXPDMAEN_Msk);
  10.                 PDMA_UART_TxTest();
  11.                 UART_ENABLE_INT(UART1, UART_INTEN_TXPDMAEN_Msk);
  12.                 while (PDMA->DSCT[UART_TX_DMA_CH].CTL & PDMA_DSCT_CTL_TXCNT_Msk) ;
  13.         
  14.         }
  15.         PB1 = 1;
  16. }
在M031中,区别于我之前用过的其他MCU,在进入中断函数之后,只要读取串口接收寄存器UART_DAT中的值,便可自动清除中断标志,并不需要去操作其他寄存器。非常好用。
DMA的启动和其他的MCU类似,需重新配置传输个数,RAM地址等,再调用一次初始化函数就行。然后利用while (PDMA->DSCT[UART_TX_DMA_CH].CTL & PDMA_DSCT_CTL_TXCNT_Msk) ;判断数据是否发送完成,实际上就是等待传输个数计数器为0。
这里插一句PB1的作用,PBI就是gpio PB1口。初始化就一句话GPIO_SetMode(PB, BIT1, GPIO_MODE_OUTPUT);这里方便示波器观察时间。在使用库函数初始化PWM时,因为每一次的启动都要调用该函数,库函数的操作很费时间,在触发串口接收中断后将PB1拉低,发送完拉高,在示波器观察到从触发接收中断到第一个串口数据发送出去,也就是DMA启动完成,大约耗时8us,效率低下。于是我将DMA初始化改用寄存器的方式,时间缩小到2.8us,好用!
  1. void PDMA_UART_TxTest(void)
  2. {
  3.     /* UART Tx PDMA channel configuration */
  4.     PDMA->DSCT[UART_TX_DMA_CH].CTL =
  5.     (UART_TEST_LENGTH - 1) << PDMA_DSCT_CTL_TXCNT_Pos | /* Transfer count */
  6.         PDMA_WIDTH_8 |  /* Transfer width 8 bits */
  7.         PDMA_DAR_FIX  | /* Fixed destination address */
  8.         PDMA_SAR_INC  | /* Increment source address */
  9.         PDMA_DSCT_CTL_TBINTDIS_Msk  | /* Table interrupt disabled */
  10.         PDMA_REQ_SINGLE  | /* Single request type */
  11.         PDMA_OP_BASIC;     /* Basic mode */
  12.     PDMA->DSCT[UART_TX_DMA_CH].SA = (uint32_t)SrcArray;     /* Source address */
  13.     PDMA->DSCT[UART_TX_DMA_CH].DA = (uint32_t)&UART1->DAT;  /* Destination address */

  14.     /* Request source selection */
  15.     PDMA->REQSEL0_3 = (PDMA->REQSEL0_3 & (~PDMA_REQSEL0_3_REQSRC1_Msk)) | (PDMA_UART1_TX << PDMA_REQSEL0_3_REQSRC1_Pos);
  16. }











打赏榜单

21小跑堂 打赏了 120.00 元 2021-06-29
理由:恭喜通过原创奖文章审核!请多多加油哦!

weiwei4dk 发表于 2021-6-30 08:16 | 显示全部楼层
新唐单片机上手还是很容易的
 楼主| 呐咯密密 发表于 2021-6-30 09:20 | 显示全部楼层
weiwei4dk 发表于 2021-6-30 08:16
新唐单片机上手还是很容易的

真的太好上手了,超简单
minzisc 发表于 2021-7-1 21:48 | 显示全部楼层
                 
caigang13 发表于 2021-7-3 08:08 来自手机 | 显示全部楼层
新唐使用体验上还是不错的
两只袜子 发表于 2021-7-3 15:44 来自手机 | 显示全部楼层
国产之光
li880wert 发表于 2021-7-7 21:13 | 显示全部楼层
这玩意一直缺货吧
 楼主| 呐咯密密 发表于 2021-7-8 10:31 | 显示全部楼层
li880wert 发表于 2021-7-7 21:13
这玩意一直缺货吧

这玩意还真有货,交期两三个月吧
koala889 发表于 2021-7-8 20:08 | 显示全部楼层
呐咯密密 发表于 2021-7-8 10:31
这玩意还真有货,交期两三个月吧

干的漂亮~~O(∩_∩)O哈哈~
kiwis66 发表于 2021-7-9 09:34 | 显示全部楼层
楼主各类主控可以为所欲为了,经历也是一种财富啊
guiguihhh 发表于 2022-4-24 17:12 | 显示全部楼层
您好,我这边关于新唐串口DMA遇到一个问题,
比如说我想设置串口DMA接收长度为10个字节,然后设置超时中断,从接收开始后一定时间接收不到10字节进超时中断处理,但现在没进去中断,比较疑惑,有什么需要注意的点吗
 楼主| 呐咯密密 发表于 2022-4-24 17:30 | 显示全部楼层
guiguihhh 发表于 2022-4-24 17:12
您好,我这边关于新唐串口DMA遇到一个问题,
比如说我想设置串口DMA接收长度为10个字节,然后设置超时中断 ...

我没用过超时中断,这边没法给你提供帮助
kiwis66 发表于 2022-6-12 14:00 | 显示全部楼层
又回过来看看,感觉不错
selongli 发表于 2022-6-20 16:47 | 显示全部楼层
PDMA?   
 楼主| 呐咯密密 发表于 2022-6-20 17:08 | 显示全部楼层

理解为DMA。新塘是这样命名的
plsbackup 发表于 2022-6-20 21:44 | 显示全部楼层
现在串口中断还行 。   
guijial511 发表于 2022-6-21 08:30 来自手机 | 显示全部楼层
这就是先批评后表扬
soodesyt 发表于 2022-6-22 20:08 | 显示全部楼层
产品性能跟上了。   
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:苏州澜宭自动化科技嵌入式工程师
简介:本人从事磁编码器研发工作,负责开发2500线增量式磁编码器以及17位、23位绝对值式磁编码器,拥有多年嵌入式开发经验,精通STM32、GD32、N32等多种品牌单片机,熟练使用单片机各种外设。

567

主题

4081

帖子

56

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