STM32串口喚醒STOP模式的實現

前言

STM32常見的低功耗模式有三種:睡眠模式、STOP模式以及待機模式,STM32L系列還有其他低功耗模式。這裏主要講的是STOP模式,STOP模式可以通過外部中斷或事件喚醒,但是不能通過串口中斷喚醒,因爲串口中斷本身不是外部中斷,那麼如何才能實現串口喚醒STOP模式呢?

因爲我這裏只是爲了做驗證,爲了快速驗證,我也就沒有用RT-Thread的PM電源管理組件進入STOP模式,感興趣的讀者可以用RT-Thread的電源管理組件去實現進行STOP模式。

 

一、爲什麼要串口喚醒STOP模式?

想象一下,在某些場合,如果你有一個無線通信模塊(例如ESP8266、SIM800C)和STM32通過串口發送AT命令來對接服務器實現與服務器的數據交互,那麼如果在沒有進行數據交互的時候,我們是不是可以讓STM32進入STOP模式來達到省電的狀態,從而讓電池續航更長。例如:STM32+ESP8266與後臺服務器進行數據交互,當不用發送數據完畢,等待下次發送數據或等待後臺下發數據給設備的這段時間可以讓STM32進入STOP模式來達到省電,當後臺服務器下發數據給設備的時候,我們可以向讓後臺發送一個喚醒設備的指令,ESP8266接收到後臺的這條指令之後通過串口下發給STM32,那麼就可以喚醒STM32了,這時候STM32就可以繼續接收後臺下發的數據。

 

二、串口喚醒STOP模式的思路

1、我們知道STOP模式只能外部中斷或事件喚醒,那麼想象一下,在STM32進行STOP模式之前,是不是可以先將UART_RX對應的GPIO引腳配置爲外部中斷引腳,而串口接收到字符相當於接收到01010...這樣的高低電平,從二可以喚醒串口,當喚醒之後,我們再馬上重新初始化串口,把UART_RX對應的GPIO引腳配置爲接收中斷模式?答案當然是可以的。

2、喚醒之後的程序是從哪裏開始執行?答案是從進行STOP模式之前的那個地方重新開始執行,一會進行驗證。

 

三、串口喚醒STOP模式實驗

光說不練都是假把式,接下來進行實驗。

1、實驗平臺:中國移動物聯網OneNET NB開發板(板載STM32)。

2、STM32F103RET6、12M外部晶振、串口3進行實驗。

3、操作系統:RT-Thread。

4、用RT-Thread創建兩個線程,一個線程用於讀取按鍵是否按下,按下則調用進入STOP模式函數進入STOP模式,另一個線程讀取串口接收到的數據。

1、如何進行STOP模式?

實驗時用的是標準庫,在這裏主要實現在進入STOP模式前將RX對應的GPIO引腳配置爲外部中斷模式以及進入STOP模式,代碼如下:

/**************************************************************
函數名稱:system_enter_stop
函數功能:系統進入STOP模式
輸入參數:無
返 回 值:無
備    注:無
**************************************************************/
void system_enter_stop(void)
{
        uart_exti_init(); /* 進入STOP模式前配置RX引腳爲外部中斷模式 */
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR , ENABLE); /* 開電源管理時鐘 */
	//PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI); /* 進入STOP模式,外部中斷喚醒 */
	PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFE); /* 進入STOP模式,外部中斷或事件喚醒 */
}

 

2、配置RX對應的GPIO引腳爲外部中斷模式

這裏採用RT-Thread的PIN設備進行配置,在配置之前需要先關閉UART中斷、復位UART、復位GPIO,然後在進行配置爲外部中斷模式,代碼如下:

/**************************************************************
函數名稱:uart_exti_init
函數功能:RX引腳配置爲外部中斷
輸入參數:無
返 回 值:無
備    注:無
**************************************************************/
void uart_exti_init(void)
{
        /* 關閉UART中斷、復位UART、復位GPIO */
	USART_ITConfig(USART3, USART_IT_RXNE, DISABLE);
	USART_Cmd(USART3, DISABLE);
	GPIO_DeInit(GPIOB);
	USART_DeInit(USART3);

	/* 配置RX對應的GPIO引腳爲外部中斷模式 */
	rt_pin_mode(PIN_UART3_RX, PIN_MODE_INPUT_PULLUP);
	rt_pin_attach_irq(PIN_UART3_RX, PIN_IRQ_MODE_FALLING, uart_exti_callback, RT_NULL);
	rt_pin_irq_enable(PIN_UART3_RX, PIN_IRQ_ENABLE);
}

 

