【STM32H7教程】第66章 STM32H7的低功耗串口LPUART應用之串口FIFO和停機喚醒實現


完整教程下載地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

第66章       STM32H7的低功耗串口LPUART應用之串口FIFO和停機喚醒實現

本章節為大家講解STM32H7的低功耗串口FIFO驅動實現和停機喚醒。

66.1 初學者重要提示

66.2 硬件設計

66.3 串口驅動設計

66.4 串口FIFO板級支持包(bsp_lpuart_fifo.c)

66.5 串口FIFO驅動移植和使用

66.6 實驗例程設計框架

66.7 實驗例程說明(MDK)

66.8 實驗例程說明(IAR)

66.9 總結

 

 

66.1 初學者重要提示

  1.   學習本章節前,務必優先學習第65章。
  2.   低功耗串口FIFO的實現跟前面章節通用串口FIFO的機制是一樣的。
  3.   大家自己做的板子,測試串口收發是亂碼的話,重點看stm32h7xx_hal_conf.h文件中的HSE_VALUE的大小跟板子上實際晶振大小是否一致,然后再看PLL配置。

66.2 硬件設計

STM32H743XIH6最多可以支持8個獨立的通用串口和一個低功耗串口LPUART1。其中串口4和串口5和SDIO的GPIO是共用的,也就是說,如果要用到SD卡,那么串口4和串口5將不能使用。串口7和SPI3共用,串口8和RGB硬件接口共用。串口功能可以分配到不同的GPIO。我們常用的引腳分配如下:

低功耗串口LPUART TX = PA9,   RX = PA10

串口USART1  TX = PA9,   RX = PA10 (低功耗串口和USART1用的相同引腳

串口USART2  TX = PA2,   RX = PA3

串口USART3  TX = PB10,  RX = PB11

串口UART4   TX = PC10,  RX = PC11 (和SDIO共用)

串口UART5   TX = PC12,  RX = PD2  (和SDIO共用)

串口USART6  TX = PG14,  RX = PC7  

串口UART7   TX = PB4,   RX = PB3  (和SPI1/3共用)

串口UART8   TX = PJ8,   RX =PJ9   (和RGB硬件接口共用)

STM32-V7開發板使用了4個串口設備。

  •   串口1用於RS232接口,很多例子的pritnf結果就是輸出到串口1
  •   串口2用於GPS
  •   串口3用於RS485接口
  •   串口6 用於TTL串口插座,板子上有GPRS插座和串口WIFI插座。

下面是RS232的原理圖:

 

關於232的PHY芯片SP3232E要注意以下幾個問題:

  •   SP3232E的作用是TTL電平轉RS232電平。
  •   電阻R130的作用是避免CPU復位期間,TX為高阻時串口線上出現異常數據。
  •   檢測SP3232E的好壞可以采用回環的方式,即短接T1OUT和R1IN,對應到DB9插座上就是短接引腳2和引腳3。

 

實際效果如下:

 

通過這種方式,可以在應用程序中通過串口發送幾個字符,查看是否可以正確接收來判斷232 PHY

芯片是否有問題。

  •   由於這里是TTL轉RS232,如果電腦端自帶DB9串口,可以找根交叉線直接接上。如果電腦端沒有,就需要用RS232轉USB的串口線。這里要注意是RS232轉USB,不是TTL轉USB。像我們用的CH340就是RS232轉USB芯片。
  •   檢測串口線的好壞跟板子上的232 PHY一樣,將電腦端的串口助手打開,串口線接到電腦端並短接串口線的2腳和3腳,然后使用串口助手進行自收發測試即可。

66.3 低功耗串口FIFO驅動設計

66.3.1 低功耗串口FIFO框架

為了方便大家理解,先來看下低功耗串口FIFO的實現框圖:

 

  第1階段,初始化:

  • 通過函數bsp_InitLPUart初始化低功耗串口結構體,低功耗串口硬件參數。

  第2階段,低功耗串口中斷服務程序:

  •   接收中斷是一直開啟的。
  •   做了發送空中斷和發送完成中斷的消息處理。

  第3階段,低功耗串口數據的收發:

  • 低功耗串口發送函數會開啟發送空中斷。
  • 低功耗串口接收中斷接收到函數后,可以使用函數lpcomGetChar獲取數據。

66.3.2 低功耗串口時鍾選擇

我們這里實現了三種時鍾選擇:

  • LPUART時鍾選擇LSE(32768Hz)

最高速度是10922bps,最低8bps(計算方法3x < 32768 < 4096x,x表示波特率)。

  • LPUART時鍾選擇HSI(64MHz)

最高值是21MHz,最小值15625bps(計算方法3x < 64MHz < 4096x,x表示波特率)。

  • LPUART時鍾選擇D3PCLK1(100MHz)

最大值33Mbps,最小值24414bps(計算方法3x < 100MHz < 4096x,x表示波特率)。

 

如果需要低功耗模式喚醒,必須使用LSE或者HSI時鍾,程序代碼如下,用戶可以根據需要,使能相應的宏定義:

//#define LPUART_CLOCK_SOURCE_LSE       
#define LPUART_CLOCK_SOURCE_HSI   
//#define LPUART_CLOCK_SOURCE_D3PCLK1    

/*
*********************************************************************************************************
*    函 數 名: InitHardLPUart
*    功能說明: 配置串口的硬件參數(波特率,數據位,停止位,起始位,校驗位,中斷使能)適合於STM32-H7開發板
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void InitHardLPUart(void)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    RCC_PeriphCLKInitTypeDef   RCC_PeriphCLKInitStruct = {0};

/* 使用LSE(32768Hz),最高速度是10922bps,最低8bps */    
#if defined (LPUART_CLOCK_SOURCE_LSE)
    {
        RCC_OscInitTypeDef RCC_OscInitStruct = {0};

        RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
        RCC_OscInitStruct.LSEState = RCC_LSE_ON;
        RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

        if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);        
        }
        
        RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPUART1;
        RCC_PeriphCLKInitStruct.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_LSE;
        HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
    }    
/* LPUART時鍾選擇HSI(64MHz),最高值是21MHz,最小值15625bps */    
#elif defined (LPUART_CLOCK_SOURCE_HSI)
    {

        RCC_OscInitTypeDef RCC_OscInitStruct = {0};

          RCC_OscInitStruct.OscillatorType      = RCC_OSCILLATORTYPE_HSI;
          RCC_OscInitStruct.HSIState            = RCC_HSI_ON;
          RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
          RCC_OscInitStruct.PLL.PLLState        = RCC_PLL_NONE;

        if (HAL_RCC_OscConfig(&RCC_OscInitStruct)!= HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);        
        }
        
        RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPUART1;
        RCC_PeriphCLKInitStruct.Lpuart1ClockSelection = RCC_LPUART1CLKSOURCE_HSI;
        HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
    }
/* LPUART時鍾選擇D3PCLK1(100MHz),最大值33Mbps,最小值24414bps */    
#elif defined (LPUART_CLOCK_SOURCE_D3PCLK1)
    
    RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPUART1;
    RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPUART1CLKSOURCE_D3PCLK1;
    HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct);
#else
    #error Please select the LPTIM Clock source inside the bsp_lpuart_fifo.c file
#endif

#if LPUART1_FIFO_EN == 
    /* 其它省略未寫 */
#endif
}

 

66.3.3 低功耗串口FIFO之相關的變量定義

低功耗串口驅動的核心文件為:bsp_lpuart_fifo.c, bsp_lpuart_fifo.h。

這里面包括有串口硬件的配置函數、中斷處理函數,以及串口的讀寫接口函數。還有printf函數的實現。

每個串口都有2個FIFO緩沖區,一個是用於發送數據的TX_FIFO,一個用於保存接收數據的RX_FIFO。

