STM32 Simple Blink CMSIS bit manip question? - Page 1
Forum section: electronics / general

author: blewisjr
author_rank: Frequent Contributor
timestamp: March 25, 2014, 09:16:00 pm
content: Hello Everyone,I am not quite sure if I did this correctly. My first blink was with the STM32 lib and I decided to convert it over to CMSIS so I can build my own library slowly as I learn the chip.Essentially I setup all the registers and then used a for loop as a delay. The code does work but I am not sure if it is working because of register defaults or if I got the bit twiddling correct. I never had to really manipulate 2 bits at the same time this way before. So I may be going about it wrong. I know MODER is suppose to be 2 bits for output it should be 01 but when I tried the double twiddle it ended up flipping the whole register 1 and had the appropriate bit set to 0 but it should have been whole register 0 with appropriate bit to 1. To fix that I relied on the default and just flipped the one bit to 1. Here is the code maybe someone can better explain how to manipulate 2 bits at once especially the case when you need a 01 or a 10 as 00 and 11 are easy? I know elementary C and I am drawing a blank on the manipulation of 2 bits.Code:#include "stm32f30x.h"void delay(int length);int main(void){/* Enable GPIOE Clock */RCC->AHBENR |= (1<<21);/* Setup PE11 as output */GPIOE->MODER |= (1<<22);GPIOE->OTYPER &= ~(1<<11);GPIOE->OSPEEDR |= (1<<23)|(1<<22);GPIOE->PUPDR &= ~(1<<23)|~(1<<22);while (1){GPIOE->BSRR = (1<<11);delay(5000000);GPIOE->BRR = (1<<11);delay(5000000);}}void delay(int length){int i = 0;for (i = 0; i < length; i++);}

author: dannyf
author_rank: Super Contributor
timestamp: March 25, 2014, 11:12:09 pm
content: The general structure is valid - I didn't check the bits for you.I would, however, rearrange the code a little bit differently in order to maximize their reusability:Code:#define LED_PORT GPIOE //leds on GPIOE#define LED_DDR GPIOE#define LED (1<<11) //led on porte.11#define PIN_SET(port, pins) port->ODR |= (pins) //set pins on port#define PIN_CLR(port, pins) port->ODR &=~(pins) //clear pins on port#define PIN_GET(port, pins) ((port->IDR) & (pins)) //read pins on port#define FPIN_SET(port, pins) port->BSRR = (pins) //fast_st pins on port#define FPIN_CLR(port, pins port->BRR = (pins) //fast_clear pins on port#define FPIN_GET(port, pins) PIN_GET(port, pins) //no fast get routines//initialize the mcuvoid mcu_init(void) {SystemCoreClockUpdate(); //update systemcore clock in case you use non-default settingsRCC->AHBENR |= RCC_AHBENR_GPIOEEN; //enable gpioe clock//set other rcc gates here too}in mainmcu_init(); //reset the mcuPIN_OUT(LED_DDR, LED); //led as outputwhile (1) {PIN_SET(LED_PORT, LED); //set leddelay(5000000);PIN_CLR(LED_PORT, LED); //clear leddelay(5000000);}}You can put mcu_init() and the associated macros in a .c/.h file to be called on in the future. With this approach you don't have to worry about remembering all those details next time.Code:GPIOE->OSPEEDR |= (1<<23)|(1<<22);Alternatively:Code:GPIOE->OSPEEDR |= (0x03<<22);