3、接收中斷回調函數

在上面的配置中,有一個接收回調函數uart_exti_callback,就是在發送中斷的時候要執行的事情,在接收回調函數裏面,我們主要實現SystemInit,重新初始化串口,代碼如下:

/**************************************************************
函數名稱:uart_exti_callback
函數功能:RX引腳外部中斷喚醒回調函數
輸入參數:args:回調函數入口參數
返 回 值:無
備    注:無
**************************************************************/
void uart_exti_callback(void *args)
{
	SystemInit();
	uart_reinit();		/* 重新初始化串口 */
	rt_kprintf("wake up\r\n");

}

 

4、進入STOP模式的線程

這裏,創建一個線程來實現判斷是否按鍵按下,按下則調用system_enter_stop函數進入STOP模式,同時爲了驗證喚醒之後時鐘正常以及程序是從進行STOP模式之前的那個地方重新開始執行,我們設計LED燈500ms亮500ms滅,再一個計數變量,每隔1秒自動加1並打印,代碼如下:

static void sleep_thread_entry(void *parameter)
{
	unsigned char key;
	unsigned int count=0;
	
	while(1)
	{
		key = key_scan(0);

		if(key == KEY4_PRES)
		{
			rt_kprintf("system_enter_stop\r\n");
			system_enter_stop();
		}
		LED1(1);
		rt_thread_mdelay(500);
		LED1(0);
		rt_thread_mdelay(500);
		rt_kprintf("count:%d\r\n",count);
		count++;
	}
}

 

5、實驗操作和現象

1、開機之後,LED閃爍,串口打印count每隔1秒加1的值,等待一小會按下按鍵KEY4進入STOP模式:

FinSH抓取的串口打印信息

2、對比進入STOP模式前和STOP模式之後的電流情況(這裏進入STOP模式之後電流還是很大是因爲我們板子還接了其他耗電的模塊,我們這對比電流有沒有降下來就可以了),很明顯,電流降下來了:

進入STOP模式前的電流

 

進入STOP模式後的電流
進入STOP模式後的電流

 

3、通過串口發送一個字符“A”,喚醒了STM32,這時候串口並不會打印字符“A”,因爲喚醒之後要重新初始化串口,第二次發送字符“A”才能顯示,這時候,我們觀察FinSH打印出來的信息,可以看到count是從9開始打印,說明STOP喚醒之後會從原來進入STOP模式之前的地方重新執行代碼:

驗證代碼的執行情況

 

喚醒之後第二次發一個字符能正常打印

 

4、接下來,我們再次按下KEY4重新然STM32進入STO模式,然後發送一個比較長的字符串來喚醒STM32,例如發“ABCDEFGHIJKLMNOPQ1234567890”,這時候,我們發現第一次發送之後,竟然會有字符出來,不是說沒有嗎?而且這些字符和我們發送的不一樣,少了,第二次才正常:

喚醒之後打印字符不正常

 

 

四、串口喚醒存在的問題

1、上面我們提到,發送一個字符喚醒就很正常,而發送比較長的字符串喚醒卻出現了不然正常的現象,這是爲什麼呢?想象一下m如果你是發一串很長的數據來喚醒串口,這串數據也是通過0101010等二進制來發送的,當RX引腳被觸發中斷喚醒MCU之後,喚醒之後串口初始化完成了,剩餘的數據也就會接着以010101的高低電平發給STM32的串口,有可能導致有些字符的01丟失了一部分(例如上面出現了K567890),從而可以接下來的字符會打印出來。如果是發一個字符,一個字符的01010101其實也就8位,發送很快的,喚醒之後都已經發送結束了,所以就會直接喚醒,也就不會接收這個字符,只有第二次發送的時候纔會接收到這個字符。

 

由於CSDN博客上傳不了附件,我也不想上傳到CSND資源去讓大家還要用積分下載,所以我將實驗代碼上傳到了原子論壇帖子附件:http://www.openedv.com/thread-289358-1-1.html

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章