我們來看下這個FIFO的定義,在bsp_lpuart_fifo.h文件。

/* 定義串口波特率和FIFO緩沖區大小,分為發送緩沖區和接收緩沖區, 支持全雙工 */
#if LPUART1_FIFO_EN == 1
    #define LPUART1_BAUD         115200
    #define LPUART1_TX_BUF_SIZE     1*1024
    #define LPUART1_RX_BUF_SIZE     1*1024
#endif

/* 串口設備結構體 */
typedef struct
{
    USART_TypeDef *uart;        /* STM32內部串口設備指針 */
    uint8_t *pTxBuf;            /* 發送緩沖區 */
    uint8_t *pRxBuf;            /* 接收緩沖區 */
    uint16_t usTxBufSize;        /* 發送緩沖區大小 */
    uint16_t usRxBufSize;        /* 接收緩沖區大小 */
    __IO uint16_t usTxWrite;    /* 發送緩沖區寫指針 */
    __IO uint16_t usTxRead;        /* 發送緩沖區讀指針 */
    __IO uint16_t usTxCount;    /* 等待發送的數據個數 */

    __IO uint16_t usRxWrite;    /* 接收緩沖區寫指針 */
    __IO uint16_t usRxRead;        /* 接收緩沖區讀指針 */
    __IO uint16_t usRxCount;    /* 還未讀取的新數據個數 */

    void (*SendBefor)(void);     /* 開始發送之前的回調函數指針(主要用於RS485切換到發送模式) */
    void (*SendOver)(void);     /* 發送完畢的回調函數指針(主要用於RS485將發送模式切換為接收模式) */
    void (*ReciveNew)(uint8_t _byte);    /* 串口收到數據的回調函數指針 */
    uint8_t Sending;            /* 正在發送中 */
}UART_T;

 

bsp_lpuart_fifo.c文件定義變量。

/* 定義低功耗串口結構體變量 */
#if LPUART1_FIFO_EN == 1
    static LPUART_T g_tLPUart1;
    static uint8_t g_TxBuf1[LPUART1_TX_BUF_SIZE];        /* 發送緩沖區 */
    static uint8_t g_RxBuf1[LPUART1_RX_BUF_SIZE];        /* 接收緩沖區 */
#endif

 

關於FIFO的機制,我們在按鍵FIFO驅動已經做過詳細的介紹,這個地方就不贅述了。每個串口有兩個FIFO緩沖區,每個FIFO對應一個寫指針和一個讀指針。這個結構中還有三個回調函數。回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用為調用它所指向的函數時,我們就說這是回調函數。

66.3.4 低功耗串口FIFO初始化

低功耗串口的初始化代碼如下;

/*
*********************************************************************************************************
*    函 數 名: bsp_InitLPUart
*    功能說明: 初始化串口硬件,並對全局變量賦初值.
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_InitLPUart(void)
{
    LPUartVarInit();        /* 必須先初始化全局變量,再配置硬件 */
    InitHardLPUart();        /* 配置串口的硬件參數(波特率等) */
}

 

下面將初始化代碼實現的功能依次為大家做個說明。

  •   函數LPUartVarInit

這個函數實現的功能比較好理解,主要是串口設備結構體變量的初始化,代碼如下:

/*
*********************************************************************************************************
*    函 數 名: LPUartVarInit
*    功能說明: 初始化串口相關的變量
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void LPUartVarInit(void)
{
#if LPUART1_FIFO_EN == 1
    g_tLPUart1.uart = LPUART1;                        /* STM32 串口設備 */
    g_tLPUart1.pTxBuf = g_TxBuf1;                    /* 發送緩沖區指針 */
    g_tLPUart1.pRxBuf = g_RxBuf1;                    /* 接收緩沖區指針 */
    g_tLPUart1.usTxBufSize = LPUART1_TX_BUF_SIZE;         /* 發送緩沖區大小 */
    g_tLPUart1.usRxBufSize = LPUART1_RX_BUF_SIZE;         /* 接收緩沖區大小 */
    g_tLPUart1.usTxWrite = 0;                        /* 發送FIFO寫索引 */
    g_tLPUart1.usTxRead = 0;                        /* 發送FIFO讀索引 */
    g_tLPUart1.usRxWrite = 0;                        /* 接收FIFO寫索引 */
    g_tLPUart1.usRxRead = 0;                        /* 接收FIFO讀索引 */
    g_tLPUart1.usRxCount = 0;                        /* 接收到的新數據個數 */
    g_tLPUart1.usTxCount = 0;                        /* 待發送的數據個數 */
    g_tLPUart1.SendBefor = 0;                        /* 發送數據前的回調函數 */
    g_tLPUart1.SendOver = 0;                        /* 發送完畢后的回調函數 */
    g_tLPUart1.ReciveNew = 0;                        /* 接收到新數據后的回調函數 */
    g_tLPUart1.Sending = 0;                             /* 正在發送中標志 */
#endif
}

 

  •   函數InitHardLPUart

此函數主要用於串口的GPIO,中斷和相關參數的配置。

/* LPUART1的GPIO  PA9, PA10 */
#define LPUART1_CLK_ENABLE()              __HAL_RCC_LPUART1_CLK_ENABLE()

#define LPUART1_TX_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOA_CLK_ENABLE()
#define LPUART1_TX_GPIO_PORT              GPIOA
#define LPUART1_TX_PIN                    GPIO_PIN_9
#define LPUART1_TX_AF                     GPIO_AF3_LPUART

#define LPUART1_RX_GPIO_CLK_ENABLE()      __HAL_RCC_GPIOA_CLK_ENABLE()
#define LPUART1_RX_GPIO_PORT              GPIOA
#define LPUART1_RX_PIN                    GPIO_PIN_10
#define LPUART1_RX_AF                     GPIO_AF3_LPUART

/*
*********************************************************************************************************
*    函 數 名: InitHardUart
*    功能說明: 配置串口的硬件參數和底層
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void InitHardUart(void)
{
    GPIO_InitTypeDef  GPIO_InitStruct;
    RCC_PeriphCLKInitTypeDef RCC_PeriphClkInit;

    /* 時鍾初始化省略未寫 */
#if LPUART1_FIFO_EN == 1        
    /* 使能 GPIO TX/RX 時鍾 */
    LPUART1_TX_GPIO_CLK_ENABLE();
    LPUART1_RX_GPIO_CLK_ENABLE();
    
    /* 使能 USARTx 時鍾 */
    LPUART1_CLK_ENABLE();    

    /* 配置TX引腳 */
    GPIO_InitStruct.Pin       = LPUART1_TX_PIN;
    GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull      = GPIO_PULLUP;
    GPIO_InitStruct.Speed     = GPIO_SPEED_FREQ_HIGH;
    GPIO_InitStruct.Alternate = LPUART1_TX_AF;
    HAL_GPIO_Init(LPUART1_TX_GPIO_PORT, &GPIO_InitStruct);    
    
    /* 配置RX引腳 */
    GPIO_InitStruct.Pin = LPUART1_RX_PIN;
    GPIO_InitStruct.Alternate = LPUART1_RX_AF;
    HAL_GPIO_Init(LPUART1_RX_GPIO_PORT, &GPIO_InitStruct);

    /* 配置NVIC the NVIC for UART */   
    HAL_NVIC_SetPriority(LPUART1_IRQn, 0, 1);
    HAL_NVIC_EnableIRQ(LPUART1_IRQn);
  
    /* 配置波特率、奇偶校驗 */
    bsp_SetLPUartParam(LPUART1,  LPUART1_BAUD, UART_PARITY_NONE, UART_MODE_TX_RX);

    SET_BIT(LPUART1->ICR, USART_ICR_TCCF);   /* 清除TC發送完成標志 */
    SET_BIT(LPUART1->RQR, USART_RQR_RXFRQ);  /* 清除RXNE接收標志 */
    SET_BIT(LPUART1->CR1, USART_CR1_RXNEIE); /* 使能PE. RX接受中斷 */
