2020-05-21 01:05 AM
Greetings.
I have been using STMCubeMX for SMTF7 processors for quite a while. The function void SystemInit(void) from the file system_stm32f7xx.c generated by older versions of STMCubeMX used to contain the following code:
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset CFGR register */
RCC->CFGR = 0x00000000;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Disable all interrupts */
RCC->CIR = 0x00000000;
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = RAMDTCM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
The code generated by newer versions of STMCubeMX (5.6.1) skips a good portion of this code:
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Configure the Vector Table location add offset address ------------------*/
#ifdef VECT_TAB_SRAM
SCB->VTOR = RAMDTCM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
Omission of these lines tends to cause issues for some highly-optimized code. From my own experience, the system clock (PLL from HSE) is set improperly.
2020-05-21 01:29 AM
2020-05-21 01:34 AM
My pleasure.
For example, this MWE fails to deliver a 216 MHz clock from a 25 MHz crystal:
#include "stm32f7xx.h"
int main()
{
RCC->APB1ENR |= RCC_APB1ENR_PWREN; // Power interface clock enable
while ((RCC->APB1ENR & RCC_APB1ENR_PWREN) != RCC_APB1ENR_PWREN)
{
};
PWR->CR1 |= PWR_CR1_VOS; // Voltage scaling = range 1
while ((PWR->CR1 & PWR_CR1_VOS) != PWR_CR1_VOS)
{
}; // Waits until scaling is ready
FLASH->ACR = ( FLASH_ACR_PRFTEN | // Prefetch enable
FLASH_ACR_LATENCY_7WS); // FLASH 4 wait states
RCC->CR &= 0;
RCC->CR |= RCC_CR_HSEON; // Enables HSE clock
while ((RCC->CR & RCC_CR_HSERDY) != RCC_CR_HSERDY)
{
}; // Waits until HSE is stable
RCC->PLLCFGR = ( RCC_PLLCFGR_PLLSRC_HSE | // PLL SRC = HSE
(25u << RCC_PLLCFGR_PLLM_Pos) | // PLL_M = 25
(432u << RCC_PLLCFGR_PLLN_Pos) | // PLL_N = 432
(0u << RCC_PLLCFGR_PLLP_Pos) | // PLL_P = 2
(2u << RCC_PLLCFGR_PLLQ_Pos)); // PLL_Q = 2
RCC->CFGR = 0;
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
RCC->CR |= RCC_CR_PLLON; // Enables the PLL
while (!(RCC->CR & RCC_CR_PLLRDY))
{
}; // Waits until PLL is stable
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while (!(RCC->CFGR & RCC_CFGR_SWS_PLL))
{
};
/* Main clock output (MCO): */
RCC->CFGR &= ~RCC_CFGR_MCO1_Msk;
RCC->CFGR |= (0x3 << RCC_CFGR_MCO1_Pos);
RCC->AHB1ENR &= ~RCC_AHB1ENR_GPIOAEN;
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
GPIOA->MODER &= ~GPIO_MODER_MODER8;
GPIOA->MODER |= GPIO_MODER_MODER8_1;
GPIOA->OTYPER &= ~GPIO_OTYPER_OT_8;
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPDR8;
GPIOA->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR8;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR8_1;
GPIOA->AFR[0] &= ~GPIO_AFRL_AFRL0;
}
The said code works just fine if the missing lines are added back.
2020-05-21 03:14 AM
Hello KR,
Actually, the removed lines of code are the RCC registers initialization to some reset value in SystemInit. RCC Registers use to be initialized to some reset value in SystemInit whereas this may cause problems, because with this initialization there is no way to know what is reset reason (System/ software/ Exit from stdby, etc…) so what has been configured previously.
RCC registers have to behave on their own depending on different type of reset and we should not modify it.
Thus, RCC reset have been removed from SystemInit() and kept handled by the HAL.
So your problem may not be related to the omission of these lines. Can you please share your ioc file and detail more the encountered issue ?
BR,
Khouloud.
2020-05-21 03:42 AM
I had the suspicion that this routine was moved to the HAL code.
You see, my issue is that I often work with projects that require very critical timing/response time. HAL tends to add a lot of unnecessary overhead. This is the case with this project as well. Nonetheless, I still use STMCubeMX to generate the project, as it gathers and/or generates all the necessary files (namely STM32F746VGTx_FLASH.ld, startup_stm32f746xx.s, system_stm32f7xx.c, and everything in the Drivers/CMSIS folder). This is what is essentially required for a bare-bones project, where you directly manipulate the registers themselves.
I am attaching a copy of my project file. Keep in mind that I do not really run the project in this state and start from a blank slate. I strip the project to its bare essentials:
The actual contents of my main.c are provided in a post above.
2020-05-21 08:50 AM
The RCC Reset routine is handled by HAL (check stm32f7xx_hal_rcc.c file). The project is correctly generated by STM32CubeMX 5.6.1 since it still manages RCC registers behavior. As I understand you use the STM32CubeMx just because it gathers the necessary file you need for your project and you are basically manipulating the registers.
2020-05-21 10:17 AM
That is correct. As mentioned above, I do this for projects that have very strict timing jitter and/or response requirements (as HAL tends to add unwanted delay in some of the more stringent cases). Direct manipulation of the registers is the only way to go in these scenarios.
I had no issues in the past with this approach, as all the necessary non-HAL files were gathered or created by STM32CubeMx. Essentially, all I had to do was to delete all the HAL-associated files from the project and to remove the USE_HAL_DRIVER preprocessor directive.
Maybe I'm in the minority here, but it would be nice to have an option to create bare-bones project somehow. I was actually rather content with the old way of removing the necessary files (as all the crucial files were still intact) :).
2020-06-15 08:20 AM
I want to bump this old question one more time.
I still believe that there is an issue with STM32Cube_FW_F7_V1.16.0 due to the missing lines in SystemInit(). I have tried this with a HAL-based piece of code (i. e., by keeping all the STMCubeMX generated files and code intact). Correct me if I am wrong here.
I am attaching a very simple project for a STM32F746VGT microcontroller (I would assume that any member of the F7 series would do).
The clock source is a 25 MHz external oscillator. The PLL upscales it to 216 MHz which can be monitored on the PA8 pin.
If I compile this code and run it (I use SW4STM32), then the PLLCLK output on PA8 is 196 MHz (I've checked it with an oscilloscope). The PLL is properly set to 216 MHz only if I either:
a) Add the
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset CFGR register */
RCC->CFGR = 0x00000000;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset PLLCFGR register */
RCC->PLLCFGR = 0x24003010;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Disable all interrupts */
RCC->CIR = 0x00000000;
lines back to SystemInit().
b) Call the HAL_RCC_DeInit() function before HAL_Init:
int main(void)
{
/* USER CODE BEGIN 1 */
HAL_RCC_DeInit();
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
2020-06-15 01:12 PM
Overall HAL/CubeMX code is flawed as hell, but removing that RCC register initialization from SystemInit() is the right thing because it doesn't belong there at all. And that is not the problem here also. The real issue is the fact that overdrive mode is not enabled and without it the datasheet (Table 17.) allows to go only up to 180 MHz. You need to add this code:
PWR->CR1 |= PWR_CR1_ODEN;
while (!(PWR->CSR1 & PWR_CSR1_ODRDY));
PWR->CR1 |= PWR_CR1_ODSWEN;
while (!(PWR->CSR1 & PWR_CSR1_ODSWRDY));
By the way there is no use of waiting on VOS bits, as those are set instantly and actual voltage scaling kicks in only when the PLL is enabled. Actually VOS bits doesn't need any initialization at all as Scale 1 is their reset value.
Also...
RCC->APB1ENR |= RCC_APB1ENR_PWREN; // Power interface clock enable
while ((RCC->APB1ENR & RCC_APB1ENR_PWREN) != RCC_APB1ENR_PWREN)
{
};
Is that while for the required delay? You might want to take a look on my universal "few peripheral bus clock cycles" delay implementation here:
https://community.st.com/s/question/0D50X0000Bmnksg/stm32f446ze-doesnt-enter-in-standby-mode
2020-06-15 01:42 PM
@Piranha Is that while for the required delay?
Truth be told, I've been using this code snippet for so long that I have completely forgotten its origin :).
However, I am very intrigued regarding the over-drive mode. It is true, that I have omitted turning it on (it must have been an oversight on my behalf). Nonetheless, I haven't had any timing issues for timers, SPI, I2C, UART or CAN. In other words, even with this kind of system clock initialization, my communications or timers always work at the proper frequency (i.e., some multiple or product of either 216, 108 or 54 MHz, that can be calculated from the equations given in the reference manual). I never seemed to have any issues with it (and I have some stuff that has been running for years with this code). For example, the code in my second post runs just fine with STM32Cube_FW_F7_V1.15.0 or older (i. e., where the SystemInit() has more register manipulation within it). The said code generates a proper 216 MHz MCO output on on PA8. Likewise, I can, for example, easily add initialization of a SPI peripheral which would work at 54 MHz without any problems (and I can prove the validity of the operational frequency by directly probing the SPI or MCO outputs). I will definitely check out what happens when the over-drive mode is turned on. Thanks for the insight!
Nonetheless, the HAL/CubeMX code that I have posted recently (or the STM32CubeMX *.ioc, to be exact) does not function properly. At leas for me this pure HAL/CubeMX code (i.e., without any changes to it) does not provide the expected clock output.