środa, 31 sierpnia 2016

[14] STM32F4 - Discovery - Obsługa SPI na bibliotekach API&HAL

We wcześniejszym poście opisałem obsługę czujnika LIS302DL poprzez SPI. Tym razem chciałem przybliżyć uruchamianie samego interfejsu za pomocą bibliotek HAL oraz API. 

Biblioteki API


W tej części definiuje się część włączającą SPI oraz odpowiednie GPIO. Następnie należy zdefiniować funkcje za pomocą których dane będą wysyłane oraz odbierane.



Włączenie pinów z diodami wbudowanymi:

  1. void LED_GPIO_INIT(void)
  2. {
  3.     GPIO_InitTypeDef GPIOINITSTRUCT;
  4.    
  5.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
  6.     GPIOINITSTRUCT.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  7.      
  8.     GPIOINITSTRUCT.GPIO_OType = GPIO_OType_PP;
  9.     GPIOINITSTRUCT.GPIO_Mode = GPIO_Mode_OUT;
  10.     GPIOINITSTRUCT.GPIO_PuPd = GPIO_PuPd_NOPULL;
  11.     GPIOINITSTRUCT.GPIO_Speed = GPIO_Speed_100MHz;
  12.     GPIO_Init(GPIOD, &GPIOINITSTRUCT);
  13. }

Włączenie SPI2 wraz z GPIO:

  1. void SPI_InitFLASH()
  2. {
  3.     GPIO_InitTypeDef GPIOINITSTRUCT;
  4.     SPI_InitTypeDef SPIINITSTRUCT;
  5.    
  6.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
  7.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
  8.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
  9.      
  10.     SPI_I2S_DeInit(SPI2);
  11.      
  12.     SPIINITSTRUCT.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
  13.     SPIINITSTRUCT.SPI_Mode = SPI_Mode_Master;    
  14.     SPIINITSTRUCT.SPI_DataSize = SPI_DataSize_8b;
  15.     SPIINITSTRUCT.SPI_CPOL = SPI_CPOL_Low;        
  16.     SPIINITSTRUCT.SPI_CPHA = SPI_CPHA_1Edge;      
  17.     SPIINITSTRUCT.SPI_NSS = SPI_NSS_Soft;                  
  18.     SPIINITSTRUCT.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
  19.     SPIINITSTRUCT.SPI_FirstBit = SPI_FirstBit_MSB;
  20.     SPI_Init( SPI2, &SPIINITSTRUCT);
  21.     SPI_Cmd( SPI2, ENABLE);
  22.    
  23.     //SCK MISO MOSI
  24.     GPIOINITSTRUCT.GPIO_Pin = ( GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 );
  25.     GPIOINITSTRUCT.GPIO_Mode = GPIO_Mode_AF;
  26.     GPIOINITSTRUCT.GPIO_OType = GPIO_OType_PP;
  27.     GPIOINITSTRUCT.GPIO_Speed = GPIO_Speed_50MHz;
  28.     GPIOINITSTRUCT.GPIO_PuPd = GPIO_PuPd_UP;
  29.     GPIO_Init(GPIOB, &GPIOINITSTRUCT);
  30.    
  31.     //Piny SPI2 jako alternatywne
  32.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
  33.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2);
  34.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
  35.     //CS
  36.     GPIOINITSTRUCT.GPIO_Pin = GPIO_Pin_1;
  37.      
  38.     GPIOINITSTRUCT.GPIO_OType = GPIO_OType_PP;
  39.     GPIOINITSTRUCT.GPIO_Mode = GPIO_Mode_OUT;
  40.     GPIOINITSTRUCT.GPIO_PuPd = GPIO_PuPd_NOPULL;
  41.     GPIOINITSTRUCT.GPIO_Speed = GPIO_Speed_100MHz;
  42.     GPIO_Init(GPIOD, &GPIOINITSTRUCT);
  43. }

