2024-03-10 11:50 AM
Hi,
I am a beginner in STM32 programming. I chose purely register based programming approach without using HAL as I find it a bit harder and slower but more contributing to the overall system performance in the long term code writing skills. This time I am asking help with the external interrupt code.
Idea is to hook the external interrupt on an pulled up button and generate the interrupt each time the button is pressed (when the MCU receives falling edge.) I wrote this code that compiles and runs, but nothing happens when the button is pressed (it should turn 2 leds off). I have already verified and tested the timer, gpio and core clock setup functions, everything is working. So the problem lies somewhere in the interrupt setup syntax or the ISR itself. Since it is my first time working with interrupts on STM32, I hope only minor syntax update will get my code doing its duty.
#include "main.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
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->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 Timer1_Setup(void){ //16 bit advanced timer
RCC->DCKCFGR1 &= ~ (1 << 24); //TIMxCLK = 2xPCLKx
//When TIMPRE bit of the RCC_DCKCFGR1 register is reset, if APBx prescaler is 1, then TIMxCLK = PCLKx, otherwise
//TIMxCLK = 2x PCLKx.
// When TIMPRE bit in the RCC_DCKCFGR1 register is set, if APBx prescaler is 1,2 or 4, then TIMxCLK = HCLK, otherwise
//TIMxCLK = 4x PCLKx.
//TIM1 CLK is HCLK in this case
RCC->APB2ENR |= (1 << 0); //Enable Timer 1 clock
TIM1->PSC = 99; //APB1 is 50 Mhz and 100 MHZ for timer (The number is set: Clock in MHz - 1) 1 full period equals 1 MHZ
TIM1->ARR = 0xFFFF; //Auto reload at 100 ticks -> around 100 micro seconds at 100 MHz timer clock
TIM1->CR1 |= (1 << 0); //Enable Timer 6 counter
while(!(TIM1->SR & (1<<0))); //Wait until timer update bit is set
}
void delay_ms (uint16_t ms){
for(uint16_t i = 0; i<ms; i++)
{
TIM1->CNT = 0; //Reset counter
while (TIM1->CNT < 2000); //Wait until counter reaches desired value
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void GPIO_Setup(void){
//PC14 OUTPUT //LEDS
RCC->AHB1ENR |= (1 << 2);
GPIOC->MODER |= (0b01 << 28); //PC15 General purpose output mode
GPIOC->OTYPER &= ~ (1 << 14); //PC15 Output push-pull (reset state)
GPIOC->OSPEEDR |= (0b11 << 28); //PC15 very high GPIO speed
GPIOC->PUPDR |= (0b10 << 28); //PC15 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
RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank A
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
/*
//PH0 INPUT
RCC->AHB1ENR |= (1 << 7);
GPIOH->MODER |= (0b00 << 0); //PH0 General purpose input mode
//PH1 INPUT
GPIOH->MODER |= (0b00 << 2); //PH1 General purpose input mode
*/
}
void Interrupt_setup(void){
//PE5
RCC->APB2ENR |= (1 << 14); //System configuration controller clock enabled
SYSCFG->EXTICR[2] |= (0b0100 << 5); //0100: PE[x] pin
EXTI->IMR |= (1<< 5); //Interrupt request from line x is not masked
EXTI->FTSR |= (1<< 5); //Falling trigger enabled (for Event and Interrupt) for input line
//EXTI->RTSR &= ~ (1<< 5); //Rising trigger disabled (for Event and Interrupt) for input line
NVIC_SetPriority(EXTI9_5_IRQn, 0);
NVIC_EnableIRQ (EXTI9_5_IRQn);
}
void EXTI9_5_IRQHandler(void){
//if (EXTI->PR & (1 << 5)){
//EXTI->PR |= (1 << 5);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOE->BSRR |= ((1 << 4) << 16);
//}
}
int main (void){
Core_Clock_Setup();
Timer1_Setup();
GPIO_Setup();
Interrupt_setup();
while(1){
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= (1 << 15);
GPIOE->BSRR |= (1 << 4);
GPIOE->BSRR |= (1 << 0);
delay_ms(500);
//if(GPIOE->IDR & (1 << 5)){
//GPIOC->BSRR |= ((1 << 14) << 16);
//}
GPIOC->BSRR |= ((1 << 15) << 16);
/*
if(GPIOE->IDR & (1 << 6)){
GPIOE->BSRR |= ((1 << 4) << 16);
}
*/
GPIOE->BSRR |= ((1 << 0) << 16);
delay_ms(500);
}
}
Side note: the datasheet tells to pick your interrupt pin (PE5 in my case) in the SYSCFG_EXTICR2 register. But the compiler says, there is no such register. I tried SYSCFG->EXTICR[1] and SYSCFG->EXTICR[2] which do compile, but did not change anything. Hope thats all the relevant details. And yes, I have already checked my wiring :) the hardware is all good.
2024-03-10 02:02 PM - edited 2024-03-10 02:02 PM
@Vilius Can you connect debugger and debug your code? As you prefer register-based programming, checking the EXTI and other register bits is on you.
In lines 82-85 (and below): when you want to assign a value to multi-bit field, you clear ALL bits of the field first before OR'ing with the desired value. Otherwise some unwanted bits may remain set in the register.
2024-03-10 02:15 PM - edited 2024-03-14 03:42 AM
When in doubts, always start with reading out the registers you've written and checking them against the expected values.
SYSCFG_EXTICR is numbered confusingly and inconsistently between RM and the device header, unfortunately. So, you want to write SYSCFG_EXTICR2.EXTI5 field, which starts at bit 4 of that register, so
SYSCFG->EXTICR[1] |= (0b0100 << 4); //0100: PE[x] pin
Maybe this is not the only problem; a general checklist for interrupts not firing here.
Also, please observe the recommendations I gave you in your previous thread; here, in particular, the need for delay between enabling a peripheral in RCC and writing to that peripheral.
Also, just like TIMx_EGR, GPIO_BSRR is also a write-only register, so don't RMW (|=) values into it; assign the values directly.
JW
2024-03-13 06:59 AM - edited 2024-03-13 07:01 AM
Your initial guess helped me to resolve my interrupt problem. Regarding the RCC peripheral observation. Did I get it right, you mean that I have to place a delay between enabling peripheral clock and actually setting the register GPIO ports, something like that ?:
RCC->AHB1ENR |= (1 << 2); //Enable clock for GPIO bank C
delay_ms(1);
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
Also, I tried direct writing full 32 bit values to registers instead of altering single bits just like you advised. I believe that it is the right way, but in either case, I was extremely surprised when I tested the GPIO toggling speed using both 32 bit writing and separate bit toggling approaches. I was expecting to see at least double digit MHz number, but it was respectively around 8 and 4 MHz. I understand the discrepancy logic analyzer can introduce - all the pin header connections, long wires, discontinuous ground paths etc are indeed introducing huge square wave signal distortions (not to mention the harmonics) that forces the logic analyzer into some false jitter-like looking signal. Having said all this, it is still orders of magnitude off what I have expected. Considering 50 and 100 MHz APB1-2 clocks (and core clock of 200 MHz) and using direct register manipulation commands to toggle GPIOs, 4-8 MHz is all that I get? Would rewriting this code in assembly give me that extra mile? What if I am looking for confident 20-30 MHz GPIO toggling speed, is the FPGA the only alternative then? Maybe I am just missing some setting out? Thank you for any reply in advance. I value your time and attention. (Check pictures down bellow)
#include "main.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
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->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 Timer1_Setup(void){ //16 bit advanced timer
RCC->DCKCFGR1 &= ~ (1 << 24); //TIMxCLK = 2xPCLKx
//When TIMPRE bit of the RCC_DCKCFGR1 register is reset, if APBx prescaler is 1, then TIMxCLK = PCLKx, otherwise
//TIMxCLK = 2x PCLKx.
// When TIMPRE bit in the RCC_DCKCFGR1 register is set, if APBx prescaler is 1,2 or 4, then TIMxCLK = HCLK, otherwise
//TIMxCLK = 4x PCLKx.
//TIM1 CLK is HCLK in this case
RCC->APB2ENR |= (1 << 0); //Enable Timer 1 clock
TIM1->PSC = 99; //APB1 is 50 Mhz and 100 MHZ for timer (The number is set: Clock in MHz - 1) 1 full period equals 1 MHZ
TIM1->ARR = 0xFFFF; //Auto reload at 100 ticks -> around 100 micro seconds at 100 MHz timer clock
TIM1->CR1 |= (1 << 0); //Enable Timer 6 counter
while(!(TIM1->SR & (1<<0))); //Wait until timer update bit is set
}
void delay_ms (uint16_t ms){
for(uint16_t i = 0; i<ms; i++)
{
TIM1->CNT = 0; //Reset counter
while (TIM1->CNT < 2000); //Wait until counter reaches desired value
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void GPIO_Setup(void){
//PC14 OUTPUT //LEDS
RCC->AHB1ENR |= (1 << 2); //Enable clock for GPIO bank C
delay_ms(1);
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
RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank E
delay_ms(1);
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
delay_ms(1);
//-----------------------------------------------------------------------------------
//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
/*
//PH0 INPUT
RCC->AHB1ENR |= (1 << 7);
GPIOH->MODER |= (0b00 << 0); //PH0 General purpose input mode
//PH1 INPUT
GPIOH->MODER |= (0b00 << 2); //PH1 General purpose input mode
*/
delay_ms(1);
}
void Interrupt_setup(void){
//PE5
RCC->APB2ENR |= (1 << 14); //System configuration controller clock enabled
SYSCFG->EXTICR[1] |= (0b0100 << 4); //0100: PE[x] pin
EXTI->IMR |= (1<< 5); //Interrupt request from line x is not masked
EXTI->FTSR |= (1<< 5); //Falling trigger enabled (for Event and Interrupt) for input line
EXTI->RTSR &= ~ (1<< 5); //Rising trigger disabled (for Event and Interrupt) for input line
NVIC_SetPriority(EXTI9_5_IRQn, 0);
NVIC_EnableIRQ (EXTI9_5_IRQn);
}
void EXTI9_5_IRQHandler(void){
EXTI->PR |= (1 << 5);
GPIOC->BSRR = (1 << 14);
delay_ms(500);
GPIOC->BSRR = ((1 << 14) << 16);
}
int main (void){
Core_Clock_Setup();
Timer1_Setup();
GPIO_Setup();
Interrupt_setup();
GPIOC->BSRR = 0x4000;
while(1){
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= ((1 << 14) << 16);
/*
GPIOC->BSRR = 0x4000;
GPIOC->BSRR = 0x40000000;
GPIOC->BSRR = 0x4000;
GPIOC->BSRR = 0x40000000;
GPIOC->BSRR = 0x4000;
GPIOC->BSRR = 0x40000000;
GPIOC->BSRR = 0x4000;
GPIOC->BSRR = 0x40000000;
GPIOC->BSRR = 0x4000;
GPIOC->BSRR = 0x40000000;
GPIOC->BSRR = 0x4000;
GPIOC->BSRR = 0x40000000;
GPIOC->BSRR = 0x4000;
GPIOC->BSRR = 0x40000000;
GPIOC->BSRR = 0x4000;
GPIOC->BSRR = 0x40000000;
GPIOC->BSRR = 0x4000;
*/
/*
if(GPIOE->IDR & (1 << 6)){
GPIOC->BSRR = 0x4000;
}
*/
}
}
2024-03-13 12:53 PM
> I understand the discrepancy logic analyzer can introduce - all the pin header connections, long wires, discontinuous ground paths etc are indeed introducing huge square wave signal distortions
Maybe that's not even the problem.
What's the LA's sampling frequency?
Are you sure the mcu runs at the frequency you think it does? (One way to be sure is outputting system clock to MCO pin and measuring it there).
> Would rewriting this code in assembly give me that extra mile?
Using asm does not necessarily give you speed. It gives you something else: control. It may mean speed, but it may be not necessary.
Realize, that the source code as you write it is far detached from the program (binary) actually run by the processor - first and foremost, there's the compiler in between (with all its components).
Same as when debugging the peripherals, where the first thing to do is to read out the content of registers (because the peripherals work out of the registers not out of the source code), here you should look at the result of compilation (i.e. disassembled binary).
Do you have compiler optimizations switched on?
JW
PS. As I've said, don't use RMW on GPIO_BSRR.
2024-03-13 01:36 PM
Per Jan, stop all the repeated RMW's
The BSRR is a write and forget, the reading and writing causes a lot of delay and bubbles the pipeline
So
GPIOC->BSRR |= (1 << 14);
GPIOC->BSRR |= (1 << 15);
GPIOE->BSRR |= (1 << 4);
GPIOE->BSRR |= (1 << 0);
Becomes
GPIOC->BSRR = (1 << 14) | (1 << 15); // Set PC14/PC15
GPIOE->BSRR = (1 << 4) | (1 << 0); // Set PE0/PE4
You can separate/decompose if the phase shift is an important consideration
As the writes are pipelined through write-buffers you don't want to enable the peripheral clock in one cycle, and modify it's registers in the next.
If you code in assembler you can likely avoid a lot of unnecessary LDR/STR operations on volatile memory, or you could fold the logic yourself, as the C compiler is prohibited from folding via optimization rules.
2024-03-14 03:48 AM
> Did I get it right, you mean that I have to place a delay between enabling peripheral clock and actually setting the register GPIO ports, something like that ?
> [code using millisecond delay]
No. As @Tesla DeLorean wrote above:
> As the writes are pipelined through write-buffers you don't want to enable the peripheral clock in one cycle, and modify it's registers in the next.
So, it's a matter of few cycles, not milliseconds; basically, it's about the latency of a signal from the fast RCC reaching peripheral on a relatively slow bus. We've discussed this extensively - maybe more than it's worth - here (I've given the wrong link in my post above, sorry, fixed now). I've outlined my solution there, too, basically, I am enabling all clocks of all peripherals I am going to use, at once as the first thing in main(); and as it is multiple writes to various RCC registers, when ordered correctly, that gives enough time for every module to start up before I start to set them up.
Of course, waiting milliseconds would do the trick, too, but at the cost of... wasting milliseconds.
JW
2024-03-14 11:11 AM
Ok, I will try to answer some pilled up concerns.
I am sure my MCU is running at 200 Mhz, because my timer period calculations match identically to the desired time periods according to the datasheet formulas.
I am not sure about exact sample rate of m LA, but it is designed for 24 MHz SPI decoding, so I am in the safe zone...
I dont mind wasting 1 ms in the setup part. Setup time isnt really important for me. I am after the high GPIO execution speed in the main loop, at least I understand your point about GPIO clocks now.
I have switched my compiler to fastest execution speed optimization.
This is my latest code down bellow. I hope I finally understood your point now about eliminating RMW from BSRR register. First I write a full 32 bit variable to the register during setup, and after that I am only toggling the GPIO via one bit direct write operation (I hope it works like that.) Unfortunately, result shown in the analyzer is exactly the same: I get very inconsistent toggling ranging 4-12 MHz (you will understand that from the logic diagram in the attachments.) Are there any workarounds left to improve the toggling speed to around consistent 20 MHz without using asm? Or is it my code mistakes again? I had a little experience with programing PIC32 via registers in the past, but STM32 is way different as I see, havent fully adapted to it yet...
#include "main.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
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->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 Timer1_Setup(void){ //16 bit advanced timer
RCC->DCKCFGR1 &= ~ (1 << 24); //TIMxCLK = 2xPCLKx
//When TIMPRE bit of the RCC_DCKCFGR1 register is reset, if APBx prescaler is 1, then TIMxCLK = PCLKx, otherwise
//TIMxCLK = 2x PCLKx.
// When TIMPRE bit in the RCC_DCKCFGR1 register is set, if APBx prescaler is 1,2 or 4, then TIMxCLK = HCLK, otherwise
//TIMxCLK = 4x PCLKx.
//TIM1 CLK is HCLK in this case
RCC->APB2ENR |= (1 << 0); //Enable Timer 1 clock
TIM1->PSC = 99; //APB1 is 50 Mhz and 100 MHZ for timer (The number is set: Clock in MHz - 1) 1 full period equals 1 MHZ
TIM1->ARR = 0xFFFF; //Auto reload at 100 ticks -> around 100 micro seconds at 100 MHz timer clock
TIM1->CR1 |= (1 << 0); //Enable Timer 6 counter
while(!(TIM1->SR & (1<<0))); //Wait until timer update bit is set
}
void delay_ms (uint16_t ms){
for(uint16_t i = 0; i<ms; i++)
{
TIM1->CNT = 0; //Reset counter
while (TIM1->CNT < 2000); //Wait until counter reaches desired value
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
void GPIO_Setup(void){
//PC14 OUTPUT //LEDS
RCC->AHB1ENR |= (1 << 2); //Enable clock for GPIO bank C
RCC->AHB1ENR |= (1 << 4); //Enable clock for GPIO bank E
delay_ms(1);
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
/*
//PH0 INPUT
RCC->AHB1ENR |= (1 << 7);
GPIOH->MODER |= (0b00 << 0); //PH0 General purpose input mode
//PH1 INPUT
GPIOH->MODER |= (0b00 << 2); //PH1 General purpose input mode
*/
}
void Interrupt_setup(void){
//PE5
RCC->APB2ENR |= (1 << 14); //System configuration controller clock enabled
SYSCFG->EXTICR[1] |= (0b0100 << 4); //0100: PE[x] pin
EXTI->IMR |= (1<< 5); //Interrupt request from line x is not masked
EXTI->FTSR |= (1<< 5); //Falling trigger enabled (for Event and Interrupt) for input line
EXTI->RTSR &= ~ (1<< 5); //Rising trigger disabled (for Event and Interrupt) for input line
NVIC_SetPriority(EXTI9_5_IRQn, 0);
NVIC_EnableIRQ (EXTI9_5_IRQn);
}
void EXTI9_5_IRQHandler(void){
EXTI->PR |= (1 << 5);
GPIOC->BSRR = (1 << 14);
delay_ms(500);
GPIOC->BSRR = ((1 << 14) << 16);
}
int main (void){
Core_Clock_Setup();
Timer1_Setup();
GPIO_Setup();
Interrupt_setup();
GPIOC->BSRR = 0x4000;
while(1){
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
GPIOC->BSRR = (1 << 14);
GPIOC->BSRR = (1 << 30);
/*
if(GPIOE->IDR & (1 << 6)){
GPIOC->BSRR = 0x4000;
}
*/
}
}
2024-03-14 11:33 AM
Well, I decided to finally probe the GPIO with a real oscilloscope, and that brought very interesting results... (check pictures in the attachments). My scope is 200 Mhz bandwidth just in case. I presume that longer pulse may be generated in the transition of infinite loop iterations. And that funny sine stuff is my real gpio toggling at 100 Mhz. Due to massive reflections, very long pin header wires, lack of ground planes etc it looks so horrible. But does this presumption make any sense to you? Can I expect to see something more similar to a 100 MHz square (not a perfect one, but still a 0-3.3V square) wave on a proper PCB?