more fun with C++, templates and GPIO

Post your cool example code here.
User avatar
Rick Kimball
Posts: 1014
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

more fun with C++, templates and GPIO

Post by Rick Kimball » Tue Jun 09, 2015 8:44 pm

I've taken the gpio code I posted in another thread to the next level. This one provides PIN abstraction and some nifty access methods.

Code: Select all

// more fun with c++ classes and templates
class GPIOPort :
  public gpio_reg_map {
public:
  void high(const uint32_t pin) {
    BSRR = 1 << pin;
  }
  void low(const uint32_t pin) {
    BRR = 1 << pin;
  }
  void pinMode(const uint32_t pin, gpio_pin_mode mode) {
      volatile uint32_t *cr = &CRL + (pin >> 3);
      uint32_t shift = (pin & 0x7) * 4;
      uint32_t tmp = *cr;
      tmp &= ~(0xF << shift);
      tmp |= (mode == GPIO_INPUT_PU ? GPIO_INPUT_PD : mode) << shift;
      *cr = tmp;

      if (mode == GPIO_INPUT_PD) {
          ODR &= ~(1u << pin);
      } else if (mode == GPIO_INPUT_PU) {
          ODR |= (1u << pin);
      }
  }
  int value(const uint32_t pin) {
      return (IDR & (1u<< pin) ? 1 : 0);
  }

};

#define GPIOPORT_REF(a) *((GPIOPort * const)(a))

static GPIOPort & gPortA = GPIOPORT_REF(0x40010800);
static GPIOPort & gPortB = GPIOPORT_REF(0x40010C00);
static GPIOPort & gPortC = GPIOPORT_REF(0x40011000);

template<const uint32_t PIN>
class GPIOPortBPin {
public:
    void high() { gPortB.high(PIN); }
    void low() { gPortB.low(PIN); }
    void operator=(const int value) {
        if ( value )
            gPortB.high(PIN);
        else
            gPortB.low(PIN);
    }

    operator int() {
        return gPortB.value(PIN);
    }

    void pinMode(WiringPinMode mode) {
        gpio_pin_mode gpio_mode;
        bool pwm = false;

        switch(mode) {
        case OUTPUT:
            gpio_mode = GPIO_OUTPUT_PP;
            break;
        case OUTPUT_OPEN_DRAIN:
            gpio_mode = GPIO_OUTPUT_OD;
            break;
        case INPUT:
        case INPUT_FLOATING:
            gpio_mode = GPIO_INPUT_FLOATING;
            break;
        case INPUT_ANALOG:
            gpio_mode = GPIO_INPUT_ANALOG;
            break;
        case INPUT_PULLUP:
            gpio_mode = GPIO_INPUT_PU;
            break;
        case INPUT_PULLDOWN:
            gpio_mode = GPIO_INPUT_PD;
            break;
        case PWM:
            gpio_mode = GPIO_AF_OUTPUT_PP;
            pwm = true;
            break;
        case PWM_OPEN_DRAIN:
            gpio_mode = GPIO_AF_OUTPUT_OD;
            pwm = true;
            break;
        default:
            return;
        }

        gPortB.pinMode(PIN, gpio_mode);
        (void)pwm; // TODO: implement timer start/stop
    }
};

typedef GPIOPortBPin<1> PB_1;
typedef GPIOPortBPin<2> PB_2;
// ... and on and on
typedef GPIOPortBPin<11> PB_15;

PB_1 LED1;

#define fastWrite(pin,value) do { (value) ? pin.high() : pin.low(); } while(0)

//

void setup() {
    LED1.pinMode(OUTPUT);
}

void loop() {
    LED1 = 1;
    delay(50);
    LED1 = 0;
    delay(250);

    LED1.high();
    delay(50);
    LED1.low();
    delay(450);
    
    fastWrite(LED1,1);
    delay(50);
    fastWrite(LED1,0);
    delay(450);
}
I think the most interesting part of this code is the syntax feels so natural to set a pin high or low. I've provided an operator= overload

Code: Select all

void loop() {
    LED1 = HI;
    delay(50);
    LED1 = LOW;
    delay(250);
}
The asm it produces is amazingly small:

Code: Select all

08000100 <loop()>:
 8000100:       b538            push    {r3, r4, r5, lr}
 8000102:       4c06            ldr     r4, [pc, #24]   ; (800011c <loop()+0x1c>)
 8000104:       2502            movs    r5, #2
 8000106:       6125            str     r5, [r4, #16]
 8000108:       2032            movs    r0, #50 ; 0x32
 800010a:       f001 fc21       bl      8001950 <delay(unsigned long)>
 800010e:       6165            str     r5, [r4, #20]
 8000110:       20fa            movs    r0, #250        ; 0xfa
 8000112:       e8bd 4038       ldmia.w sp!, {r3, r4, r5, lr}
 8000116:       f001 bc1b       b.w     8001950 <delay(unsigned long)>
 800011a:       bf00            nop
 800011c:       40010c00        .word   0x40010c00
-rick
-rick

User avatar
mrburnette
Posts: 1803
Joined: Mon Apr 27, 2015 12:50 pm
Location: Greater Atlanta
Contact:

Re: more fun with C++, templates and GPIO

Post by mrburnette » Wed Jun 10, 2015 12:23 am

Well done!

Ray

User avatar
ahull
Posts: 1597
Joined: Mon Apr 27, 2015 11:04 pm
Location: Sunny Scotland
Contact:

Re: more fun with C++, templates and GPIO

Post by ahull » Wed Jun 10, 2015 12:54 am

Certainly the loop is pretty tidy, I presume the over all code size is equally succinct.
- Andy Hull -

User avatar
Rick Kimball
Posts: 1014
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

Re: more fun with C++, templates and GPIO

Post by Rick Kimball » Wed Jun 10, 2015 1:05 am

Sadly, no @ahull. I'm still anchored to this barge of a thing we call libmaple ; )
-rick

porellan63
Posts: 3
Joined: Wed Jun 10, 2015 12:38 pm

Re: more fun with C++, templates and GPIO

Post by porellan63 » Fri Jun 12, 2015 5:40 pm

Rick
Hello , I saw your example code "C++, templates an GPIO "and I have trying to rebuild it in arduino and in maple but I have not been able yet
In arduino 1.6.4 the IDE hung in the progressive bar trying to compile it and In Maple gave the error message
"error: expected class-name before '{' token In member function 'void GPIOPort::high(int)':"
I am not an expert so if you want to help Could you do it ?
I have an olimexino board
any help it could be ver appreciated
Best Regards
Pablo
Electronics

User avatar
Rick Kimball
Posts: 1014
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

Re: more fun with C++, templates and GPIO

Post by Rick Kimball » Fri Jun 12, 2015 6:23 pm

porellan63 wrote:I have an olimexino board
any help it could be ver appreciated
Best Regards
Pablo
Electronics
Does your board have an LED on PB_1 ?

I'm using this with maple_mini board and rogers code. * Although I'm probably a week back .. maybe I should try his latest * ... If you mean the Maple IDE, I'm guessing the arm-none-eabi-gcc compiler that ships with that is probably too old to deal with this code.

Here is the code in a file maybe that will make it easier to copy and paste.
https://gist.github.com/RickKimball/7d9 ... c8ea2e4d8e

-rick
-rick

User avatar
Rick Kimball
Posts: 1014
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

Re: more fun with C++, templates and GPIO

Post by Rick Kimball » Fri Jun 12, 2015 6:29 pm

OK, I just did a github pull on https://github.com/rogerclarkmelbourne/Arduino_STM32, so now I'm in sync with the latest. And the sketch compiles and seems to work fine.

I'm using the due compiler and arduino 1.6.5 built from source from github. (the arudino.cc master branch) on linux with a maple mini clone board programmed via an stlink-v2 programmer.

-rick
-rick

User avatar
Rick Kimball
Posts: 1014
Joined: Tue Apr 28, 2015 1:26 am
Location: Eastern NC, US
Contact:

Re: more fun with C++, templates and GPIO

Post by Rick Kimball » Fri Jun 12, 2015 6:35 pm

Sorry, I'm not using maple-mini selection. I have the generic f103cb with stlink one picked. I am using a maple mini board, but without a DFU uploader.

-rick
-rick

porellan63
Posts: 3
Joined: Wed Jun 10, 2015 12:38 pm

Re: more fun with C++, templates and GPIO

Post by porellan63 » Fri Jun 12, 2015 7:44 pm

Rick
I have tested your code in my olimexino board and it does work perfect

Code: Select all

// more fun with c++ classes and templates
class GPIOPort :
  public gpio_reg_map {
public:
  void high(const uint32_t pin) {
    BSRR = 1 << pin;
  }
  void low(const uint32_t pin) {
    BRR = 1 << pin;
  }
  void pinMode(const uint32_t pin, gpio_pin_mode mode) {
      volatile uint32_t *cr = &CRL + (pin >> 3);
      uint32_t shift = (pin & 0x7) * 4;
      uint32_t tmp = *cr;
      tmp &= ~(0xF << shift);
      tmp |= (mode == GPIO_INPUT_PU ? GPIO_INPUT_PD : mode) << shift;
      *cr = tmp;
 
      if (mode == GPIO_INPUT_PD) {
          ODR &= ~(1u << pin);
      } else if (mode == GPIO_INPUT_PU) {
          ODR |= (1u << pin);
      }
  }
  int value(const uint32_t pin) {
      return (IDR & (1u<< pin) ? 1 : 0);
  }
 
};
 
#define GPIOPORT_REF(a) *((GPIOPort * const)(a))
 
static GPIOPort & gPortA = GPIOPORT_REF(0x40010800);
static GPIOPort & gPortB = GPIOPORT_REF(0x40010C00);
static GPIOPort & gPortC = GPIOPORT_REF(0x40011000);
 
template<const uint32_t PIN>
class GPIOPortBPin {
public:
    void high() { gPortB.high(PIN); }
    void low() { gPortB.low(PIN); }
    void operator=(const int value) {
        if ( value )
            gPortB.high(PIN);
        else
            gPortB.low(PIN);
    }
 
    operator int() {
        return gPortB.value(PIN);
    }
 
void pinMode(WiringPinMode mode) {
 gpio_pin_mode gpio_mode;
 bool pwm = false;
 
 switch(mode) {
        case OUTPUT:
            gpio_mode = GPIO_OUTPUT_PP;
            break;
        case OUTPUT_OPEN_DRAIN:
            gpio_mode = GPIO_OUTPUT_OD;
            break;
        case INPUT:
        case INPUT_FLOATING:
            gpio_mode = GPIO_INPUT_FLOATING;
            break;
        case INPUT_ANALOG:
            gpio_mode = GPIO_INPUT_ANALOG;
            break;
        case INPUT_PULLUP:
            gpio_mode = GPIO_INPUT_PU;
            break;
        case INPUT_PULLDOWN:
            gpio_mode = GPIO_INPUT_PD;
            break;
        case PWM:
            gpio_mode = GPIO_AF_OUTPUT_PP;
            pwm = true;
            break;
        case PWM_OPEN_DRAIN:
            gpio_mode = GPIO_AF_OUTPUT_OD;
            pwm = true;
            break;
        default:
            return;
        }
 
        gPortB.pinMode(PIN, gpio_mode);
        (void)pwm; // TODO: implement timer start/stop
    }
};

template<const uint32_t PIN>
class GPIOPortAPin {
public:
    void high() { gPortA.high(PIN); }
    void low() { gPortA.low(PIN); }
    void operator=(const int value) {
        if ( value )
            gPortA.high(PIN);
        else
            gPortA.low(PIN);
    }
 
    operator int() {
        return gPortA.value(PIN);
    }
 
void pinMode(WiringPinMode mode) {
 gpio_pin_mode gpio_mode;
 bool pwm = false;
 
 switch(mode) {
        case OUTPUT:
            gpio_mode = GPIO_OUTPUT_PP;
            break;
        case OUTPUT_OPEN_DRAIN:
            gpio_mode = GPIO_OUTPUT_OD;
            break;
        case INPUT:
        case INPUT_FLOATING:
            gpio_mode = GPIO_INPUT_FLOATING;
            break;
        case INPUT_ANALOG:
            gpio_mode = GPIO_INPUT_ANALOG;
            break;
        case INPUT_PULLUP:
            gpio_mode = GPIO_INPUT_PU;
            break;
        case INPUT_PULLDOWN:
            gpio_mode = GPIO_INPUT_PD;
            break;
        case PWM:
            gpio_mode = GPIO_AF_OUTPUT_PP;
            pwm = true;
            break;
        case PWM_OPEN_DRAIN:
            gpio_mode = GPIO_AF_OUTPUT_OD;
            pwm = true;
            break;
        default:
            return;
        }
 
        gPortA.pinMode(PIN, gpio_mode);
        (void)pwm; // TODO: implement timer start/stop
    }
};

typedef GPIOPortAPin<1> PA_1;
typedef GPIOPortAPin<2> PA_2;
typedef GPIOPortAPin<3> PA_3;
typedef GPIOPortAPin<4> PA_4;
typedef GPIOPortAPin<5> PA_5;
// ... and on and on
typedef GPIOPortAPin<15> PA_15;
 
typedef GPIOPortBPin<1> PB_1;
typedef GPIOPortBPin<2> PB_2;
// ... and on and on
typedef GPIOPortBPin<11> PB_15;
 
PA_1 LED2;
PA_5 LED1;
 
#define fastWrite(pin,value) do { (value) ? pin.high() : pin.low(); } while(0)
 
//
 
void setup() {
    LED1.pinMode(OUTPUT);
    LED2.pinMode(OUTPUT);
}
 
void loop() {
    LED1 = 1;
    LED2 = 1;
    delay(200);
    LED1 = 0;
    LED2 = 0;
    delay(200);
 
    LED1.high();
    LED2.high();
    delay(200);
    LED1.low();
    LED2.low();
    delay(200);
    
    //fastWrite(LED1,1);
    //delay(50);
    //fastWrite(LED1,0);
    //delay(450);
}
The code is only to show the modifications that have been done for olimexino Rev.c
Thanks a lot
Best Regards

zmemw16
Posts: 1420
Joined: Wed Jul 08, 2015 2:09 pm
Location: St Annes, Lancs,UK

Re: more fun with C++, templates and GPIO

Post by zmemw16 » Sat Aug 08, 2015 11:42 pm

i'm trying to use your code to drive a SSD1306 SPI 0.96" OLED

i've had the standard 'using spi' example working and wondered why the CS waveform seemed overly long before SCLK starts and also after SCLK stops.

see http://www.stm32duino.com/viewtopic.php?f=9&t=481

victor_py suggested looking at your post.

i'm using a ZET6 board, so i have ports A-G, now the addresses, I'm guessing because i can't seem to find the mapping info.
so as the interval seems to be 0x400, i've just gambled:-)

Code: Select all

static GPIOPort & gPortA = GPIOPORT_REF(0x40010800);
static GPIOPort & gPortB = GPIOPORT_REF(0x40010C00);
static GPIOPort & gPortC = GPIOPORT_REF(0x40011000);
& extended it to

Code: Select all

static GPIOPort & gPortA = GPIOPORT_REF(0x40010800);
static GPIOPort & gPortB = GPIOPORT_REF(0x40010C00);
static GPIOPort & gPortC = GPIOPORT_REF(0x40011000);
static GPIOPort & gPortD = GPIOPORT_REF(0x40011400);
static GPIOPort & gPortE = GPIOPORT_REF(0x40011800);
static GPIOPort & gPortF = GPIOPORT_REF(0x40011C00);
static GPIOPort & gPortG = GPIOPORT_REF(0x40012000);
just spotted my problem, i hadn't realised it was for just port B

Code: Select all

template<const uint32_t PIN>
class GPIO[b]PortB[/b]Pin {
is it possible write this so that it could be used for any port eg A, B, ... G etc ?

nomenclature is causing me a problem, as a macro or a nested template??

alternatively i could just cut, paste and replace - really horrible idea ....
or i could just put all the connections on portA as i'm using SPI1

any pointers to helpful documentation would be nice, i found this from hitex development tools
'The Insider’s Guide To The STM32 ARM Based Microcontroller' which helped simplify it a bit

stephen

Post Reply