Using an int array to program the GPIO port mode register (MODER)

  arrays, c++, cmsis, platformio, stm32f4

I am currently trying to write a ‘simple’ driver to setup GPIO ports and drive pins on a legit ST NUCLEO-F446RE dev board using CMSIS framework in PlatformIO. Using a switch case statement, I was able to set and reset the appropriate bits in the MODER register (00,01,10,11 for input, general purpose, alternate functions, analogue modes) using |= 1 and &= ~1 shifted to the correct index respectively. I have been using a simple (and bad practice) blinky to test my code using the green LD2 on PA5.

But using a switch case statement annoyed me when the bit order for each mode can be hard coded as it does not change. So I had the idea of using constant int arrays with the bit orders for each mode hard coded in the header file, then accessing the index of the arrays to set the relative bits in MODER. When trying this the project builds without any warnings or errors. HOWEVER, when flashing to the board, the chip locks up with the error code:

*** [upload] Error 1" and "The terminal process "C:UsersXXXXXX_PC.platformiopenvScriptsplatformio.exe 'run', '--target', 'upload'" terminated with exit code: 1."

When trying to upload again I get the same error but with:

Error: init mode failed (unable to connect to the target) in procedure 'program' ** OpenOCD init failed ** shutdown command invoked" also.

The only way to upload even working code after this is by holding down the RESET button on the board at the start of a new upload. This lead me to the conclusion the chip is locking up??

This is the code for simple.cpp file. Note: setting of the bits is happening in the configMODE() function which is called from the configurePORTS() function. You can see the working switch case statement commented out to test my new idea.

#include "simple.h"
#include "stm32f4xx.h"
//#include <iostream>

//----------Hidden Functions----------
void configMODE(GPIO_TypeDef *port_x, int pin, const int MODE_[])//int mode)//const int MODE_[])
{
    /*switch(mode)
    {
        case 0:                                                 //Input (reset state)
            port_x -> MODER &= ~(1 << (pin * 2));               //Reset bit to 0
            port_x -> MODER &= ~(1 << ((pin * 2) + 1));         //Reset bit to 0
            break;
        case 1:                                                 //Gerneral purpose output mode
            port_x -> MODER |= (1 << (pin * 2));                //Set bit to 1
            port_x -> MODER &= ~(1 << ((pin * 2) + 1));         //Reset bit to 0
            break;
        case 2:                                                 //Alturnate function mode
            port_x -> MODER &= ~(1 << (pin * 2));               //Reset bit to 0
            port_x -> MODER |= (1 << ((pin * 2) + 1));          //Set bit to 1                        
            break;
        case 3:                                                 //Analogue mode
            port_x -> MODER |= (1 << (pin * 2));                //Set bit to 1
            port_x -> MODER |= (1 << ((pin * 2) + 1));          //Set bit to 1
            break;
    }*/

    port_x -> MODER =  (MODE_[0] << (pin * 2));
    port_x -> MODER =  (MODE_[1] << ((pin * 2) + 1));

}

void configOUTPUTTYPE(GPIO_TypeDef *port_x, int pin, int type_output)
{
    switch(type_output)
    {
        case 0:                                                 //Output Push Pull
            port_x -> OTYPER &= ~(1 << pin);                      //Reset pin bit to 0
            break;
        case 1:                                                 //Output Open-Drain
            port_x -> OTYPER |= (1 << pin);                       //Set pin bit to 1
            break;
    }

    //port_x -> OTYPER = (type_output << pin);                              //Make sure bit is 0 before XOR flip

}

/*void configOUTPUTSPEED(GPIO_TypeDef *port_x, int pin, int speed[])
{
    //port_x -> OSPEEDR = 
}*/

//----------User Functions----------
void configurePorts(char port, int pin, const int MODE_[], int type_output)//int mode, int type_output)//const int MODE_[], int type_output)
{
    switch(port)
    {
        case 'A':
            RCC -> AHB1ENR |= (1 << 0);                         //Enable Clock for GPIOA
            configMODE(GPIOA, pin, MODE_);//mode);//MODE_);
            configOUTPUTTYPE(GPIOA, pin, type_output);
            break;
        /*case 'B':
            RCC -> AHB1ENR |= (1 << 1);                         //Enable Clock for GPIOB
            configMODE(GPIOB, pin, mode);
            configOUTPUTTYPE(GPIOB, pin, type_output);
            break;
        case 'C':
            RCC -> AHB1ENR |= (1 << 2);                         //Enable Clock for GPIOC
            configMODE(GPIOC, pin, mode);
            configOUTPUTTYPE(GPIOC, pin, type_output);
            break;
        case 'D':
            RCC -> AHB1ENR |= (1 << 3);                         //Enable Clock for GPIOD
            configMODE(GPIOD, pin, mode);
            configOUTPUTTYPE(GPIOD, pin, type_output);
            break;
        case 'E':
            RCC -> AHB1ENR |= (1 << 4);                         //Enable Clock for GPIOE
            configMODE(GPIOE, pin, mode);
            configOUTPUTTYPE(GPIOE, pin, type_output);
            break;
        case 'F':
            RCC -> AHB1ENR |= (1 << 5);                         //Enable Clock for GPIOF
            configMODE(GPIOF, pin, mode);
            configOUTPUTTYPE(GPIOF, pin, type_output);
            break;
        case 'G':
            RCC -> AHB1ENR |= (1 << 6);                         //Enable Clock for GPIOG
            configMODE(GPIOG, pin, mode);
            configOUTPUTTYPE(GPIOG, pin, type_output);
            break;
        case 'H':
            RCC -> AHB1ENR |= (1 << 7);                         //Enable Clock for GPIOG
            configMODE(GPIOH, pin, mode);
            configOUTPUTTYPE(GPIOH, pin, type_output);
            break;*/
        default:
            //asm("nop");                 //Dont setup port because of wrong port value
            return;
    }
}

