2025-01-07 04:37 AM
Hi,
I am just starting to learn the STMCubeIDE debugger's capabilities. I have tested all the essential functions of the debugger on a led blink project, and after everything worked fine I moved on to an I2C code that actually requires some debugging. And this time it did not work properly. Starting in the top of the main function, I can only make two consecutive step overs until I get a hard fault. Step over acts like a step into, it still gets me inside every function. Pressing step into many times does not generate any faults, but once I get into blocking delay function, it becomes useless. I tried to use breakpoints and also tried it without any breakpoints. My step over still got me into every single function. Its also interesting, that this code does not work properly, but it gets to the infinite loop part - problems in the real time execution occur way later that during debugging. I tried to change debuggers optimization level, tried to create a new project with the same code, nothing worked. I also checked for similar threads in this forum, but I could not find a solution suitable for me. Please excuse my lack of experience, but at this point I really need some advice to get this moving.
#include "main.h"
#include <stdio.h>
void Core_Clock_Setup (void){
RCC->CR |= RCC_CR_HSEON; //Set the clock source to external crystal/resonator (HSE)
while (!(RCC->CR & RCC_CR_HSEON)); //Wait until clock gets stable
RCC->APB1ENR |= RCC_APB1ENR_PWREN; //Enable power interface clock
PWR->CR1 &= ~(1U << 14);
PWR->CR1 &= ~(1U << 15); //Set internal voltage regulator to is reset value (scale 1)
FLASH->ACR &= ~FLASH_ACR_ARTEN; //Disable ART accelerator
FLASH->ACR &= ~FLASH_ACR_ARTRST; //Reset ART accelerator
FLASH->ACR |= FLASH_ACR_PRFTEN; //Enable prefetch
FLASH->ACR |= FLASH_ACR_LATENCY_6WS; //Set 7 CPU clock cycle flash memory access time (in order to get 200 MHz core clock)
//@ 25 MHz crystal, 200 MHz core clock configuration down below (200 MHZ verified as output on MCO2)
RCC->CFGR &= ~(((1 << (7 - 4 + 1)) - 1) << 4);
RCC->CFGR &= ~(1 << 4); //Core clock division by 1 (core clock is not devided)
RCC->CFGR &= ~(1 << 5);
RCC->CFGR &= ~(1 << 6);
RCC->CFGR &= ~(1 << 7);
RCC->CFGR &= ~(1 << 29);
RCC->CFGR &= ~(1 << 30); //Set MCO2 as SYSCLK, no division
RCC->CFGR &= ~(1 << 31);
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;//HSE is set to be PLL entry
RCC->PLLCFGR &= ~(1 << 16); //PLLP Setting corresponding PLL prescalers (division by 2)
RCC->PLLCFGR &= ~(1 << 17);
RCC->PLLCFGR &= ~((1 << 6) - 1);
RCC->PLLCFGR |= (16 & ((1 << 6) - 1)); //PLLM Setting corresponding PLL prescalers (division by 16)
//RCC->PLLCFGR |= (16 << 0);
RCC->PLLCFGR &= ~(((1 << (14 - 6 + 1)) - 1) << 6);
RCC->PLLCFGR |= (256 << 6); //PLLN Setting corresponding PLL prescalers ( multiplication by 256)
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; //APB1 Low speed prescaler of 4 (50 MHz, max is 54 Mhz)
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; //APB2 High speed prescaler of 2 (100 MHz, max is 108 Mhz)
RCC->CR |= RCC_CR_PLLON; //Enable PLL
while (!(RCC->CR & RCC_CR_PLLRDY)); //Wait until PLL gets stable
RCC->CFGR |= RCC_CFGR_SW_PLL; //PLL is set to be core clock
//RCC->CFGR |= RCC_CFGR_SW_HSE; //HSE is set to be core clock
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); // Wait until PLL indeed becomes core clock source
}
//----------------------------------------------------------------------------------------------------------------------------------------------------------------------
void delay_ms (uint32_t ms){
SysTick->LOAD = (200000-1); // Configure for milisecond delay
SysTick->CTRL |= (1<<2); // Select Processor Clock for blocking delay function
SysTick->CTRL |= (1<<0); // Counnter Enable
SysTick->VAL = 0;
for(int i =0; i<ms ; i++)
{
while ((SysTick->CTRL & (1<<16)) == 0);
}
SysTick->CTRL &= ~(1<<0); // Counnter Disable
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void GPIO_Setup(void){
RCC->AHB1ENR |= (1 << 2); //Enable clock for GPIO bank C
RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank E
delay_ms(1);
//PC14 OUTPUT
GPIOC->MODER |= (0b01 << 28); //PC14 General purpose output mode
GPIOC->OTYPER &= ~ (1 << 14); //PC14 Output push-pull (reset state)
GPIOC->OSPEEDR |= (0b11 << 28); //PC14 very high GPIO speed
//GPIOC->PUPDR |= (0b10 << 28); //PC14 pull down resistors
//PC15 OUTPUT
GPIOC->MODER |= (0b01 << 30); //PC15 General purpose output mode
GPIOC->OTYPER &= ~ (1 << 15); //PC15 Output push-pull (reset state)
GPIOC->OSPEEDR |= (0b11 << 30); //PC15 very high GPIO speed
GPIOC->PUPDR |= (0b10 << 30); //PC15 pull down resistors
//PE4 OUTPUT
GPIOE->MODER |= (0b01 << 8); //PE4 General purpose output mode
GPIOE->OTYPER &= ~ (1 << 4); //PE4 Output push-pull (reset state)
GPIOE->OSPEEDR |= (0b11 << 8); //PE4 very high GPIO speed
GPIOE->PUPDR |= (0b10 << 8); //PE4 pull down resistors
//PE0 OUTPUT
GPIOE->MODER |= (0b01 << 0); //PE0 General purpose output mode
GPIOE->OTYPER &= ~ (1 << 0); //PE0 Output push-pull (reset state)
GPIOE->OSPEEDR |= (0b11 << 0); //PE0 very high GPIO speed
GPIOE->PUPDR |= (0b10 << 0); //PE0 pull down resistors
//-----------------------------------------------------------------------------------
//PE5 INPUT ALL BUTTONS EXTERNALLY PULLED UP
GPIOE->MODER |= (0b00 << 10); //PE5 General purpose input mode
//PE6 INPUT
GPIOE->MODER |= (0b00 << 12); //PE6 General purpose input mode
}
void I2C_Setup(void){
//SDA at pin PB7 and SCL at pin PB6
RCC->APB1ENR |= (1 << 21); //Enable I2C1 Clock
RCC->AHB1ENR |= (1 << 1); //Enable clock for GPIO bank B
GPIOA->AFR[0] = (0b0100 << 24); //Select alternate function AF4 as I2C on pin PB6
GPIOA->AFR[0] |= (0b0100 << 28); //Select alternate function AF4 as I2C on pin PB7
GPIOB->MODER = (0b10 << 12); //PB6 as alternate function
GPIOB->MODER |= (0b10 << 14); //PB7 as alternate function
GPIOB->OTYPER = (1 << 6); //PB6 as open drain
GPIOB->OTYPER |= (1 << 7); //PB7 as open drain
GPIOB->OSPEEDR = (0b11 << 12); //PB6 as very high speed
GPIOB->OSPEEDR |= (0b11 << 14); //PB7 as very high speed
//GPIOB->PUPDR = (0b01 << 12); //PB6 has pull up resistor
//GPIOB->PUPDR |= (0b01 << 14); //PB7 has pull up resistor
I2C1->CR1 = (1 << 0); //Reset I2C peripheral
I2C1->CR1 &= ~(1 << 0);
I2C1->TIMINGR = 0x10950D1F; //This value has been taken from a reference MX generated project for 300 ns max rise/fall times, 400 KHz, no filters at all
I2C1->CR1 |= (1 << 17); //Clock stretching disabled
I2C1->CR1 |= (1 << 12); //Analog noise filter disabled
I2C1->CR1 |= (1 << 0); //Enable I2C
}
void I2C_Start(void){
I2C1->CR2 |= (1 << 13); //Generate start condition
while (!(I2C1->ISR&(1 << 15))); //Wait until start is generated
}
void I2C_Send(uint8_t data){
while (!(I2C1->ISR&(1 << 0))); //Wait TX buffer is empty
I2C1->TXDR = data; //Send data
while (!(I2C1->ISR&(1 << 6))); //Wait until transfer is complete
}
/*
void I2C_Send_Data_Multi(uint8_t* data, uint8_t size){ //Different procedures are used if sending more or less than 255 bytes of data
while (!(I2C1->ISR&(1 << 0))); //Wait TX buffer is empty
while (size){
while (!(I2C1->ISR&(1 << 0))); //Wait TX buffer is empty
I2C1->TXDR = (volatile uint32_t)*data++; //Send data
size--;
}
while (!(I2C1->ISR&(1 << 6))); //Wait until transfer is complete
}
*/
uint8_t I2C_Receive(){
uint8_t data;
while (!(I2C1->ISR&(1 << 2))); //Wait while RX buffer becomes not empty
data = I2C1->RXDR; //Send data
return data;
}
void I2C_Address(uint8_t address){
I2C1->TXDR = address; //Send address
while (!(I2C1->ISR&(1 << 3))); //Wait until ADDR bit sets
I2C1->ICR |= (1 << 3); //Clear ADDR bit and start condition bit
}
void I2C_Stop(void){
I2C1->CR2 |= (1 << 14); //Generate stop condition
}
int main (void){
Core_Clock_Setup();
GPIO_Setup();
I2C_Setup();
uint8_t dummy = 0;
while(1){
GPIOC->BSRR |= (1 << 14);
delay_ms(100);
GPIOC->BSRR |= ((1 << 14) << 16);
delay_ms(100);
GPIOC->BSRR |= (1 << 14);
delay_ms(100);
GPIOC->BSRR |= ((1 << 14) << 16);
delay_ms(100);
GPIOC->BSRR |= ((1 << 14) << 16);
delay_ms(100);
GPIOC->BSRR |= (1 << 14);
delay_ms(100);
// I2C1->CR2 |= (0b111<< 16); this has to be configured before starting the I2C. It indicates how many bytes must be read or written, so it sends as much ACKs followed by a last NACK when stopping I2C
if(!(GPIOE->IDR&(1 << 6)))
{
I2C1->CR2 |= (0b010<< 16);
I2C_Start();
I2C_Address(0b10100000);//writing
I2C_Send(0b00000000);
I2C_Send(0b00010011);
I2C1->CR2 |= (0b011<< 16);
I2C_Start();
I2C_Address(0b10100001);//reading
dummy=I2C_Receive();
dummy=I2C_Receive();
dummy=I2C_Receive();
I2C_Stop();
delay_ms(1000);
}
}
}
2025-01-07 04:53 AM
@Vilius wrote:I moved on to an I2C code that actually requires some debugging.
Why does it need some debugging - is it having a Hard Fault?
@Vilius wrote:I can only make two consecutive step overs until I get a hard fault.
If the problem you're trying to debug is that Hard Fault, then this is to be expected: a Hard Fault is a Hard Fault - whether the code reaches the problem point "naturally" or under step control of the debugger.
On debugging Hard Faults:
2025-01-07 05:33 AM - edited 2025-01-07 05:34 AM
I am not sure what is the problem with i2c, but at least my code passes all 4 setup functions and starts the infinite loop (operating as usual). When I try to debug, I get a hard fault in the first two lines starting from top. But hard fault itself is not the main topic of the problem. My main problem is that I can not even use step over function as it was intended to. Any ideas why step over still puts me inside every function?
2025-01-07 05:43 AM
First, I dislike the CubeIDE for certain reasons, and do not use it. Thus I cannot comment on regarding details.
But first, turn off all compiler optimisations first. Otherwise, what you see is rarely what you get.
Second, you should name the specific MCU variant you are working with. MCUs support only a limited number of breakpoints at a time, and single-step consumes at least one breakpoint. Although this should not be a problem with F7 devices, if your label is correct.
> Starting in the top of the main function, I can only make two consecutive step overs until I get a hard fault.
I would suggest to start with that.
When ending up in the hardfault, check the respective fault status registers in the SCB.
And, you can switch to instruction stepping (or however stepping through assembler instructions is called in Eclipse environments), and find the exact location causing the fault. Inspecting register values before and after.
> Pressing step into many times does not generate any faults, but once I get into blocking delay function, it becomes useless.
Another reason for me to dislike HAL/Cube and it's ubiquitous busy-wait.
Usually, you can try to set a breakpoint to a location after the busy-wait call, and run the code.
Additionally, be aware that a debugger can change the code execution path, depending on display windows open. Especially interrupt flags cleared on register read are a problem here.
On a related note,you could try Segger's Ozone debugger, which you can download for free, and use for non-commercial purposes. Although this might require a JLink debugger. But Segger has a JLink firmware for onboard ST-Links for free download as well, which worked fine for me in several instances. Again, for non-commercial use, and only for onboard ST-Links, not stand-alone devices.
And you can reflash it with ST's firmware at anytime, if you don't like it.