Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
9 years, 1 month ago.
How to enable Nucleo's internal lowspeed crystal?
Hello, All!
I'm going on a flight controller project with mbed. It needs accurate high speed xtal to generate accurate pwm wave(HSE). I have already fixed this problem(Nucleo uses HSE already). But another problem came to me. It does NOT need accurate RTC. I want to save the place of an external 32K xtal, using LSI instead of LSE. But the origin Nucleo Board comes with an external 32k crystal. If I want to use stm32's internal lowspeed clock source. I should just leave the osc32 in&out open. Or should I change some defination in the mbed lib?
Thank you !
2 Answers
9 years, 1 month ago.
If you use the F401 and have the crystals fitted (with the loading capacitors), they will be auto-detected on start up. The F401 has a more functional RTC where as the F103 is more basic. If you want to use the RTC properly with vBAT connected with a 3v backup cell then you will need to use this code in the rtc_api.c file (simply copy-paste the code in to replace the existing code). Otherwise your RTC will be reset on power down or nRST.
STM F401 rtc_api.c for backup RTC function
/* mbed Microcontroller Library ******************************************************************************* * Copyright (c) 2014, STMicroelectronics * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************* */ #include "rtc_api.h" #if DEVICE_RTC #include "mbed_error.h" static RTC_HandleTypeDef RtcHandle; void rtc_init(void) { RCC_OscInitTypeDef RCC_OscInitStruct; uint32_t rtc_freq = 0; if(RTC->ISR == 7) { // RTC initialization and status register (RTC_ISR), cold start (with no backup domain power) RTC reset value RtcHandle.Instance = RTC; // Enable Power clock __PWR_CLK_ENABLE(); // Enable access to Backup domain HAL_PWR_EnableBkUpAccess(); // Reset Backup domain __HAL_RCC_BACKUPRESET_FORCE(); __HAL_RCC_BACKUPRESET_RELEASE(); // Enable LSE Oscillator RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; /* Mandatory, otherwise the PLL is reconfigured! */ RCC_OscInitStruct.LSEState = RCC_LSE_ON; /* External 32.768 kHz clock on OSC_IN/OSC_OUT */ if (HAL_RCC_OscConfig(&RCC_OscInitStruct) == HAL_OK) { // Connect LSE to RTC __HAL_RCC_RTC_CLKPRESCALER(RCC_RTCCLKSOURCE_LSE); __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE); rtc_freq = LSE_VALUE; } else { // Enable LSI clock RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; // Mandatory, otherwise the PLL is reconfigured! RCC_OscInitStruct.LSEState = RCC_LSE_OFF; RCC_OscInitStruct.LSIState = RCC_LSI_ON; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { error("RTC error: LSI clock initialization failed."); } // Connect LSI to RTC __HAL_RCC_RTC_CLKPRESCALER(RCC_RTCCLKSOURCE_LSI); __HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSI); // [TODO] This value is LSI typical value. To be measured precisely using a timer input capture rtc_freq = LSI_VALUE; } // Enable RTC __HAL_RCC_RTC_ENABLE(); RtcHandle.Init.HourFormat = RTC_HOURFORMAT_24; RtcHandle.Init.AsynchPrediv = 127; RtcHandle.Init.SynchPrediv = (rtc_freq / 128) - 1; RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE; RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH; RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN; if (HAL_RTC_Init(&RtcHandle) != HAL_OK) { error("RTC error: RTC initialization failed."); } } } void rtc_free(void) { // Enable Power clock __PWR_CLK_ENABLE(); // Enable access to Backup domain HAL_PWR_EnableBkUpAccess(); // Reset Backup domain __HAL_RCC_BACKUPRESET_FORCE(); __HAL_RCC_BACKUPRESET_RELEASE(); // Disable access to Backup domain HAL_PWR_DisableBkUpAccess(); // Disable LSI and LSE clocks RCC_OscInitTypeDef RCC_OscInitStruct; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; RCC_OscInitStruct.LSIState = RCC_LSI_OFF; RCC_OscInitStruct.LSEState = RCC_LSE_OFF; HAL_RCC_OscConfig(&RCC_OscInitStruct); } int rtc_isenabled(void) { if(RTC->ISR != 7) { return 1; } else { return 0; } } /* RTC Registers RTC_WeekDay 1=monday, 2=tuesday, ..., 7=sunday RTC_Month 1=january, 2=february, ..., 12=december RTC_Date day of the month 1-31 RTC_Year year 0-99 struct tm tm_sec seconds after the minute 0-61 tm_min minutes after the hour 0-59 tm_hour hours since midnight 0-23 tm_mday day of the month 1-31 tm_mon months since January 0-11 tm_year years since 1900 tm_wday days since Sunday 0-6 tm_yday days since January 1 0-365 tm_isdst Daylight Saving Time flag */ time_t rtc_read(void) { RTC_DateTypeDef dateStruct; RTC_TimeTypeDef timeStruct; struct tm timeinfo; RtcHandle.Instance = RTC; // Read actual date and time // Warning: the time must be read first! HAL_RTC_GetTime(&RtcHandle, &timeStruct, FORMAT_BIN); HAL_RTC_GetDate(&RtcHandle, &dateStruct, FORMAT_BIN); // Setup a tm structure based on the RTC timeinfo.tm_wday = dateStruct.WeekDay; timeinfo.tm_mon = dateStruct.Month - 1; timeinfo.tm_mday = dateStruct.Date; timeinfo.tm_year = dateStruct.Year + 100; timeinfo.tm_hour = timeStruct.Hours; timeinfo.tm_min = timeStruct.Minutes; timeinfo.tm_sec = timeStruct.Seconds; // Convert to timestamp time_t t = mktime(&timeinfo); return t; } void rtc_write(time_t t) { RTC_DateTypeDef dateStruct; RTC_TimeTypeDef timeStruct; RtcHandle.Instance = RTC; // Enable write access to Backup domain HAL_PWR_EnableBkUpAccess(); // Convert the time into a tm struct tm *timeinfo = localtime(&t); // Fill RTC structures dateStruct.WeekDay = timeinfo->tm_wday; dateStruct.Month = timeinfo->tm_mon + 1; dateStruct.Date = timeinfo->tm_mday; dateStruct.Year = timeinfo->tm_year - 100; timeStruct.Hours = timeinfo->tm_hour; timeStruct.Minutes = timeinfo->tm_min; timeStruct.Seconds = timeinfo->tm_sec; timeStruct.TimeFormat = RTC_HOURFORMAT12_PM; timeStruct.DayLightSaving = RTC_DAYLIGHTSAVING_NONE; timeStruct.StoreOperation = RTC_STOREOPERATION_RESET; // Change the RTC current date/time HAL_RTC_SetDate(&RtcHandle, &dateStruct, FORMAT_BIN); HAL_RTC_SetTime(&RtcHandle, &timeStruct, FORMAT_BIN); } #endif
You may want to change the HSE start up timeout, takes 5 seconds to nRST otherwise, to 500mS as below. But check it does lock on the crystal, you may need longer time.
stm32f4xx_hal_conf.h set HSE timeout to 500mS
#if !defined (HSE_STARTUP_TIMEOUT) #define HSE_STARTUP_TIMEOUT ((uint32_t)500) /*!< Time out for HSE start up, in ms */ #endif /* HSE_STARTUP_TIMEOUT */
Thanks a lot, Paul. I will try this later. According to you and Erik's answers. I think the mbed lib for stm32 has some kind of automatic clock source detection function. (I saw in wiki and it said the high-speed clock has this kind of function, but it didn't mention about the lowspeed clock.) I have reserved the place for a 32K external RTC crystal and a backup battery(just a simple Li battery and a diode to charge it.) I'm now using nucleo f411, but my new project will be based on f103 since the price of a single f411 chip is 10 times of f103 chip here(It's strange that f411 is not popular here, maybe due to its price). Thank both of you. @Paul Staron @Erik Olieman
posted by 03 Oct 2015Jerry, I couldn't resolve the RTC reset issue with the F103, it uses different, 'cut down' hardware in the MCU. I found overall the F401 worked well in many areas compared with some of the other ST's. Can you use this chip instead of the F103, maybe cheaper that the F411. It may save you a lot of trouble in the long run.
posted by 04 Oct 20159 years, 1 month ago.
By default the Nucleo boards don't have this populated, and they use instead the LSI. However the only thing they use it for is their RTC (which is horribly inaccurate running at the internal oscillator). So unless you use the RTC in your flight controller it isn't even used.
Thanks,Eric. But I found that there's an external 32k crystal populated on the nucleo board. I planned to use GPS time to fix the RTC timer on every flight. So I don't need an external crystal. But I need RTC function(even inaccurate). So do you mean I should just ignore this thing and call RTC function in the program, and the mbed HAL will automatically switch to internal low-speed crystal when it failed to initialize the external 32k crystal?
posted by 03 Oct 2015Hello, I had the same question as Jerry's OP. I was able to use the above code with a slight mod to force LSI init, then was able to verify via RCC_BDCR and RCC_CSR that LSI was running. The weird thing is after reset (with power maintained), I get a Backup Domain Reset in the case if using LSI, whereas with LSE running (I have a 32K XTAL present), all is good and the RTC has not been stopped or reset. Same with Sleep (with power left ON). Any ideas on this one?
I just verified that in SystemInit() before HalInit() called, that RCC_ISR is already 0x7 in case of LSI running, where the low order 16 bits are another number like 0x27 when LSE is running. Maybe I should pull this comment, and make it a question. Thanks!
posted by 24 Jan 2016