Port manipulation STM32F411

Post here first, or if you can't find a relevant section!
Post Reply
InXoconochtli
Posts: 6
Joined: Tue Feb 23, 2021 12:54 am

Port manipulation STM32F411

Post by InXoconochtli »

I am building an open-source peristaltic pump. My first prototype was using an Arduino nano, but I was quickly limited by its capabilities, so I switched to an STM32F103 bluepill. I originally wrote the code with Roger Clark's core. Now I am migrating to an STM32F411 blackpill and the STM32duino core and I'm finding some issues when trying some things.

Especially when pumping, I need fast response, so I was doing direct port manipulation like this:

Code: Select all

switchValueA = GPIOB->regs->IDR & 0b0000000000100000;
GPIOA->regs->BSRR = 0b0000001000000000;
Now I am getting the following error

'struct GPIO_TypeDef' has no member named 'regs'

How is direct port manipulation done in this core? I can use the standard Arduino calls, but is isn't fast enough. Do I have to use HAL? Any references you'd recommend?
fredbox
Posts: 125
Joined: Thu Dec 19, 2019 3:05 am
Answers: 2

Re: Port manipulation STM32F411

Post by fredbox »

Before debugging direct port manipulation, try digitalWriteFast().
See viewtopic.php?p=477#p477.

Note that digitalWriteFast uses different arguments (PA_1 instead of PA1) for operation. There are macros that do the conversion between pinNames and pinNumbers. Search the forum for examples.
InXoconochtli
Posts: 6
Joined: Tue Feb 23, 2021 12:54 am

Re: Port manipulation STM32F411

Post by InXoconochtli »

Thank you, fredbox. This should solve the main problem. Pin number conversion is not really that inconvenient. But now my limitation becomes an AnalogRead. I couldn't find a faster alternative before, so I just ran with it. I was thinking about using a counter to only read every 10 loops, for example. Is there an alternative you know of?
mlundin
Posts: 94
Joined: Wed Nov 04, 2020 1:20 pm
Answers: 6
Location: Sweden

Re: Port manipulation STM32F411

Post by mlundin »

You can use PWM outputs for precisely timed sequences of pulses, or make it yourself using the timer library, its really convenient but the interrupt overhead in HAL for attach'd interrupts is a bit long, depends on your speed and timing requirements.

Another pattern for timed events is something like this

Code: Select all

uint32_t blinktime=0;
uint32_t blinkstate=0;
uint32_t adctime=0;

loop() {
  if (millis() - adctime > 10) {   /* take an analog reading every 10 milliseconds */
    adctime += 10;                   /* update timer value */
    adcvalue = analogRead(ADCPIN);
  }
  
  if (blinkstate == 1) {              /* Blink LED two times per second with 100/400 ms on off times */
    if (millis()-blinktime >= 100) {
       blinktime += 100;
       blinkstate = 0;
       digitalWriteFast(LEDPIN, 0);
    }
  }

  if (blinkstate == 0) {
    if (millis()-blinktime >= 400) {
       blinktime += 400;
       blinkstate = 1;
       digitalWriteFast(LEDPIN, 1);
    }
  }


}
InXoconochtli
Posts: 6
Joined: Tue Feb 23, 2021 12:54 am

Re: Port manipulation STM32F411

Post by InXoconochtli »

This is the relevant part of my code I'm trying to improve. In the pumping loop, there is always an analogRead, and only if there is a change greater than a tolerance, then it recalculates the delay and refreshes the LCD. Else, it goes into the pumping function (it is 8 high low switching steps, but I only included 1 for space).

What I'm thinking is reducing the sample frequency (I gave it a time change tolerance of 500 ms, anyway), but I'd like the analogRead to be faster, if possible.

Code: Select all

case 0:
        potValueRead = analogRead(potInput);
        diffPot = abs(potValueRead - potValueOld);
        diffTimeChange = timeCurrent - timeChange;
        
        if(switchValueA != switchValueALast) {            
          modeString = "Pumping";
          delayCalc(potValueRead, potValueLow, potValueHigh, potFlowValue, potDelayValue);
          potValueOld = potValueRead;     
          LCDFlowStart(potFlowValue);
          digitalWrite(enableState, LOW);
            
          switchValueALast = 0; 
        }

        if(diffPot < changeTolerance) {    
          timeChange = millis();      
        }

        if(diffPot >= changeTolerance) {
          delayCalc(potValueRead, potValueLow, potValueHigh, potFlowValue, potDelayValue);     
        }
    
        if(diffPot >= changeTolerance && diffTimeChange >= 500) { 
          LCDFlowUpdate(potFlowValue);
          potValueOld = potValueRead;
          timeChange = millis();       
        }
  
        else {
          pump(potDelayValue);     
        }
      break;
.
.
.
inline void pump() __attribute__((always_inline)); 

void pump(int _DelayValue) {
  digitalWriteFast(PA_9, HIGH);
  delayMicroseconds(_DelayValue);
  digitalWriteFast(PA_9, LOW);
  delayMicroseconds(_DelayValue);
  }
  
stevestrong
Posts: 502
Joined: Fri Dec 27, 2019 4:53 pm
Answers: 8
Location: Munich, Germany
Contact:

Re: Port manipulation STM32F411

Post by stevestrong »

I am using this simple tasker library to schedule my repetitive tasks, have a look on it: https://github.com/joysfera/arduino-tasker
mlundin
Posts: 94
Joined: Wed Nov 04, 2020 1:20 pm
Answers: 6
Location: Sweden

Re: Port manipulation STM32F411

Post by mlundin »

The analogRead() function is very generic, and it intializes and deinitializes the HAL ADC layer for every call. A quick test on a F407 Discovery board gave around 60uS per call or 18kHz sampling frequency. If you do your own calls to the HAL layer, this can be improved a lot. I tested the code from : https://visualgdb.com/tutorials/arm/stm32/adc/ and it gave 85kHz using 480 cycles sample time, and 270kHz using 144 cycles sample time.
Post Reply

Return to “General discussion”