Funkcja wysyłająca oraz odbierająca dane, taka sama będzie w dla bibliotek HAL-a:

  1. uint8_t SPI2_SEND_DATA(uint8_t data)
  2. {
  3.     if (!((SPI2)->CR1 & SPI_CR1_SPE)) {return 0;}
  4.    
  5.     while ((SPI2->SR & SPI_FLAG_TXE) == 0 || (SPI2->SR & SPI_FLAG_BSY));
  6.    
  7.     SPI2->DR = data;
  8.    
  9.     while ((SPI2->SR & SPI_FLAG_RXNE) == 0 || (SPI2->SR & SPI_FLAG_BSY));
  10.    
  11.     return SPI2->DR;
  12. }

Główna pętla programu:

  1. uint8_t wyslij[15];
  2. uint8_t odbierz[15];
  3. int main(void)
  4. {
  5.     uint8_t i;
  6.    
  7.     SystemInit();
  8.    
  9.     LED_GPIO_INIT();
  10.    
  11.     SPI_InitFLASH();
  12.    
  13.     for(= 0; i<15;i++)
  14.     {
  15.         wyslij[i] = 0xAB;
  16.         odbierz[i] = SPI2_SEND_DATA(wyslij[i]);
  17.     }
  18.    
  19.   /* Memory compare */
  20.   if (memcmp(wyslij, odbierz, 15) == 0)
  21.     {
  22.         GPIO_SetBits(GPIOD, GPIO_Pin_12);
  23.         GPIO_SetBits(GPIOD, GPIO_Pin_13);
  24.   }
  25.     else {  GPIO_SetBits(GPIOD, GPIO_Pin_14);   }
  26.    
  27.   while(1) { }
  28. }

Do porównania wykorzystałem funkcje memcmp, która jest zdefiniowana w bibliotece string.h. Może ona zwracać trzy rodzaje wartości, liczbę mniejszą od zera gdy wyslij jest mniejsze od odbierz. Zwróci 0 gdy wartości będą sobie równe albo wartość większą od zera gdy wyślij będzie większe od odbierz. 

Biblioteki HAL


Podobnie jak poprzednio należy zdefiniować włączenie odpowiedniego SPI wraz z funkcjami wysyłającymi i odbierającymi dane poprzez SPI. Tylko tym razem zostanie to wykonane z wykorzystaniem bibliotek HAL. 

W pierwszej kolejności zostanie włączony GPIO odpowiedzialny za sterowanie diodami wbudowanymi, która dla bibliotek HAL-a wygląda następująco:

  1. void LED_GPIOD_WLACZ(void)
  2. {
  3.      GPIO_InitTypeDef GPIO_InitDef;
  4.      
  5.      __GPIOD_CLK_ENABLE();
  6.      
  7.      GPIO_InitDef.Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  8.            
  9.      GPIO_InitDef.Mode = GPIO_MODE_OUTPUT_PP;
  10.      GPIO_InitDef.Pull = GPIO_NOPULL;
  11.      GPIO_InitDef.Speed = GPIO_SPEED_LOW;
  12.      HAL_GPIO_Init(GPIOD, &GPIO_InitDef);
  13. }