#endif
}

 

低功耗定時器的參數配置API如下:

/*
*********************************************************************************************************
*    函 數 名: bsp_SetLPUartParam
*    功能說明: 配置串口的硬件參數(波特率,數據位,停止位,起始位,校驗位,中斷使能)適合於STM32- H7開發板
*    形    參: Instance   USART_TypeDef類型結構體
*             BaudRate   波特率
*             Parity     校驗類型,奇校驗或者偶校驗
*             Mode       發送和接收模式使能
*    返 回 值: 無
*********************************************************************************************************
*/    
void bsp_SetLPUartParam(USART_TypeDef *Instance,  uint32_t BaudRate, uint32_t Parity, uint32_t Mode)
{
    /*##-1- 配置串口硬件參數 ######################################*/
    /* 異步串口模式 (UART Mode) */
    /* 配置如下:
      - 字長    = 8 位
      - 停止位  = 1 個停止位
      - 校驗    = 參數Parity
      - 波特率  = 參數BaudRate
      - 硬件流控制關閉 (RTS and CTS signals) */

    UartHandle.Instance        = Instance;
    UartHandle.Init.BaudRate   = BaudRate;
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
    UartHandle.Init.StopBits   = UART_STOPBITS_1;
    UartHandle.Init.Parity     = Parity;
    UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
    UartHandle.Init.Mode       = Mode;
    UartHandle.Init.OverSampling = UART_OVERSAMPLING_16;
    UartHandle.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
    UartHandle.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
    
    if (HAL_UART_Init(&UartHandle) != HAL_OK)
    {
        Error_Handler(__FILE__, __LINE__);
    }
}

 

66.3.5 低功耗串口中斷服務程序工作流程

串口中斷服務程序是最核心的部分,主要實現如下三個功能

  •   收到新的數據后,會將數據壓入RX_FIFO。
  •   檢測到發送緩沖區空后,會從TX_FIFO中取下一個數據並發送。
  •   如果是RS485半雙工串口,發送前會設置一個GPIO=1控制RS485收發器進入發送狀態,當最后一個字節的最后一個bit傳送完畢后,設置這個GPIO=0讓RS485收發器進入接收狀態。

 

下面我們分析一下串口中斷處理的完整過程。

當產生串口中斷后,CPU會查找中斷向量表,獲得中斷服務程序的入口地址。入口函數為LPUART1_IRQHandler,這個函數在啟動文件startup_stm32h743xx.s匯編代碼中已經有實現。我們在c代碼中需要重寫一個同樣名字的函數就可以重載它。如果不重載,啟動文件中缺省的中斷服務程序就是一個死循環,等於 while(1);

我們將串口中斷服務程序放在bsp_lpuart_fifo.c文件,沒有放到 stm32h7xx_it.c。當應用不需要串口功能時,直接從工程中刪除bsp_lpuart_fifo.c接口,不必再去整理stm32h7xx_it.c這個文件。下面展示的代碼是低功耗串口的中斷服務程序:

#if LPUART1_FIFO_EN == 1
void LPUART1_IRQHandler(void)
{
    LPUartIRQ(&g_tLPUart1);
}
#endif

 

下面,我們來看看UartIRQ函數的實現代碼。

/*
*********************************************************************************************************
*    函 數 名: UartIRQ
*    功能說明: 供中斷服務程序調用,通用串口中斷處理函數
*    形    參: _pUart : 串口設備
*    返 回 值: 無
*********************************************************************************************************
*/
static void UartIRQ(UART_T *_pUart)
{
    uint32_t isrflags   = READ_REG(_pUart->uart->ISR);
    uint32_t cr1its     = READ_REG(_pUart->uart->CR1);
    uint32_t cr3its     = READ_REG(_pUart->uart->CR3);
    
    /* 處理接收中斷  */
    if ((isrflags & USART_ISR_RXNE) != RESET)
    {
        /* 從串口接收數據寄存器讀取數據存放到接收FIFO */
        uint8_t ch;

        ch = READ_REG(_pUart->uart->RDR);               /* 讀串口接收數據寄存器 */
        _pUart->pRxBuf[_pUart->usRxWrite] = ch;         /* 填入串口接收FIFO */
        if (++_pUart->usRxWrite >= _pUart->usRxBufSize) /* 接收FIFO的寫指針+1 */
        {
            _pUart->usRxWrite = 0;
        }
        if (_pUart->usRxCount < _pUart->usRxBufSize)    /* 統計未處理的字節個數 */
        {
            _pUart->usRxCount++;
        }

        /* 回調函數,通知應用程序收到新數據,一般是發送1個消息或者設置一個標記 */
        //if (_pUart->usRxWrite == _pUart->usRxRead)
        //if (_pUart->usRxCount == 1)
        {
            if (_pUart->ReciveNew)
            {
                _pUart->ReciveNew(ch); /* 比如,交給MODBUS解碼程序處理字節流 */
            }
        }
    }

    /* 處理發送緩沖區空中斷 */
    if ( ((isrflags & USART_ISR_TXE) != RESET) && (cr1its & USART_CR1_TXEIE) != RESET)
    {
        //if (_pUart->usTxRead == _pUart->usTxWrite) 
        if (_pUart->usTxCount == 0)  /* 發送緩沖區已無數據可取 */
        {
        /* 發送緩沖區的數據已取完時, 禁止發送緩沖區空中斷 (注意:此時最后1個數據還未真正發送完畢)*/
            //USART_ITConfig(_pUart->uart, USART_IT_TXE, DISABLE);
            CLEAR_BIT(_pUart->uart->CR1, USART_CR1_TXEIE);

            /* 使能數據發送完畢中斷 */
            //USART_ITConfig(_pUart->uart, USART_IT_TC, ENABLE);
            SET_BIT(_pUart->uart->CR1, USART_CR1_TCIE);
        }
        Else  /* 還有數據等待發送 */
        {
            _pUart->Sending = 1;
            
            /* 從發送FIFO取1個字節寫入串口發送數據寄存器 */
            //USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
            _pUart->uart->TDR = _pUart->pTxBuf[_pUart->usTxRead];
            if (++_pUart->usTxRead >= _pUart->usTxBufSize)
            {
                _pUart->usTxRead = 0;
            }
            _pUart->usTxCount--;
        }

    }
    /* 數據bit位全部發送完畢的中斷 */
    if (((isrflags & USART_ISR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET))
    {
        //if (_pUart->usTxRead == _pUart->usTxWrite)
        if (_pUart->usTxCount == 0)
        {
            /* 如果發送FIFO的數據全部發送完畢,禁止數據發送完畢中斷 */
            //USART_ITConfig(_pUart->uart, USART_IT_TC, DISABLE);
            CLEAR_BIT(_pUart->uart->CR1, USART_CR1_TCIE);

            /* 回調函數, 一般用來處理RS485通信,將RS485芯片設置為接收模式,避免搶占總線 */
            if (_pUart->SendOver)
            {
                _pUart->SendOver();
            }
            
            _pUart->Sending = 0;
        }
        else
        {
            /* 正常情況下,不會進入此分支 */

            /* 如果發送FIFO的數據還未完畢,則從發送FIFO取1個數據寫入發送數據寄存器 */
            //USART_SendData(_pUart->uart, _pUart->pTxBuf[_pUart->usTxRead]);
            _pUart->uart->TDR = _pUart->pTxBuf[_pUart->usTxRead];
            if (++_pUart->usTxRead >= _pUart->usTxBufSize)
            {
                _pUart->usTxRead = 0;
            }
            _pUart->usTxCount--;
        }
    }
    
    /* 清除中斷標志 */
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_PEF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_FEF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_NEF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_OREF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_IDLEF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_TCF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_LBDF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_CTSF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_CMF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_WUF);
    SET_BIT(_pUart->uart->ICR, UART_CLEAR_TXFECF);    
}

 