void bitBashHIGH(GPIO_TypeDef *GPIOx, int pin)
{
    GPIOx -> BSRR = (1 << pin);                                 //Set Pin High
}

void bitBashLOW(GPIO_TypeDef *GPIOx, int pin)
{
    GPIOx -> BSRR = (1 << (pin + 16));                          //Set Pin Low
}

void bitToggle(GPIO_TypeDef *GPIOx, int pin)
{
    static bool odd_or_even = 0;

    GPIOx -> BSRR ^= (1 << pin);                                //XOR (^) to toggle
    if(odd_or_even == 1)
    {
        GPIOx -> BSRR ^= (1 << (pin + 16));
    }
    odd_or_even = !odd_or_even;
}

And this is the code for simple.h file. Note: you can see the original working code commented out in both the configurePORTS() function prototype and the mode variables.

#ifndef simple
#define simple

#include "stm32f4xx.h"

/*
NOTES
- NOTE 1:       The function bitToggle() is slower than the functions bitBashHIGH() and bitBashLOW().
                If used in conjunction with bitBashHIGH() and bitBashLOW(), this may result in
                unexpected behaviour as the Set and Reset bits may be opposite to what the XOR of these
                bits expects in relation to the 'if' statement in the function bitToggle().
*/

//----------User Visible Defines-----------

//**Pin Modes GPIOx_MODER**
//#define MODE_INPUT_RESET_STATE          0
//#define MODE_OUTPUT_GENERAL_PURPOSE     1
//#define MODE_ALTURNATE_FUNCTION         2
//#define MODE_ANALOG                     3
const int MODE_INPUT_RESET_STATE[] =        {0,0};
const int MODE_OUTPUT_GENERAL_PURPOSE[] =   {0,1};
const int MODE_ALTURNATE_FUNCTION[] =       {1,0};
const int MODE_ANALOG[] =                   {1,1};

//**Output Type GPIOx_OTYPER**
#define TYPE_OUTPUT_PUSH_PULL           0
#define TYPE_OUTPUT_OPEN_DRAIN          1

//**Output Speed GPIOx_OSPEEDER**
//#define SPEED_LOW                       {0,0}
//#define SPEED_MEDIUM                    {0,1}
//#define SPEED_FAST                      {1,0}
//#define SPEED_HIGH                      {1,1}

//----------Function Prototypes----------
void configurePorts(char port, int pin, const int MODE_[], int type_output);//int mode, int type_output);//const int MODE_[], int type_output);
void bitBashHIGH(GPIO_TypeDef *GPIOx, int pin);
void bitBashLOW(GPIO_TypeDef *GPIOx, int pin);
void bitToggle(GPIO_TypeDef *GPIOx, int pin);       //NOTE 1: Slower than bit bashing

#endif

And finally the code for main.cpp file.

//CMSIS Test

#include "stm32f4xx.h"
#include "simple.h"

void wait_a_bit(int number)
{
    int i = 0;

    while(i < number)
    {
        asm("nop");
        i++;
    }
}

int main()
{
    configurePorts('A', 5, MODE_OUTPUT_GENERAL_PURPOSE, TYPE_OUTPUT_PUSH_PULL);

    while(1)
    {
        bitBashHIGH(GPIOA, 5);

        wait_a_bit(180000);

        bitBashLOW(GPIOA, 5);

        wait_a_bit(1800000);

        //bitToggle(GPIOA, 5);

        //wait_a_bit(180000);
        
    }
}

My current thoughts are one of two things:

  1. I am doing something wrong with the const int arrays. However, I would have thought something like this would have been caught when I build the project?
  2. I am setting the bits in the MODER register wrong. I assumed you could just assign bits (=) as the BSRR register handles this fine, but maybe the registers work differently??

So what am I missing here?? haha Thank you in advance for you guys help!!

PS If anyone notices any better practices in any of the rest of the code (apart from the useless loop in the wait_a_bit() function, the timers driver will be next!!) I’d love the feedback, cheers!

Source: Windows Questions C++

LEAVE A COMMENT