author: dannyf
author_rank: Super Contributor
timestamp: March 25, 2014, 11:15:01 pm
content: If you were to use the oem library, setting up the pins as output would be as simple as this:Code:void PIN_OUT(GPIO_TypeDef* port, uint16_t pins) {GPIO_InitTypeDef GPIO_InitStruct;/* Configure PC8 and PC9 in output pushpull mode */GPIO_InitStruct.GPIO_Pin = pins;//GPIO_Pin_0..15 + GPIO_Pin_AllGPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;//GPIO_Mode_Out_PP -> push-pull; GPIO_Mode_Out_OD -> open drainGPIO_InitStruct.GPIO_OType = GPIO_OType_PP;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;//GPIO_Speed_50Mhz -> 50Mhz; GPIO_Speed_2Mhz -> 2Mhz; GPIO_Speed_10Mhz -> 10MhzGPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(port, &GPIO_InitStruct);}

author: dannyf
author_rank: Super Contributor
timestamp: March 25, 2014, 11:18:20 pm
content: Code:#define LED (1<<11) //led on porte.11This approach allows you to set/clear multiple pins, by defining LED differently.Code:#define LED0 (1<<11) //led on porte.11#define LED1 (1<<2) //led on porte.2#define LED2 (1<<5) //led on porte.5#define LEDs (LED0 | LED1 | LED2) //leds = led0+led1+led2PIN_SET(LED_PORT, LEDs); //set led0/1/2PIN_CLR(LED_PORT, LED2); //clear led2

author: dannyf
author_rank: Super Contributor
timestamp: March 25, 2014, 11:20:50 pm
content: The FPIN_xxx() vs. PIN_xxx() come from my LPC days. They allow faster port actions.Those macros or routines can be easily ported to ARM chips that allow bit banding (LM4F120 from TI or luminary chips for example). Pin actions on those chips are single cycle. Very handy.

author: blewisjr
author_rank: Frequent Contributor
timestamp: March 26, 2014, 12:00:16 am
content: Wow thanks for all the replies danny. Yes I do know it is simple to do with the OEM library. Thanks for some of the bit set tricks as well. Macros would be useful but I just wanted to make sure I could get the thing working with CMSIS before I started adding in macros. Like I said my big issue is figuring out the bit routine to get a 10 or 01 on the double registers. I will have to tinker around with the bit math a bit to figure it out might be as simple as using a hex value instead of 1 on the shift.

author: westfw
author_rank: Super Contributor
timestamp: March 26, 2014, 06:37:55 am
content: QuoteIs that sarcasm? It doesn't look that much simpler than modifying the raw register bits to me. Slightly more abstracted, but not a lot, and it seems to me that you pay substantially for that abstraction (setting up a bunch of memory as an abstracted GPIO_InitStruct, and then the library code presumably un-abstracting it back...) And for all it's abstraction, that GPIO_InitStruct is still ST specific, right?Setting a multi-bit field to a particular value requires that you make sure that the one and zero bits get set.Something like:Code:GPIOE->MODER = (GPIO_MODER & ~(3<<22)) // old contents with our bitfield zero'ed| (0b01<<22); // or in our desired value.(although in this case, you're only setting one bit anyway...)

author: poorchava
author_rank: Super Contributor
timestamp: March 26, 2014, 10:59:16 am
content: Cortex M3 has a feature called bit-banding, this is an ideal solution which allows for atomic operations on single bits. It means that every register has its own address, but also every bit in that register is mapped to a location in address space. If that location is written then you get a single operation atomic access to that bit. No |=1<<x needed. You can find handy macros for that one the net (you want this to be a macro, so it's evaluated at compile time, not at runtime.

author: dannyf
author_rank: Super Contributor
timestamp: March 26, 2014, 11:06:11 am
content: QuoteYou should thinking about writing code as investment: write it once and use it as many times as possible.For that to work, you should avoid hardcoding as much as you can. Instead of using hex values, you has the defined names in the header file as much as you can, and shield your code from the details as much as you can.For example, use the PIN_xxx() macros for pin / port operations. When you move the code to a different chip, all you need is to include the appropriate PIN_xxx() macros for the new target and you are in business.

author: blewisjr
author_rank: Frequent Contributor
timestamp: March 26, 2014, 11:41:12 am
content: Quote from: westfw on March 26, 2014, 06:37:55 amIs that sarcasm? It doesn't look that much simpler than modifying the raw register bits to me. Slightly more abstracted, but not a lot, and it seems to me that you pay substantially for that abstraction (setting up a bunch of memory as an abstracted GPIO_InitStruct, and then the library code presumably un-abstracting it back...) And for all it's abstraction, that GPIO_InitStruct is still ST specific, right?Setting a multi-bit field to a particular value requires that you make sure that the one and zero bits get set.Something like:Code:GPIOE->MODER = (GPIO_MODER & ~(3<<22)) // old contents with our bitfield zero'ed| (0b01<<22); // or in our desired value.(although in this case, you're only setting one bit anyway...)Ah yes thanks a bunch westfw that joggled my memory makes perfect sense now I will try it out and see if the registers get set the way I expect.

author: dannyf
author_rank: Super Contributor
timestamp: March 26, 2014, 12:32:40 pm
content: Take a look at the _gpio.h and _gpio.c files and you can see how ST implemented that. For example, the '3' in '3<<22' are defined in the device header file and should be used instead of a hardcoded '3'.

author: westfw
author_rank: Super Contributor
timestamp: March 26, 2014, 05:39:19 pm
content: Are there "standard" C macros for dealing with bitfields?Something likeBITFIELD(MODER11, 22, 23); Or BITFIELD(MODER11, 0x00C00000);SET_BITFIELD(MODER11, 3);i = GET_BITFIELD(MODER11);The CMSIS peripheral definitions don't seem to break things down any smaller than words or bytes (no bitfields), making them relatively useless :-((Is there a description somewhere on how these are supposed to work? I keep getting redirected to the CMSIS functions that work on the core, where much more of the info is "standardized.")

author: dannyf
author_rank: Super Contributor
timestamp: March 27, 2014, 12:32:07 pm
content: QuoteThree approaches reduce the usefulness of bit banding:1) the mask - as used by NXP on their LPC chips; Very handy.2) the BSRR and BRR registers - as used by ST;3) BME - by Freescale.

author: westfw
author_rank: Super Contributor
timestamp: March 27, 2014, 04:56:53 pm
content: I don't see how bitbanding helps very much on muti-bit fields.(most of the microcontroller class chips have additional mechanisms for dealing with GPIO pins, but that's not what we're talking about here...)ARM CM3 and up do have bitfield instructions. "BFI" looks to do just the right thing, if your values are in register; I'm not sure it would be quicker or shorter than the and/or combinations if the goal is to set a constant in a memory location (ie peripheral register.)