LinkedIn YouTube Facebook
Szukaj

Wstecz
IoT

Precyzyjne opóźnienia w połączeniu z trybami oszczędzania energii w STM32, część 1

List. 2. Implementacja funkcji opóźniających umieszczona w pliku sleep.c

#include "check.h"
#include "clock.h"
#include "sleep.h"
#include 
#include 
#include 
#include 
#include 
#include 

/* Licznik dla opóźnień mikrosekundowych,
   zwiększany z częstotliwością 1 MHz */
#define US_TIM                TIM2
#define US_TIM_IRQn           TIM2_IRQn
#define US_RCC_APB1Periph_TIM RCC_APB1Periph_TIM2
#define US_TIM_IRQHandler     TIM2_IRQHandler

/* Licznik dla opóźnień milisekundowych,
   zwiększany z częstotliwością 2 kHz */
#define MS_TIM                TIM3
#define MS_TIM_IRQn           TIM3_IRQn
#define MS_RCC_APB1Periph_TIM RCC_APB1Periph_TIM3
#define MS_TIM_IRQHandler     TIM3_IRQHandler

static unsigned usCalibration = 0;

void USleep(unsigned count) {
  if (count > usCalibration)
    count -= usCalibration;
  else if (count > 0)
    count = 1; /* minimalna możliwa wartość */
  else
    return; /* count == 0 */

  US_TIM->CNT = 0;
  US_TIM->CCR1 = count;
  US_TIM->CR1 |= TIM_CR1_CEN;
  do
    __WFE();
  while (US_TIM->CR1 & TIM_CR1_CEN);
}

void US_TIM_IRQHandler(void) {
  if (TIM_GetITStatus(US_TIM, TIM_IT_CC1)) {
    TIM_ClearITPendingBit(US_TIM, TIM_IT_CC1);
    US_TIM->CR1 &= ~TIM_CR1_CEN;
    __SEV();
  }
}

void MSleep(unsigned count) {
  if (count > 0) {
    MS_TIM->CNT = 0;
    MS_TIM->CCR1 = count << 1; /* tyka 2 kHz */ MS_TIM->CR1 |= TIM_CR1_CEN;
    do
      __WFE();
    while (MS_TIM->CR1 & TIM_CR1_CEN);
  }
}

void MS_TIM_IRQHandler(void) {
  if (TIM_GetITStatus(MS_TIM, TIM_IT_CC1)) {
    TIM_ClearITPendingBit(MS_TIM, TIM_IT_CC1);
    MS_TIM->CR1 &= ~TIM_CR1_CEN;
    __SEV();
  }
}

/* #define PWR_REGULATOR_MODE PWR_Regulator_ON */
#define PWR_REGULATOR_MODE PWR_Regulator_LowPower

static unsigned hclkMHz;

void Sleep(unsigned count) {
  if (count > 0) {
    /* Alarm zgłaszany jest w ostatnim cyklu oscylatora RTC
       odpowiadającego sekundzie, w której alarm należy
       zgłosić, więc count <= czas uśpienia < count + 1. */
    RTC_WaitForLastTask();
    RTC_SetAlarm(RTC_GetCounter() + count);
    RTC_WaitForLastTask();

    do
      PWR_EnterSTOPMode(PWR_REGULATOR_MODE, PWR_STOPEntry_WFE);
    while (!RTC_GetFlagStatus(RTC_IT_ALR));

    RTC_WaitForLastTask();
    RTC_ClearITPendingBit(RTC_IT_ALR);

    /* Po obudzeniu trzeba przywrócić pracę oscylatorów. */
    ClockConfigure(hclkMHz);
  }
}

void Standby(unsigned time) {
  PWR_WakeUpPinCmd(ENABLE);
  RTC_WaitForLastTask();
  RTC_SetAlarm(RTC_GetCounter() + time);
  RTC_WaitForLastTask();
  PWR_EnterSTANDBYMode();
}

