prettyprint

2020年10月28日 星期三

使用STM32F103C8T6 RTC 實作時鐘、鬧鐘

本實驗使用STM32F103C8T6 MCU來實作時鐘(含鬧鐘功能),

使用元件:

  1. STM32F103C8T6 x1
  2. 按鈕 x3
  3. 1k電阻 x3
  4. OLED128x64   x1
  5. 蜂鳴器 x1
  6. 鈕扣電池 x1
功能需求:
  1. 可調整時間
  2. 設定鬧鐘
  3. 斷電VBAT供電
  4. 鬧鐘響起時按任何按鈕停止鬧鈴,響20秒未按按鈕,停止鬧鈴,5分鐘後再響鬧鈴,共三次。
  5. 2分鐘關螢幕進入STOP模式,按任一按鈕或鬧鈴響起喚醒。
在實作過程中遇到一些問題,例如無法進入STOP mode,在移除電源後,隔日復電日期未能自動更新等問題,可參閱另兩篇備忘文章
  1. https://rfwumcu.blogspot.com/2020/10/stm32-rtc.html
  2. https://rfwumcu.blogspot.com/2020/10/stm32-pwr-low-power-mode.html
實作過程:
一、線路圖


使用STM32CubeIDE開發環境與HAL library。

Timers分別使用RTC,TIM1,TIM2,TIM3。

RTC產稱每秒一次中斷(hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND)計時更新顯示。
TIM1: 設定為base timer,計時2分鐘關閉螢幕,進入STOP Mode以節省電源。
TIM2:設定為base timer,作為Software Debounce用,按鈕按下後啟動50ms後中斷。
TIM3:設定為1Hz PWM,驅動鬧鈴蜂鳴器,鬧鐘鬧鈴響起後,未按取消鬧鈴,20秒後關閉鬧鈴,5分鐘後再響鬧鈴共3次。

  • RCC Clock configuration 




    RCC啟用LSE作為RTC Clock,周邊clock為HSI,當系統進入STOP Mode使用的Clock。
  • RTC設定參數
在Parameter選取Automatic Predivider Calculation Enable 以產生每秒一次中斷(RTC_AUTO_1_SECOND)。

在NVIC Enable RTC global interrupt 以便每秒產生一次中斷並呼叫
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef* hrtc) Callback function
在這function 中更新時間顯示。

啟用NVIC RTC alarm interrupt以便在呼叫HAL_RTC_SetAlarm_IT啟動Alarm Interrupt,並在設定Alarm時間呼叫
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef* hrtc) Callback function
 
  • TIM1參數設定
TIM1設定為base timer 時間為120秒關閉螢幕,進入stop mode,系統clock為8M:
8M/(39999+1)=200(Hz),
200/24000=0.0083(週期為1/0.0083=120秒)


NVIC啟用update interrupt

  • TIM2參數設定
TIM2仍設為Base timer 週期為50ms(Prescaler:7999, ARR:50) 
PB13~15設為EXTI,PB13為時間設定選擇鍵,PB15為鬧鐘設定選擇鍵,PB14為數值調整鍵。


Enable EXTI line[15:10] interrupts

當按鍵按下時產生中斷呼叫HAL_GPIO_EXTI_Callbak function,啟動TIM2
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if ((GPIO_Pin == GPIO_PIN_13 ||GPIO_Pin == GPIO_PIN_14 || GPIO_Pin == GPIO_PIN_15) && !buttonPress)
	{
		buttonPress=1;
		HAL_TIM_Base_Start_IT(&htim2);
	}


}
50ms後產生中斷呼叫HAL_TIM_PeriodElapsedCallback檢查是否仍是按下狀態,以達到software debounce功能。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)
{
	if (htim->Instance==TIM2)
	{
		if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET)
		{
			buttonPress=0;
			HAL_TIM_Base_Stop_IT(&htim2);
			EXTIButton13Press();
		}
.
.
.
}
  • TIM3參數設定:
        TIM3 Channel1(對應PB4) 設定為1Hz PWM, duty為70%,驅動鬧鈴蜂鳴器。


藉由呼叫HAL_RTC_SetAlarm_IT(&hrtc, &atime, RTC_FORMAT_BIN);設定Alarm,當時間到達時系統呼叫 callback function void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef* hrtc),並啟動TIM3 Channel 1 PWM(HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_1);)讓蜂鳴器發出聲音。