中斷服務程序的處理主要分為兩部分,接收數據的處理和發送數據的處理,詳情看程序注釋即可,已經比較詳細,下面重點把思路說一下。

  •   接收數據處理

接收數據的處理是判斷ISR寄存器的USART_ISR_RXNE標志是否置位,如果置位表示RDR接收寄存器已經存入數據。然后將數據讀入到接收FIFO空間。

特別注意里面的ReciveNew處理,這個在Modbus協議里面要用到。

  •   發送數據處理

發送數據主要是發送空中斷TEX和發送完成中斷TC的處理,當TXE=1時,只是表示發送數據寄存器為空了,此時可以填充下一個准備發送的數據了。當為TDR發送寄存器賦值后,硬件啟動發送,等所有的bit傳送完畢后,TC標志設置為1。如果是RS232全雙工通信,可以只用TXE標志控制發送過程。如果是RS485半雙工通信,就需要利用TC標志了,因為在最后一個bit傳送完畢后,需要設置RS485收發器進入到接收狀態。

66.3.6 低功耗串口數據發送

低功耗串口數據的發送主要涉及到下面三個函數:

/*
*********************************************************************************************************
*    函 數 名: lpcomSendBuf
*    功能說明: 向串口發送一組數據。數據放到發送緩沖區后立即返回,由中斷服務程序在后台完成發送
*    形    參: _ucPort: 端口號(LPCOM1)
*              _ucaBuf: 待發送的數據緩沖區
*              _usLen : 數據長度
*    返 回 值: 無
*********************************************************************************************************
*/
void lpcomSendBuf(LPCOM_PORT_E _ucPort, uint8_t *_ucaBuf, uint16_t _usLen)
{
    LPUART_T *pUart;

    pUart = ComToLPUart(_ucPort);
    if (pUart == 0)
    {
        return;
    }

    if (pUart->SendBefor != 0)
    {
        pUart->SendBefor();        /* 如果是RS485通信,可以在這個函數中將RS485設置為發送模式 */
    }

    LPUartSend(pUart, _ucaBuf, _usLen);
}

/*
*********************************************************************************************************
*    函 數 名: lpcomSendChar
*    功能說明: 向串口發送1個字節。數據放到發送緩沖區后立即返回,由中斷服務程序在后台完成發送
*    形    參: _ucPort: 端口號(LPCOM1)
*              _ucByte: 待發送的數據
*    返 回 值: 無
*********************************************************************************************************
*/
void lpcomSendChar(LPCOM_PORT_E _ucPort, uint8_t _ucByte)
{
    lpcomSendBuf(_ucPort, &_ucByte, 1);
}

/*
*********************************************************************************************************
*    函 數 名: LPUartSend
*    功能說明: 填寫數據到UART發送緩沖區,並啟動發送中斷。中斷處理函數發送完畢后,自動關閉發送中斷
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void LPUartSend(LPUART_T *_pUart, uint8_t *_ucaBuf, uint16_t _usLen)
{
    uint16_t i;

    for (i = 0; i < _usLen; i++)
    {
        /* 如果發送緩沖區已經滿了,則等待緩沖區空 */
        while (1)
        {
            __IO uint16_t usCount;

            DISABLE_INT();
            usCount = _pUart->usTxCount;
            ENABLE_INT();

            if (usCount < _pUart->usTxBufSize)
            {
                break;
            }
            else if(usCount == _pUart->usTxBufSize)/* 數據已填滿緩沖區 */
            {
                if((_pUart->uart->CR1 & USART_CR1_TXEIE) == 0)
                {
                    SET_BIT(_pUart->uart->CR1, USART_CR1_TXEIE);
                }  
            }
        }

        /* 將新數據填入發送緩沖區 */
        _pUart->pTxBuf[_pUart->usTxWrite] = _ucaBuf[i];

        DISABLE_INT();
        if (++_pUart->usTxWrite >= _pUart->usTxBufSize)
        {
            _pUart->usTxWrite = 0;
        }
        _pUart->usTxCount++;
        ENABLE_INT();
    }

    SET_BIT(_pUart->uart->CR1, USART_CR1_TXEIE);    /* 使能發送中斷(緩沖區空) */
}

 

函數lpcomSendChar是發送一個字節,通過調用函數lpcomSendBuf實現,而函數lpcomSendBuf又是通過調用函數LPUartSend實現,這個函數是重點。

函數LPUartSend的作用就是把要發送的數據填到發送緩沖區里面,並使能發送空中斷。

  •   如果要發送的數據沒有超過發送緩沖區大小,實現起來還比較容易,直接把數據填到FIFO里面,並使能發送空中斷即可。
  •   如果超過了FIFO大小,就需要等待有空間可用,針對這種情況有個重要的知識點,就是當緩沖剛剛填滿的時候要判斷發送空中斷是否開啟了,如果填滿了還沒有開啟,就會卡死在while循環中,所以多了一個剛填滿時的判斷,填滿了還沒有開啟發送空中斷,要開啟下。

 

注意:由於函數LPUartSend做了static作用域限制,僅可在bsp_lpuart_fifo.c文件中調用。函數lpcomSendChar和lpcomSendBuf是供用戶調用的。

 

函數lpcomSendBuf中調用了一個函數pUart = ComToLPUart(_ucPort),這個函數是將整數的COM端口號轉換為LPUART結構體指針。

/*
*********************************************************************************************************
*    函 數 名: ComToLPUart
*    功能說明: 將COM端口號轉換為LPUART指針
*    形    參: _ucPort: 端口號(LPCOM1)
*    返 回 值: uart指針
*********************************************************************************************************
*/
LPUART_T *ComToLPUart(LPCOM_PORT_E _ucPort)
{
    if (_ucPort == LPCOM1)
    {
        #if LPUART1_FIFO_EN == 1
            return &g_tLPUart1;
        #else
            return 0;
        #endif
    }
    else
    {
        Error_Handler(__FILE__, __LINE__);
        return 0;
    }
}

 

66.3.7 低功耗串口數據接收

下面我們再來看看接收的函數:

/*
*********************************************************************************************************
*    函 數 名: lpcomGetChar
*    功能說明: 從接收緩沖區讀取1字節,非阻塞。無論有無數據均立即返回。
*    形    參: _ucPort: 端口號(LPCOM1)
*              _pByte: 接收到的數據存放在這個地址
*    返 回 值: 0 表示無數據, 1 表示讀取到有效字節
*********************************************************************************************************
*/
uint8_t lpcomGetChar(LPCOM_PORT_E _ucPort, uint8_t *_pByte)
{
    LPUART_T *pUart;

    pUart = ComToLPUart(_ucPort);
    if (pUart == 0)
    {
        return 0;
    }

    return LPUartGetChar(pUart, _pByte);
}

