wtorek, 26 stycznia 2016

[1] STM32F4 - Discovery - Włączenie pinów I/O - biblioteka

W tym przedstawię przygotowaną bibliotekę do STM32F4 zawierającą deklarację pinów GPIO. Dodatkowo dołączę do niej odpowiednie funkcje, które będą odpowiadać za prawidłowe zdeklarowanie podanych wartości. 

Deklaracje nazw zmiennych


Programowanie należy rozpocząć od poprawnego zdefiniowania podanych wartości rejestru GPIO, która będzie odpowiadać za wybranie danego wyprowadzenia. 

#define GPIO_Pin_0                 ((uint16_t)0x0001)  // Wybranie pinu 0
#define GPIO_Pin_1                 ((uint16_t)0x0002)  // Wybranie pinu 1
#define GPIO_Pin_2                 ((uint16_t)0x0004)  // Wybranie pinu 2
#define GPIO_Pin_3                 ((uint16_t)0x0008)  // Wybranie pinu 3
#define GPIO_Pin_4                 ((uint16_t)0x0010)  // Wybranie pinu 4
#define GPIO_Pin_5                 ((uint16_t)0x0020)  // Wybranie pinu 5
#define GPIO_Pin_6                 ((uint16_t)0x0040)  // Wybranie pinu 6
#define GPIO_Pin_7                 ((uint16_t)0x0080)  // Wybranie pinu 7
#define GPIO_Pin_8                 ((uint16_t)0x0100)  // Wybranie pinu 8
#define GPIO_Pin_9                 ((uint16_t)0x0200)  // Wybranie pinu 9
#define GPIO_Pin_10                ((uint16_t)0x0400)  // Wybranie pinu 10
#define GPIO_Pin_11                ((uint16_t)0x0800)  // Wybranie pinu 11
#define GPIO_Pin_12                ((uint16_t)0x1000)  // Wybranie pinu 12
#define GPIO_Pin_13                ((uint16_t)0x2000)  // Wybranie pinu 13
#define GPIO_Pin_14                ((uint16_t)0x4000)  // Wybranie pinu 14
#define GPIO_Pin_15                ((uint16_t)0x8000)  // Wybranie pinu 15
#define GPIO_Pin_All               ((uint16_t)0xFFFF)  // Wybranie wszystkich pinów

Następnie należy zdeklarować nazwy pinów źródłowych. Nazwy tego typu są wykorzystywane np. podczas włączenia funkcji alternatywnych (np. SPI) na wybranych portach.

#define GPIO_PinSource0            ((uint8_t)0x00) // Pin zrodlowy 0
#define GPIO_PinSource1            ((uint8_t)0x01) // Pin zrodlowy 1
#define GPIO_PinSource2            ((uint8_t)0x02) // Pin zrodlowy 2
#define GPIO_PinSource3            ((uint8_t)0x03) // Pin zrodlowy 3
#define GPIO_PinSource4            ((uint8_t)0x04) // Pin zrodlowy 4
#define GPIO_PinSource5            ((uint8_t)0x05) // Pin zrodlowy 5
#define GPIO_PinSource6            ((uint8_t)0x06) // Pin zrodlowy 6
#define GPIO_PinSource7            ((uint8_t)0x07) // Pin zrodlowy 7
#define GPIO_PinSource8            ((uint8_t)0x08) // Pin zrodlowy 8
#define GPIO_PinSource9            ((uint8_t)0x09) // Pin zrodlowy 9
#define GPIO_PinSource10           ((uint8_t)0x0A) // Pin zrodlowy 10
#define GPIO_PinSource11           ((uint8_t)0x0B) // Pin zrodlowy 11
#define GPIO_PinSource12           ((uint8_t)0x0C) // Pin zrodlowy 12
#define GPIO_PinSource13           ((uint8_t)0x0D) // Pin zrodlowy 13
#define GPIO_PinSource14           ((uint8_t)0x0E) // Pin zrodlowy 14
#define GPIO_PinSource15           ((uint8_t)0x0F) // Pin zrodlowy 15

Pierwsza funkcja pozwala na włączenie zegarów dla wybranego portu. Jeśli zostanie wybrany inny port niż zdeklarowane, wtedy taktowanie zostanie włączone na wszystkich portach.

void Init_GPIO_Clock(GPIO_TypeDef* GPIOx)
{
 int selectedport = 0;
 
 if (GPIOx == GPIOA) {
  selectedport = 1;
 } else if (GPIOx == GPIOB) {
  selectedport = 2;
 } else if (GPIOx == GPIOC) {
  selectedport = 3;
 } else if (GPIOx == GPIOD) {
  selectedport = 4;
 } else if (GPIOx == GPIOE) {
  selectedport = 5;
 }
 
 switch(selectedport)
 {
  case 1:
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOA;
   break;
  case 2:
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOB;
   break;
  case 3:
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOC;
   break;
  case 4:
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOD;
   break;
  case 5:
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOE;
   break;
  default:
  {
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOA;
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOB;
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOC;
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOD;
   RCC->AHB1ENR = RCC_AHB1Periph_GPIOE;
   break;
  }
 }
}