HAL_RTC_SetAlarm_IT(&hrtc, &atime, RTC_FORMAT_BIN);
.
.
.
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef* hrtc)
{
.
.
.
            HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_1);
.
.
.
}

在TIM3 PWM interrupt callback function 中檢查時間是否需要停止蜂鳴聲
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef* htim)
{
        if (alarm_count >= ALARM_BUZZ) //after ALARM_BUZZ Second turn off alarm
{
clearAlarm();
.
                .
                .
           };
}



實作展示:





使用OLED128x64 I2C介面200歐姆上拉電阻,使用程式庫
複製ssd1306.c ssd1306_fonts,c ssd1306.h ssd1306_fonts.h,ssd1306_conf.h

完整程式碼:
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * 

© Copyright (c) 2020 STMicroelectronics. * All rights reserved.

* * This software component is licensed by ST under BSD 3-Clause license, * the "License"; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "ssd1306.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define BLINK_DELAY 200 #define ALARM_BUZZ 20 #define ALARM_REPEAT 3 #define ALARM_RESUME_MIN 5 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ I2C_HandleTypeDef hi2c1; RTC_HandleTypeDef hrtc; TIM_HandleTypeDef htim1; TIM_HandleTypeDef htim2; TIM_HandleTypeDef htim3; /* USER CODE BEGIN PV */ RTC_TimeTypeDef stime; RTC_DateTypeDef sdate; RTC_AlarmTypeDef atime; uint8_t year, month,day,hour, min,sec,ahour,amin; char sDateBuf[12], sTimeBuf[10], sWeekdayBuf[10],sAlarmBuf[10]; uint8_t alarmSet=0; uint8_t alarmEvent=0; uint8_t buttonPress=0; uint8_t startTimer1=0; uint8_t inStopMode=0,enterStopMode=0; uint8_t opMode=0; //0:normal, 1: in adjust time mode, 2: in set Alarm mode uint8_t adjustTimeItem = 0; //0:year,1:month, 2:day, 3;Hour, 4:min, 5:sec uint8_t adjustAlarmItem=0; // 0: enter alarm set mode, 1:on-off,2:hour,3:minute uint8_t strWeekday[7][10]={ "Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; uint8_t alarm_count=0; uint8_t alarm_resume=0; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_RTC_Init(void); static void MX_I2C1_Init(void); static void MX_TIM1_Init(void); static void MX_TIM2_Init(void); static void MX_TIM3_Init(void); /* USER CODE BEGIN PFP */ void EXTIButton13Press(void); void EXTIButton14Press(void); void EXTIButton15Press(void); void wakeUpFromStopMode(void); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ static uint8_t isLeapYear(uint16_t nYear) { if ((nYear % 4U) != 0U) { return 0U; } if ((nYear % 100U) != 0U) { return 1U; } if ((nYear % 400U) == 0U) { return 1U; } else { return 0U; } } void adjustAlarm(void) { char buf[4]; switch(adjustAlarmItem) { case 1: if(alarmSet) { sprintf(buf,"ON"); } else { sprintf(buf,"OFF"); } ssd1306_SetCursor(1, 1); ssd1306_WriteString(" ", Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); ssd1306_SetCursor(1, 1); ssd1306_WriteString(buf, Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; case 2: ssd1306_SetCursor(5, 22); ssd1306_WriteString(" ", Font_16x26, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); sprintf(buf, "%02d", ahour); ssd1306_SetCursor(5, 22); ssd1306_WriteString(buf, Font_16x26, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; case 3: ssd1306_SetCursor(53, 22); ssd1306_WriteString(" ", Font_16x26, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); sprintf(buf, "%02d", amin); ssd1306_SetCursor(53, 22); ssd1306_WriteString(buf, Font_16x26, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; } } void adjustTime(void) { char buf[4]; switch(adjustTimeItem) { case 0: ssd1306_SetCursor(1, 1); ssd1306_WriteString(" ", Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); sprintf(buf, "%d", year); ssd1306_SetCursor(1, 1); ssd1306_WriteString(buf, Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; case 1: ssd1306_SetCursor(34, 1); ssd1306_WriteString(" ", Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); sprintf(buf, "%02d", month); ssd1306_SetCursor(34, 1); ssd1306_WriteString(buf, Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; case 2: ssd1306_SetCursor(67, 1); ssd1306_WriteString(" ", Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); sprintf(buf, "%02d", day); ssd1306_SetCursor(67, 1); ssd1306_WriteString(buf, Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; case 3: ssd1306_SetCursor(1, 25); ssd1306_WriteString(" ", Font_16x26, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); sprintf(buf, "%02d", hour); ssd1306_SetCursor(1, 25); ssd1306_WriteString(buf, Font_16x26, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; case 4: ssd1306_SetCursor(49, 25); ssd1306_WriteString(" ", Font_16x26, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); sprintf(buf, "%02d", min); ssd1306_SetCursor(49, 25); ssd1306_WriteString(buf, Font_16x26, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; case 5: ssd1306_SetCursor(97, 31); ssd1306_WriteString(" ", Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY/4); sprintf(buf, "%02d", sec); ssd1306_SetCursor(97, 31); ssd1306_WriteString(buf, Font_11x18, White); ssd1306_UpdateScreen(); HAL_Delay(BLINK_DELAY); break; } } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_RTC_Init(); MX_I2C1_Init(); MX_TIM1_Init(); MX_TIM2_Init(); MX_TIM3_Init(); /* USER CODE BEGIN 2 */ ssd1306_Init(); HAL_TIM_Base_Start_IT(&htim1); startTimer1 = 1; HAL_TIM_Base_Start_IT(&htim2); HAL_TIM_Base_Stop_IT(&htim2); atime.Alarm=RTC_ALARM_A; atime.AlarmTime.Hours=0; atime.AlarmTime.Minutes=0; atime.AlarmTime.Seconds=0; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { if (enterStopMode) { enterStopMode=0; HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI); } switch(opMode) { case 1: //in adjust Time Mode adjustTime(); break; case 2: // in set Alarm Mode adjustAlarm(); break; } /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.LSEState = RCC_LSE_ON; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC; PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /** * @brief I2C1 Initialization Function * @param None * @retval None */ static void MX_I2C1_Init(void) { /* USER CODE BEGIN I2C1_Init 0 */ /* USER CODE END I2C1_Init 0 */ /* USER CODE BEGIN I2C1_Init 1 */ /* USER CODE END I2C1_Init 1 */ hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C1_Init 2 */ /* USER CODE END I2C1_Init 2 */ } /** * @brief RTC Initialization Function * @param None * @retval None */ static void MX_RTC_Init(void) { /* USER CODE BEGIN RTC_Init 0 */ /* USER CODE END RTC_Init 0 */ RTC_TimeTypeDef sTime = {0}; RTC_DateTypeDef DateToUpdate = {0}; /* USER CODE BEGIN RTC_Init 1 */ /* USER CODE END RTC_Init 1 */ /** Initialize RTC Only */ hrtc.Instance = RTC; hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND; hrtc.Init.OutPut = RTC_OUTPUTSOURCE_ALARM; if (HAL_RTC_Init(&hrtc) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN Check_RTC_BKUP */ if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x6060) { /* USER CODE END Check_RTC_BKUP */ /** Initialize RTC and set the Time and Date */ sTime.Hours = 13; sTime.Minutes = 56; sTime.Seconds = 0; if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } DateToUpdate.WeekDay = RTC_WEEKDAY_SUNDAY; DateToUpdate.Month = RTC_MONTH_SEPTEMBER; DateToUpdate.Date = 26; DateToUpdate.Year = 20; if (HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN RTC_Init 2 */ uint32_t dateToStore; memcpy(&dateToStore,&(hrtc.DateToUpdate),sizeof(uint32_t)); BKP->DR2 = dateToStore >> 16; BKP->DR3 = dateToStore & 0xffff; HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x6060); } else { uint32_t dateMem; dateMem = BKP->DR2 << 16; dateMem |= BKP->DR3; memcpy(&DateToUpdate,&dateMem,sizeof(uint32_t)); HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN);           HAL_RTC_GetTime(&hrtc, &stime, RTC_FORMAT_BIN); //get time counter and call RTC_DateUpdate HAL_RTCEx_SetSecond_IT(&hrtc); } /* USER CODE END RTC_Init 2 */ } /** * @brief TIM1 Initialization Function * @param None * @retval None */ static void MX_TIM1_Init(void) { /* USER CODE BEGIN TIM1_Init 0 */ /* USER CODE END TIM1_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; /* USER CODE BEGIN TIM1_Init 1 */ /* USER CODE END TIM1_Init 1 */ htim1.Instance = TIM1; htim1.Init.Prescaler = 39999; htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 24000; htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.RepetitionCounter = 0; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(&htim1) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM1_Init 2 */ /* USER CODE END TIM1_Init 2 */ } /** * @brief TIM2 Initialization Function * @param None * @retval None */ static void MX_TIM2_Init(void) { /* USER CODE BEGIN TIM2_Init 0 */ /* USER CODE END TIM2_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; /* USER CODE BEGIN TIM2_Init 1 */ /* USER CODE END TIM2_Init 1 */ htim2.Instance = TIM2; htim2.Init.Prescaler = 7999; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 50; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM2_Init 2 */ /* USER CODE END TIM2_Init 2 */ } /** * @brief TIM3 Initialization Function * @param None * @retval None */ static void MX_TIM3_Init(void) { /* USER CODE BEGIN TIM3_Init 0 */ /* USER CODE END TIM3_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; /* USER CODE BEGIN TIM3_Init 1 */ /* USER CODE END TIM3_Init 1 */ htim3.Instance = TIM3; htim3.Init.Prescaler = 7999; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 999; htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim3) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } if (HAL_TIM_PWM_Init(&htim3) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 700; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } /* USER CODE BEGIN TIM3_Init 2 */ /* USER CODE END TIM3_Init 2 */ HAL_TIM_MspPostInit(&htim3); } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); /*Configure GPIO pins : PB13 PB14 PB15 */ GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); } /* USER CODE BEGIN 4 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if ((GPIO_Pin == GPIO_PIN_13 ||GPIO_Pin == GPIO_PIN_14 || GPIO_Pin == GPIO_PIN_15) && !buttonPress) { buttonPress=1; HAL_TIM_Base_Start_IT(&htim2); } } void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef* hrtc) { if (opMode==0) { HAL_RTC_GetTime(hrtc, &stime, RTC_FORMAT_BIN); HAL_RTC_GetDate(hrtc, &sdate, RTC_FORMAT_BIN); ssd1306_Fill(Black); sprintf(sTimeBuf, "%02d", stime.Seconds); ssd1306_SetCursor(97, 31); ssd1306_WriteString(sTimeBuf, Font_11x18, White); sprintf(sTimeBuf, "%02d:%02d:", stime.Hours,stime.Minutes); ssd1306_SetCursor(1,25); ssd1306_WriteString(sTimeBuf, Font_16x26, White); sprintf(sDateBuf, "%02d/%02d/%02d", sdate.Year,sdate.Month,sdate.Date); ssd1306_SetCursor(1,1); ssd1306_WriteString(sDateBuf, Font_11x18, White); sprintf(sWeekdayBuf, "%s", strWeekday[sdate.WeekDay]); ssd1306_SetCursor((int)(128-strlen(sWeekdayBuf)*7),7); ssd1306_WriteString(sWeekdayBuf, Font_7x10, White); if (alarmSet) { sprintf(sAlarmBuf, "%02d:%02d", atime.AlarmTime.Hours,atime.AlarmTime.Minutes); ssd1306_SetCursor(52, 52); ssd1306_WriteString(sAlarmBuf, Font_7x10, White); ssd1306_SetCursor(1, 52); ssd1306_WriteString("Alarm: ", Font_7x10, White); } ssd1306_UpdateScreen(); } } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) { if (htim->Instance==TIM2) { if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_13) == GPIO_PIN_RESET) { buttonPress=0; HAL_TIM_Base_Stop_IT(&htim2); EXTIButton13Press(); } if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_14) == GPIO_PIN_RESET) { buttonPress=0; HAL_TIM_Base_Stop_IT(&htim2); EXTIButton14Press(); } if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_15) == GPIO_PIN_RESET) { buttonPress=0; HAL_TIM_Base_Stop_IT(&htim2); EXTIButton15Press(); } } if (htim->Instance==TIM1) { if (startTimer1 && !alarmEvent) { ssd1306_SetDisplayOn(0); startTimer1=0; HAL_TIM_Base_Stop_IT(htim); inStopMode=1; enterStopMode=1; } } } void clearAlarm(void) { HAL_TIM_PWM_Stop_IT(&htim3, TIM_CHANNEL_1); alarmEvent=0; alarmSet=0; alarm_count=0; } void EXTIButton13Press(void) { if(alarmEvent && alarmSet) { clearAlarm(); return; } if (inStopMode) { wakeUpFromStopMode(); } else { if (startTimer1) { startTimer1=0; HAL_TIM_Base_Stop_IT(&htim1); } switch(opMode) { case 0: opMode++; hour=stime.Hours; min=stime.Minutes; sec=stime.Seconds; year=sdate.Year; month=sdate.Month; day=sdate.Date; break; case 1: adjustTimeItem++; if (adjustTimeItem==6) { opMode=0; adjustTimeItem=0; stime.Hours=hour; stime.Minutes=min; stime.Seconds=sec; sdate.Year=year; sdate.Month=month; sdate.Date=day; atime.AlarmTime.Hours=ahour; atime.AlarmTime.Minutes=amin; if (HAL_RTC_SetTime(&hrtc, &stime, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } if (HAL_RTC_SetDate(&hrtc, &sdate, RTC_FORMAT_BIN) != HAL_OK) { Error_Handler(); } if (alarmSet) { HAL_RTC_SetAlarm_IT(&hrtc, &atime, RTC_FORMAT_BIN); } uint32_t dateToStore; memcpy(&dateToStore,&(hrtc.DateToUpdate),sizeof(uint32_t)); BKP->DR2 = dateToStore >> 16; BKP->DR3 = dateToStore & 0xffff; startTimer1=1; HAL_TIM_Base_Start_IT(&htim1); } } } } void EXTIButton14Press(void) { if(alarmEvent && alarmSet) { clearAlarm(); } if (inStopMode) { wakeUpFromStopMode(); } else { if (opMode==1) { int mDays=31; switch(adjustTimeItem) { case 0: year++; if (year > 99) year=20; break; case 1: month++; if (month > 12) month=1; case 2: if (month == 2) { if (isLeapYear(year)) { mDays=29; } else { mDays=28; } } if (month == 4 || month == 6 || month == 9 || month == 11) { mDays=30; } day++; if (day > mDays) day=1; break; case 3: hour=(hour+1)%24; break; case 4: min=(min+1)%60; break; case 5: sec=(sec+1)%60; break; } } if (opMode==2) { switch(adjustAlarmItem) { case 1: alarmSet = (alarmSet+1)%2; break; case 2: ahour = (ahour+1)%24; break; case 3: amin = (amin+1)%60; break; } } } } void EXTIButton15Press(void) { char buf[6]; if(alarmEvent && alarmSet) { clearAlarm(); return; } if (inStopMode) { wakeUpFromStopMode(); } else { opMode=2; if (startTimer1) { startTimer1=0; HAL_TIM_Base_Stop_IT(&htim1); } if (adjustAlarmItem == 0) { ssd1306_Fill(Black); if (alarmSet) { sprintf(buf,"ON "); } else { sprintf(buf, "OFF"); } ssd1306_SetCursor(1, 1); ssd1306_WriteString(buf, Font_11x18, White); sprintf(buf, "%02d:%02d", ahour, amin); ssd1306_SetCursor(5, 22); ssd1306_WriteString(buf, Font_16x26, White); ssd1306_UpdateScreen(); } adjustAlarmItem++; if (adjustAlarmItem == 4) { opMode=0; adjustAlarmItem=0; atime.AlarmTime.Hours=ahour; atime.AlarmTime.Minutes=amin; if (alarmSet) { alarmEvent=0; alarm_resume=0; HAL_RTC_SetAlarm_IT(&hrtc, &atime, RTC_FORMAT_BIN); } startTimer1=1; HAL_TIM_Base_Start_IT(&htim1); } } } void wakeUpFromStopMode(void) { inStopMode=0; ssd1306_SetDisplayOn(1); startTimer1=1; HAL_TIM_Base_Start_IT(&htim1); } void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef* hrtc) { if(alarmSet) { if (inStopMode) { wakeUpFromStopMode(); } alarmEvent=1; HAL_TIM_PWM_Start_IT(&htim3, TIM_CHANNEL_1); } } void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef* htim) { alarm_count++; if (alarm_count >= ALARM_BUZZ) //after ALARM_BUZZ Second turn off alarm { clearAlarm(); alarm_resume++; if (alarm_resume < ALARM_REPEAT) { amin += ALARM_RESUME_MIN; // resume alarm ALARM_RESUME_MIN minutes later if (amin >=60) { ahour++; amin = amin % 60; if (ahour >=24) ahour % 24; } atime.AlarmTime.Hours=ahour; atime.AlarmTime.Minutes=amin; alarmSet = 1; HAL_RTC_SetAlarm_IT(&hrtc, &atime, RTC_FORMAT_BIN); } else { alarm_resume=0; } } } /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/


2020年10月25日 星期日

STM32 RTC實驗備忘錄

  1. STM32F1有ONE_SECOND interrupt,STM32F4沒有,只能用RTC Wakeup代替。
  2. STM32F4 注意要在HAL_RTC_GetTime之後呼叫HAL_RTC_GetDate才能取得取得數值(浪費一天得到)。在HAL_RTC_GetTime有說明:note:You must call HAL_RTC_GetDate() after HAL_RTC_GetTime() to unlock the values  in the higher-order calendar shadow registers to ensure consistency between the time and date values. Reading RTC current time locks the values in calendar shadow registers until current date is read.
  3. STM32F4 有ALARM A AND B,STM32F1 只有ALARM A
  4. STM32F1接ST-Link LSE可能電源不過,因此外接電源即可正常。
  5. STM32F1 RTC 在電池模式下(Power off, Standby, low power mode)日期不會更新問題,網路上搜尋解決方法較好為smt32fxx_hal_rtc.c 的RTC_DateUpdate function 下最後加入

uint32_t dateToStore;

   memcpy(&dateToStore,&hrtc->DateToUpdate,sizeof(uint32_t));

   BKP->DR2 = dateToStore >> 16;

   BKP->DR3 = dateToStore & 0xffff;

然後

在main.c的MX_RTC_Init 

判斷若backup register 是否儲存,在restore回日期

/* USER CODE BEGIN Check_RTC_BKUP */

  if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x30)

  {

  /* USER CODE END Check_RTC_BKUP */

  /** Initialize RTC and set the Time and Date

  */

.

.

.

/* USER CODE BEGIN RTC_Init 2 */

        uint32_t dateToStore;

       memcpy(&dateToStore,&hrtc->DateToUpdate,sizeof(uint32_t));

       BKP->DR2 = dateToStore >> 16;

       BKP->DR3 = dateToStore & 0xffff;

  HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x30);

}

else

  {

  uint32_t dateMem;

  dateMem = BKP->DR2 << 16;

  dateMem |= BKP->DR3;

  memcpy(&DateToUpdate,&dateMem,sizeof(uint32_t));

  HAL_RTC_SetDate(&hrtc, &DateToUpdate, RTC_FORMAT_BIN);

  }

/* USER CODE END RTC_Init 2 */

2020年10月2日 星期五

STM32F103C8T6 進入 Low Power Mode與WakeUp 實驗備忘

實驗環境: 使用STM32CubeIDE與HAL functions

Q1.為何在Low Power mode下,無法透過GPIO EXTI wake-up?
A1:
  1. HAL_PWR_EnterSTOPMode,HAL_PWR_EnterSLEEPMode不可在callback function中呼叫,否則無法透過EXTI wake-up
  2. 在callback function 中設定flag於 main while(1) loop中check flag並啟動 low power mode,則可順利由low power mode中順利喚醒。
 int main(void)
{
    .
    .
    .
    /* USER CODE BEGIN 2 */
    enterSTOPMode=0;
  /* USER CODE END 2 */

    .
    .
    .
  while (1)
  {
	  if(enterSTOPMode)
{ enterSTOPMode=0;
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI); } }   .     .     . }
Q2.為何HAL_PWR_EnterSTANDBYMode使用PA0 pin喚醒後無法再一次進入STANDBY mode?
A2:
        呼叫HAL_PWR_EnterSTANDBYMode前必須先CLEAR FLAG。以下順序呼叫
 int main(void)
{
    .
    .
    .
    /* USER CODE BEGIN 2 */
    enterStandbyMode=0;
  /* USER CODE END 2 */

    .
    .
    .
  while (1)
  {
	  if(enterStandbyMode)
{ enterStandbyMode=0;
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU); HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1); HAL_PWR_EnterSTANDBYMode(); } }   .     .     . }