/*
*********************************************************************************************************
*    函 數 名: LPUartGetChar
*    功能說明: 從串口接收緩沖區讀取1字節數據 (用於主程序調用)
*    形    參: _pUart : 串口設備
*              _pByte : 存放讀取數據的指針
*    返 回 值: 0 表示無數據  1表示讀取到數據
*********************************************************************************************************
*/
static uint8_t LPUartGetChar(LPUART_T *_pUart, uint8_t *_pByte)
{
    uint16_t usCount;

    /* usRxWrite 變量在中斷函數中被改寫,主程序讀取該變量時,必須進行臨界區保護 */
    DISABLE_INT();
    usCount = _pUart->usRxCount;
    ENABLE_INT();

    /* 如果讀和寫索引相同,則返回0 */
    //if (_pUart->usRxRead == usRxWrite)
    if (usCount == 0)    /* 已經沒有數據 */
    {
        return 0;
    }
    else
    {
        *_pByte = _pUart->pRxBuf[_pUart->usRxRead];        /* 從串口接收FIFO取1個數據 */

        /* 改寫FIFO讀索引 */
        DISABLE_INT();
        if (++_pUart->usRxRead >= _pUart->usRxBufSize)
        {
            _pUart->usRxRead = 0;
        }
        _pUart->usRxCount--;
        ENABLE_INT();
        return 1;
    }
}

 

函數lpcomGetChar是專門供用戶調用的,用於從接收FIFO中讀取1個數據。具體代碼的實現也比較好理解,主要是接收FIFO的空間調整。

注意:由於函數LPUartGetChar做了static作用域限制,僅可在bsp_lpuart_fifo.c文件中調用。

66.3.8 低功耗串口printf實現

printf函數是標准c庫函數。最原來的意思是打印輸出到顯示器。在單片機,我們常用它來打印調試信息到串口,通過計算機上運行的串口軟件來監視程序的運行狀態。

為什么要用printf函數,而不用串口發送的函數。因為printf函數的形參功能很強大,它支持各種數值轉換。比如將整數、浮點數轉換為字符串,支持整數左對齊、右對齊顯示等。

我們設計的很多裸機例子都是用printf函數輸出運行結果的。因為如果加上顯示屏驅動后,會將程序搞的很復雜,顯示部分的代碼量超過了例程本身要演示的核心功能代碼。用串口做輸出,移植很方便,現在很少有不帶串口的單片機。

實現printf輸出到串口,只需要在工程中添加兩個函數:

/*
*********************************************************************************************************
*    函 數 名: fputc
*    功能說明: 重定義putc函數,這樣可以使用printf函數從串口1打印輸出
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
int fputc(int ch, FILE *f)
{
#if 0    /* 將需要printf的字符通過串口中斷FIFO發送出去,printf函數會立即返回 */
    lpcomSendChar(LPCOM1, ch);
    
    return ch;
#else    /* 采用阻塞方式發送每個字符,等待數據發送完畢 */
    /* 寫一個字節到USART1 */
    LPUART1->TDR = ch;
    
    /* 等待發送結束 */
    while((LPUART1->ISR & USART_ISR_TC) == 0)
    {}
    
    return ch;
#endif
}

/*
*********************************************************************************************************
*    函 數 名: fgetc
*    功能說明: 重定義getc函數,這樣可以使用getchar函數從串口1輸入數據
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
int fgetc(FILE *f)
{

#if 1    /* 從串口接收FIFO中取1個數據, 只有取到數據才返回 */
    uint8_t ucData;

    while(lpcomGetChar(LPCOM1, &ucData) == 0);

    return ucData;
#else
    /* 等待接收到數據 */
    while((LPUART1->ISR & USART_ISR_RXNE) == 0)
    {}

    return (int)LPUART1->RDR;
#endif
}

 

通過上面代碼中的條件編譯,可以設置printf函數阻塞和非阻塞方式,如果采用非阻塞方式,執行后會立即返回,串口中斷服務程序會陸續將數據發送出去。

 

66.3.9 低功耗串口停機喚醒方式

低功耗串口的喚醒主要是通過接收數據來喚醒,具體喚醒的方如下:

  •   檢測到起始位喚醒。

低功耗串口設置為起始位檢測方式如下,並且設置進入停機模式。

如果想喚醒H7,發一個起始位即可,簡單些也可以任意發送一個數據:

/* 使能LPUART的停機喚醒 */
HAL_UARTEx_EnableStopMode(&UartHandle); 

/* 確保LPUART沒有在通信中 */
while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

/* 接收起始位喚醒 */
WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_STARTBIT;
if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);                        
}

/* 進入停機模式 */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

/* 退出停機模式要重新配置HSE和PLL*/
SystemClock_Config();

/* 關閉LPUART的停機喚醒 */
HAL_UARTEx_DisableStopMode(&UartHandle);

 

  •   檢測到RXNE標志喚醒,即接收到數據。

低功耗串口設置為RXNE檢測方式如下,並且設置進入停機模式。

如果想喚醒H7,發一個任意數據即可。

/* 使能LPUART的停機喚醒 */
HAL_UARTEx_EnableStopMode(&UartHandle); 

/* 確保LPUART沒有在通信中 */
while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

/* 接收到數據喚醒,即RXNE標志置位 */
WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY;
if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);                        
}

/* 進入停機模式 */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

/* 退出停機模式要重新配置HSE和PLL*/
SystemClock_Config();

/* 關閉LPUART的停機喚醒 */
HAL_UARTEx_DisableStopMode(&UartHandle);

 

  •   檢測到匹配地址時喚醒。

低功耗串口設置為地址匹配檢測方式如下,並且設置進入停機模式。

如果想喚醒H7,必須發送指定的匹配地址。匹配地址支持7bit和4bit匹配兩種方式,比如我們采用7bit匹配,設置地址是0x19,那么用戶喚醒的時候要將最高bit設置為1,即發生地址0x99(0b1001 1001)才可以喚醒。

/* 使能LPUART的停機喚醒 */
HAL_UARTEx_EnableStopMode(&UartHandle); 

/* 確保LPUART沒有在通信中 */
while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

/* 接收地址0x99(發送的數據MSB位要為1),可以喚醒 */
WakeUpSelection.WakeUpEvent   = UART_WAKEUP_ON_ADDRESS;
WakeUpSelection.AddressLength = UART_ADDRESS_DETECT_7B;
WakeUpSelection.Address       = 0x19;
if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
{
    Error_Handler(__FILE__, __LINE__);                        
}

CLEAR_BIT(LPUART1->CR1, USART_CR1_RXNEIE); /* 關閉串口接收中斷 */

/* 進入停機模式 */
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

/* 退出停機模式要重新配置HSE和PLL*/
SystemClock_Config();

SET_BIT(LPUART1->CR1, USART_CR1_RXNEIE);  /* 使能串口接收中斷 */

/* 關閉LPUART的停機喚醒 */
HAL_UARTEx_DisableStopMode(&UartHandle);

 

 

這里有一點要特別注意,程序啟動后,調用下面兩個函數:

__HAL_RCC_LPUART1_CLKAM_ENABLE();     /* 激活LPUART的自主模式,即停機狀態下可以繼續接收消息 */
__HAL_UART_ENABLE_IT(&UartHandle, UART_IT_WUF);/* 使能喚醒中斷 */

 

66.4 低功耗串口FIFO板級支持包(bsp_lpuart_fifo.c)

串口驅動文件bsp_lpuart_fifo.c主要實現了如下幾個API供用戶調用:

  •   bsp_InitLPUart
  •   lpcomSendBuf
  •   lpcomSendChar
  •   lpcomGetChar

66.4.1 函數bsp_InitLPUart

函數原型:

void bsp_InitLPUart(void)

函數描述:

此函數主要用於串口的初始化,使用所有其它API之前,務必優先調用此函數。

使用舉例:

串口的初始化函數在bsp.c文件的bsp_Init函數里面調用。

66.4.2 函數lpcomSendBuf

函數原型:

void lpcomSendBuf(LPCOM_PORT_E _ucPort, uint8_t *_ucaBuf, uint16_t _usLen);

函數描述:

