From 245e5be2ad06c54d4ba7d2eaa64e45d29e34657b Mon Sep 17 00:00:00 2001 From: Venelin Efremov Date: Mon, 2 Apr 2018 01:11:51 -0700 Subject: [PATCH 1/6] Enable IWDG module for Nucleo-64 F446RE board. Not sure why it was not enabled in the first place. It would probably work fine for other boards as well. --- variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h b/variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h index e98ba0870d..f4fb947a4c 100644 --- a/variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h +++ b/variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h @@ -69,7 +69,7 @@ #define HAL_GPIO_MODULE_ENABLED #define HAL_I2C_MODULE_ENABLED /* #define HAL_I2S_MODULE_ENABLED */ -/* #define HAL_IWDG_MODULE_ENABLED */ +#define HAL_IWDG_MODULE_ENABLED /* #define HAL_LTDC_MODULE_ENABLED */ /* #define HAL_DSI_MODULE_ENABLED */ #define HAL_PWR_MODULE_ENABLED From f5ca1bcc44f3a1faebb16a9f64a2f1007875da9f Mon Sep 17 00:00:00 2001 From: Venelin Efremov Date: Wed, 4 Apr 2018 00:36:55 -0700 Subject: [PATCH 2/6] Revert the HAL modification. --- variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h b/variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h index f4fb947a4c..e98ba0870d 100644 --- a/variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h +++ b/variants/NUCLEO_F446RE/stm32f4xx_hal_conf.h @@ -69,7 +69,7 @@ #define HAL_GPIO_MODULE_ENABLED #define HAL_I2C_MODULE_ENABLED /* #define HAL_I2S_MODULE_ENABLED */ -#define HAL_IWDG_MODULE_ENABLED +/* #define HAL_IWDG_MODULE_ENABLED */ /* #define HAL_LTDC_MODULE_ENABLED */ /* #define HAL_DSI_MODULE_ENABLED */ #define HAL_PWR_MODULE_ENABLED From ac814fe021157fe6280f98f3cb30d8dcaaa3099a Mon Sep 17 00:00:00 2001 From: Venelin Efremov Date: Wed, 4 Apr 2018 00:42:38 -0700 Subject: [PATCH 3/6] Add Watchdog library to expose the IWDG component. --- libraries/Watchdog/README.md | 81 +++++++++++++++++++ libraries/Watchdog/Watchdog.cpp | 34 ++++++++ libraries/Watchdog/Watchdog.h | 66 +++++++++++++++ libraries/Watchdog/examples/Button/Button.ino | 55 +++++++++++++ .../Watchdog/examples/Button/build_opt.h | 1 + libraries/Watchdog/keywords.txt | 20 +++++ libraries/Watchdog/library.properties | 9 +++ 7 files changed, 266 insertions(+) create mode 100644 libraries/Watchdog/README.md create mode 100644 libraries/Watchdog/Watchdog.cpp create mode 100644 libraries/Watchdog/Watchdog.h create mode 100644 libraries/Watchdog/examples/Button/Button.ino create mode 100644 libraries/Watchdog/examples/Button/build_opt.h create mode 100644 libraries/Watchdog/keywords.txt create mode 100644 libraries/Watchdog/library.properties diff --git a/libraries/Watchdog/README.md b/libraries/Watchdog/README.md new file mode 100644 index 0000000000..e999f570d1 --- /dev/null +++ b/libraries/Watchdog/README.md @@ -0,0 +1,81 @@ +## **Watchdog Library V1.0** for Arduino + +**Written by:** _Venelin Efremov_. + +### **What is the Watchdog library.** + +Th Watchdog library provides an interface to the independent watchdog module (IWDG) inside STM32 chips. +The IWDG module is used in production systems to generate a reset signal to the CPU in case some +catastrophic event in causes the software to become "stuck" or unresponsive. + +The IWDG module contains a count-down timer. The module would generate a reset condition when the timer reaches zero. In normal operation mode the software running on the CPU would reload the timer periodically to +prevent the reset condition from happening. However if a software bug or other error causes the CPU to execute a different code path for too long, the reload would not happen and the IWDG module would reset the CPU. + +### **How to use it** +The Watchdog library is included with the STM32 core download. To add its functionality to your sketch you'll need to reference the library header file. You do this by adding an include directive to the top of your sketch. + +```Arduino +#include + +void setup() { + ... + // Initialize the IWDG with 4 seconds timeout. + // This would cause a CPU reset if the IWDG timer + // is not reloaded in approximately 4 seconds. + watchdog_init(4000); +} + +void loop() { + ...your code here... + // make sure the code in this loop is executed in + // less than 2 seconds to leave 50% headroom for + // the timer reload. + watchdog_reset(); +} + +``` + +### **Library functions** + +#### **`watchdog_init(uint16_t timeout_ms)`** + +The `watchdog_init()` function would initialize the IWDG hardware block. + +The timeout_ms parameter would set the timer reset timeout. When the +timer reaches zero the hardware block would generate a reset signal +for the CPU. + +When specifying timeout value plan to refresh the timer at least twice +as often. The `watchdog_reset()` operation is not expensive. + +The downside of slecting a very large timeout value is that your system +may be left in a "stuck" state for too long, before the reset is +generated. + +Valid timeout values are between 1ms and 32,768ms (~32.8 seconds). For +values above 4095 the timeout paramater starts to loose precision: + + | timeout value range | timeout value precision | + | ------------------- |:-----------------------:| + | 1 - 4096 | 1ms + | 4097 - 8192 | 2ms + | 8193 - 16384 | 4ms + | 16385 - 32768 | 8ms + +Calling the `watchdog_init()` method with value outside of the valid range +would return without initializing the watchdog timer. + +**WARNING:** +*Once started the watchdog timer can not be stopped. If you are +planning to debug the live system, the watchdog timer may cause the +system to be reset while you are stopped in the debugger. Also consider +the watchdog timer implications if you are designing a system which puts +the CPU in sleep mode.* + +#### **`watchdog_reset()`** + +The `watchdog_reset()` function would reset the watchdog timer. + +Once you have initialized the watchdog you **HAVE** to call `watchdog_reset()` +periodically to prevent the CPU being reset. + diff --git a/libraries/Watchdog/Watchdog.cpp b/libraries/Watchdog/Watchdog.cpp new file mode 100644 index 0000000000..31f01635f7 --- /dev/null +++ b/libraries/Watchdog/Watchdog.cpp @@ -0,0 +1,34 @@ +#include "Watchdog.h" + +static IWDG_HandleTypeDef hiwdg; + +void watchdog_init(uint16_t timeout_ms) { + if (timeout_ms == 0 || timeout_ms > 32768) { + return; + } + hiwdg.Instance = IWDG; + // Compute the prescaler value. Start with 32 which gives 1ms precision. + hiwdg.Init.Prescaler = IWDG_PRESCALER_32; + timeout_ms--; + if (timeout_ms > 4095) { + // 2ms precision + hiwdg.Init.Prescaler = IWDG_PRESCALER_64; + timeout_ms >>= 1; + } + if (timeout_ms > 4095) { + // 4ms precision + hiwdg.Init.Prescaler = IWDG_PRESCALER_128; + timeout_ms >>= 1; + } + if (timeout_ms > 4095) { + // 8ms precision + hiwdg.Init.Prescaler = IWDG_PRESCALER_256; + timeout_ms >>= 1; + } + hiwdg.Init.Reload = timeout_ms; + HAL_IWDG_Init(&hiwdg); +} + +void watchdog_reset() { + HAL_IWDG_Refresh(&hiwdg); +} diff --git a/libraries/Watchdog/Watchdog.h b/libraries/Watchdog/Watchdog.h new file mode 100644 index 0000000000..7a616b2f2e --- /dev/null +++ b/libraries/Watchdog/Watchdog.h @@ -0,0 +1,66 @@ +#ifndef __WATCHDOG_STM32_H__ +#define __WATCHDOG_STM32_H__ + +#include "Arduino.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * To enable the IWDG HAL component used by this library you must add a + * file named build_opt.h to your sketch with the following content: + * + * -DHAL_IWDG_MODULE_ENABLED + * + * Include the dash (-) in front of DHAL_IWDG_MODULE_ENABLED + */ + +/* + * The watchdog_init function would initialize the IWDG hardware block. + * + * The timeout_ms parameter would set the timer reset timeout. When the + * timer reaches zero the hardware block would generate a reset signal + * for the CPU. + * + * When specifying timeout value plan to refresh the timer at least twice + * as often. The reset operation is not very expensive. + * + * The downside of slecting a very large timeout value is that your system + * may be left in a "stuck" state for too long, before the reset is + * generated. + * + * Valid timeout values are between 1ms and 32,768ms (~32.8 seconds). For + * values above 4095 the timeout paramater starts to loose precision: + * + * timeout value range | timeout value precision + * 00001-04096 | 1ms + * 04097-08192 | 2ms + * 08193-16384 | 4ms + * 16385-32768 | 8ms + * + * Calling the watchdog_init method with value outside of the valid range + * would return without initializing the watchdog timer. + * + * WARNING: Once started the watchdog timer can not be stopped. If you are + * planning to debug the live system, the watchdog timer may cause the + * system to be reset while you are stopped in the debugger. Also consider + * the watchdog timer implications if you are designing a system which puts + * the CPU in sleep mode. + * + */ +void watchdog_init(uint16_t timeout_ms); + +/* + * The watchdog_reset() function would reset the watchdog timer. Once you + * have initialized the watchdog you HAVE to call watchdog_reset() + * periodically to prevent the CPU being reset. + */ +void watchdog_reset(); + +#ifdef __cplusplus +} +#endif + +#endif // __WATCHDOG_STM32_H__ diff --git a/libraries/Watchdog/examples/Button/Button.ino b/libraries/Watchdog/examples/Button/Button.ino new file mode 100644 index 0000000000..f13a1d20be --- /dev/null +++ b/libraries/Watchdog/examples/Button/Button.ino @@ -0,0 +1,55 @@ +/* + Watchdog + Button + + This example code is in the public domain. + + The code demonstrates the use of a watchdog timer. The watchdog timer is + initialized with timeout of 10 seconds. Every time the button is pressed, + the watchdog timer is reset. If left unattended the system would reset + itself about every 10 seconds. + + You would have to keep pressing the button frequently (< 10 seoncds apart) + to prevent the system from reseting itself. + + You would recongnize the reset condition when the LED blinks rapidly a + few times. + + This is not a practical example, in real code you would reset the watchdog + timer in the main loop without requiring user input. + + The code is modified version of the code from: + http://www.arduino.cc/en/Tutorial/Button +*/ + +#include + +const int buttonPin = USER_BTN; +const int ledPin = LED_BUILTIN; + +void setup() { + pinMode(ledPin, OUTPUT); + pinMode(buttonPin, INPUT); + + // Blink the LED to indicate reset. + for(uint8_t idx = 0; idx < 5; idx++) { + digitalWrite(ledPin, HIGH); + delay(100); + digitalWrite(ledPin, LOW); + delay(100); + } + // Init the watchdog timer with 10 seconds timeout + watchdog_init(10000); +} + +void loop() { + // read the state of the pushbutton value: + int buttonState = digitalRead(buttonPin); + + if (buttonState == HIGH) { + digitalWrite(ledPin, HIGH); + } else { + digitalWrite(ledPin, LOW); + // Reset the watchdog only when the button is pressed. + watchdog_reset(); + } +} diff --git a/libraries/Watchdog/examples/Button/build_opt.h b/libraries/Watchdog/examples/Button/build_opt.h new file mode 100644 index 0000000000..911022e87a --- /dev/null +++ b/libraries/Watchdog/examples/Button/build_opt.h @@ -0,0 +1 @@ +-DHAL_IWDG_MODULE_ENABLED diff --git a/libraries/Watchdog/keywords.txt b/libraries/Watchdog/keywords.txt new file mode 100644 index 0000000000..0d24219152 --- /dev/null +++ b/libraries/Watchdog/keywords.txt @@ -0,0 +1,20 @@ +####################################### +# Syntax Coloring Map For Watchdog +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +watchdog_init KEYWORD2 +watchdog_reset KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/libraries/Watchdog/library.properties b/libraries/Watchdog/library.properties new file mode 100644 index 0000000000..ada12a745a --- /dev/null +++ b/libraries/Watchdog/library.properties @@ -0,0 +1,9 @@ +name=Watchdog +version=1.0 +author=Arduino, Venelin Efremov +maintainer=stm32duino +sentence=Enables support for independent watchdog hardware on STM32 processors. +paragraph=Independent watchdog (IWDG) is a hardware timer on the chip which would generate a reset condition if the times is not reloaded withing the specified time. It is genrally used in production systems to reset the system if the CPU becomes "stuck". +category=Timing +url= +architectures=stm32 From fa7b449b51d2101802db94f1b460c360f328e2b1 Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 11 May 2018 16:30:22 +0200 Subject: [PATCH 4/6] [DISCO_F746NG] Use HSE instead of HSE ByPass Signed-off-by: Frederic.Pillon --- variants/DISCO_F746NG/variant.cpp | 32 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/variants/DISCO_F746NG/variant.cpp b/variants/DISCO_F746NG/variant.cpp index 027661de9f..2c0f49e7ef 100644 --- a/variants/DISCO_F746NG/variant.cpp +++ b/variants/DISCO_F746NG/variant.cpp @@ -63,7 +63,7 @@ extern "C" { /** * @brief System Clock Configuration - * The system Clock is configured as follow : + * The system Clock is configured as follow : * System Clock source = PLL (HSE) * SYSCLK(Hz) = 216000000 * HCLK(Hz) = 216000000 @@ -89,16 +89,14 @@ WEAK void SystemClock_Config(void) RCC_ClkInitTypeDef RCC_ClkInitStruct; RCC_PeriphCLKInitTypeDef PeriphClkInitStruct; - /**Configure the main internal regulator output voltage - */ + /* Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); - /**Initializes the CPU, AHB and APB busses clocks - */ + /* Initializes the CPU, AHB and APB busses clocks */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; - RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 25; @@ -107,18 +105,16 @@ WEAK void SystemClock_Config(void) RCC_OscInitStruct.PLL.PLLQ = 9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { - while(1) { ; } + _Error_Handler(__FILE__, __LINE__); } - /**Activate the Over-Drive mode - */ + /* Activate the Over-Drive mode */ if (HAL_PWREx_EnableOverDrive() != HAL_OK) { - while(1) { ; } + _Error_Handler(__FILE__, __LINE__); } - /**Initializes the CPU, AHB and APB busses clocks - */ + /* Initializes the CPU, AHB and APB busses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; @@ -128,26 +124,28 @@ WEAK void SystemClock_Config(void) if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7) != HAL_OK) { - while(1) { ; } + _Error_Handler(__FILE__, __LINE__); } PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC|RCC_PERIPHCLK_USART1 |RCC_PERIPHCLK_CLK48; - PeriphClkInitStruct.PLLSAI.PLLSAIN = 192; + PeriphClkInitStruct.PLLSAI.PLLSAIN = 100; PeriphClkInitStruct.PLLSAI.PLLSAIR = 2; PeriphClkInitStruct.PLLSAI.PLLSAIQ = 2; - PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV4; + PeriphClkInitStruct.PLLSAI.PLLSAIP = RCC_PLLSAIP_DIV2; PeriphClkInitStruct.PLLSAIDivQ = 1; PeriphClkInitStruct.PLLSAIDivR = RCC_PLLSAIDIVR_2; PeriphClkInitStruct.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2; - PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLLSAIP; + PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLL; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) { - while(1) { ; } + _Error_Handler(__FILE__, __LINE__); } + /* Configure the Systick interrupt time */ HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); + /* Configure the Systick */ HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); /* SysTick_IRQn interrupt configuration */ From a71ed8de18ba2054d21e2112c57cd1c245c103df Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 11 May 2018 17:42:01 +0200 Subject: [PATCH 5/6] [CM7] Enable I-Cache and D-Cache To skip use of those cache, define 'UNUSED_ID_CACHE' Signed-off-by: Frederic.Pillon --- cores/arduino/main.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cores/arduino/main.cpp b/cores/arduino/main.cpp index 3bf32a6616..e6b1470f1f 100644 --- a/cores/arduino/main.cpp +++ b/cores/arduino/main.cpp @@ -34,6 +34,11 @@ void initVariant() { } #ifdef NVIC_PRIORITYGROUP_4 HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); #endif +#if (__CORTEX_M == 0x07U) && !defined(UNUSED_ID_CACHE) +// Defined in CMSIS core_cm7.h + SCB_EnableICache(); + SCB_EnableDCache(); +#endif init(); } From 1ff1e3e235dc19e64e01450ee5db8a642d15a539 Mon Sep 17 00:00:00 2001 From: "Frederic.Pillon" Date: Fri, 11 May 2018 17:24:20 +0200 Subject: [PATCH 6/6] [RTC] Make some arguments optional (part 2) day and mask pointers arguments of RTC_GetAlarm() could be NULL Fix unused variable warning Signed-off-by: Frederic.Pillon --- cores/arduino/stm32/rtc.c | 50 ++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/cores/arduino/stm32/rtc.c b/cores/arduino/stm32/rtc.c index a4350edf1d..562d09110e 100644 --- a/cores/arduino/stm32/rtc.c +++ b/cores/arduino/stm32/rtc.c @@ -443,7 +443,12 @@ void RTC_GetTime(uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *s if(subSeconds != NULL) { *subSeconds = RTC_TimeStruct.SubSeconds; } +#else + UNUSED(subSeconds); #endif +#else + UNUSED(period); + UNUSED(subSeconds); #endif /* !STM32F1xx */ } } @@ -581,19 +586,21 @@ void RTC_StopAlarm(void) /** * @brief Get RTC alarm - * @param day: 1-31 (day of the month) - * @param hours: 0-12 or 0-23 depends on the hours mode. + * @param day: 1-31 day of the month (optional could be NULL) + * @param hours: 0-12 or 0-23 depends on the hours mode * @param minutes: 0-59 * @param seconds: 0-59 * @param subSeconds: 0-999 (optional could be NULL) * @param period: AM or PM (optional could be NULL) + * @param mask: alarm behavior using alarmMask_t combination (optional could be NULL) + * See AN4579 Table 5 for possible values * @retval None */ void RTC_GetAlarm(uint8_t *day, uint8_t *hours, uint8_t *minutes, uint8_t *seconds, uint32_t *subSeconds, hourAM_PM_t *period, uint8_t *mask) { RTC_AlarmTypeDef RTC_AlarmStructure; - if((day != NULL) && (hours != NULL) && (minutes != NULL) && (seconds != NULL) && (mask != NULL)) { + if((hours != NULL) && (minutes != NULL) && (seconds != NULL)) { HAL_RTC_GetAlarm(&RtcHandle, &RTC_AlarmStructure, RTC_ALARM_A, RTC_FORMAT_BIN); *seconds = RTC_AlarmStructure.AlarmTime.Seconds; @@ -601,7 +608,9 @@ void RTC_GetAlarm(uint8_t *day, uint8_t *hours, uint8_t *minutes, uint8_t *secon *hours = RTC_AlarmStructure.AlarmTime.Hours; #if !defined(STM32F1xx) - *day = RTC_AlarmStructure.AlarmDateWeekDay; + if (day != NULL) { + *day = RTC_AlarmStructure.AlarmDateWeekDay; + } if(period != NULL) { if(RTC_AlarmStructure.AlarmTime.TimeFormat == RTC_HOURFORMAT12_PM) { *period = PM; @@ -613,20 +622,29 @@ void RTC_GetAlarm(uint8_t *day, uint8_t *hours, uint8_t *minutes, uint8_t *secon if(subSeconds != NULL) { *subSeconds = RTC_AlarmStructure.AlarmTime.SubSeconds; } +#else + UNUSED(subSeconds); #endif /* !STM32F2xx && !STM32L1xx || STM32L1_ULPH */ - *mask = OFF_MSK; - if(!(RTC_AlarmStructure.AlarmMask & RTC_ALARMMASK_SECONDS)) { - *mask |= SS_MSK; - } - if(!(RTC_AlarmStructure.AlarmMask & RTC_ALARMMASK_MINUTES)) { - *mask |= MM_MSK; - } - if(!(RTC_AlarmStructure.AlarmMask & RTC_ALARMMASK_HOURS)) { - *mask |= HH_MSK; - } - if(!(RTC_AlarmStructure.AlarmMask & RTC_ALARMMASK_DATEWEEKDAY)) { - *mask |= D_MSK; + if (mask != NULL) { + *mask = OFF_MSK; + if(!(RTC_AlarmStructure.AlarmMask & RTC_ALARMMASK_SECONDS)) { + *mask |= SS_MSK; + } + if(!(RTC_AlarmStructure.AlarmMask & RTC_ALARMMASK_MINUTES)) { + *mask |= MM_MSK; + } + if(!(RTC_AlarmStructure.AlarmMask & RTC_ALARMMASK_HOURS)) { + *mask |= HH_MSK; + } + if(!(RTC_AlarmStructure.AlarmMask & RTC_ALARMMASK_DATEWEEKDAY)) { + *mask |= D_MSK; + } } +#else + UNUSED(day); + UNUSED(period); + UNUSED(subSeconds); + UNUSED(mask); #endif /* !STM32F1xx */ } }