Skocz do zawartości

STM32F103RB - oszczędzanie energii


chuck9271

Pomocna odpowiedź

Witam drogich użytkowników, mam problem z poprawnym zaprogramowaniem trybu oszczędzania energii w STM32. Znalazłem przykładowy kod który mógłbym wykorzystać aczkolwiek

nie wiem jak poprawnie zdefiniować funkcję NVIC wywołaną na samym początku listingu. Gdyby ktoś miał chwilę i mógł mi z tym pomóc byłbym bardzo wdzięczny. 🙂 Dopiero zaczynam

przygodę z STM32 i nie bardzo mogę się w tym wszystkim połapać. Z góry dzięki za pomoc.

int main(void)
{
 EXTI_InitTypeDef EXTI_InitStructure;

 DBGMCU_Config(DBGMCU_SLEEP, ENABLE);

 RCC_Conf();
 NVIC_Conf();      // ?????????
 GPIO_Conf();

 // Linia 17 jako zdarzenie (RTC)
 EXTI_StructInit(&EXTI_InitStructure);
 EXTI_InitStructure.EXTI_Line = EXTI_Line17;
 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
 EXTI_InitStructure.EXTI_LineCmd = ENABLE;
 EXTI_Init(&EXTI_InitStructure);

 PWR_BackupAccessCmd(ENABLE); // Zezwolenie na dostep do Backup domain

 BKP_DeInit();

 RCC_LSEConfig(RCC_LSE_ON);  // Wlacz LSE

 while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);   // Czekaj, az wystartuje

 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);   // LSE zrodlem sygn. zeg. dla RTC

 RCC_RTCCLKCmd(ENABLE);  // Wlacz taktowanie RTC

 RTC_WaitForSynchro();   // Czekaj na synchronizacje RTC z APB
 RTC_WaitForLastTask();

 RTC_SetPrescaler(32768);  // Zliczane beda impulsy co 1s
 RTC_WaitForLastTask();

 while (1)
 {
   NVIC_SystemLPConfig(NVIC_LP_SEVONPEND, ENABLE);
   // Najwyzszy priorytet
   NVIC_SETPRIMASK();

   RTC_SetAlarm(RTC_GetCounter()+ 30);   // Wybudzenie co 30s
   RTC_WaitForLastTask();  

   GPIO_ResetBits(GPIOC, GPIO_Pin_6);    

   __WFE();  // Wait for event

   GPIO_SetBits(GPIOC, GPIO_Pin_6);
   delay_ms(2000);
 }
}

Ewentualnie może ktoś doradzi mi jak prościej zaprogramować uC by przechodził w tryb uśpienia i wybudzał się co określony czas by wykonać pomiary?

Link do komentarza
Share on other sites

W CubeMX od ST możesz skonfigurować wybudzanie procka poprzez wyklikanie konfiguracji 😉