此函數用於向串口發送一組數據,非阻塞方式,數據放到發送緩沖區后立即返回,由中斷服務程序在后台完成發送。

函數參數:

  •   第1個參數_ucPort是端口號。
  •   第2個參數_ucaBuf是待發送的數據緩沖區地址。
  •   第3個參數_usLen是要發送數據的字節數。

注意事項:

  •  此函數的解讀在本章66.3.5小節。
  •  發送的數據最好不要超過bsp_lpuart_fifo.h文件中定義的發送緩沖區大小,從而實現最優的工作方式。因為超過后需要在發送函數等待有發送空間可用。

使用舉例:

調用此函數前,務必優先調用函數bsp_InitLPUart進行初始化。

const char buf1[] = "接收到串口命令1\r\n";
lpcomSendBuf(LPCOM1, (uint8_t *)buf1, strlen(buf1));

 

66.4.3 函數lpcomSendChar

函數原型:

void lpcomSendChar(LPCOM_PORT_E _ucPort, uint8_t _ucByte);

函數描述:

此函數用於向串口發送1個字節,非阻塞方式,數據放到發送緩沖區后立即返回,由中斷服務程序在后台完成發送。此函數是通過調用函數lpcomSendBuf實現的。

  • 函數參數:
  •   第1個參數_ucPort是端口號。
  •   第2個參數_ucByte是待發送的數據。

注意事項:

  •   此函數的解讀在本章66.3.2小節。

使用舉例:

調用此函數前,務必優先調用函數bsp_InitLPUart進行初始化。比如通過串口1發送一個字符c:

lpcomSendChar(LPCOM1, 'c')。

66.4.4 函數lpcomGetChar

函數原型:

uint8_t lpcomGetChar(LPCOM_PORT_E _ucPort, uint8_t *_pByte)

函數描述:

此函數用於從接收緩沖區讀取1字節,非阻塞。無論有無數據均立即返回。

函數參數:

  •   第1個參數_ucPort是端口號。
  •   第2個參數_pByte用於存放接收到的數據。
  •   返回值,返回0表示無數據, 1 表示讀取到有效字節。

注意事項:

  •   此函數的解讀在本章66.3.6小節。

使用舉例:

調用此函數前,務必優先調用函數bsp_InitLPUart進行初始化。

比如從串口1讀取一個字符就是:lpcomGetChar(LPCOM1, &read)。

66.5 低功耗串口FIFO驅動移植和使用

串口FIFO移植步驟如下:

  •   第1步:復制bsp_lpuart_fifo.h和bsp_lpuart_fifo.c到自己的工程目錄,並添加到工程里面。
  •   第2步:根據自己要使用的串口和收發緩沖大小,修改下面的宏定義即可。
#define    LPUART1_FIFO_EN    1

/* 定義串口波特率和FIFO緩沖區大小,分為發送緩沖區和接收緩沖區, 支持全雙工 */
#if LPUART1_FIFO_EN == 1
    #define LPUART1_BAUD         115200
    #define LPUART1_TX_BUF_SIZE     1*1024
    #define LPUART1_RX_BUF_SIZE     1*1024
#endif

 

  •   第3步:這幾個驅動文件主要用到HAL庫的GPIO和串口驅動文件,簡單省事些可以添加所有HAL庫.C源文件進來。
  •   第4步,應用方法看本章節配套例子即可。

66.6 實驗例程設計框架

通過程序設計框架,讓大家先對配套例程有一個全面的認識,然后再理解細節,本次實驗例程的設計框架如下:

 

  第1階段,上電啟動階段:

  • 這部分在第14章進行了詳細說明。

  第2階段,進入main函數:

  • 第1部分,硬件初始化,主要是MPU,Cache,HAL庫,系統時鍾,滴答定時器和LED。
  • 第2部分,應用程序設計部分,實現了三種停機喚醒方法。

66.7 實驗例程說明(MDK)

配套例子:

V7-046_低功耗串口的停機喚醒(串口FIFO方式)

實驗目的:

  1. 學習低功耗串口的停機喚醒。

實驗內容:

  1. 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
  2. 當前程序使用的串口打印就是用的低功耗串口,即USART1和LPUART1都可以使用PA9和PA10。
  3. 上電啟動了一個軟件定時器,每100ms翻轉一次LED2。
  4. USART1和LPUART都可以使用PA9和PA10引腳做串口打印功能,本例子是用的LPUART做開發板串口打印。
  5. LPUART可以選擇HSI時鍾,LSE時鍾和D3PCLK1時鍾,在bsp_lpuart_fifo.c文件開頭可以配置。如果需要低功耗模式喚醒,必須使用LSE或者HSI時鍾,波特率在bsp_lpuart_fifo.h定義,本例子是用的HSI時鍾。

LPUART時鍾選擇LSE(32768Hz),最高速度是10922bps,最低8bps。

LPUART時鍾選擇HSI(64MHz),最高值是21MHz,最小值15625bps。

LPUART時鍾選擇D3PCLK1(100MHz),最大值33Mbps,最小值24414bps。

實驗操作:

  1. K1鍵按下,進入停機模式,低功耗串口接收任意字節數據可以喚醒。
  2. K2鍵按下,進入停機模式,低功耗串口檢測到起始位可以喚醒。
  3. K3鍵按下,進入停機模式,低功耗串口檢測到地址0x99可以喚醒。

上電后串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1。

 

程序設計:

  系統棧大小分配:

 

  RAM空間用的DTCM:

 

  硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾:
       - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。
       - 設置NVIV優先級分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鍾到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
bsp_InitDWT();      /* 初始化DWT時鍾周期計數器 */       
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitLPUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
    bsp_InitExtSDRAM(); /* 初始化SDRAM */
}

 

  MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  每10ms調用一次蜂鳴器處理:

蜂鳴器處理是在滴答定時器中斷里面實現,每10ms執行一次檢測。

