#include "stm32f0xx.h" // indikace bdělosti #define TEST_H GPIOA->BSRR = GPIO_Pin_4 #define TEST_L GPIOA->BRR = GPIO_Pin_4 void pull_unused_gpio(void); void _delay_ms(uint32_t Delay); void clock_8(void); void gpio_init(void); void init_rtc(void); void init_uart(void); void USART_puts(volatile char *s); void print_time(void); RTC_DateTypeDef date; RTC_TimeTypeDef time; char txt[30]; int main(void){ clock_8(); pull_unused_gpio(); // pulldown pro nepoužité piny gpio_init(); // PA4 (TEST výtup) init_uart(); // nastavíme UART na 115200 s clockem přímo z HSI init_rtc(); // nastaví RTC (nezkoumám zda už běží) while (1){ RTC_ClearFlag(RTC_FLAG_ALRAF); // před usnutím smažeme vlajku Alarmu RTC PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFE); // uspíme čip TEST_H; // pro informaci jak dlouho je čip vzhůru si nastavíme pomocný výstup clock_8(); // taktujeme čip na požadovanou frekvenci (není nutné - po probuzení je 8MHz HSI automaticky) print_time(); // uděláme nějakou zajímavou / užitečnou činnost TEST_L; // ... informuje o konci bdělého stavu } } void print_time(void){ // vyčteme čas a datum z RTC RTC_GetTime(RTC_Format_BCD, &time); RTC_GetDate(RTC_Format_BCD, &date); // využijeme BCD formátu a vyhneme se použití sprintf ... // ... a připravíme si řetězec txt[0]=(time.RTC_Hours >> 4) + '0'; // desítky hodin txt[1]=(time.RTC_Hours & 0x0f) + '0'; // jednotky hodin txt[2]=':'; txt[3]=(time.RTC_Minutes >> 4) + '0'; // desítky minut txt[4]=(time.RTC_Minutes & 0x0f) + '0'; // jednotky minut txt[5]=':'; txt[6]=(time.RTC_Seconds >> 4) + '0'; // desítky sekund txt[7]=(time.RTC_Seconds & 0x0f) + '0'; // jednotky sekund txt[8] = ' '; // vypíšeme den v týdnu if(date.RTC_WeekDay == RTC_Weekday_Monday){txt[9]='P';txt[10]='o';} if(date.RTC_WeekDay == RTC_Weekday_Tuesday){txt[9]='U';txt[10]='t';} if(date.RTC_WeekDay == RTC_Weekday_Wednesday){txt[9]='S';txt[10]='t';} if(date.RTC_WeekDay == RTC_Weekday_Thursday){txt[9]='C';txt[10]='t';} if(date.RTC_WeekDay == RTC_Weekday_Friday){txt[9]='P';txt[10]='a';} if(date.RTC_WeekDay == RTC_Weekday_Saturday){txt[9]='S';txt[10]='o';} if(date.RTC_WeekDay == RTC_Weekday_Sunday){txt[9]='N';txt[10]='e';} // vypíšeme datum txt[11] = ' '; txt[12]=(date.RTC_Date >> 4) + '0'; // den (desítky) txt[13]=(date.RTC_Date & 0x0f) + '0'; // den (jednotky) txt[14]='.'; txt[15]=(date.RTC_Month >> 4) + '0'; // měsíc (desítky) txt[16]=(date.RTC_Month & 0x0f) + '0'; // měsíc (jednotky) txt[17]=' '; // a rok (2019) txt[18]='2'; txt[19]='0'; txt[20]=(date.RTC_Year >> 4) + '0'; txt[21]=(date.RTC_Year & 0x0f) + '0'; txt[22]=0; // ukončení řetězce // vše pošleme na trochu optimalizovaný UART USART_puts(txt); // vypíšeme připravený řetězec } void init_rtc(void){ RTC_InitTypeDef rtc; RTC_AlarmTypeDef alarm; EXTI_InitTypeDef EXTI_InitStructure; ErrorStatus err; // PWR ovládá přístup do backup domény RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); // spustit LSI oscilátor (~40kHz) RCC_LSICmd(ENABLE); // počkat na rozběh LSI while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) != SET); // povolit přístup do backu domény PWR_BackupAccessCmd(ENABLE); // LSI jako clock pro RTC RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); // Povolit clock do RTC RCC_RTCCLKCmd(ENABLE); // nastavit dělení clocku pro RTC (jen z legrace zkusíme velmi nepřesné LSI trochu umravnit) // moje LSI mělo při pokojové teplotě (důležitá informace) 41525Hz, takže nejlepší způsob jak udělat 1s je: // 41525/11/3775 = 1Hz // pokud bychom chtěli periodické buzení s kratším intervalem než 1s, museli bychom SynchPrediv volit chytře ! rtc.RTC_AsynchPrediv = 10; // 11-1 rtc.RTC_SynchPrediv = 3774; // 3775-1 rtc.RTC_HourFormat = RTC_HourFormat_24; err=RTC_Init(&rtc); // nastavit datum (pro pouhé periodické buzení není třeba) date.RTC_Date = 5; date.RTC_Month = RTC_Month_May; date.RTC_WeekDay = RTC_Weekday_Sunday; date.RTC_Year = 19; err=RTC_SetDate(RTC_Format_BIN,&date); // nastavit čas (pro pouhé periodické buzení není třeba) time.RTC_H12 = RTC_H12_AM; time.RTC_Hours = 9; time.RTC_Minutes = 48; time.RTC_Seconds = 0; RTC_SetTime(RTC_Format_BIN,&time); // nastavujeme alarm na buzení každou sekundu // předplníme si nastavení sekund,minut,hodin... protože nehrají roli RTC_AlarmStructInit(&alarm); // klasické položky (rok, měsíc, den, hodina, minuta, sekunda) alarmu maskujeme alarm.RTC_AlarmMask = RTC_AlarmMask_All; // alarm lze přepisovat jen když je vypnutý (takže ho pro jistotu vypneme !) err=RTC_AlarmCmd(RTC_Alarm_A, DISABLE); // Aplkikujeme naše nastavení Alarmu RTC_SetAlarm(RTC_Format_BIN,RTC_Alarm_A,&alarm); // nastavíme sub-second alarm na periodické buzení každou sekundu RTC_AlarmSubSecondConfig(RTC_Alarm_A,0,RTC_AlarmSubSecondMask_None); // povolíme Alarm err=RTC_AlarmCmd(RTC_Alarm_A, ENABLE); // povolíme přerušení od Alarmu RTC_ITConfig(RTC_IT_ALRA,ENABLE); // Přerušení od RTC vedou do EXTI (pustíme EXTI clock) RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // povolíme Event od RTC (ten nás bude budit) EXTI_InitStructure.EXTI_Line = EXTI_Line17; // To je IRQ od Alarmu EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event; // Event (!) EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd = ENABLE; EXTI_Init(&EXTI_InitStructure); } void init_uart(void){ GPIO_InitTypeDef gp; USART_InitTypeDef usart; // inicializace PA2 (USRT1 Tx) RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); gp.GPIO_Pin = GPIO_Pin_2; gp.GPIO_Mode = GPIO_Mode_AF; gp.GPIO_OType = GPIO_OType_PP; gp.GPIO_PuPd = GPIO_PuPd_NOPULL; gp.GPIO_Speed = GPIO_Speed_Level_1; GPIO_Init(GPIOA, &gp); GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_1); // USART si bere clock rovnou z HSI (můžeme pak čip i další periferie podtaktovat) RCC_USARTCLKConfig(RCC_USART1CLK_HSI); // povolíme clock USARTu abychom ho mohli konfigurovat RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE); // "klasická" konfigurace usart.USART_BaudRate = 115200; usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None; usart.USART_Mode = USART_Mode_Tx; usart.USART_Parity = USART_Parity_No; usart.USART_StopBits = USART_StopBits_1; usart.USART_WordLength = USART_WordLength_8b; USART_Init(USART1,&usart); // USART ještě nepovoluji (až ho budu potřebovat) } // funkce k odeslání řetězce, využívající režim spánku void USART_puts(volatile char *s){ // chceme během vysílání spát (jádro nemá co na práci) // povolíme buzení libovolným přerušením NVIC_SystemLPConfig(NVIC_LP_SEVONPEND, ENABLE); USART_Cmd(USART1,ENABLE); // zapneme UART // vyčistíme vlajku "Transfer Complete" (dokončené vysílání) USART_ClearFlag(USART1,USART_FLAG_TC); // povolíme přerušení od "Transfer Complete" USART_ITConfig(USART1,USART_IT_TC,ENABLE); while(*s){ // dokud je co vysílat USART_SendData(USART1, *s); // naložíme znak k odeslání s++; // posuneme se na další znak // než se vysílání dokončí tak si zdřímneme PWR_EnterSleepMode(PWR_SLEEPEntry_WFE); // mělkým spánkem (SLEEP) // vysílání dokončeno // tady by asi bylo na místě si pro jistotu zkontrolovat zda nás nevzbudilo něco jiného USART_ClearFlag(USART1,USART_FLAG_TC); // vyčistíme vlajku (jinak znovu neusneme) NVIC_ClearPendingIRQ(USART1_IRQn); // vyčistíme "vlajku" i v NVIC (jinak znovu neusneme) } // po odeslání celé zprávy vypneme přerušení od USARTu USART_ITConfig(USART1,USART_IT_TC,DISABLE); USART_Cmd(USART1,DISABLE); // vypneme UART // a deaktivujeme buzení libovolným přerušením NVIC_SystemLPConfig(NVIC_LP_SEVONPEND, DISABLE); } // PA4 je testovací výstup void gpio_init(void){ GPIO_InitTypeDef gp; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); gp.GPIO_Pin = GPIO_Pin_4; gp.GPIO_Mode = GPIO_Mode_OUT; gp.GPIO_OType = GPIO_OType_PP; gp.GPIO_PuPd = GPIO_PuPd_NOPULL; gp.GPIO_Speed = GPIO_Speed_Level_1; GPIO_Init(GPIOA, &gp); } // Delay na bázi systicku (vyžaduje korektní nastavení SystemCoreClock) void _delay_ms(uint32_t Delay){ __IO uint32_t tmp = SysTick->CTRL; // Clear the COUNTFLAG first ((void)tmp); // init systick to us delays ... SysTick->LOAD = (SystemCoreClock/1000)-1; // 1us time SysTick->VAL = 0UL; SysTick->CTRL = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_CLKSOURCE_Msk; while (Delay){ if((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) != 0U){Delay--;} } } void clock_8(void){ RCC_HCLKConfig(RCC_SYSCLK_Div1); // SYSCLK nijak nedělit RCC_PCLKConfig(RCC_HCLK_Div1); // HCLK ze SYSCLK nijak nedělit (periferiím stejný takt jako jádru) RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI); // zdrojem clocku je 8MHz HSI SystemCoreClock = 8000000; // clock je 8MHz } // ošetření nevyužitých pinů void pull_unused_gpio(void){ GPIO_InitTypeDef gp; // nastavíme všem pinům že jsou to vstupy s pull-down rezistorem // ponecháme si jen konfiguraci pinů SWD, které mají interní pullup/pulldown rezistory // na našem čipu jsou jen GPIOA,GPIOB a GPIOF RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOF, ENABLE); gp.GPIO_Pin = GPIO_Pin_All; gp.GPIO_Mode = GPIO_Mode_IN; gp.GPIO_OType = GPIO_OType_PP; gp.GPIO_PuPd = GPIO_PuPd_DOWN; gp.GPIO_Speed = GPIO_Speed_Level_1; GPIO_Init(GPIOB, &gp); GPIO_Init(GPIOF, &gp); gp.GPIO_Pin = GPIO_Pin_All & (~(GPIO_Pin_13 | GPIO_Pin_14)); // vše krom PA13 a PA14 (SWD) GPIO_Init(GPIOA, &gp); // nezapomeneme vypnout clock ;) RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA | RCC_AHBPeriph_GPIOB | RCC_AHBPeriph_GPIOF, DISABLE); }