Na forum jest cały poradnik na temat Cube`a. Biblioteka HAL jest rozwijana ta której używasz już nie.

Jak chcesz wiedzieć co naprawdę dzieje się w procku to polecam reference manual (rejestry) + CMSIS (od strony 79): LINK

NVIC konfiguruje się poprzez CMSIS np. NVIC_Enable. O NVIC możesz poczytać w programming manual

Link do komentarza
Share on other sites

Ok skorzystałem z poradnika do STM32, kod działa rzeczywiście aczkolwiek procesor sam się wybudza od razu (migają diody). Wie ktoś co może być przyczyną? Z poradnika wyczytałem, że powodem jest debugger ale nie wiem co w nim jest nie tak "OpenOCD debugger sam ustawia sobie bit DBG_STOP, jeśli debugger nie będzie podłączony to bit nie będzie ustawiany".

Oto mój kod:

int main(void)
{


GPIO_InitTypeDef gpio;
	RCC->APB2ENR = RCC_APB2ENR_IOPBEN | RCC_APB2ENR_TIM1EN;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);


GPIO_StructInit(&gpio);
 gpio.GPIO_Pin = GPIO_Pin_5;
 gpio.GPIO_Speed = GPIO_Speed_2MHz;
 gpio.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_Init(GPIOA, &gpio);

 GPIO_StructInit(&gpio);
 	 gpio.GPIO_Pin = GPIO_Pin_6;
 	 gpio.GPIO_Speed = GPIO_Speed_2MHz;
 	 gpio.GPIO_Mode = GPIO_Mode_Out_PP;
 	 GPIO_Init(GPIOC, &gpio);


 TIM1->PSC = 10000;
 TIM1->ARR = 500;
 TIM1->DIER = TIM_DIER_UIE;
 TIM1->CR1 = TIM_CR1_CEN;

 NVIC_EnableIRQ(TIM1_UP_IRQn);

 SysTick_Config(800000);

 SCB->SCR |= SCB_SCR_SLEEPDEEP;

 while (1){
 __WFI();
 }

 } /* main */




 __attribute__((interrupt)) void TIM1_UP_IRQHandler(void){
 if (TIM1->SR & TIM_SR_UIF){
 TIM1->SR = ~TIM_SR_UIF;
 GPIOA->ODR ^= GPIO_Pin_5;
 }
 }
 __attribute__((interrupt)) void SysTick_Handler(void){
    GPIOC->ODR ^= GPIO_Pin_6;
 }

PS. Używam oprogwamowania Ac6 System Workbench for STM32

Link do komentarza
Share on other sites

1. Dlaczego funkcja z przerwania jest w main ?

2.Co to za dziwna konstrukcja ?

  while (1){ 
   __WFI(); 
   } 

   } /* main */ 

3. WFI to wybudzenie procesora, gdy nastąpi przerwanie 😉

4.Przeczytaj dokładnie cały rozdział jeszcze raz, na końcu masz kompletny program. Przetestuj go bez zmian potem się baw i testuj.

Ten kod chyba się nawet nie skompiluje ?

Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Program działa poprawnie - wybudza się co każde przerwanie, czyli po sekundzie (Przerwanie do SysTick`a -- 1 Hz) i po około kolejnych 600 milisekundach (Przerwanie od Timera -- częstotliwość 1,6 Hz (8Mhz/(10001*501) )). Jak się wybudzi to mrugnie diodą z pinu 6 zasypia, budzi się i mruga diodą z pinu 5, zasypia i tak w kółko.

