Author Topic: STM32 - 4.4us gaps in my signal every 1ms  (Read 2109 times)

0 Members and 2 Guests are viewing this topic.

Offline dentakuTopic starter

  • Frequent Contributor
  • **
  • Posts: 881
  • Country: ca
STM32 - 4.4us gaps in my signal every 1ms
« on: October 31, 2020, 01:00:54 am »
I just wrote a very simple Arduino sketch to toggle PA0 on a Blue Pill.

Code: [Select]
void setup() {
  pinMode(PA0, OUTPUT);
}

void loop() {
  GPIOA->ODR |= 0b0000000000000001;   //set 1rst bit in PORTA (GPIOA) to 1 using Output Data Register
  GPIOA->ODR &= ~(0b0000000000000001); //low
}

I just wanted to see how fast the pulses would be.

QUESTION:
There are gaps in my signal where it stays high for 4.4us and this happens exactly every 1ms.
There also seems to be a point where it stays low for around 5us every 1ms.

Is there something in the STM32F103C8T6 that ticks every 1ms that interrupts with my pin toggling?
 

Offline drvtech

  • Regular Contributor
  • *
  • Posts: 111
  • Country: gb
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #1 on: October 31, 2020, 01:07:46 am »
Yes, the timer interrupts which allow you to use the millis() function. Whether you are using it or not the timer is still running and causing interrupts
 
The following users thanked this post: I wanted a rude username

Offline dentakuTopic starter

  • Frequent Contributor
  • **
  • Posts: 881
  • Country: ca
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #2 on: October 31, 2020, 01:26:28 am »
Yes, the timer interrupts which allow you to use the millis() function. Whether you are using it or not the timer is still running and causing interrupts

OK. so all the pins just freeze for 4.4us in whatever state they're in every 1ms.
I suppose if you were using SPI at unusually high speeds the clock would look strange once in a while but I guess it's not a problem.
 

Offline Dabbot

  • Regular Contributor
  • *
  • Posts: 192
  • Country: au
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #3 on: October 31, 2020, 02:52:12 am »
Yes, the timer interrupts which allow you to use the millis() function. Whether you are using it or not the timer is still running and causing interrupts

OK. so all the pins just freeze for 4.4us in whatever state they're in every 1ms.
I suppose if you were using SPI at unusually high speeds the clock would look strange once in a while but I guess it's not a problem.

No. Your code is suspended while the interrupt is handled. And if you're bit-banging pins as you are here, you observe the results.

Usually, and especially for SPI, the pins are going to be driven by an onboard peripheral which doesn't care what your code is up to except when its buffers are empty / full.
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 840
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #4 on: October 31, 2020, 04:14:28 am »
You have BSSR and BRR registers to get you atomic setting/clearing of pins-

https://godbolt.org/z/qorhP9

and will also better your chances at the 'how fast can I toggle a pin' contest. The atomic part is the important part, as the pin toggle contest is won by those that use peripherals.
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8317
  • Country: fi
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #5 on: October 31, 2020, 08:48:12 am »
You are using arduino outside the intended usage.

You are seeing the result of their interrupt handler running. Ditch the Arduino code and you can decide what interrupt handlers you write, if any.

I also suggest using BSRR registers to avoid read-modify-write operation of the IO registers: with a simple write operation, you can set any bits to 0 and 1. I use these macros:

Code: [Select]
#define HI(port, idx) do{(port)->BSRR = 1UL<<(idx);}while(0)
#define LO(port, idx) do{(port)->BSRR = 1UL<<(16+idx);}while(0)

Usage is obvious, isn't it?
« Last Edit: October 31, 2020, 08:50:52 am by Siwastaja »
 

Offline drvtech

  • Regular Contributor
  • *
  • Posts: 111
  • Country: gb
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #6 on: October 31, 2020, 07:18:39 pm »
Why do you need to wrap the macro definition in a do/while?
 

Online krish2487

  • Frequent Contributor
  • **
  • Posts: 508
  • Country: dk
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #7 on: October 31, 2020, 09:59:56 pm »
For this reason
https://stackoverflow.com/questions/1067226/c-multi-line-macro-do-while0-vs-scope-block

Tl;dr - the do while(0) block allows for correct expansion of multiline statements in a macro.
And do it enough times and it becomes a muscle memory to implement every macro the correct way - even for a single line macro.

Why do you need to wrap the macro definition in a do/while?
If god made us in his image,
and we are this stupid
then....
 
The following users thanked this post: Siwastaja

Offline dentakuTopic starter

  • Frequent Contributor
  • **
  • Posts: 881
  • Country: ca
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #8 on: October 31, 2020, 10:25:32 pm »
Yes, the timer interrupts which allow you to use the millis() function. Whether you are using it or not the timer is still running and causing interrupts

OK. so all the pins just freeze for 4.4us in whatever state they're in every 1ms.
I suppose if you were using SPI at unusually high speeds the clock would look strange once in a while but I guess it's not a problem.