Podobnie wygląda sytuacja w przypadku funkcji wyłączającej zdeklarowane nazwy portów.

void Init_GPIO_Function(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pinx, 
                        uint8_t GPIO_Modex, uint8_t GPIO_OTypex, 
                        uint8_t GPIO_PuPdx, uint8_t GPIO_Speedx) 
{
 //Wlaczenie zegara dla wybranego pinu
 Init_GPIO_Clock(GPIOx);
 
 GPIO_InitStr.GPIO_Pin = GPIO_Pinx;
 //GPIOx -> MODER |= GPIO_Mode
 GPIO_InitStr.GPIO_Mode = (GPIOMode_TypeDef)GPIO_Modex;
 //GPIOx -> OTYPER |= GPIO_OType
 GPIO_InitStr.GPIO_OType = (GPIOOType_TypeDef)GPIO_OTypex;
 //GPIOx -> PUPDR |= GPIO_PuPd
 GPIO_InitStr.GPIO_PuPd = (GPIOPuPd_TypeDef)GPIO_PuPdx;
 //GPIOx -> OSPEEDR |= GPIO_Speed
 GPIO_InitStr.GPIO_Speed = (GPIOSpeed_TypeDef)GPIO_Speedx;
 
 //Inicjalizacja struktury
 GPIO_Init(GPIOx, &GPIO_InitStr);
}

Kolejna funkcja ma za zadanie włączenie funkcji alternatywnych dla wybranych pinów:

//Wlaczenie funckji alternatywnej
void Init_GPIO_Alternate(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pinx, 
                         uint8_t GPIO_OTypex, uint8_t GPIO_PuPdx, 
                         uint8_t GPIO_Speedx, uint8_t Alternatex) 
{
 //Wlaczenie zegara dla wybranego pinu
 Init_GPIO_Clock(GPIOx);
 
 //GPIOx -> MODER |= GPIO_Mode
 GPIO_InitStr.GPIO_Mode= GPIO_Mode_AF;
 //GPIOx -> OTYPER |= GPIO_OType
 GPIO_InitStr.GPIO_OType = (GPIOOType_TypeDef)GPIO_OTypex;
 GPIO_InitStr.GPIO_Pin = GPIO_Pinx;
 //GPIOx -> PUPDR |= GPIO_PuPd
 GPIO_InitStr.GPIO_PuPd = (GPIOPuPd_TypeDef)GPIO_PuPdx;
 //GPIOx -> OSPEEDR |= GPIO_Speed
 GPIO_InitStr.GPIO_Speed = (GPIOSpeed_TypeDef)GPIO_Speedx;
 
 GPIO_PinAFConfig(GPIOx, GPIO_Pinx, Alternatex);
 
 //Inicjalizacja struktury
 GPIO_Init(GPIOx, &GPIO_InitStr);
}

Następna funkcja, pozwala na włączenie odpowiednich portów, poprzez operację bezpośrednio na rejestrach.

//Wlaczenie wybranego portu poprzez rejestry
void Init_GPIO_Reg(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, 
                   uint8_t GPIO_Mode, uint8_t GPIO_OType,
                   uint8_t GPIO_PuPd, uint8_t GPIO_Speed)
{
 Init_GPIO_Clock(GPIOx);
 
 GPIOA->MODER |= GPIO_Mode;
 GPIOA->OSPEEDR |= GPIO_Speed;
 GPIOA->OTYPER |= GPIO_OType;
 GPIOA->PUPDR |= GPIO_PuPd;
}

Dodatkowe funkcje dostępne w programie, one są również dostępne z poziomu biblioteki gpio zdefiniowanej w programie.

//Ustawienie badz wyzerowanie wybranych pinów
void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
{
  if (BitVal != Bit_RESET)
  {
    GPIOx->BSRRL = GPIO_Pin;
  }
  else
  {
    GPIOx->BSRRH = GPIO_Pin ;
  }
}
 
//Przelaczenie bitów
void GPIO_ToggleBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
{
  GPIOx->ODR ^= GPIO_Pin;
}

Poniżej przedstawiam prosty przykład działania, program ma za zadanie, standardowo zamrugać wbudowanymi diodami.

#include "stm32f4xx.h"
#include "STM32F4_GPIOx.h"
 
void DelayMain(void)
{
 volatile uint32_t i;
 for (i = 0; i != 0xFFFFF; i++);
 for (i = 0; i != 0xFFFFF; i++);
}
 
int main(void)
{
 Init_GPIO_Function(GPIOD, GPIO_Pin_12 | GPIO_Pin_13 | 
                           GPIO_Pin_14 | GPIO_Pin_15, GPIO_Mode_OUT, 
                           GPIO_OType_PP, GPIO_PuPd_NOPULL, GPIO_Speed_100MHz);
 
 while (1)
  {
  Init_GPIO_PinHigh(GPIOD, GPIO_Pin_12 | GPIO_Pin_13 | 
                                  GPIO_Pin_14 | GPIO_Pin_15);
  DelayMain();
  Init_GPIO_PinLow(GPIOD, GPIO_Pin_12 | GPIO_Pin_13 | 
                                 GPIO_Pin_14 | GPIO_Pin_15);
  DelayMain();
  }
}