Ten post jest kontynuacją wcześniejszego o czujniku BMI160. Tym razem przygotuję wersję obsługującą komunikację po SPI.
[Moduł czujnika BMI160]
Podłączenie czujnika do płytki STM32-Discovery jest następujące:
- 3V3 - 3V3
- GND - GND
- CS (SS) - PA8
- MISO (SAO) - PA6
- MOSI (SDA) - PA7
- SCK (SCL) - PA5
Inicjalizacja interfejsu SPI:
- static void MX_SPI1_Init(void)
- {
- hspi1.Instance = SPI1;
- hspi1.Init.Mode = SPI_MODE_MASTER;
- hspi1.Init.Direction = SPI_DIRECTION_2LINES;
- hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
- hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
- hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
- hspi1.Init.NSS = SPI_NSS_SOFT;
- hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
- hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
- hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
- hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
- hspi1.Init.CRCPolynomial = 10;
- if (HAL_SPI_Init(&hspi1) != HAL_OK)
- {
- Error_Handler();
- }
- }
Poniżej przedstawię najważniejsze elementy zmienionej biblioteki:
Głowna i właściwie jedyna różnica względem wcześniejszej biblioteki dotyczy funkcji przesyłających i odbierających danych. Dotyczy to zmiany interfejsu komunikacyjnego z I2C (opisany we wcześniejszym poście) na SPI:
- static HAL_StatusTypeDef BMI160_WriteSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize)
- {
- uint8_t spiReg = registerAddr;
- _BMI160_CS_ENABLE();
- HAL_StatusTypeDef opStatus = HAL_SPI_Transmit(&hspi1, &spiReg, 1, 10);
- if(opStatus != HAL_OK) {
- _BMI160_CS_DISABLE();
- return opStatus;
- }
- opStatus = HAL_SPI_Transmit(&hspi1, bufferPtr, bufferSize, 10);
- _BMI160_CS_DISABLE();
- if(opStatus != HAL_OK) {
- return opStatus;
- }
- return 0;
- }
- static HAL_StatusTypeDef BMI160_ReadSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize)
- {
- uint8_t spiBuf[15] = {0x00};
- spiBuf[0] = registerAddr | 0x80;
- _BMI160_CS_ENABLE();
- HAL_StatusTypeDef opStatus = HAL_SPI_Transmit(&hspi1, spiBuf, 1, 20);
- if(opStatus != HAL_OK) {
- _BMI160_CS_DISABLE();
- return opStatus;
- }
- opStatus = HAL_SPI_Receive(&hspi1, spiBuf, bufferSize, 30);
- _BMI160_CS_DISABLE();
- if(opStatus != HAL_OK) {
- return opStatus;
- }
- for(uint8_t i=0; i<(bufferSize); i++){
- *(bufferPtr + i) = spiBuf[i];
- }
- return 0;
- }
- static uint8_t BMI160_SPI_ReadRegBits(uint8_t reg, unsigned pos, unsigned len)
- {
- uint8_t readData = 0;
- HAL_StatusTypeDef opStatus = BMI160_ReadSPI(reg, &readData, 1);
- if(opStatus != HAL_OK) { return 0x00; }
- uint8_t mask = (1 << len) - 1;
- readData >>= pos;
- readData &= mask;
- return readData;
- }
- static HAL_StatusTypeDef BMI160_SPI_WriteRegisterBits(uint8_t reg, uint8_t data, unsigned pos, unsigned len)
- {
- uint8_t readData = 0;
- HAL_StatusTypeDef opStatus = BMI160_ReadSPI(reg, &readData, 1);
- if(opStatus != HAL_OK) { return opStatus; }
- uint8_t mask = ((1 << len) - 1) << pos;
- data <<= pos;
- data &= mask;
- readData &= ~(mask);
- readData |= data;
- return BMI160_WriteSPI(reg, &readData, 1);
- }
- static HAL_StatusTypeDef BMI160_ReadWriteSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize)
- {
- uint8_t spiBuf[15] = {0x00};
- uint8_t recBuffer[15] = {0x00};
- spiBuf[0] = registerAddr | 0x80;
- _BMI160_CS_ENABLE();
- HAL_StatusTypeDef opstatus = HAL_SPI_TransmitReceive(&hspi1, spiBuf, recBuffer, bufferSize, 50);
- _BMI160_CS_DISABLE();
- for(uint8_t i=0; i<(bufferSize); i++){
- *(bufferPtr + i) = recBuffer[i+1];
- }
- return opstatus;
- }
Pozostałe funkcje są identyczne względem poprzedniej biblioteki.
Do komunikacji po SPI została przygotowana osobna biblioteka. Jednak jeśli chcielibyśmy korzystać z jednej biblioteki do komunikacji po SPI i po I2C, wystarczy użyć wskaźników na funkcję.
Przed ich zastosowaniem należy się upewnić, że każda z funkcji wysyłania/odbierania danych ma te same parametry na tych samych miejscach.
- static HAL_StatusTypeDef BMI160_WriteRegister(uint8_t address, uint8_t cmd);
- static HAL_StatusTypeDef BMI160_ReadRegister(uint8_t address, uint8_t dataSize, uint8_t *rec);
- static uint8_t BMI160_ReadRegBits(uint8_t reg, unsigned pos, unsigned len);
- static HAL_StatusTypeDef BMI160_WriteRegisterBits(uint8_t reg, uint8_t data, unsigned pos, unsigned len);
Funkcje SPI, wspomniane wcześniej wyglądają następująco:
- static HAL_StatusTypeDef BMI160_WriteSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize);
- static HAL_StatusTypeDef BMI160_ReadSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize);
- static HAL_StatusTypeDef BMI160_SPI_WriteRegisterBits(uint8_t reg, uint8_t data, unsigned pos, unsigned len);
- static uint8_t BMI160_SPI_ReadRegBits(uint8_t reg, unsigned pos, unsigned len);
- static HAL_StatusTypeDef BMI160_ReadWriteSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize);
Jak można zaobserwować, należy dostosować funkcję ReadSPI oraz WriteSPI, ponieważ zamieniona jest pozycja argumentu 2 i 3 względem funkcji WriteRegister i ReadRegister. Najprostrzym rozwiązaniem (obsługującym wskaźniki) będzie jednak przygotowanie dodatkowych funkcji o identycznych parametrach jak dla biblioteki po I2C.
- static HAL_StatusTypeDef BMI160_SPI_WriteRegister(uint8_t address, uint8_t cmd)
- {
- uint8_t buff[1] ={cmd};
- return BMI160_WriteSPI(address, &buff[0], 1);
- }
- static HAL_StatusTypeDef BMI160_SPI_ReadRegister(uint8_t address, uint8_t dataSize, uint8_t *rec)
- {
- uint8_t array[20] = {0xA5};
- HAL_StatusTypeDef opStatus = BMI160_ReadSPI(address, &array[0], dataSize);
- if(opStatus != HAL_OK) {return HAL_ERROR;}
- for(uint8_t i=0; i<dataSize; i++)
- {
- *(rec + i) = array[i];
- }
- return opStatus;
- }
Teraz należy zdefiniować wskaźniki na funckję:
- HAL_StatusTypeDef (*BMI160_WriteToRegister)(uint8_t, uint8_t);
- HAL_StatusTypeDef (*BMI160_ReadFromRegister)(uint8_t, uint8_t, uint8_t*);
Następnie w części inicjalizacyjnej czujnika wprowadzamy odpowiednie funkcji do do wskaźników:
- HAL_StatusTypeDef BMI160_SPI_Initialize(void)
- {
- HAL_StatusTypeDef opStatus = HAL_ERROR;
- BMI160_WriteToRegister = BMI160_SPI_WriteRegister;
- BMI160_ReadFromRegister = BMI160_SPI_ReadRegister;
Można także skorzystać z dyrektywy preprocesora define. Na tej podstawie dobierać rodzaj interfejsu na etapie kompilacji. Czy to w przypadku wskaźników:
- #if defined(BMI160_SPI)
- BMI160_WriteToRegister = BMI160_SPI_WriteRegister;
- BMI160_ReadFromRegister = BMI160_SPI_ReadRegister;
- #elif defined(BMI160_I2C)
- BMI160_WriteToRegister = BMI160_I2C_WriteRegister;
- BMI160_ReadFromRegister = BMI160_I2C_ReadRegister;
- #else
- #error "No selected interface for BMI160"
- #endif
lub bezpośrednio w funkcjach np. w taki sposób:
- static HAL_StatusTypeDef BMI160_SPI_WriteRegister(uint8_t address, uint8_t cmd)
- {
- #if defined(BMI160_SPI)
- uint8_t buff[1] ={cmd};
- return BMI160_WriteSPI(address, &buff[0], 1);
- #elif defined (BMI_I2C)
- uint8_t data[2] = {0x00};
- data[0] = address;
- data[1] = cmd;
- return HAL_I2C_Master_Transmit(&hi2c1, BMI160_I2C_ADDR, data, 2, 10);
- #else
- #error "No selected interface for BMI160"
- #endif
- }
Brak komentarzy:
Prześlij komentarz