Skip to main content

Stm32 Peripheral Drivers from Scartch : GPIO Programming Part 3

In this part of the series we begin to write the functions that will make use of all the defines in our header file.

The first function we will need to implement is one to configure the pin and it looks something like this:

static void config_pin(GPIO_TypeDef *gpio, uint32_t pinNumber , uint32_t mode_type)

The first parameter in the function is going to be one of GPIO_TypeDef. I am not pulling this typedef out of thin air, it is defined the header file for the microcontroller stm32f10x.h. The parameter The next parameter is of an integer type which tells the function the pin number we will be configuring. The last parameter tells the function what is the mode type, for example if its an output mode do we want it to be a general purpose output or an open drain output  or an alternate function output.  An acceptable function call will use the definitions we made in our header file and it will look something like this:

config_pin(PORTA, 13 , OUTPUT_GEN_PURPOSE);

Now lets think about what we have to do to configure a pin taking into account the information we are given through the arguments of the function call. From a previous post we know certain things
about configuring a pin. Those things are that we have to either write to the high register or the low register depending on the pin number we are using. Furthermore we know we have different modes we can choose  from and we also know the values for some of those modes are redundant for both input or output mode.

All that being said lets look at the inner workings of the function now:
static void config_pin (GPIO_TypeDef *port , uint32_t pinNumber , uint32_t mode_type)
{

  if(pinNumber >= 8) // CONTROL HIGH REGISTER
  {
 switch(mode_type)
 {
  
 //---------------OUTPUT & INPUT MODES---------------------------
 case OUTPUT_GEN_PURPOSE | INPUT_ANALOG:
  port->CRH &= ~(  (1<<CNF_POS_BIT1 ) | (1<<CNF_POS_BIT2) );
 break;
  
 case OUTPUT_OD | INPUT_FLOATING:
  port->CRH &= ~( 1<<CNF_POS_BIT2 );
  port->CRH |= (1<<CNF_POS_BIT1);
 break;
  
 case OUTPUT_ALT_FUNCTION | INPUT_PU_PD:
  port->CRH |= OUTPUT_ALT_FUNCTION<<(CNF_POS_BIT1);    
 break;
  
 case OUTPUT_ALT_FUNCTION_OD:
  port->CRH |= OUTPUT_ALT_FUNCTION_OD<<(CNF_POS_BIT1);
 break;  
  
  
 }//end switch
 
 
  }
  else  //CONTROL LOW REGISTER
  {
 switch(mode_type)
 {
  
 //---------------OUTPUT & INPUT MODES---------------------------
 case OUTPUT_GEN_PURPOSE | INPUT_ANALOG:
  port->CRL &= ~(  (1<<CNF_POS_BIT1 ) | (1<<CNF_POS_BIT2) );
 break;
  
 case OUTPUT_OD | INPUT_FLOATING:
  port->CRL &= ~( 1<<CNF_POS_BIT2 );
  port->CRL |= (1<<CNF_POS_BIT1);
 break;
  
 case OUTPUT_ALT_FUNCTION | INPUT_PU_PD:
  port->CRL |= OUTPUT_ALT_FUNCTION<<(CNF_POS_BIT1);    
 break;
  
 case OUTPUT_ALT_FUNCTION_OD:
  port->CRL |= OUTPUT_ALT_FUNCTION_OD<<(CNF_POS_BIT1);
 break;  
  
  
 }//end switch
 
 
  }//end else
//end function


Comments

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."
W…

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…