Hi,
I have discovered recently this forum and because I came here to solve a stupid bug and lost the time of nice people, I decided to show something that you could put in a 'tips & tricks' folder.
Question : how to manage on a Giga R1 the digital output ports in cases when time is critical ?
I have seen some complicated libraries, but let's try to keep simple things simple. Here we do NOT do multi threading.
Rem : the code below has been improved thanks to comments made by various users. It's the last version, those before having no interest for someone searching a solution. **The test results are in post #41.
Idea : create a c++ class, and use it !
class DigitalOutput
{
public:
GPIO_TypeDef *gpio;
uint32_t setMask;
uint32_t unsetMask;
bool bInverted; // true -> High=0, Low=1
// false -> High=1, Low=0
public:
DigitalOutput()
{
gpio = NULL;
setMask =
unsetMask = 0;
bInverted = false;
}
// Initialisation with defined PIN name (as D2, LED_BUILTIN,...)
void init(int _arduinoPin, bool _bInverted = false)
{
uint32_t bit, portNum,pinName,portAddr;
// low 4 bits of "pinName" is the bit number
// upper bits of pinName are the port number (GPIOA = 0, GPIOB = 1, ...)
// These are evenly spaced in the memory map, so we can just offset from PORTA_BASE
pinName = digitalPinToPinName(_arduinoPin);
bit = pinName & 0x0F;
portNum = (pinName >> 4) & 0x0F;
portAddr = GPIOA_BASE + portNum * (GPIOB_BASE - GPIOA_BASE);
init((GPIO_TypeDef *)portAddr, bit, _bInverted);
}
// Initialisation with GPIO / bit rank
void init(GPIO_TypeDef *_gpio, uint32_t _bit, bool _bInverted = false)
{
gpio = _gpio;
setMask = 1<<_bit; // BSRR 0..15
unsetMask = setMask<<16; // BSRR 16..31
bInverted = _bInverted;
// Set bit as digital output
// Rem : in MODER 2 bits configuration per bit
// 00 : input
// 01 : output
// 11 : analog
// 10 : alternate function mode (not used as i/o port)
gpio->MODER = (gpio->MODER & ~(0b11<<(_bit<<1))) | (0b01<<(_bit<<1));
}
// Handles logical/electrical levels association
inline void setInverted(bool _bInverted) { bInverted = _bInverted; }
inline bool isInverted() __attribute__((always_inline))
{ return bInverted; }
// Changes to logical Low
inline void setLow() __attribute__((always_inline))
{ gpio->BSRR = (bInverted)? setMask : unsetMask; }
// Changes to logical High
inline void setHigh() __attribute__((always_inline))
{ gpio->BSRR = (bInverted)? unsetMask : setMask; }
// Changes to logical level (false = logical Low, true = logical High)
inline void setLevel(bool _bLevel) __attribute__((always_inline))
{ gpio->BSRR = (_bLevel ^ bInverted)? setMask : unsetMask; }
// Flips the output pin
inline void flip() __attribute__((always_inline))
{ gpio->BSRR = (gpio->ODR & setMask)? unsetMask : setMask; }
// Gets the real value of output pin
inline int getRealValue() __attribute__((always_inline))
{ return (gpio->ODR & setMask) ? HIGH : LOW; }
};
If you want to invert the behavior, you call setInverted(bool) to ask for it. If true, high and low states are 0/1 and not 1/0.
Example of use : the nice RGB LED.
If you check the documentation, the RGB leds are associated to PI12, PE3, PJ13.
It means GPIO I bit 12, GPIO E bit 3 and GPIO J bit 13.
For them, the logic is inverted : 0 is HIGH in the sense that light is produced, 1 : LOW.
Now, the code :
// Variables declaration
DigitalOutput led[3];
//
// Fonction qui fait clignoter une led
//
void Blink(DigitalOutput &_out, int _ms, int _nb=10)
{
for (int i=0; i < _nb; i++)
{
_out.setHigh();
delay(_ms);
_out.setLow();
delay(_ms);
}
}
void setup()
{
// Each LED is declared and initialized as digital output (inverted)
led[0].init(GPIOI ,12, true); // R
led[1].init(GPIOJ ,13, true); // G
led[2].init(GPIOE , 3, true); // B
// Each LED blinks 5 times 200ms
for(int i = 0; i < 3;i++)
Blink(led[i], 200,5);
}
Why is it interesting to do that way ?
Because during the execution everything is resolved in the Blink() function, nothing is called with stack handling and other complicated stuff.
Other example : stepper motor control.
At high speed, 3 motors with 1600 pulses (200 full steps at 1/8) for one complete rotation each, at 10 rotations/s you have 1600x3x10x2 (high/low) changes to do each second, 96000 outputs !
The M7 is the core dedicated to wifi and USB with the computer : high level control.
The M4 does the low level job, ie generates the pulses and the direction changes.
At 240MHz, if you round 96000 to 100K, you have about 2400Hz/pulse, time for 5000 instructions.
If pulsing a motor takes a little bit time, the fact that at a moment you have to move several together can change the delay (d) between pulses :
[(motor 3)] - d - [(motor 3)] - d - [(motor 1)(motor 2)(motor 3)] - d - [(motor 3)] - ...
For motor 3, the delays are d, d + (motor 1&2 handling), d, ...
The execution has to be fast and smooth, the shorter the time used by each motor check, the less chance there is for a stall.
All optimisation of this code is welcome but, please, it should be kept human compatible !