/*
*********************************************************************************************************
*    函 數 名: bsp_RunPer10ms
*    功能說明: 該函數每隔10ms被Systick中斷調用1次。詳見 bsp_timer.c的定時中斷服務程序。一些處理時間要求
*              不嚴格的任務可以放在此函數。比如:按鍵掃描、蜂鳴器鳴叫控制等。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
    bsp_KeyScan10ms();
}

 

  主功能:

主程序實現如下操作:

  • 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
  • K1鍵按下,進入停機模式,低功耗串口接收任意字節數據可以喚醒。
  • K2鍵按下,進入停機模式,低功耗串口檢測到起始位可以喚醒。
  • K3鍵按下,進入停機模式,低功耗串口檢測到地址0x99可以喚醒。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;    /* 按鍵代碼 */
    uint8_t ucReceive;
    

    bsp_Init();        /* 硬件初始化 */
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */
    
    HAL_EnableDBGStopMode(); /* 使能停機模式下,LPUART工程可以繼續調試 */
    __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); /* 從停機模式喚醒后使用HSI時鍾 */
    __HAL_RCC_LPUART1_CLKAM_ENABLE();     /* 激活LPUART的自主模式,即停機狀態下可以繼續接收消息 */
    __HAL_UART_ENABLE_IT(&UartHandle, UART_IT_WUF);/* 使能喚醒中斷 */
    
    
    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */
    
    while (1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */

        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:    /* K1鍵按下,進入停機模式,低功耗串口接收任意字節數據可以喚醒 */
                    /* 使能LPUART的停機喚醒 */
                    HAL_UARTEx_EnableStopMode(&UartHandle); 

                    /* 確保LPUART沒有在通信中 */
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

                    /* 接收到數據喚醒,即RXNE標志置位 */
                    WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY;
                    if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
                    {
                        Error_Handler(__FILE__, __LINE__);                        
                    }

                    /* 進入停機模式 */
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

                    /* 退出停機模式要重新配置HSE和PLL*/
                    SystemClock_Config();

                    /* 關閉LPUART的停機喚醒 */
                    HAL_UARTEx_DisableStopMode(&UartHandle);
                    
                    lpcomGetChar(LPCOM1, &ucReceive);
                    
                    printf("低功耗串口接收到數據 %x 后喚醒\r\n", ucReceive);
                    break;
                    
                case KEY_DOWN_K2:     /* K2鍵按下,進入停機模式,低功耗串口檢測到起始位可以喚醒 */
                    /* 使能LPUART的停機喚醒 */
                    HAL_UARTEx_EnableStopMode(&UartHandle); 

                    /* 確保LPUART沒有在通信中 */
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

                    /* 接收起始位喚醒 */
                    WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_STARTBIT;
                    if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
                    {
                        Error_Handler(__FILE__, __LINE__);                        
                    }

                    /* 進入停機模式 */
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

                    /* 退出停機模式要重新配置HSE和PLL*/
                    SystemClock_Config();

                    /* 關閉LPUART的停機喚醒 */
                    HAL_UARTEx_DisableStopMode(&UartHandle);
                    
                    lpcomGetChar(LPCOM1, &ucReceive);
                    
                    printf("低功耗串口檢測到起始位(數據) %x 后喚醒\r\n", ucReceive);
                    break;
                    
                case KEY_DOWN_K3:    /* K3鍵按下,進入停機模式,低功耗串口檢測到地址0x99可以喚醒 */
                    /* 使能LPUART的停機喚醒 */
                    HAL_UARTEx_EnableStopMode(&UartHandle); 

                    /* 確保LPUART沒有在通信中 */
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

                    /* 接收地址0x99(發送的數據MSB位要為1),可以喚醒 */
                    WakeUpSelection.WakeUpEvent   = UART_WAKEUP_ON_ADDRESS;
                    WakeUpSelection.AddressLength = UART_ADDRESS_DETECT_7B;
                    WakeUpSelection.Address       = 0x19;
                    if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
                    {
                        Error_Handler(__FILE__, __LINE__);                        
                    }
                    
                    CLEAR_BIT(LPUART1->CR1, USART_CR1_RXNEIE); /* 關閉串口接收中斷 */
                    
                    /* 進入停機模式 */
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

                    /* 退出停機模式要重新配置HSE和PLL*/
                    SystemClock_Config();

                    SET_BIT(LPUART1->CR1, USART_CR1_RXNEIE);  /* 使能串口接收中斷 */
                    
                    /* 關閉LPUART的停機喚醒 */
                    HAL_UARTEx_DisableStopMode(&UartHandle);
                    
                    break;

                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        }
    }
}

 

66.8 實驗例程說明(IAR)

配套例子:

V7-046_低功耗串口的停機喚醒(串口FIFO方式)

實驗目的:

  1. 學習低功耗串口的停機喚醒。

實驗內容:

  1. 啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
  2. 當前程序使用的串口打印就是用的低功耗串口,即USART1和LPUART1都可以使用PA9和PA10。
  3. 上電啟動了一個軟件定時器,每100ms翻轉一次LED2。
  4. USART1和LPUART都可以使用PA9和PA10引腳做串口打印功能,本例子是用的LPUART做開發板串口打印。
  5. LPUART可以選擇HSI時鍾,LSE時鍾和D3PCLK1時鍾,在bsp_lpuart_fifo.c文件開頭可以配置。如果需要低功耗模式喚醒,必須使用LSE或者HSI時鍾,波特率在bsp_lpuart_fifo.h定義,本例子是用的HSI時鍾。

LPUART時鍾選擇LSE(32768Hz),最高速度是10922bps,最低8bps。

LPUART時鍾選擇HSI(64MHz),最高值是21MHz,最小值15625bps。

LPUART時鍾選擇D3PCLK1(100MHz),最大值33Mbps,最小值24414bps。

實驗操作:

  1. K1鍵按下,進入停機模式,低功耗串口接收任意字節數據可以喚醒。
  2. K2鍵按下,進入停機模式,低功耗串口檢測到起始位可以喚醒。
  3. K3鍵按下,進入停機模式,低功耗串口檢測到地址0x99可以喚醒。

上電后串口打印的信息:

波特率 115200,數據位 8,奇偶校驗位無,停止位 1

 

程序設計:

  系統棧大小分配:

 

  RAM空間用的DTCM:

 

  硬件外設初始化

硬件外設的初始化是在 bsp.c 文件實現:

/*
*********************************************************************************************************
*    函 數 名: bsp_Init
*    功能說明: 初始化所有的硬件設備。該函數配置CPU寄存器和外設的寄存器並初始化一些全局變量。只需要調用一次
*    形    參:無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_Init(void)
{
    /* 配置MPU */
    MPU_Config();
    
    /* 使能L1 Cache */
    CPU_CACHE_Enable();

    /* 
       STM32H7xx HAL 庫初始化,此時系統用的還是H7自帶的64MHz,HSI時鍾:
       - 調用函數HAL_InitTick,初始化滴答時鍾中斷1ms。
       - 設置NVIV優先級分組為4。
     */
    HAL_Init();

    /* 
       配置系統時鍾到400MHz
       - 切換使用HSE。
       - 此函數會更新全局變量SystemCoreClock,並重新配置HAL_InitTick。
    */
    SystemClock_Config();

    /* 
       Event Recorder:
       - 可用於代碼執行時間測量,MDK5.25及其以上版本才支持,IAR不支持。
       - 默認不開啟,如果要使能此選項,務必看V7開發板用戶手冊第xx章
    */    
#if Enable_EventRecorder == 1  
    /* 初始化EventRecorder並開啟 */
    EventRecorderInitialize(EventRecordAll, 1U);
    EventRecorderStart();
#endif
    
bsp_InitDWT();      /* 初始化DWT時鍾周期計數器 */       
    bsp_InitKey();        /* 按鍵初始化,要放在滴答定時器之前,因為按鈕檢測是通過滴答定時器掃描 */
    bsp_InitTimer();      /* 初始化滴答定時器 */
    bsp_InitLPUart();    /* 初始化串口 */
    bsp_InitExtIO();    /* 初始化FMC總線74HC574擴展IO. 必須在 bsp_InitLed()前執行 */    
    bsp_InitLed();        /* 初始化LED */    
    bsp_InitExtSDRAM(); /* 初始化SDRAM */
}

 

  MPU配置和Cache配置:

數據Cache和指令Cache都開啟。配置了AXI SRAM區(本例子未用到AXI SRAM)和FMC的擴展IO區。

/*
*********************************************************************************************************
*    函 數 名: MPU_Config
*    功能說明: 配置MPU
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void MPU_Config( void )
{
    MPU_Region_InitTypeDef MPU_InitStruct;

    /* 禁止 MPU */
    HAL_MPU_Disable();

    /* 配置AXI SRAM的MPU屬性為Write back, Read allocate,Write allocate */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x24000000;
    MPU_InitStruct.Size             = MPU_REGION_SIZE_512KB;
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_CACHEABLE;
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER0;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL1;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;

    HAL_MPU_ConfigRegion(&MPU_InitStruct);
    
    
    /* 配置FMC擴展IO的MPU屬性為Device或者Strongly Ordered */
    MPU_InitStruct.Enable           = MPU_REGION_ENABLE;
    MPU_InitStruct.BaseAddress      = 0x60000000;
    MPU_InitStruct.Size             = ARM_MPU_REGION_SIZE_64KB;    
    MPU_InitStruct.AccessPermission = MPU_REGION_FULL_ACCESS;
    MPU_InitStruct.IsBufferable     = MPU_ACCESS_BUFFERABLE;
    MPU_InitStruct.IsCacheable      = MPU_ACCESS_NOT_CACHEABLE;    
    MPU_InitStruct.IsShareable      = MPU_ACCESS_NOT_SHAREABLE;
    MPU_InitStruct.Number           = MPU_REGION_NUMBER1;
    MPU_InitStruct.TypeExtField     = MPU_TEX_LEVEL0;
    MPU_InitStruct.SubRegionDisable = 0x00;
    MPU_InitStruct.DisableExec      = MPU_INSTRUCTION_ACCESS_ENABLE;
    
    HAL_MPU_ConfigRegion(&MPU_InitStruct);

    /*使能 MPU */
    HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT);
}