int SleepConfigure(unsigned freqMHz) {
  TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
  TIM_OCInitTypeDef       TIM_OCInitStruct;
  NVIC_InitTypeDef        NVIC_InitStruct;
  EXTI_InitTypeDef        EXTI_InitStruct;

  hclkMHz = freqMHz;

  /* Kalibracja 1 us + 120 cykle zegara */
  usCalibration = 1U + (120U / freqMHz);

  RCC_APB1PeriphClockCmd(US_RCC_APB1Periph_TIM |
                         MS_RCC_APB1Periph_TIM |
                         RCC_APB1Periph_PWR |
                         RCC_APB1Periph_BKP,
                         ENABLE);

  TIM_TimeBaseStructInit(&TIM_TimeBaseStruct);
  TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;

  TIM_OCStructInit(&TIM_OCInitStruct);
  TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_Timing;

  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
  NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;

  /* Konfiguruj licznik mikrosekundowy na 1 MHz. */
  TIM_TimeBaseStruct.TIM_Prescaler = freqMHz - 1;
  TIM_TimeBaseInit(US_TIM, &TIM_TimeBaseStruct);
  TIM_OC1Init(US_TIM, &TIM_OCInitStruct);
  TIM_OC1PreloadConfig(US_TIM, TIM_OCPreload_Disable);
  NVIC_InitStruct.NVIC_IRQChannel = US_TIM_IRQn;
  NVIC_Init(&NVIC_InitStruct);
  TIM_ClearITPendingBit(US_TIM, TIM_IT_CC1);
  TIM_ITConfig(US_TIM, TIM_IT_CC1, ENABLE);

  /* Konfiguruj licznik milisekundowy na 2 kHz. */
  TIM_TimeBaseStruct.TIM_Prescaler = freqMHz * 500 - 1;
  TIM_TimeBaseInit(MS_TIM, &TIM_TimeBaseStruct);
  TIM_OC1Init(MS_TIM, &TIM_OCInitStruct);
  TIM_OC1PreloadConfig(MS_TIM, TIM_OCPreload_Disable);
  NVIC_InitStruct.NVIC_IRQChannel = MS_TIM_IRQn;
  NVIC_Init(&NVIC_InitStruct);
  TIM_ClearITPendingBit(MS_TIM, TIM_IT_CC1);
  TIM_ITConfig(MS_TIM, TIM_IT_CC1, ENABLE);

  /* Konfiguruj licznik sekundowy. */
  PWR_BackupAccessCmd(ENABLE);
  if (PWR_GetFlagStatus(PWR_FLAG_SB) != RESET) {
    /* Mikrokontroler obudził się ze stanu czuwania. Nie ma
       potrzeby ponownego konfigurowania RTC - konfiguracja
       jest zachowywana po wybudzeniu ze stanu czuwania. */
    PWR_ClearFlag(PWR_FLAG_SB);
    RTC_WaitForSynchro();
  }
  else {
    /* Mikrokontroler został zresetowany, konfiguruj RTC, aby
       był taktowany kwarcem 32768 Hz i tykał z okresem 1 s. */
    RCC_LSEConfig(RCC_LSE_ON);
    active_check(RCC_GetFlagStatus(RCC_FLAG_LSERDY), 10000000);
    RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    RCC_RTCCLKCmd(ENABLE);
    RTC_WaitForSynchro();
    RTC_SetPrescaler(32767);
    RTC_WaitForLastTask();
  }
  /* Konfiguruj linię 17 EXTI (alarm RTC), aby narastające
     zbocze ustawiało rejestr zdarzenia */
  EXTI_StructInit(&EXTI_InitStruct);
  EXTI_InitStruct.EXTI_Line = EXTI_Line17;
  EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
  EXTI_InitStruct.EXTI_LineCmd = ENABLE;
  EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Event;
  EXTI_Init(&EXTI_InitStruct);
  RTC_WaitForLastTask();
  RTC_ClearITPendingBit(RTC_IT_ALR);

  return 0;
}