Ustaw sobie breakpoint`a na GPIOA->ODR ^= GPIO_Pin_5; to zobaczysz

Link do komentarza
Share on other sites

Chyba jest trochę inaczej niż mówisz bo miga dioda PC6 a dioda PA5 świeci cały czas więc procesor w ogóle nie wchodzi w tryb uśpienia. Nawet gdy wyrzuciłem całkowicie te przerwania to stm i tak nie wchodził w tryb stop, bo pobierał tyle samo energii co zwykle czyli 40-50mA w zależności od migania diody LD1 (od programatora). W trybie uśpienia chyba nie powinna żadna dioda się świecić a świeca jak zwykle. Nie wiem co tu się dzieje ale on w żaden sposób nie chce się uśpić. Mój układ dokładnie to Nuleco STM32F103RB.

Ustawianie breakpoint'a przy debugowaniu daje ciągłe świecenie obu led.

Link do komentarza
Share on other sites

1.Powinno działać, nie mam jak sprawdzić, nie mam przy sobie zestawu z F103. Najszybciej poniedziałek/wtorek.

2. Sprawdź czy w pliku który nazywa się (nie pamiętam dokładnie 😋 ) startup_stm32f103xx_md.s w pfnvectors (tablica wektorów) masz w większości pozycji po słowie .word same zera czy konkretne nazwy.Jak używałem workbencha to miał same zera i wtedy przerwania nie działają - używam attolic truestudio i w nim jest wypełniona tablica wektorów.

#include <stm32f10x.h> //workbench moze miec troche inne nazwy plikow
#include <system_stm32f10x.h>

typedef enum {
/* Push-Pull */
gpio_mode_output_PP_2MHz = 2,
gpio_mode_output_PP_10MHz = 1,
gpio_mode_output_PP_50MHz = 3,
/* Open-Drain */
gpio_mode_output_OD_2MHz = 6,
gpio_mode_output_OD_10MHz = 5,
gpio_mode_output_OD_50MHz = 7,
/* Push-Pull */
gpio_mode_alternate_PP_2MHz = 10,
gpio_mode_alternate_PP_10MHz = 9,
gpio_mode_alternate_PP_50MHz = 11,
/* Open-Drain */
gpio_mode_alternate_OD_2MHz = 14,
gpio_mode_alternate_OD_10MHz = 13,
gpio_mode_alternate_OD_50MHz = 15,
/* Analog input (ADC) */
gpio_mode_input_analog = 0,
/* Floating digital input. */
gpio_mode_input_floating = 4,
/* Digital input with pull-up/down (depending on the ODR reg.). */
gpio_mode_input_pull = 8
} GpioMode_t;

void gpio_pin_cfg(GPIO_TypeDef * const port, uint32_t pin, GpioMode_t mode);

enum { SRAM_BB_REGION_START = 0x20000000 };
enum { SRAM_BB_REGION_END = 0x200fffff };
enum { SRAM_BB_ALIAS = 0x22000000 };
enum { PERIPH_BB_REGION_START = 0x40000000 };
enum { PERIPH_BB_REGION_END = 0x400fffff };
enum { PERIPH_BB_ALIAS = 0x42000000 };
#define SRAM_ADR_COND(adres) ( (uint32_t)&adres >= SRAM_BB_REGION_START && (uint32_t)&adres <= SRAM_BB_REGION_END )
#define PERIPH_ADR_COND(adres) ( (uint32_t)&adres >= PERIPH_BB_REGION_START && (uint32_t)&adres <= PERIPH_BB_REGION_END )
#define BB_SRAM2(adres, bit) ( SRAM_BB_ALIAS + ((uint32_t)&adres - SRAM_BB_REGION_START)*32u + (uint32_t)(bit*4u) )
#define BB_PERIPH(adres, bit) ( PERIPH_BB_ALIAS + ((uint32_t)&adres - PERIPH_BB_REGION_START)*32u + (uint32_t)(__builtin_ctz(bit))*4u)
/* bit - bit mask, not bit position! */
#define BB(adres, bit) *(__IO uint32_t *)( SRAM_ADR_COND(adres) ? BB_SRAM2(adres, bit) : ( PERIPH_ADR_COND(adres) ? BB_PERIPH(adres, bit) : 0 ))
#define BB_SRAM(adres, bit) *(__IO uint32_t *)BB_SRAM2(adres, bit)

int main(void)
{
RCC->APB2ENR = RCC_APB2ENR_IOPBEN | RCC_APB2ENR_TIM1EN;
gpio_pin_cfg(GPIOA, (1<<5), gpio_mode_output_PP_2MHz);
gpio_pin_cfg(GPIOC, (1<<6), gpio_mode_output_PP_2MHz);
TIM1->PSC = 10000;
TIM1->ARR = 500;
TIM1->DIER = TIM_DIER_UIE;
TIM1->CR1 = TIM_CR1_CEN;
NVIC_EnableIRQ(TIM1_UP_IRQn);
SysTick_Config(800000);
SCB->SCR |= SCB_SCR_SLEEPDEEP;
while (1)
{
	__WFI();
}
} /* main */


void gpio_pin_cfg(GPIO_TypeDef * const port, uint32_t pin, GpioMode_t mode)
{
pin = __builtin_ctz(pin)*4;
uint32_t volatile * cr_reg;
uint32_t cr_val;
cr_reg = &port->CRL;
if (pin > 28){
	pin -= 32;
	cr_reg = &port->CRH;
}
cr_val = *cr_reg;
cr_val &= ~((uint32_t)(0x0f << pin));
cr_val |= (uint32_t)(mode << pin);
*cr_reg = cr_val;
}
__attribute__((interrupt)) void TIM1_UP_IRQHandler(void)
{
if (TIM1->SR & TIM_SR_UIF)
{
	TIM1->SR = ~TIM_SR_UIF;
	BB(GPIOA->ODR, (1<<5)) ^=1;
}
}

__attribute__((interrupt)) void SysTick_Handler(void)
{
BB(GPIOC->ODR, (1<<6)) ^= 1;
}
Link do komentarza
Share on other sites

Dziękuję za zainteresowanie!

Wrzuciłem Twój program, zero błędów ale ja dalej różnicy nie widzę w poborze prądu. Może Nucleo tyle pobiera po prostu w trybie low power (40-50mA)? Chociaż to się wydaje bez sensu całkowicie... Nie wiem jestem bezradny. Wydaje mi się że powinno wszysto gasnąć na płytce gdy wchodzi w tryb low power ale te diody dalej świecą jak wcześniej.

Jest jeszcze jedna sprawa, zauważyłem że rzeczywiście STM się chyba przełącza w tryb low power ponieważ program ST-Link po wgraniu Twojego programu nie łączy się w debugowanie dopiero jak ustawie w parametrach "połącz w trybie low power" to łączy się z uC. Załóżmy, że działa ten tryb low power ale jeśli jego efekty są takie, że nie ma żadnej oszczędności energii to jest to bez sensu. Różnica może być tylko taka, że ten tryb zatrzyma wykonywanie mojego programu w którym mam ADC co da oszczędnośc prądu około 20mA ale to wciąż mało gdyż cały układ pobiera dalej te 50mA.

Masz jakieś informacje ile mA pobiera sama płytka Nucleo? Być może wszystko jest w porządku tylko ja oczekuje niemożliwego?

Link do komentarza
Share on other sites

Wyłączą sie tylko diody sterowane procesorem,diody przy złączu usb są od programatora i jedna dioda oznacza zasilanie procesora i ona bedzie świecić cały czas.

Jak mierzysz pobór prądu ?

Zasilanie może być tak jak teraz podłączone, ale jak chcesz zmierzyć pobór procesora to zdejmij zworkę IDD koło procka i tam podepnij amperomierz tj. Zamiast zworki.

Link do komentarza
Share on other sites

Tak właśnie czułem... To bieda, nie zejde z tym prądem niżej :/. Mierzyłem ogólny prąd jaki ciągnie całe nucleo. Gdy mierze prąd na IDD to rzeczywiście jest różnica bez Twojego programu ~1.8mA a z programem ~0.43mA. Ale taka oszczędność to dla mnie żadna oszczędność. Chce zejść z całym prądem pobieranym przez układ myślałem, że różnice będą znaczące a tu klapa.

W ogóle dlaczego samo Nucleo pobiera aż tyle energii??

Mam jeszcze jedno ważne pytanie, zasilając Nucleo z pinu 3.3V jest potrzebna do prawidłowej pracy zworka E5V? Gdy ją wypinam widzę, że jest zasilanie procka ale odłącza się programator i wtedy mam duży spadek pobieranego prądu do około 10mA, to by była dla mnie już jakaś szansza na osiągnięcie celu.

I kolejna sprawa Twój kod to kod wybudzający w jakikolwiek sposób procesor z low power czy on tylko wchodzi w ten tryb bo nie widzę żeby się wybudzał? A muszę mieć takie coś co będzie wybudzało procka co jakieś 15min żeby wykonało mój program i poszło spać dalej.

Link do komentarza
Share on other sites

chuck9271, nucleo zużywa sporo prądu, bo jest to płytka ewaluacyjna. Poza tym jest na niej programator, który nie jest i właściwie nie ma powodu być energooszczędny.

Jeśli chcesz uzyskać mniejszy pobór prądu to musisz odłączyć programator - chyba najłatwiej jest przełamać nucleo na dwie części. Do progrmowania będziesz musiał podłączać kabelki, ale sam moduł z mikrokontrolerem będziesz mógł wykorzystać w docelowym zastosowaniu.

Przemyśl jeszcze taktowanie procesora - domyślnie jest to 64MHz, więc całkiem sporo. Do migania diodą wystarczy Ci bezpośrednio 8MHz z generatora RC, wyłączysz PLL i sporo mocy zaoszczędzisz.

Link do komentarza
Share on other sites

ps19, to trochę zależy od środowiska programowania. Oczywiście MCU startuje z 8MHz RC, ale najczęściej jeszcze przed wejściem do main() wywoływana jest funkcja SystemInit(), która uruchamia PLL. Dlatego napisałem o domyślnym 64MHz dla nucleo. Ale jak jest w rzeczywistości zależy od kodu, którego chuck9271 nie dołączył, więc to tylko zgadywanie.

Link do komentarza
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.