雖然這個稱做出來的樣子不是便攜式,外觀有些簡陋(自己用木頭架子搭起來的),但是對於使用兩節3.7V的18650的鋰電池供電來說,還是需要設計一下低功耗的。
稱的使用頻率不高,不能讓觸摸屏一直亮着,也不能讓單片機一直處於工作狀態,那樣也太不節能、太不綠色了。
一、STM32低功耗設計
查閱stm32參考手冊,可以看到低功耗有以下三種:
我想要的效果是在稱上沒有放任何東西的時候,如果持續30秒沒有放置,立即進入低功耗模式,但是SRAM和寄存器中的數據不要丟失,在這個基礎上,功耗盡量小就可以了。
對比上面的模式說明,我需要進入的是停止模式。
停止模式是在Cortex™-M3的深睡眠模式基礎上結合了外設的時鍾控制機制,在停止模式下電壓
調節器可運行在正常或低功耗模式。此時在1.8V供電區域的的所有時鍾都被停止, PLL、 HSI和
HSE RC振盪器的功能被禁止, SRAM和寄存器內容被保留下來。
關鍵的一點是在停止模式下,所有的I/O引腳都保持它們在運行模式時的狀態。
確定了進入的是停止模式,那么如何才能進入停止模式呢?
其實這么多操作,ST全都給我們封裝在了一個庫函數中:void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry)
具體內容是:
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry) { uint32_t tmpreg = 0; /* Check the parameters */ assert_param(IS_PWR_REGULATOR(PWR_Regulator)); assert_param(IS_PWR_STOP_ENTRY(PWR_STOPEntry)); /* Select the regulator state in STOP mode ---------------------------------*/ tmpreg = PWR->CR; /* Clear PDDS and LPDS bits */ tmpreg &= CR_DS_MASK; /* Set LPDS bit according to PWR_Regulator value */ tmpreg |= PWR_Regulator; /* Store the new value */ PWR->CR = tmpreg; /* Set SLEEPDEEP bit of Cortex System Control Register */ SCB->SCR |= SCB_SCR_SLEEPDEEP; /* Select STOP mode entry --------------------------------------------------*/ if(PWR_STOPEntry == PWR_STOPEntry_WFI) { /* Request Wait For Interrupt */ __WFI(); } else { /* Request Wait For Event */ __WFE(); } /* Reset SLEEPDEEP bit of Cortex System Control Register */ SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP); }
我們只需要在需要低功耗的時候,調用這個函數就行了。
但是我們要選擇自己需要喚醒時的方式——中斷WFI(wait for interrupt) or 事件WFE(wait for event)
這兩個有點繞:事件是中斷的觸發源,開放了對應的中斷屏蔽位,則事件可以觸發相應的中斷。在STM32中,中斷與事件不是等價的,一個中斷肯定對應一個事件,但一個事件不一定對應一個中斷。
比如我想要使用外部按鍵喚醒停止中的STM32,那么需要把按鍵引腳映射在了外部中斷線上,然后對應的上面配置成中斷喚醒方式:
PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);
在按鍵的中斷函數中配置退出低功耗時的操作即可。
void EXTI15_10_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line15) != RESET) //確保是否產生了EXTI Line中斷 { Restart_From_Low_Power(); //停機喚醒后需要啟動HSE EXTI_ClearITPendingBit(EXTI_Line15); //清除中斷標志位 } }
關於退出時的操作,參考手冊上說:
HSI時鍾是板子上的8M晶振提供的,而我們使用的是72M的時鍾,所以還需要重新配置一下時鍾:
//啟動並配置stm32 ErrorStatus HSEStartUpStatus; //使能 HSE RCC_HSEConfig(RCC_HSE_ON); //等待 HSE 准備就緒 HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { //使能 PLL RCC_PLLCmd(ENABLE); //等待 PLL 准備就緒 while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } //選擇PLL作為系統時鍾源 RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); //等待PLL被選擇為系統時鍾源 while(RCC_GetSYSCLKSource() != 0x08) { } }
二、觸摸屏低功耗設計
使用的是HMI串口屏,一個指令即刻讓屏幕進入sleep模式
//HMI息屏 void HMI_Sleep_Mode(void) { sprintf(buf,"sleep=1"); HMI_Send_String(buf); Delay_ms(20); }
三、CS1237低功耗設計
還是查看芯片手冊:
void CS1237_power_down(void) { SCLK_1; CS1237_delay_us(100); SCLK_1; CS1237_delay_us(100); } //cs1237重新喚醒,SCLK回到低電平並保持10us void CS1237_restart(void) { SCLK_0; CS1237_delay_us(20); }
四、進入低功耗的判斷
一開始考慮使用定時器定時對比讀出的重量數據,如果數據在30s內沒有變化並且一直小於1g,則進入低功耗模式,但是又怕定時器的中斷正好發生在CS1237的讀寫過程中,
這樣會打斷時序,造成讀數誤差。
我看了一下我程序主循環循環一次的用時,大概在0.2s左右,其實這個也能當做一個基准,因為每次循環的時間都是差不多的。
那么我可以每循環一次就進行一次數據對比,每滿足上面的情況就+1,當循環計數150次的時候,進入低功耗。否則清零計數。
這樣每次進入低功耗的時間其實都是相差無幾,而且節省了一個定時器。運用循環體本身的時間作為計時標志。
//下面是關於進入低功耗的判斷 // 仿真發現在沒有收到觸摸屏的按下時,循環一次的時間大致為4s,這樣省去了一個定時器,避免了中斷 low_power_weight_1 = now_weight; if((low_power_weight_1 < 1) && ((low_power_weight_1-low_power_weight_2 < 1) || (low_power_weight_2-low_power_weight_1 < 1))) { low_power_num++; } else { low_power_num = 0; low_power_weight_2 = low_power_weight_1; } //重量低於1g並且在40秒內沒有變化,即開始進入低功耗 if(low_power_num >= 80) { //計數清零,准備下一次的計數 low_power_num = 0; //蜂鳴器首先響用來提示 Beep_Warning_Slowly(3); //進入低功耗 Low_Power_Mode(); }
參考資料:
https://www.cnblogs.com/yangguang-it/p/7441756.html
https://www.jianshu.com/p/540fff36fcc0