/*
*********************************************************************************************************
*    函 數 名: CPU_CACHE_Enable
*    功能說明: 使能L1 Cache
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
static void CPU_CACHE_Enable(void)
{
    /* 使能 I-Cache */
    SCB_EnableICache();

    /* 使能 D-Cache */
    SCB_EnableDCache();
}

 

  每10ms調用一次蜂鳴器處理:

蜂鳴器處理是在滴答定時器中斷里面實現,每10ms執行一次檢測。

/*
*********************************************************************************************************
*    函 數 名: bsp_RunPer10ms
*    功能說明: 該函數每隔10ms被Systick中斷調用1次。詳見 bsp_timer.c的定時中斷服務程序。一些處理時間要求
*              不嚴格的任務可以放在此函數。比如:按鍵掃描、蜂鳴器鳴叫控制等。
*    形    參: 無
*    返 回 值: 無
*********************************************************************************************************
*/
void bsp_RunPer10ms(void)
{
    bsp_KeyScan10ms();
}

 

  主功能:

主程序實現如下操作:

  •  啟動一個自動重裝軟件定時器,每100ms翻轉一次LED2。
  •  K1鍵按下,進入停機模式,低功耗串口接收任意字節數據可以喚醒。
  •  K2鍵按下,進入停機模式,低功耗串口檢測到起始位可以喚醒。
  •  K3鍵按下,進入停機模式,低功耗串口檢測到地址0x99可以喚醒。
/*
*********************************************************************************************************
*    函 數 名: main
*    功能說明: c程序入口
*    形    參: 無
*    返 回 值: 錯誤代碼(無需處理)
*********************************************************************************************************
*/
int main(void)
{
    uint8_t ucKeyCode;    /* 按鍵代碼 */
    uint8_t ucReceive;
    

    bsp_Init();        /* 硬件初始化 */
    PrintfLogo();    /* 打印例程名稱和版本等信息 */
    PrintfHelp();    /* 打印操作提示 */
    
    HAL_EnableDBGStopMode(); /* 使能停機模式下,LPUART工程可以繼續調試 */
    __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI); /* 從停機模式喚醒后使用HSI時鍾 */
    __HAL_RCC_LPUART1_CLKAM_ENABLE();     /* 激活LPUART的自主模式,即停機狀態下可以繼續接收消息 */
    __HAL_UART_ENABLE_IT(&UartHandle, UART_IT_WUF);/* 使能喚醒中斷 */
    
    
    bsp_StartAutoTimer(0, 100);    /* 啟動1個100ms的自動重裝的定時器 */
    
    while (1)
    {
        bsp_Idle();        /* 這個函數在bsp.c文件。用戶可以修改這個函數實現CPU休眠和喂狗 */

        /* 判斷定時器超時時間 */
        if (bsp_CheckTimer(0))    
        {
            /* 每隔100ms 進來一次 */  
            bsp_LedToggle(2);
        }

        /* 按鍵濾波和檢測由后台systick中斷服務程序實現,我們只需要調用bsp_GetKey讀取鍵值即可。 */
        ucKeyCode = bsp_GetKey();    /* 讀取鍵值, 無鍵按下時返回 KEY_NONE = 0 */
        if (ucKeyCode != KEY_NONE)
        {
            switch (ucKeyCode)
            {
                case KEY_DOWN_K1:    /* K1鍵按下,進入停機模式,低功耗串口接收任意字節數據可以喚醒 */
                    /* 使能LPUART的停機喚醒 */
                    HAL_UARTEx_EnableStopMode(&UartHandle); 

                    /* 確保LPUART沒有在通信中 */
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

                    /* 接收到數據喚醒,即RXNE標志置位 */
                    WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_READDATA_NONEMPTY;
                    if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
                    {
                        Error_Handler(__FILE__, __LINE__);                        
                    }

                    /* 進入停機模式 */
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

                    /* 退出停機模式要重新配置HSE和PLL*/
                    SystemClock_Config();

                    /* 關閉LPUART的停機喚醒 */
                    HAL_UARTEx_DisableStopMode(&UartHandle);
                    
                    lpcomGetChar(LPCOM1, &ucReceive);
                    
                    printf("低功耗串口接收到數據 %x 后喚醒\r\n", ucReceive);
                    break;
                    
                case KEY_DOWN_K2:     /* K2鍵按下,進入停機模式,低功耗串口檢測到起始位可以喚醒 */
                    /* 使能LPUART的停機喚醒 */
                    HAL_UARTEx_EnableStopMode(&UartHandle); 

                    /* 確保LPUART沒有在通信中 */
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

                    /* 接收起始位喚醒 */
                    WakeUpSelection.WakeUpEvent = UART_WAKEUP_ON_STARTBIT;
                    if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
                    {
                        Error_Handler(__FILE__, __LINE__);                        
                    }

                    /* 進入停機模式 */
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

                    /* 退出停機模式要重新配置HSE和PLL*/
                    SystemClock_Config();

                    /* 關閉LPUART的停機喚醒 */
                    HAL_UARTEx_DisableStopMode(&UartHandle);
                    
                    lpcomGetChar(LPCOM1, &ucReceive);
                    
                    printf("低功耗串口檢測到起始位(數據) %x 后喚醒\r\n", ucReceive);
                    break;
                    
                case KEY_DOWN_K3:    /* K3鍵按下,進入停機模式,低功耗串口檢測到地址0x99可以喚醒 */
                    /* 使能LPUART的停機喚醒 */
                    HAL_UARTEx_EnableStopMode(&UartHandle); 

                    /* 確保LPUART沒有在通信中 */
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_BUSY) == SET){}
                    while(__HAL_UART_GET_FLAG(&UartHandle, USART_ISR_REACK) == RESET){}

                    /* 接收地址0x99(發送的數據MSB位要為1),可以喚醒 */
                    WakeUpSelection.WakeUpEvent   = UART_WAKEUP_ON_ADDRESS;
                    WakeUpSelection.AddressLength = UART_ADDRESS_DETECT_7B;
                    WakeUpSelection.Address       = 0x19;
                    if (HAL_UARTEx_StopModeWakeUpSourceConfig(&UartHandle, WakeUpSelection)!= HAL_OK)
                    {
                        Error_Handler(__FILE__, __LINE__);                        
                    }
                    
                    CLEAR_BIT(LPUART1->CR1, USART_CR1_RXNEIE); /* 關閉串口接收中斷 */
                    
                    /* 進入停機模式 */
                    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

                    /* 退出停機模式要重新配置HSE和PLL*/
                    SystemClock_Config();

                    SET_BIT(LPUART1->CR1, USART_CR1_RXNEIE);  /* 使能串口接收中斷 */
                    
                    /* 關閉LPUART的停機喚醒 */
                    HAL_UARTEx_DisableStopMode(&UartHandle);
                    
                    break;

                default:
                    /* 其它的鍵值不處理 */
                    break;
            }
        }
    }
}

 

66.9 總結

本章節就為大家講解這么多, 重點是低功耗串口的三種喚醒方式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM