Skip to main content

Stm32 Peripheral Drivers from Scartch : GPIO Programming Part 1

Programming the GPIO is nothing out of this world. A series of steps must be taken to get those pins to do our bidding.

1. Initialize the clock for the Port you are going to use
2.Configure the Pin
3.Use the pin.

Pretty easy is'nt it? The board which I showed in an earlier post has an LED wired to Pin 13 on PORT C.  SO lets make this LED blink using the steps above.

1.Initializing the clock

To initialize the clock we must access the RCC Peripheral which controls the enabling and disabling of clocks for all the peripherals. The Stm32 has most of its peripherals turned off after a reset to consume less power, so it is your job to turn on what you need.

Above you can see the exact register (Page 145)  we need to turn on the clock gating for various GPIO Ports and highlighted is the one we need labeled InputOutputPortC (IOPC-ENable)

The code looks like this:

RCC->APB2ENR |= 1<<4;

That it as far as enabling the clock. Now lets look at step 2 which is configuring the pin or pins you want to use.

Lets take a look at the register required to do so.

Okay we are now in the GPIO peripheral part of the data sheet (Page 171) and here is the eact register we need to configure Pin 13 , since 13 is not between 0 and 7  we need to configure it in the HIGH register which handles pins 8 through 15. Also keep in mind to help you figure out what to type Look at the top where GPIOx_CRH is highlighted. GPIOx is the nameof the peripheral , the x gets replaced with the port you are configuring , in our case Port C , after that is the specific register within that peripheral in this case CRH so that is exactly what our code will look like.

GPIOC->CRH |=  blah bla blah

Okay now that we know where we are getting these names from lets figure out how we are going to configure this pin.
Under the MODE section i have highlighted how I want to configure the mode, which is output at 50Mhz, and in the CNF section i have highlighted that i want it as a General Purpose Push Pull.
I have also highlighted the corresponding bits in the register the configure Pin13.

the code looks like this:

GPIOC->CRH  |= (1<<20) |(1<<21) ;  // shift a 1 into bit 20 and 21

GPIOC-> CRH &= ~(  (1<<22) | (1<<23)  );  //AND NOT a 1 into 22 and 23, 
//this means put a 1 in   //those bits, and then change those ones to zeros

And that is all there is to configuring a pin as input or output. Now to set it HIGH or LOW we turn to the BSRR register seen below (Page 172)

We will first SET/HIGH the pin and then RESET/LOW the pin. To set the pin we must write a 1 into bit 13 labeled BS13 (bit set 13) next we will enter a silly delay by way of a for loop and then RESET the pin by writing a 1 to the BR13 bit which is Bit 29.  Remember that the RESET bit is Exactly 16 bits away from the SET bit so we dont have to remember the exact number of the RESET bit , As long as we know the SET bit which is the same number as our pin we are good to go.

The code looks like this:

GPIOC->BSRR = 1<<13; // set the Pin HIGH
for(int i= 0; i<=5000000; i++) ; //dummy delay not how it should be done
GPIOC->BSRR = 1<<(13+16) ; //to get to the reset bit we just add 16 to our pin number
for(int i= 0; i<=5000000; i++) ; //dummy delay not how it should be done

Note that i set my uVision to C99 mode which allows me to initialize the variable "i" inside my for loop statement, other versions of C do not allow this.
Andthere you have it. How to program the GPIO of most STM32 chips. Now lets take all of this and write a driver for it which will be a more readable and sensible way of doing this.

This is the video version of the what i explained above


Popular posts from this blog

Interface a Rotary Encoder the right way.

So whilst reading my favorite odyssey found here I stumbled onto something I never spotted before. It turns out that the general purpose timers support hardware interfacing with an incremental encoder. This means no more interrupts and no need to increment whatever variable you had. Now all you have to do is get the value from the Count register in the timer. Did I also mention it takes care of figuring out which direction you are turning it? Amazing! Lets talk about it.

Encoder Interface Mode Verbatim from the reference manual of the STM32F103 page 392 or Section 15.3.12  "Encoder interface mode acts simply as an external clock with direction selection. This 
means that the counter just counts continuously between 0 and the auto-reload value in the 
TIMx_ARR register (0 to ARR or ARR down to 0 depending on the direction). So the user 
must configure TIMx_ARR before starting. In the same way, the capture, compare, 
prescaler, trigger output features continue to work as normal."

STM32L0/F0/F3 I2C : Part 3 RX TX via DMA

Most modern microcontroller come with a peripheral called DMA which allows for an even more hands-off approach. The Direct Memory Access controller will get a tutorial of it's own in the future. However, it is so simple to use that I can easily explain the required bits in this tutorial without feeling like I will overwhelm the reader. In this iteration of the I2C series I will cover how to TX and RX date on the peripheral in conjunction with the DMA controller. 

First let me briefly explain in a high level what the DMA controller does. It allows you to transfer data in 3 ways: Peripheral to memoryMemory to peripheralMemory to memory. What does this mean? For example, when you receive data on I2C in your RXDR register, you can have the DMA controller move that data received into an array. At the end of the day your variables are just memory locations, so if you have declared an array it has an address associated with it, this is address is what the DMA considers  "Memory"…

STM32 I2C Does it suck?

Recently I have been annoyed with  bugs in the I2C implementation in ST's F1/L1/F40/F2 series, and  any microcontroller made by ST before 2012. There appears to be, what I can only describe as, a race condition when attempting to receive data in I2C interrupt(low priority)  or polling mode.

Just to be clear a race condition is defined as , verbatim from wiki: "the behavior of an electronics, software, or other system where the system's substantive behavior is dependent on the sequence or timing of other uncontrollable events. It becomes a bug when one or more of the possible behaviors is undesirable."
I was humming along trying to make my YouTube tutorials when I arrived to the I2C protocol implementation. I have used I2C with the F1 series plenty of times, however I have never had the need to receive data from a slave. However for the sake of completeness I decided to implement an RXing routine in I2C for my tutorial. Only to find statements like this in the referen…