[DemoCode下载] 在省电模式下 LPUART 接收不定长度数据

[复制链接]
1108|7
 楼主| wanduzi 发表于 2025-3-29 11:04 | 显示全部楼层 |阅读模式
en-us--EC_M2L31_LPUART_RX_in_PD_V1.01.zip (1.45 MB, 下载次数: 1)
,通过 LPUART 接收不定长度的数据后,再唤醒 MCU 进行数据处理。实现 M2L31 低功耗 LPUART 运行特征。


LPUART0 设置为自动操作模式会在省电模式下接收数据;同时启动 LPUART RX 触发LPPDMA,当 LPUART 收到数据后会触发 LPPDMA 将数据搬运到 LPSRAM;启动了总线空闲超时唤醒功能,当开始接收第一个数据时超时计数器开始计数,收到新数据会重置计数器,计数器超时后会唤醒 MCU,利用这个功能可以实现接收不定长度的数据后唤醒系统。


  1. /**************************************************************************************************
  2. * [url=home.php?mod=space&uid=288409]@file[/url]     main.c
  3. * [url=home.php?mod=space&uid=895143]@version[/url]  V1.00
  4. * [url=home.php?mod=space&uid=247401]@brief[/url]    Demonstrate LPUART receives data of variable length in power-down mode, and then
  5. *           wakes up the MCU for data processing. Implement the operating characteristics of
  6. *           M2L31 low power LPUART.
  7. *
  8. * SPDX-License-Identifier: Apache-2.0
  9. * [url=home.php?mod=space&uid=17282]@CopyRight[/url] (C) 2023 Nuvoton Technology Corp. All rights reserved.
  10. ***************************************************************************************************/
  11. #include <stdio.h>
  12. #include "NuMicro.h"

  13. /*------------------------------------------------------------------------------------------------*/
  14. /* Define                                                                                         */
  15. /*------------------------------------------------------------------------------------------------*/
  16. #define LPUART_RX_LPPDMA_CH  0                        /* LPPDMA channel for LPUART RX */
  17. #define RXBUFSIZE            1024                     /* Buffer size */
  18. #define POWER_DOWN_MODE      CLK_PMUCTL_PDMSEL_NPD4   /* Power-down mode */
  19. #define LPPDMA_GET_TRANS_CNT(lppdma,u32Ch) ((uint32_t)((lppdma->LPDSCT[(u32Ch)].\
  20.                              CTL&LPPDMA_DSCT_CTL_TXCNT_Msk) >> \
  21.                              LPPDMA_DSCT_CTL_TXCNT_Pos)) /* Read the remaining number of transfers */
  22. #define GPIO_P0_TO_P15       0xFFFF

  23. /*------------------------------------------------------------------------------------------------*/
  24. /* Global variables                                                                               */
  25. /*------------------------------------------------------------------------------------------------*/
  26. #if (defined(__GNUC__) && !defined(__ARMCC_VERSION))
  27.     uint8_t g_u8RecData[RXBUFSIZE] __attribute__((section(".lpSram")));
  28. #else
  29.     uint8_t g_u8RecData[RXBUFSIZE] __attribute__((section(".ARM.__at_0x28000000")));
  30. #endif
  31. uint32_t g_u32cnt = 0; /* Receive counter */

  32. /*------------------------------------------------------------------------------------------------*/
  33. /* Clear Buffer function                                                                          */
  34. /*------------------------------------------------------------------------------------------------*/
  35. void ClearBuf(uint32_t u32Addr, uint32_t u32Length, uint8_t u8Pattern)
  36. {
  37.     uint8_t *pu8Ptr;       
  38.     uint32_t i;

  39.     pu8Ptr = (uint8_t *)u32Addr;

  40.     for (i = 0; i < u32Length; i++)
  41.     {
  42.         *pu8Ptr++ = u8Pattern;
  43.     }
  44. }

  45. /*------------------------------------------------------------------------------------------------*/
  46. /* LPPDMA Interrupt Handler                                                                       */
  47. /*------------------------------------------------------------------------------------------------*/
  48. void LPPDMA0_IRQHandler(void)
  49. {
  50.     volatile uint32_t u32regISR;

  51.     u32regISR = LPPDMA0->INTSTS;

  52.     if ((u32regISR & LPPDMA_INTSTS_TDIF_Msk) == LPPDMA_INTSTS_TDIF_Msk) /* transfer done */
  53.     {
  54.         /* Record the number of data */
  55.         g_u32cnt = RXBUFSIZE;

  56.         printf("Warning:The received data is greater than or equal to the buffer size!!!\n");
  57.     }
  58.    
  59.     /* Clear interrupt flag */
  60.     LPPDMA0->TDSTS = LPPDMA0->TDSTS;
  61. }

  62. /*------------------------------------------------------------------------------------------------*/
  63. /* Init LPPDMA                                                                                    */
  64. /*------------------------------------------------------------------------------------------------*/
  65. void LPPDMA_RX_Init(void)
  66. {
  67.     /*----------------------------------------------------------------------------------------------
  68.             LPPDMA transfer configuration:

  69.             Channel = 0
  70.             Operation mode = basic mode
  71.             Request source = LPUART0 RX(peripheral to memory)
  72.             transfer done interrupt = enable

  73.             Transfer count = RXBUFSIZE
  74.             Transfer width = 8 bits(one byte)
  75.             Source address = LPUART0->DAT
  76.             Source address increment size = 0(fixed)
  77.             Destination address = g_u8RecData
  78.             Destination address increment size = 8 bits(one byte)
  79.             Transfer type = Single transfer

  80.             Total transfer length = RXBUFSIZE * 8 bits
  81.         ------------------------------------------------------------------------------------------*/
  82.     /* Open Channel 0 */
  83.     LPPDMA_Open(LPPDMA0, 1 << LPUART_RX_LPPDMA_CH);

  84.     /* Transfer count is RXBUFSIZE, transfer width is 8 bits(one byte) */
  85.     LPPDMA_SetTransferCnt(LPPDMA0, LPUART_RX_LPPDMA_CH, LPPDMA_WIDTH_8, RXBUFSIZE);

  86.     /* Set address */
  87.     LPPDMA_SetTransferAddr(LPPDMA0,
  88.                            LPUART_RX_LPPDMA_CH,
  89.                            (uint32_t) & (LPUART0->DAT), /* Source address */
  90.                            LPPDMA_SAR_FIX,              /* Source address fixed */
  91.                            (uint32_t)g_u8RecData,       /* Destination address */
  92.                            LPPDMA_DAR_INC);             /* Destination address incremented, size = 1byte */

  93.     /* Request source is LPUART0 RX to memory */
  94.     LPPDMA_SetTransferMode(LPPDMA0, LPUART_RX_LPPDMA_CH, LPPDMA_LPUART0_RX, FALSE, 0);

  95.     /* Transfer type is Single transfer */
  96.     LPPDMA_SetBurstType(LPPDMA0, LPUART_RX_LPPDMA_CH, LPPDMA_REQ_SINGLE, 1);

  97.     /* Enable transfer done interrupt */
  98.     LPPDMA_EnableInt(LPPDMA0, LPUART_RX_LPPDMA_CH, LPPDMA_INT_TRANS_DONE);

  99.     /* Enable LPPDMA0 IRQ */
  100.     NVIC_EnableIRQ(LPPDMA0_IRQn);
  101. }

  102. /*------------------------------------------------------------------------------------------------*/
  103. /* LPUART0 interrupt Handler                                                                      */
  104. /*------------------------------------------------------------------------------------------------*/
  105. void LPUART0_IRQHandler(void)
  106. {
  107.     if (LPUART_GET_INT_FLAG(LPUART0, LPUART_INTSTS_HWTOINT_Msk))
  108.     {
  109.         /* Record the number of data */
  110.         g_u32cnt = RXBUFSIZE - LPPDMA_GET_TRANS_CNT(LPPDMA0, LPUART_RX_LPPDMA_CH) - 1;
  111.     }

  112.     /* Clear interrupt flag */
  113.     LPUART0->INTSTS = LPUART0->INTSTS;
  114. }

  115. /*------------------------------------------------------------------------------------------------*/
  116. /* Init LPUART0                                                                                   */
  117. /*------------------------------------------------------------------------------------------------*/
  118. void LPUART0_Init(void)
  119. {
  120.     /* Reset Low Power UART0 */
  121.     SYS_ResetModule(LPUART0_RST);

  122.     /* Configure Low Power UART0 and set Low Power UART0 Baudrate */
  123.     LPUART_Open(LPUART0, 9600);

  124.     //    /* Open this statement. If you want to keep LPUART clock at power-down mode NPD3/NDP4.
  125.     //       To ensure accuracy, if the baud rate is set to 115200, it needs to be opened
  126.     //    */
  127.     //    LPSCC->CLKKEEP0 |= LPSCC_CLKKEEP0_LPUART0KEEP_Msk;

  128.     /* Set Automatic Operation Enable */
  129.     LPUART0->AUTOCTL |= LPUART_AUTOCTL_AOEN_Msk;

  130.     /* Enable LPUART PDMA RX */
  131.     LPUART0->INTEN |= LPUART_INTEN_RXPDMAEN_Msk;

  132.     /* Enable bus idle time-out(Mode 1) and set time-out counter */
  133.     LPUART0->TOUT =  255 | LPUART_TOUT_BITOMEN_Msk;

  134.     /* Enable time-out counter */
  135.     LPUART0->INTEN |= LPUART_INTEN_TOCNTEN_Msk;

  136.     /* Enable Bus Idle Time-out Wake-up */
  137.     LPUART0->AUTOCTL |= LPUART_AUTOCTL_WKAOTOEN_Msk;

  138.     /* Enable RX Time-out interrupt */
  139.     LPUART_EnableInt(LPUART0, LPUART_INTEN_RXTOIEN_Msk);

  140.     /* Enable LPUART0 IRQ */
  141.     NVIC_EnableIRQ(LPUART0_IRQn);
  142. }

  143. /*------------------------------------------------------------------------------------------------*/
  144. /* Stop LPPDMA and LPUART bus idle timeout count                                                  */
  145. /*------------------------------------------------------------------------------------------------*/
  146. void Transfer_Stop(void)
  147. {
  148.     /* Disable LPUART PDMA RX  and time-out counter */
  149.     LPUART0->INTEN &= ~(LPUART_INTEN_RXPDMAEN_Msk | LPUART_INTEN_TOCNTEN_Msk);

  150.     /* Stop LPPDMA */
  151.     LPPDMA0->LPDSCT[1].CTL &= ~LPPDMA_DSCT_CTL_OPMODE_Msk;
  152. }

  153. /*------------------------------------------------------------------------------------------------*/
  154. /* Restart LPPDMA and LPUART                                                                      */
  155. /*------------------------------------------------------------------------------------------------*/
  156. void Transfer_Recfg(void)
  157. {
  158.     ClearBuf((uint32_t)g_u8RecData, RXBUFSIZE, 0xFF);
  159.    
  160.     g_u32cnt = 0;

  161.     /* Restart LPPDMA channel */
  162.     /* Transfer count is RXBUFSIZE, transfer width is 8 bits(one byte) */
  163.     LPPDMA_SetTransferCnt(LPPDMA0, LPUART_RX_LPPDMA_CH, LPPDMA_WIDTH_8, RXBUFSIZE);

  164.     /* Request source is LPUART RX to memory */
  165.     LPPDMA_SetTransferMode(LPPDMA0, LPUART_RX_LPPDMA_CH, LPPDMA_LPUART0_RX, FALSE, 0);

  166.     /* Restart LPUART */
  167.     LPUART0_Init();
  168. }

  169. /*------------------------------------------------------------------------------------------------*/
  170. /* Init GPIO                                                                                      */
  171. /*------------------------------------------------------------------------------------------------*/
  172. void GPIO_Init(void)
  173. {
  174.     /* Enable GPIO clock */
  175.     CLK_EnableModuleClock(GPA_MODULE);
  176.     CLK_EnableModuleClock(GPB_MODULE);
  177.     CLK_EnableModuleClock(GPC_MODULE);
  178.     CLK_EnableModuleClock(GPD_MODULE);
  179.     CLK_EnableModuleClock(GPE_MODULE);
  180.     CLK_EnableModuleClock(GPF_MODULE);
  181.     CLK_EnableModuleClock(GPG_MODULE);
  182.     CLK_EnableModuleClock(GPH_MODULE);

  183.     /* Configure all GPIO mode*/
  184.     GPIO_SetMode(PA, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
  185.     GPIO_SetMode(PB, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
  186.     GPIO_SetMode(PC, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
  187.     GPIO_SetMode(PD, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
  188.     GPIO_SetMode(PE, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
  189.     GPIO_SetMode(PF, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
  190.     GPIO_SetMode(PG, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
  191.     GPIO_SetMode(PH, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);

  192.     /* Set all GPIO output high, except for special function pins*/
  193.     PA->DOUT = 0xEFFF; /* USB_VBUSP need output low, otherwise LDO will be backfilled */
  194.     PB->DOUT = 0x7FFF; /* PB15_VBUS_SEN need output low, otherwise there will be leakage*/
  195.     PC->DOUT = 0xFFF0; /* PC0~3 is CCDB, basically 1M ohm register always being */
  196.     PD->DOUT = 0xFFFF;
  197.     PE->DOUT = 0xFFFF;
  198.     PF->DOUT = 0xFFFF;
  199.     PG->DOUT = 0xFFFF;
  200.     PH->DOUT = 0xFFFF;

  201.     /* Disable GPIO clock */
  202.     CLK_DisableModuleClock(GPA_MODULE);
  203.     CLK_DisableModuleClock(GPB_MODULE);
  204.     CLK_DisableModuleClock(GPC_MODULE);
  205.     CLK_DisableModuleClock(GPD_MODULE);
  206.     CLK_DisableModuleClock(GPE_MODULE);
  207.     CLK_DisableModuleClock(GPF_MODULE);
  208.     CLK_DisableModuleClock(GPG_MODULE);
  209.     CLK_DisableModuleClock(GPH_MODULE);
  210. }

  211. /*------------------------------------------------------------------------------------------------*/
  212. /* Enter NPD4 mode                                                                                */
  213. /*------------------------------------------------------------------------------------------------*/
  214. void PowerDown_Enter(void)
  215. {
  216.     /* Set Power-down mode */
  217.     SYS_UnlockReg();

  218.     /* Set SRAM retention range. */
  219.     CLK->PMUCTL &= ~(CLK_PMUCTL_SRETSEL_Msk);

  220.     /* 8K SRAM retention when chip enter NPD3/SPDx mode. */
  221.     CLK->PMUCTL |= (CLK_SPDSRETSEL_8K);

  222.     /* Select Power-down mode */
  223.     CLK_SetPowerDownMode(POWER_DOWN_MODE);

  224.     /* BOD must be disabled */
  225.     SYS_DISABLE_BOD();

  226.     /* Disable Power-on Reset */
  227.     SYS_DISABLE_POR();

  228.     /* Clear all wakeup flag */
  229.     CLK->PMUSTS |= CLK_PMUSTS_CLRWK_Msk;

  230.     /* Clear LPPDMA channel transfer done flag */
  231.     LPPDMA0->TDSTS = LPPDMA0->TDSTS;

  232.     /* Clear LPUART interrupt flag */
  233.     LPUART0->INTSTS =  LPUART0->INTSTS;

  234.     printf("System enter to Power-down mode NPD%d.\n", (int)(CLK->PMUCTL & CLK_PMUCTL_PDMSEL_Msk));

  235.     /* Check if all the debug messages are finished */
  236.     UART_WAIT_TX_EMPTY(UART0);

  237.     /* Enter Power-down mode */
  238.     CLK_PowerDown();
  239. }

  240. /*------------------------------------------------------------------------------------------------*/
  241. /* Init clock and pins                                                                            */
  242. /*------------------------------------------------------------------------------------------------*/
  243. void SYS_Init(void)
  244. {
  245.     /* Unlock protected registers */
  246.     SYS_UnlockReg();

  247.     /* Enable External XTAL (4~24 MHz) */
  248.     CLK_EnableXtalRC(CLK_PWRCTL_HIRCEN_Msk);

  249.     /* Switch HCLK clock source to HIRC */
  250.     CLK->CLKSEL0 = (CLK->CLKSEL0 & ~CLK_CLKSEL0_HCLK0SEL_Msk) | CLK_CLKSEL0_HCLKSEL_HIRC48M ;
  251.    
  252.     /* Switch HCLK1 clock source to MIRC */
  253.     CLK->CLKSEL0 = (CLK->CLKSEL0 & ~CLK_CLKSEL0_HCLK1SEL_Msk) | CLK_CLKSEL0_HCLK1SEL_MIRC ;

  254.     /* Select UART0 clock source is HIRC and UART module clock divider as 1 */
  255.     CLK_SetModuleClock(UART0_MODULE, CLK_CLKSEL4_UART0SEL_HIRC, CLK_CLKDIV0_UART0(1));

  256.     /* Select LPUART0 clock source is MIRC and LPUART module clock divider as 1*/
  257.     CLK_SetModuleClock(LPUART0_MODULE, LPSCC_CLKSEL0_LPUART0SEL_MIRC, LPSCC_CLKDIV0_LPUART0(1));

  258.     /* Enable UART0 peripheral clock */
  259.     CLK_EnableModuleClock(UART0_MODULE);

  260.     /* Enable LPUART0 peripheral clock */
  261.     CLK_EnableModuleClock(LPUART0_MODULE);

  262.     /* Enable LPSRAM clock */
  263.     CLK_EnableModuleClock(LPSRAM_MODULE);

  264.     /* LPPDMA Clock Enable */
  265.     CLK_EnableModuleClock(LPPDMA0_MODULE);

  266.     /* Set GPIO mode for unused pins */
  267.     GPIO_Init();

  268.     /* Set GPB multi-function pins for UART0 RXD and TXD */
  269.     Uart0DefaultMPF();

  270.     /* Set PA multi-function pins for LPUART0 TXD and RXD */
  271.     SYS->GPA_MFP0 = (SYS->GPA_MFP0 & ~(SYS_GPA_MFP0_PA0MFP_Msk | SYS_GPA_MFP0_PA1MFP_Msk)) | \
  272.                     (SYS_GPA_MFP0_PA0MFP_LPUART0_RXD | SYS_GPA_MFP0_PA1MFP_LPUART0_TXD);

  273.     /* Lock protected registers */
  274.     SYS_LockReg();
  275. }

  276. /*------------------------------------------------------------------------------------------------*/
  277. /* Init  PUART0                                                                                   */
  278. /*------------------------------------------------------------------------------------------------*/
  279. void UART_Init()
  280. {
  281.     /* Reset UART0 */
  282.     SYS_ResetModule(UART0_RST);

  283.     /* Configure UART0 and set UART0 baud rate */
  284.     UART_Open(UART0, 115200);
  285. }

  286. int main(void)
  287. {
  288.     uint32_t i, count = 0;

  289.     /* Init System, peripheral clock and multi-function I/O */
  290.     SYS_Init();

  291.     /* Init UART0 for printf */
  292.     UART_Init();

  293.     /*--------------------------------------------------------------------------------------------*/
  294.     /* LPUART Receives Variable Length Data in Power-down Mode Test                               */
  295.     /* 1. LPUART uses LPPDMA Channel-0 to transfer RX data to g_u8RecData                         */
  296.     /* 2. LPUART RX pin (PA.0) is connected to UART Tool                                          */
  297.     /* 3. System enters power-down mode and UART RX receives data in power-down mode              */
  298.     /* 4. When RX transfer done or timed out and wake-up system                                   */
  299.     /* 5. Print the received data through uart0                                                   */
  300.     /* 6. Repeat steps 3~5                                                                        */
  301.     /*--------------------------------------------------------------------------------------------*/
  302.     printf("+-------------------------------------------------------------------------------+\n");
  303.     printf("|     M2L31 LPUART receive under Power-Down mode Sample Code                    |\n");
  304.     printf("+-------------------------------------------------------------------------------+\n\n");
  305.     printf("  >> Please connect PA.0 to PC by UART tool. << \n");
  306.     printf("     Press any key to start test\n\n");

  307.     getchar();

  308.     /* Clear buffer */
  309.     ClearBuf((uint32_t)g_u8RecData, RXBUFSIZE, 0xFF);

  310.     /* LPPDMA CH-0 basic Mode RX to receive data */
  311.     LPPDMA_RX_Init();

  312.     /* Init LPUART0 as Auto-operation Mode for test */
  313.     LPUART0_Init();

  314.     while (1)
  315.     {
  316.         /* Reconfigure transmission  */
  317.         Transfer_Recfg();

  318.         count++;

  319.         /* Enter power down mode */
  320.         PowerDown_Enter();

  321.         printf("\nWakeup %d\n", count);

  322.         /* Wait for the number of data to be recorded  */
  323.         while (g_u32cnt == 0);

  324.         Transfer_Stop();

  325.         /* Add data processing code */
  326.         printf("Received Data(%d):\n", g_u32cnt);

  327.         for (i = 0; i < g_u32cnt; i++)
  328.             printf("%c", g_u8RecData[i]);

  329.         printf("\n\n");
  330.     }
  331. }
  332. /*** (C) COPYRIGHT 2023 Nuvoton Technology Corp. ***/


小明的同学 发表于 2025-3-29 11:54 | 显示全部楼层
这个特性在低功耗上是太好了。
zhuotuzi 发表于 2025-3-29 20:43 | 显示全部楼层
这个实现的原理是什么啊
绝影孤狼 发表于 2025-3-29 20:45 | 显示全部楼层
在省电模式下唤醒MCU后,是不是可以直接处理接收到的数据,还是需要额外的同步操作
破晓战神 发表于 2025-3-30 10:05 | 显示全部楼层
总线空闲超时唤醒功能的计时器超时时间是可以设置的吗,还是固定的
瞌睡虫本虫 发表于 2025-3-30 14:23 | 显示全部楼层
这个功能很实用呀,之前我还在为怎么在省电模式下接收数据发愁呢
暗夜幽灵骑士 发表于 2025-3-30 16:33 | 显示全部楼层
LPUART0的自动操作模式是不是在所有新唐MCU型号里都支持呢
逆鳞风暴 发表于 2025-3-30 23:33 | 显示全部楼层
在省电模式下接收数据,是不是对数据的传输速率有要求
您需要登录后才可以回帖 登录 | 注册

本版积分规则

156

主题

1882

帖子

3

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