No. Your code is suspended while the interrupt is handled. And if you're bit-banging pins as you are here, you observe the results.

Usually, and especially for SPI, the pins are going to be driven by an onboard peripheral which doesn't care what your code is up to except when its buffers are empty / full.

I'm glad you mentioned that because after posting my reply I wondered about things like SPI that already have hardware built into the microcontroller and how they shouldn't be interfered by the timer interrupt.

I just did an experiment using SPI.beginTransaction(SPISettings(36000000, MSBFIRST, SPI_MODE0)); and my scope measures the clock at 36MHz. I'm assuming it won't go any faster because that's half the 72MHz clock speed of the STM32F103.

I have absolutely no need to toggle pins this fast, I'm just curious.
« Last Edit: October 31, 2020, 11:26:24 pm by dentaku »
 

Offline drvtech

  • Regular Contributor
  • *
  • Posts: 111
  • Country: gb
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #9 on: October 31, 2020, 11:49:48 pm »
krish2487 - Thanks for the do/while explanation. I'll stop hijacking the thread now :-)
Drvtech
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 840
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #10 on: November 01, 2020, 12:55:09 am »
One can also create static functions for these things, where you are then in the compiler's world instead of having a word processor transforming your code into something you cannot see directly. Since cannot see the code directly, you have to employ things like do/while0 for multi statement macros, which is easy enough to forget unless you do it all the time, even when not needed- and makes it look like you are in a code obfuscation contest. Can't easily get away from the pre-processor in C, but moving as much as possible to the compiler is a better option in my opinion.

https://godbolt.org/z/PePWW9

While you are playing around, you could find out what happens with non-atomic pin manipulation- find that 1ms isr, and in that 1ms isr add some code to toggle a different pin on the same port, your main code pin toggle still using the ODR register as before. What you should see from the isr pin is 1ms toggle, but with the non-atomic work being done on the same port in the main loop, that 1ms toggle should see some 'extra' toggles because of the main code 'undoing' what the isr just did. Maybe a few minutes spent seeing this in action will set in your mind why some of these registers for atomic manipulation exist.

 

Offline dentakuTopic starter

  • Frequent Contributor
  • **
  • Posts: 881
  • Country: ca
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #11 on: November 01, 2020, 02:44:45 am »
You have BSSR and BRR registers to get you atomic setting/clearing of pins-

https://godbolt.org/z/qorhP9

and will also better your chances at the 'how fast can I toggle a pin' contest. The atomic part is the important part, as the pin toggle contest is won by those that use peripherals.

Considering this is just a useless exercise I tried because I wanted to use something other than an AVR I've been reading about a lot about stuff I've never needed to use before. :)

May I ask what the values in this line of code are for?
static volatile uint32_t* GpioA_ODR  = (volatile uint32_t*)(0x40010800+0x0C);
To me that's (0b1000000000000010000100000000000+0b1100) which = 0100 0000 0000 0001 0000 1000 0000 1100

having read this.
"IDR registers are responsible for digital inputs while ODR registers are responsible for digital outputs. In both registers the upper 16 bits are always kept 0 because GPIOs are 16 bits wide while their representative registers are 32 bit wide."
Why does your code have two ones in the upper 16bits which are supposed to be reserved?

By the way, your code...

  GPIOA->BSRR = 1; //set
  GPIOA->BSRR = 1 << 16; //reset

IS slightly faster than my original code.
 

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 840
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #12 on: November 01, 2020, 03:55:29 am »
>May I ask what the values in this line of code are for?

static volatile uint32_t* GpioA_ODR  = (volatile uint32_t*)(0x40010800+0x0C);

GpioA_ODR is a pointer to a volatile uint32_t and its address is 0x40010800C which was derived from the datasheet (the addition is done in parens so the byte offset address listed in the datasheet can be used, as without parens the addition is done to the first value treated as pointer address, and since the pointer is pointing to a 4byte value/uint32_t, the addition will be 0x0C*4 which is not what is wanted).

The address 'stored' in the var GpioA_ODR has nothing to do with what it is pointing to, so the 0x4001 has nothing to do with the upper bits of ODR. This would also be correct-

static volatile uint16_t* GpioA_ODR  = (volatile uint16_t*)(0x40010800+0x0C);

where GpioA_ODR still stores the same address, but now its pointing to a uint16_t instead. Probably a better idea as you will then get a warning of you try to write anything other than a 16bit value. Was a simple example, so did not worry about it.



You may want to figure out what the code you wrote does. Decode it using the headers, and you will not be too far from what I wrote. The online compiler does not have headers for an mcu (with some exceptions), so I created my own little 'header' to create an example. All headers, drivers, and code ultimately ends up as standard C code for the compiler to digest, so if you take the time to figure out what a header/driver may be doing you will also discover what the C language can do, as it all ends up there. Once you know how the sausage is made, you can then make your own in whatever manner you prefer.
 
