打印
[DemoCode下载]

在省电模式下 LPUART 接收不定长度数据

[复制链接]
214|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wanduzi|  楼主 | 2025-3-29 11:04 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
en-us--EC_M2L31_LPUART_RX_in_PD_V1.01.zip (1.45 MB)
,通过 LPUART 接收不定长度的数据后,再唤醒 MCU 进行数据处理。实现 M2L31 低功耗 LPUART 运行特征。


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


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

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

/*------------------------------------------------------------------------------------------------*/
/* Global variables                                                                               */
/*------------------------------------------------------------------------------------------------*/
#if (defined(__GNUC__) && !defined(__ARMCC_VERSION))
    uint8_t g_u8RecData[RXBUFSIZE] __attribute__((section(".lpSram")));
#else
    uint8_t g_u8RecData[RXBUFSIZE] __attribute__((section(".ARM.__at_0x28000000")));
#endif
uint32_t g_u32cnt = 0; /* Receive counter */

/*------------------------------------------------------------------------------------------------*/
/* Clear Buffer function                                                                          */
/*------------------------------------------------------------------------------------------------*/
void ClearBuf(uint32_t u32Addr, uint32_t u32Length, uint8_t u8Pattern)
{
    uint8_t *pu8Ptr;       
    uint32_t i;

    pu8Ptr = (uint8_t *)u32Addr;

    for (i = 0; i < u32Length; i++)
    {
        *pu8Ptr++ = u8Pattern;
    }
}

