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:
- void LED_GPIO_INIT(void)
- {
- GPIO_InitTypeDef GPIOINITSTRUCT;
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
- GPIOINITSTRUCT.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
- GPIOINITSTRUCT.GPIO_OType = GPIO_OType_PP;
- GPIOINITSTRUCT.GPIO_Mode = GPIO_Mode_OUT;
- GPIOINITSTRUCT.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIOINITSTRUCT.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(GPIOD, &GPIOINITSTRUCT);
- }
Włączenie SPI2 wraz z GPIO:
- void SPI_InitFLASH()
- {
- GPIO_InitTypeDef GPIOINITSTRUCT;
- SPI_InitTypeDef SPIINITSTRUCT;
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
- SPI_I2S_DeInit(SPI2);
- SPIINITSTRUCT.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
- SPIINITSTRUCT.SPI_Mode = SPI_Mode_Master;
- SPIINITSTRUCT.SPI_DataSize = SPI_DataSize_8b;
- SPIINITSTRUCT.SPI_CPOL = SPI_CPOL_Low;
- SPIINITSTRUCT.SPI_CPHA = SPI_CPHA_1Edge;
- SPIINITSTRUCT.SPI_NSS = SPI_NSS_Soft;
- SPIINITSTRUCT.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2;
- SPIINITSTRUCT.SPI_FirstBit = SPI_FirstBit_MSB;
- SPI_Init( SPI2, &SPIINITSTRUCT);
- SPI_Cmd( SPI2, ENABLE);
- //SCK MISO MOSI
- GPIOINITSTRUCT.GPIO_Pin = ( GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15 );
- GPIOINITSTRUCT.GPIO_Mode = GPIO_Mode_AF;
- GPIOINITSTRUCT.GPIO_OType = GPIO_OType_PP;
- GPIOINITSTRUCT.GPIO_Speed = GPIO_Speed_50MHz;
- GPIOINITSTRUCT.GPIO_PuPd = GPIO_PuPd_UP;
- GPIO_Init(GPIOB, &GPIOINITSTRUCT);
- //Piny SPI2 jako alternatywne
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2);
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2);
- GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2);
- //CS
- GPIOINITSTRUCT.GPIO_Pin = GPIO_Pin_1;
- GPIOINITSTRUCT.GPIO_OType = GPIO_OType_PP;
- GPIOINITSTRUCT.GPIO_Mode = GPIO_Mode_OUT;
- GPIOINITSTRUCT.GPIO_PuPd = GPIO_PuPd_NOPULL;
- GPIOINITSTRUCT.GPIO_Speed = GPIO_Speed_100MHz;
- GPIO_Init(GPIOD, &GPIOINITSTRUCT);
- }
Funkcja wysyłająca oraz odbierająca dane, taka sama będzie w dla bibliotek HAL-a:
- uint8_t SPI2_SEND_DATA(uint8_t data)
- {
- if (!((SPI2)->CR1 & SPI_CR1_SPE)) {return 0;}
- while ((SPI2->SR & SPI_FLAG_TXE) == 0 || (SPI2->SR & SPI_FLAG_BSY));
- SPI2->DR = data;
- while ((SPI2->SR & SPI_FLAG_RXNE) == 0 || (SPI2->SR & SPI_FLAG_BSY));
- return SPI2->DR;
- }
Główna pętla programu:
- uint8_t wyslij[15];
- uint8_t odbierz[15];
- int main(void)
- {
- uint8_t i;
- SystemInit();
- LED_GPIO_INIT();
- SPI_InitFLASH();
- for(i = 0; i<15;i++)
- {
- wyslij[i] = 0xAB;
- odbierz[i] = SPI2_SEND_DATA(wyslij[i]);
- }
- /* Memory compare */
- if (memcmp(wyslij, odbierz, 15) == 0)
- {
- GPIO_SetBits(GPIOD, GPIO_Pin_12);
- GPIO_SetBits(GPIOD, GPIO_Pin_13);
- }
- else { GPIO_SetBits(GPIOD, GPIO_Pin_14); }
- while(1) { }
- }
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:
- void LED_GPIOD_WLACZ(void)
- {
- GPIO_InitTypeDef GPIO_InitDef;
- __GPIOD_CLK_ENABLE();
- GPIO_InitDef.Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
- GPIO_InitDef.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitDef.Pull = GPIO_NOPULL;
- GPIO_InitDef.Speed = GPIO_SPEED_LOW;
- HAL_GPIO_Init(GPIOD, &GPIO_InitDef);
- }
Następnie włączenie SPI2 oraz odpowiednich portów GPIO:
- void SPI2_WLACZENIE()
- {
- GPIO_InitTypeDef GPIO_InitStruct;
- SPI_HandleTypeDef SPIHandle;
- SPIHandle.Instance = SPI2;
- //Wlaczenie zegara dla GPIOA & GPIOB
- __GPIOA_CLK_ENABLE();
- __GPIOB_CLK_ENABLE();
- //Wlaczenie zegara dla SPI2
- __HAL_RCC_SPI2_CLK_ENABLE();
- //SPI2 PIN PACK 2
- //PB13 ------> SPI1_SCK
- //PB14 ------> SPI1_MISO
- //PB15 ------> SPI1_MOSI
- GPIO_InitStruct.Pin = GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
- GPIO_InitStruct.Alternate = GPIO_AF5_SPI2;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- //Konfiguracja PB2
- GPIO_InitStruct.Pin = GPIO_PIN_1;
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- GPIO_InitStruct.Speed = GPIO_SPEED_LOW;
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
- SPIHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
- SPIHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
- SPIHandle.Init.Mode = SPI_MODE_MASTER;
- SPIHandle.Init.Direction = SPI_DIRECTION_2LINES;
- SPIHandle.Init.DataSize = SPI_DATASIZE_8BIT;
- SPIHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
- SPIHandle.Init.CRCPolynomial = 7;
- SPIHandle.Init.TIMode = SPI_TIMODE_DISABLE;
- SPIHandle.Init.CLKPolarity = SPI_POLARITY_LOW;
- SPIHandle.Init.CLKPhase = SPI_PHASE_1EDGE;
- SPIHandle.Init.NSS = SPI_NSS_SOFT;
- //Wylaczenie SPI
- __HAL_SPI_DISABLE(&SPIHandle);
- //Init SPI
- HAL_SPI_Init(&SPIHandle);
- //Wlaczenie SPI
- __HAL_SPI_ENABLE(&SPIHandle);
- }
Teraz czas na funkcję odbierającą dane przez SPI:
- uint8_t SPI2_SEND_DATA(uint8_t data)
- {
- if (!((SPI2)->CR1 & SPI_CR1_SPE)) {return 0;}
- while ((SPI2->SR & SPI_FLAG_TXE) == 0 || (SPI2->SR & SPI_FLAG_BSY));
- SPI2->DR = data;
- while ((SPI2->SR & SPI_FLAG_RXNE) == 0 || (SPI2->SR & SPI_FLAG_BSY));
- return SPI2->DR;
- }
Właściwie na tym można zakończyć główna funkcja programu wygląda następująco:
- uint8_t wyslij[15];
- uint8_t odbierz[15];
- int main(void)
- {
- uint8_t i, pomocnicza;
- uint8_t pomocnicza = 0;
- //Ustawienie zegarow
- RCC_ON();
- //Wlaczenie hala
- HAL_Init();
- //Wlaczenie led
- LED_GPIOD_WLACZ();
- //Wlaczenie SPI
- SPI2_WLACZENIE();
- //Wypelnienie bufora danymi oraz przeslanie ich
- for(i = 0; i<15;i++)
- {
- wyslij[i] = 0xAB;
- odbierz[i] = SPI2_SEND_DATA(wyslij[i]);
- }
- //Sprawdzenie poprawnosci danych
- i=0;
- while(i<15)
- {
- if(wyslij[i]==odbierz[i])
- {
- pomocnicza++;
- }
- else{ }
- i++;
- }
- if(pomocnicza==15)
- {
- HAL_GPIO_WritePin(GPIOD, GPIO_Pin_12, GPIO_PIN_SET);
- HAL_GPIO_WritePin(GPIOD, GPIO_Pin_13, GPIO_PIN_SET);
- }
- else
- {
- HAL_GPIO_WritePin(GPIOD, GPIO_Pin_14, GPIO_PIN_SET);
- }
- while (1) {}
- }
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.