cancel
Showing results for 
Search instead for 
Did you mean: 

How to precisely control GPIO pulse width using instruction cycles?

Marlon
Associate

The STM32F429 processor operates at 180MHz, with a clock cycle of 5.56ns. Can we generate pulses with a width granularity of 5.56ns using only ARM instruction delays?

For example: 5.56ns, 11.12ns, 16.68ns, 22.24ns?

In practice, I have encountered difficulties achieving the above operations, and I suspect that the pipeline affects the intuitive control of pulse width.

The following code controls the pulse width of PF0 output, the width = 'w':

```c
GPIOF->BSRR = 0x00000001; // PF0 output High
GPIOF->BSRR = 0x00010000; // PF0 output Low
```

Without considering the 3-stage instruction pipeline, the pulse width obtained would be 'w + 5.56ns':

```c
GPIOF->BSRR = 0x00000001; // PF0 output High
asm("nop"); // w + 5.56ns
GPIOF->BSRR = 0x00010000; // PF0 output Low
```

Similarly, by adding more `nop` instructions, we can achieve different pulse widths:

```c
GPIOF->BSRR = 0x00000001; // PF0 output High
// asm("nop"); // w + 5.56ns
asm("nop"); asm("nop"); // w + 11.12ns
// asm("nop"); asm("nop"); asm("nop"); // w + 16.68ns
GPIOF->BSRR = 0x00010000; // PF0 output Low
```

However, in practice, I have had to rely on an oscilloscope to adjust the pulse width,
the code lacks readability.

I am unsure whether adding 5.56ns requires 1 `nop` instruction or 2-3 `nop` instructions.

```c
__disable_irq();
GPIOF->BSRR = 0x00000001; // PF0 output High
asm("nop"); // w + 5.56ns
// asm("nop"); asm("nop"); asm("nop"); asm("nop"); // w + 11.12ns
// asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); // w + 16.68ns
GPIOF->BSRR = 0x00010000; // PF0 output Low
__enable_irq();
```

When I copy the above code to another location, I have to modify it to achieve the same effect:

```c
__disable_irq();
GPIOF->BSRR = 0x00000001; // PF0 output High
asm("nop"); asm("nop"); // w + 5.56ns
// asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); // w + 11.12ns
// asm("nop"); asm("nop"); asm("nop"); asm("Question:
```
============================================================
1. What is the reason for the phenomenon described above?
2. Are there any remedies?
============================================================


The observed phenomenon is likely caused by the 3-stage instruction pipeline.

Instructions like `ISB` , `DMB` or `DSB` is can be used to achieve pipeline flush or clear ?

```c
__disable_irq();

__TODO_clear_pipeline__(); // how to do that

GPIOF->BSRR = 0x00000001; // PF0 output High
asm("nop"); asm("nop"); // w + 5.56ns
// asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); // w + 11.12ns
// asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); // w + 16.68ns
GPIOF->BSRR = 0x00010000; // PF0 output Low

__enable_irq();
```
2 REPLIES 2

Don't...

This type of pin driving seems like abuse of a MCU, is there some way you can do this with a cheap/small CPLD or something? FPGA or FIFO Memory?

If you want to drive patterns at GPIO in a remotely controlled manner, use TIM+DMA to move the pattern buffer to the GPIO->BSRR, in a sequenced / timed fashion.

 

Code alignment on prefetch boundaries, branch prediction, flash/cache line boundaries, ART caching hits vs misses ? Figure a FLASH line read is on the order of 35ns. Stuff that prefetchs can be done within the same cycle, and is faster than reading from zero wait state RAM.

 

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Andrew Neil
Evangelist III

@Marlon wrote:

```c
GPIOF->BSRR = 0x00000001; // PF0 output High
GPIOF->BSRR = 0x00010000; // PF0 output Low
```

Kudos for trying, but the 3 back-tics don't work on this forum - you need to use this button:

AndrewNeil_0-1713458954469.png

 

and then you get:

 

GPIOF->BSRR = 0x00000001; // PF0 output High
GPIOF->BSRR = 0x00010000; // PF0 output Low

 


@Marlon wrote:

I am unsure whether adding 5.56ns requires 1 `nop` instruction or 2-3 `nop` instructions.

It might be none at all - the NOP isn't guaranteed to consume any clock cycles.