/*------------------------------------------------------------------------------------------------*/
/* LPPDMA Interrupt Handler                                                                       */
/*------------------------------------------------------------------------------------------------*/
void LPPDMA0_IRQHandler(void)
{
    volatile uint32_t u32regISR;

    u32regISR = LPPDMA0->INTSTS;

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

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

/*------------------------------------------------------------------------------------------------*/
/* Init LPPDMA                                                                                    */
/*------------------------------------------------------------------------------------------------*/
void LPPDMA_RX_Init(void)
{
    /*----------------------------------------------------------------------------------------------
            LPPDMA transfer configuration:

            Channel = 0
            Operation mode = basic mode
            Request source = LPUART0 RX(peripheral to memory)
            transfer done interrupt = enable

            Transfer count = RXBUFSIZE
            Transfer width = 8 bits(one byte)
            Source address = LPUART0->DAT
            Source address increment size = 0(fixed)
            Destination address = g_u8RecData
            Destination address increment size = 8 bits(one byte)
            Transfer type = Single transfer

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

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

    /* Set address */
    LPPDMA_SetTransferAddr(LPPDMA0,
                           LPUART_RX_LPPDMA_CH,
                           (uint32_t) & (LPUART0->DAT), /* Source address */
                           LPPDMA_SAR_FIX,              /* Source address fixed */
                           (uint32_t)g_u8RecData,       /* Destination address */
                           LPPDMA_DAR_INC);             /* Destination address incremented, size = 1byte */

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

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

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

    /* Enable LPPDMA0 IRQ */
    NVIC_EnableIRQ(LPPDMA0_IRQn);
}

/*------------------------------------------------------------------------------------------------*/
/* LPUART0 interrupt Handler                                                                      */
/*------------------------------------------------------------------------------------------------*/
void LPUART0_IRQHandler(void)
{
    if (LPUART_GET_INT_FLAG(LPUART0, LPUART_INTSTS_HWTOINT_Msk))
    {
        /* Record the number of data */
        g_u32cnt = RXBUFSIZE - LPPDMA_GET_TRANS_CNT(LPPDMA0, LPUART_RX_LPPDMA_CH) - 1;
    }

    /* Clear interrupt flag */
    LPUART0->INTSTS = LPUART0->INTSTS;
}

/*------------------------------------------------------------------------------------------------*/
/* Init LPUART0                                                                                   */
/*------------------------------------------------------------------------------------------------*/
void LPUART0_Init(void)
{
    /* Reset Low Power UART0 */
    SYS_ResetModule(LPUART0_RST);

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

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

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

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

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

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

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

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

    /* Enable LPUART0 IRQ */
    NVIC_EnableIRQ(LPUART0_IRQn);
}

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

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

/*------------------------------------------------------------------------------------------------*/
/* Restart LPPDMA and LPUART                                                                      */
/*------------------------------------------------------------------------------------------------*/
void Transfer_Recfg(void)
{
    ClearBuf((uint32_t)g_u8RecData, RXBUFSIZE, 0xFF);
   
    g_u32cnt = 0;

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

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

    /* Restart LPUART */
    LPUART0_Init();
}

/*------------------------------------------------------------------------------------------------*/
/* Init GPIO                                                                                      */
/*------------------------------------------------------------------------------------------------*/
void GPIO_Init(void)
{
    /* Enable GPIO clock */
    CLK_EnableModuleClock(GPA_MODULE);
    CLK_EnableModuleClock(GPB_MODULE);
    CLK_EnableModuleClock(GPC_MODULE);
    CLK_EnableModuleClock(GPD_MODULE);
    CLK_EnableModuleClock(GPE_MODULE);
    CLK_EnableModuleClock(GPF_MODULE);
    CLK_EnableModuleClock(GPG_MODULE);
    CLK_EnableModuleClock(GPH_MODULE);

    /* Configure all GPIO mode*/
    GPIO_SetMode(PA, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PB, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PC, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PD, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PE, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PF, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PG, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PH, GPIO_P0_TO_P15, GPIO_MODE_OUTPUT);

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

    /* Disable GPIO clock */
    CLK_DisableModuleClock(GPA_MODULE);
    CLK_DisableModuleClock(GPB_MODULE);
    CLK_DisableModuleClock(GPC_MODULE);
    CLK_DisableModuleClock(GPD_MODULE);
    CLK_DisableModuleClock(GPE_MODULE);
    CLK_DisableModuleClock(GPF_MODULE);
    CLK_DisableModuleClock(GPG_MODULE);
    CLK_DisableModuleClock(GPH_MODULE);
}

/*------------------------------------------------------------------------------------------------*/
/* Enter NPD4 mode                                                                                */
/*------------------------------------------------------------------------------------------------*/
void PowerDown_Enter(void)
{
    /* Set Power-down mode */
    SYS_UnlockReg();

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

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

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

    /* BOD must be disabled */
    SYS_DISABLE_BOD();

    /* Disable Power-on Reset */
    SYS_DISABLE_POR();

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

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

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

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

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

    /* Enter Power-down mode */
    CLK_PowerDown();
}

/*------------------------------------------------------------------------------------------------*/
/* Init clock and pins                                                                            */
/*------------------------------------------------------------------------------------------------*/
void SYS_Init(void)
{
    /* Unlock protected registers */
    SYS_UnlockReg();

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

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

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

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

    /* Enable UART0 peripheral clock */
    CLK_EnableModuleClock(UART0_MODULE);

    /* Enable LPUART0 peripheral clock */
    CLK_EnableModuleClock(LPUART0_MODULE);

    /* Enable LPSRAM clock */
    CLK_EnableModuleClock(LPSRAM_MODULE);

    /* LPPDMA Clock Enable */
    CLK_EnableModuleClock(LPPDMA0_MODULE);

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

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

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

    /* Lock protected registers */
    SYS_LockReg();
}

/*------------------------------------------------------------------------------------------------*/
/* Init  PUART0                                                                                   */
/*------------------------------------------------------------------------------------------------*/
void UART_Init()
{
    /* Reset UART0 */
    SYS_ResetModule(UART0_RST);

    /* Configure UART0 and set UART0 baud rate */
    UART_Open(UART0, 115200);
}

int main(void)
{
    uint32_t i, count = 0;

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

    /* Init UART0 for printf */
    UART_Init();

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

    getchar();

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

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

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

    while (1)
    {
        /* Reconfigure transmission  */
        Transfer_Recfg();

        count++;

        /* Enter power down mode */
        PowerDown_Enter();

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

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

        Transfer_Stop();

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

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

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


使用特权

评论回复
沙发
小明的同学| | 2025-3-29 11:54 | 只看该作者
这个特性在低功耗上是太好了。

使用特权

评论回复
板凳
zhuotuzi| | 2025-3-29 20:43 | 只看该作者
这个实现的原理是什么啊

使用特权

评论回复
地板
绝影孤狼| | 2025-3-29 20:45 | 只看该作者
在省电模式下唤醒MCU后,是不是可以直接处理接收到的数据,还是需要额外的同步操作

使用特权

评论回复
5
破晓战神| | 2025-3-30 10:05 | 只看该作者
总线空闲超时唤醒功能的计时器超时时间是可以设置的吗,还是固定的

使用特权

评论回复
6
瞌睡虫本虫| | 2025-3-30 14:23 | 只看该作者
这个功能很实用呀,之前我还在为怎么在省电模式下接收数据发愁呢

使用特权

评论回复
7
暗夜幽灵骑士| | 2025-3-30 16:33 | 只看该作者
LPUART0的自动操作模式是不是在所有新唐MCU型号里都支持呢

使用特权

评论回复
8
逆鳞风暴| | 2025-3-30 23:33 | 只看该作者
在省电模式下接收数据,是不是对数据的传输速率有要求

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

151

主题

1830

帖子

3

粉丝