more fun with C++, templates and GPIO

Post your cool example code here.
User avatar
Rick Kimball
Posts: 1054
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 » Sun Aug 09, 2015 4:07 pm

zmemw16 wrote:is it possible write this so that it could be used for any port eg A, B, ... G etc ?
any pointers to helpful documentation would be nice, i found this from hitex development tools
Sure, you could create a base template that implements the functions and accepts the specific port as a template argument. I didn't do this in the example to avoid confusing people more than just the simple template code already does. The simplest solution if you aren't template savvy is to cut and paste and just change the port.

Here is one solution I used for the LPC1114FN28:

I created a base class template here that expects a number argument for the port #
https://github.com/RickKimball/fabooh/b ... ore/gpio.h

Here I create port classes using a number to pick the different ports
https://github.com/RickKimball/fabooh/b ... pins.h#L13
https://github.com/RickKimball/fabooh/b ... pins.h#L27

For the msp430, I took a different approach as the its GPIO ports can have different capabilities. Some of the ports are interrupt capable and other ports are not. I created base class templates that have different features and capabilities:

https://github.com/RickKimball/fabooh/b ... gpio.h#L59

I haven't done a fabooh public port of the stm32 at this point. Maybe some time in the future.
zmemw16 wrote: 'The Insider’s Guide To The STM32 ARM Based Microcontroller' which helped simplify it a bit
Thanks, I hadn't seen that before.

-rick
Last edited by Rick Kimball on Sun Aug 09, 2015 7:20 pm, edited 1 time in total.
-rick

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

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

Post by zmemw16 » Sun Aug 09, 2015 4:41 pm

another doc link
google with 'Discovering the STM32 Microcontroller Geoffrey Brown'

quite a course content and just scanning - exercise 4.3 looks rather useful!

stephen

victor_pv
Posts: 1736
Joined: Mon Apr 27, 2015 12:12 pm

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

Post by victor_pv » Fri Jun 30, 2017 12:52 am

Rick, resurrecting this thread for a question.

What would happen if I used PB1 vs PB_1?
Such as:
typedef GPIOPortBPin<1> PB1;
Would it give an error because PB1 is already defined somewhere as something else?

User avatar
Rick Kimball
Posts: 1054
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 30, 2017 12:17 pm

Yes it will fail. PB2 is already being used as an enum.

Code: Select all

sketch_jun30a:100: error: 'typedef class GPIOPortBPin<2ul> PB2' redeclared as different kind of symbol
 typedef GPIOPortBPin<2> PB2;
                         ^~~
In file included from /home/kimballr/Arduino/hardware/arduino_stm32/STM32F1/cores/maple/boards.h:39:0,
                 from /home/kimballr/Arduino/hardware/arduino_stm32/STM32F1/cores/maple/wirish.h:54,
                 from /home/kimballr/Arduino/hardware/arduino_stm32/STM32F1/cores/maple/Arduino.h:30,
                 from /tmp/arduino_build_107833/sketch/sketch_jun30a.ino.cpp:1:
/home/kimballr/Arduino/hardware/arduino_stm32/STM32F1/variants/generic_stm32f103c/board/board.h:81:12: note: previous declaration '<anonymous enum> PB2'
  PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13,PB14,PB15,

            ^~~
exit status 1
'typedef class GPIOPortBPin<2ul> PB2' redeclared as different kind of symbol
-rick

User avatar
Rick Kimball
Posts: 1054
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 » Sat Sep 16, 2017 6:03 pm

zmemw16 wrote:
Sat Aug 08, 2015 11:42 pm
is it possible write this so that it could be used for any port eg A, B, ... G etc ?
@zmemw16 I was looking at this again for some reason. I made a simple change that lets you do what you want.

I added a generic template member function called GPIOPortX() that returns the proper port based on a number. You have to use a different syntax for the pin template. Still as efficient just more flexible.

typedef GPIOPortPin<1, 0> PA_0; // where 1 is PORTA and 0 is PIN 0 ...
...
typedef GPIOPortPin<2, 0> PB_0; // where 2 is PORTB and 0 is PIN 0
...
typedef GPIOPortPin<3, 13> PC_13; // where 3 is PORTC and 13 is PIN 13 (blue pill led)
...
typedef GPIOPortPin<7, 0> PG_13; // where 7 is PORTG and 0 is PIN 0

Here is the changed code: or grab from here github take2.ino

Code: Select all

// more fun with c++ classes and templates
// http://www.stm32duino.com/viewtopic.php?f=18&t=303

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))

template<const uint8_t PORT, const uint8_t PIN>
class GPIOPortPin {
  public:
    GPIOPort & GPIOPortX() {
      switch (PORT) {
        case 1:  return GPIOPORT_REF(0x40010800); break; // GPIOA
        case 2:  return GPIOPORT_REF(0x40010C00); break; // GPIOB
        case 3:  return GPIOPORT_REF(0x40011000); break; // GPIOC
        case 4:  return GPIOPORT_REF(0x40011400); break; // GPIOD
        case 5:  return GPIOPORT_REF(0x40011800); break; // GPIOE
        case 6:  return GPIOPORT_REF(0x40011C00); break; // GPIOF
        case 7:  return GPIOPORT_REF(0x40012000); break; // GPIOG

        default: return GPIOPORT_REF(0x40010800); break; // ignore error, default to A
      }
    }
    void high() {
      GPIOPortX().high(PIN);
    }
    void low() {
      GPIOPortX().low(PIN);
    }

    // set using operator overload
    void operator=(const int value) {
      if ( value )
        GPIOPortX().high(PIN);
      else
        GPIOPortX().low(PIN);
    }

    // get using operator overload
    operator int() {
      return GPIOPortX().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;
      }

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

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

typedef GPIOPortPin<1, 0> PA_0;
typedef GPIOPortPin<1, 1> PA_1;
// ... and on and on
typedef GPIOPortPin<1, 15> PA_15;

typedef GPIOPortPin<2, 0> PB_0;
typedef GPIOPortPin<2, 1> PB_1;
typedef GPIOPortPin<2, 2> PB_2;
// ... and on and on
typedef GPIOPortPin<2, 15> PB_15;

typedef GPIOPortPin<3, 13> PC_13;
typedef GPIOPortPin<3, 14> PC_14;
typedef GPIOPortPin<3, 15> PC_15;

PC_13 LED1; // blue pill led

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

void loop() {

#if 1
  // set using operator=
  LED1 = LOW;
  delay(50);
  // set using operator=, get using operator int
  LED1 = !LED1;
  delay(250);
#endif

#if 1
  int v = 0;

  // set using operator=
  LED1 = v++;
  delay(50);
  LED1 = v--;
  delay(250);
#endif

#if 1
  // set using operator=
  LED1 = LOW;
  delay(50);
  LED1 = HIGH;
  delay(250);
#endif

#if 1
  // set using member function call
  LED1.low();
  delay(50);
  LED1.high();
  delay(250);
#endif

#if 1
  // set using a macro calling member function
  fastWrite(LED1, 0);
  delay(50);
  fastWrite(LED1, 1);
  delay(950);
#endif
}
-rick

Post Reply