Następnie włączenie SPI2 oraz odpowiednich portów GPIO:

  1. void SPI2_WLACZENIE()
  2. {
  3.     GPIO_InitTypeDef GPIO_InitStruct;
  4.     SPI_HandleTypeDef SPIHandle;
  5.    
  6.     SPIHandle.Instance = SPI2;
  7.    
  8.     //Wlaczenie zegara dla GPIOA & GPIOB
  9.     __GPIOA_CLK_ENABLE();
  10.     __GPIOB_CLK_ENABLE();
  11.    
  12.     //Wlaczenie zegara dla SPI2
  13.     __HAL_RCC_SPI2_CLK_ENABLE();
  14.    
  15.     //SPI2 PIN PACK 2
  16.     //PB13     ------> SPI1_SCK
  17.     //PB14     ------> SPI1_MISO
  18.     //PB15     ------> SPI1_MOSI
  19.    
  20.     GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  21.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  22.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  23.     GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  24.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
  25.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  26.    
  27.     //Konfiguracja PB2
  28.     GPIO_InitStruct.Pin = GPIO_PIN_1;
  29.     GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  30.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  31.     GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
  32.     HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  33.     SPIHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;    
  34.     SPIHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;                                
  35.     SPIHandle.Init.Mode = SPI_MODE_MASTER;                                     
  36.     SPIHandle.Init.Direction = SPI_DIRECTION_2LINES;                   
  37.     SPIHandle.Init.DataSize = SPI_DATASIZE_8BIT;
  38.    
  39.     SPIHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  40.     SPIHandle.Init.CRCPolynomial = 7;
  41.     SPIHandle.Init.TIMode = SPI_TIMODE_DISABLE;
  42.     SPIHandle.Init.CLKPolarity = SPI_POLARITY_LOW;                 
  43.     SPIHandle.Init.CLKPhase = SPI_PHASE_1EDGE;                             
  44.     SPIHandle.Init.NSS = SPI_NSS_SOFT;                                                     
  45.    
  46.     //Wylaczenie SPI
  47.     __HAL_SPI_DISABLE(&SPIHandle);
  48.    
  49.     //Init SPI
  50.     HAL_SPI_Init(&SPIHandle);
  51.     //Wlaczenie SPI
  52.     __HAL_SPI_ENABLE(&SPIHandle);
  53. }

Teraz czas na funkcję odbierającą dane przez SPI:

  1. uint8_t SPI2_SEND_DATA(uint8_t data)
  2. {
  3.     if (!((SPI2)->CR1 & SPI_CR1_SPE)) {return 0;}
  4.    
  5.     while ((SPI2->SR & SPI_FLAG_TXE) == 0 || (SPI2->SR & SPI_FLAG_BSY));
  6.    
  7.     SPI2->DR = data;
  8.    
  9.     while ((SPI2->SR & SPI_FLAG_RXNE) == 0 || (SPI2->SR & SPI_FLAG_BSY));
  10.    
  11.     return SPI2->DR;
  12. }

Właściwie na tym można zakończyć główna funkcja programu wygląda następująco:

  1. uint8_t wyslij[15];
  2. uint8_t odbierz[15];
  3. int main(void)
  4. {
  5.     uint8_t i, pomocnicza;
  6.     uint8_t pomocnicza 0;
  7.     //Ustawienie zegarow
  8.     RCC_ON();
  9.    
  10.     //Wlaczenie hala
  11.     HAL_Init();
  12.    
  13.     //Wlaczenie led
  14.     LED_GPIOD_WLACZ();
  15.    
  16.     //Wlaczenie SPI
  17.     SPI2_WLACZENIE();
  18.    
  19.     //Wypelnienie bufora danymi oraz przeslanie ich
  20.     for(= 0; i<15;i++)
  21.     {
  22.         wyslij[i] = 0xAB;
  23.         odbierz[i] = SPI2_SEND_DATA(wyslij[i]);
  24.     }
  25.    
  26.     //Sprawdzenie poprawnosci danych
  27.    
  28.     i=0;
  29.     while(i<15)
  30.     {
  31.         if(wyslij[i]==odbierz[i])
  32.         {
  33.             pomocnicza++;
  34.         }
  35.         else{ }
  36.         i++;
  37.     }
  38.    
  39.     if(pomocnicza==15)
  40.     {
  41.         HAL_GPIO_WritePin(GPIOD, GPIO_Pin_12, GPIO_PIN_SET);
  42.         HAL_GPIO_WritePin(GPIOD, GPIO_Pin_13, GPIO_PIN_SET);
  43.     }
  44.     else
  45.     {
  46.         HAL_GPIO_WritePin(GPIOD, GPIO_Pin_14, GPIO_PIN_SET);
  47.     }
  48.     while (1) {}
  49. }

W programie dla API wykorzystałem porównywanie danych funkcją memcmp, dla hala natomiast porównuje wartości między sobą. Jeśli wszystkie będą takie same to zostaną zapalone odpowiednie diody.