The following users thanked this post: lucazader

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4240
  • Country: us
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #13 on: November 01, 2020, 08:32:07 am »
Quote
May I ask what the values in this line of code are for?
static volatile uint32_t* GpioA_ODR  = (volatile uint32_t*)(0x40010800+0x0C);
Normally you'd use predefined names for the ODR included from some massive .h file provided by ST.The godbolt code analysis tool doesn't have access to those files, so you have to include a "cut down" replacement in your source code.
 
The following users thanked this post: dentaku, newbrain, lucazader

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8317
  • Country: fi
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #14 on: November 01, 2020, 10:10:41 am »
static volatile uint32_t* GpioA_ODR  = (volatile uint32_t*)(0x40010800+0x0C);
static volatile uint16_t* GpioA_ODR  = (volatile uint16_t*)(0x40010800+0x0C);

Do note these two do not perform the exact same operation. Dereferencing the first one does a 32-bit bus operation, dereferencing the second one does a 16-bit bus operation. (Bus meaning the APB/AHB bus between the core and the GPIO peripheral). In this particular case (the ODR register), I don't believe there is any difference in the result, performance or functionality. But some peripheral registers, notably SPI data registers in those STM32 chips that implement FIFOs in SPI peripherals, the width of the read/write bus operations define how many elements are pushed to / popped from the FIFOs. For example, if SPI1 on STM32F7/H7 is configured for 8-bit SPI word,

*((volatile uint16_t*)&SPI1->TXDR) = 0;

sends TWO consecutive words on the SPI bus, while

*((volatile uint8_t*)&SPI1->TXDR) = 0;

sends only one. Of course, the stupid ST header writers haven't defined such type aliases for accessing the the regs properly at all, so you need to create your own and cast the one given in header to another type. As a result, every young player accidentally sends 4 bytes each time they want to just write a byte on SPI.
« Last Edit: November 01, 2020, 10:12:48 am by Siwastaja »
 
The following users thanked this post: newbrain

Offline cv007

  • Frequent Contributor
  • **
  • Posts: 840
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #15 on: November 01, 2020, 07:06:18 pm »
> This would also be correct-
>static volatile uint16_t* GpioA_ODR  = (volatile uint16_t*)(0x40010800+0x0C);

Actually, I was wrong. If I would have read the datasheet closer, I would have seen that the GPIO registers have to be accessed as 32bit words (at least that is what is says). So, that is another piece of info you have to seek out when putting together your own header or any other of your own register access code. The access requirements will vary, and there is no way to know what they are for any mcu/peripheral/register until you find what the datasheet has to say about it.

More header creation, with some 'driver' code-
C
https://godbolt.org/z/v5djhY
C++
https://godbolt.org/z/Yn6Ees
(could be mistakes in both, as I just wrote them without looking too close at the asm output)

lots of ways to use these languages.
« Last Edit: November 01, 2020, 10:18:12 pm by cv007 »
 
The following users thanked this post: lucazader

Offline dentakuTopic starter

  • Frequent Contributor
  • **
  • Posts: 881
  • Country: ca
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #16 on: November 01, 2020, 10:31:40 pm »
That's all interesting stuff I might learn more about some day. I've never had to do any type of coding like this before but I DO like knowing who stuff works so diving deeper into what my code is doing behind the scenes is sometimes worth doing.

When I saw the * in the godbolt code I wondered if it had something to do with pointers, which I've never understood considering I've never gone much beyond typical Arduino programming.

This all came about because I wanted to find how to do what I normally do with PORTx = on AVRs on a BluePill and this thread went out on a tangent :)
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4240
  • Country: us
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #17 on: November 02, 2020, 03:11:48 am »
Quote
this thread went out on a tangent

See also "bit banding."  If you don't have enough tangents yet.

On chips that support bit-banding (some Cortex-M3s, mostly.  Including the STM32f103) you can define an address that lets you access a single bit of the peripheral IO space.  Something like:
Code: [Select]
static volatile uint32_t* GpioA12  = (volatile uint16_t*)(0x42000000 +(10800+0x0C)*32 + 12*4);
GpioA12 = 0;  //set A12 to 0
GpioA12 = 1;  // set A12 to 1
(I don't really recommend looking at this too much; bit banding seems to have "not caught on.")
 

Offline S. Petrukhin

  • Super Contributor
  • ***
  • Posts: 1270
  • Country: ru
Re: STM32 - 4.4us gaps in my signal every 1ms
« Reply #18 on: November 02, 2020, 04:40:48 am »
Don't expect Arduino to be completely transparent and in control of the situation. This is a child's toy, very stupid in relation to the MCU. :)

In addition to using the registers to control bits, the Cortex has a bit alias area. For each bit (each!) of RAM (including peripheral registers), 32 bits are allocated in a separate memory area, in which the lowest bit controls. That is, to control any bit, you can define a boolean variable in the alias area.
And sorry for my English.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf