// 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);
}
Ray
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
any help it could be ver appreciated
Best Regards
Pablo
Electronics
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
I have tested your code in my olimexino board and it does work perfect
// 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);
}
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:-)
static GPIOPort & gPortA = GPIOPORT_REF(0x40010800);
static GPIOPort & gPortB = GPIOPORT_REF(0x40010C00);
static GPIOPort & gPortC = GPIOPORT_REF(0x40011000);
any pointers to helpful documentation would be nice, i found this from hitex development tools
google with ‘Discovering the STM32 Microcontroller Geoffrey Brown’
quite a course content and just scanning – exercise 4.3 looks rather useful!
stephen
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?
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
[zmemw16 – 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
// 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
}
// 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))
typedef enum GPIOPortName {
PORT_A = 1,
PORT_B = 2,
PORT_C = 3,
PORT_D = 4,
PORT_E = 5,
PORT_F = 6,
PORT_G = 7
} GPIOPortName;
template<const GPIOPortName PORT, const uint8_t PIN>
class GPIOPortPin {
public:
GPIOPort & GPIOPortX() {
switch (PORT) {
case PORT_A: return GPIOPORT_REF(0x40010800); break; // GPIOA
case PORT_B: return GPIOPORT_REF(0x40010C00); break; // GPIOB
case PORT_C: return GPIOPORT_REF(0x40011000); break; // GPIOC
case PORT_D: return GPIOPORT_REF(0x40011400); break; // GPIOD
case PORT_E: return GPIOPORT_REF(0x40011800); break; // GPIOE
case PORT_F: return GPIOPORT_REF(0x40011C00); break; // GPIOF
case PORT_G: 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)
Just recently I’m trying to get started with STM32F103 / Blue Pills / Maple Mini’s.
I think I really like the template way of handling GPIO, but I think that for now it is wise for me to let it be untill I’m more familiar with the STM32F103 itself.
In my search for “stuff” for STM32, I found a bunch of template based Libraries / Frameworks / Cores, and I think it might be a good Idea to share some (quite random) notes / links here:
– XPCC
http://xpcc.io/getting-started/
https://github.com/roboterclubaachen/xpcc
http://blog.salkinium.com/typesafe-regi … ss-in-c++/
http://www.eld.leidenuniv.nl/~moene/Home/books/
– Kvasir
https://github.com/kvasir-io/Kvasir
– bmptk
Bare Metal Programming Tool Kit
https://www.voti.nl/bmptk/docs/index.html
https://www.youtube.com/watch?v=mNPfsUZb3vs
– Templates
Mini Davey Jones also does it with templates:
https://www.youtube.com/watch?v=A_saS93Clgk
– JeeLabs / Jeenode & templates:
viewtopic.php?f=42&t=4460&p=51937#p51937
https://jeelabs.org/projects/jeeh/
https://jeelabs.org/2018/getting-started-bp/

