piątek, 17 czerwca 2022

[49] STM32F4 - BMI160 SPI

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:

  1. static void MX_SPI1_Init(void)
  2. {
  3.   hspi1.Instance = SPI1;
  4.   hspi1.Init.Mode = SPI_MODE_MASTER;
  5.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  6.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  7.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  8.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  9.   hspi1.Init.NSS = SPI_NSS_SOFT;
  10.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  11.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  12.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  13.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  14.   hspi1.Init.CRCPolynomial = 10;
  15.   if (HAL_SPI_Init(&hspi1) != HAL_OK)
  16.   {
  17.     Error_Handler();
  18.   }
  19. }

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:

  1. static HAL_StatusTypeDef BMI160_WriteSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize)
  2. {
  3.     uint8_t spiReg = registerAddr;
  4.     _BMI160_CS_ENABLE();
  5.  
  6.     HAL_StatusTypeDef opStatus = HAL_SPI_Transmit(&hspi1, &spiReg, 1, 10);
  7.  
  8.     if(opStatus != HAL_OK) {
  9.         _BMI160_CS_DISABLE();
  10.         return opStatus;
  11.     }
  12.     opStatus = HAL_SPI_Transmit(&hspi1, bufferPtr, bufferSize, 10);
  13.  
  14.     _BMI160_CS_DISABLE();
  15.  
  16.     if(opStatus != HAL_OK) {
  17.         return opStatus;
  18.     }
  19.     return 0;
  20. }
  21.  
  22. static HAL_StatusTypeDef BMI160_ReadSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize)
  23. {
  24.     uint8_t spiBuf[15] = {0x00};
  25.     spiBuf[0] = registerAddr | 0x80;
  26.  
  27.     _BMI160_CS_ENABLE();
  28.  
  29.     HAL_StatusTypeDef opStatus = HAL_SPI_Transmit(&hspi1, spiBuf, 1, 20);
  30.  
  31.     if(opStatus != HAL_OK) {
  32.         _BMI160_CS_DISABLE();
  33.         return opStatus;
  34.     }
  35.  
  36.     opStatus = HAL_SPI_Receive(&hspi1, spiBuf, bufferSize, 30);
  37.  
  38.     _BMI160_CS_DISABLE();
  39.  
  40.     if(opStatus != HAL_OK) {
  41.         return opStatus;
  42.     }
  43.  
  44.     for(uint8_t i=0; i<(bufferSize); i++){
  45.         *(bufferPtr + i) = spiBuf[i];
  46.     }
  47.  
  48.     return 0;
  49. }
  50.  
  51. static uint8_t BMI160_SPI_ReadRegBits(uint8_t reg, unsigned pos, unsigned len)
  52. {
  53.     uint8_t readData = 0;
  54.     HAL_StatusTypeDef opStatus = BMI160_ReadSPI(reg, &readData, 1);
  55.  
  56.     if(opStatus != HAL_OK) { return 0x00; }
  57.  
  58.     uint8_t mask = (1 << len) - 1;
  59.     readData >>= pos;
  60.     readData &= mask;
  61.  
  62.     return readData;
  63. }
  64.  
  65. static HAL_StatusTypeDef BMI160_SPI_WriteRegisterBits(uint8_t reg, uint8_t data, unsigned pos, unsigned len)
  66. {
  67.     uint8_t readData = 0;
  68.     HAL_StatusTypeDef opStatus = BMI160_ReadSPI(reg, &readData, 1);
  69.  
  70.     if(opStatus != HAL_OK) { return opStatus; }
  71.  
  72.     uint8_t mask = ((1 << len) - 1) << pos;
  73.  
  74.     data <<= pos;
  75.     data &= mask;
  76.     readData &= ~(mask);
  77.     readData |= data;
  78.  
  79.     return BMI160_WriteSPI(reg, &readData, 1);
  80. }
  81.  
  82. static HAL_StatusTypeDef BMI160_ReadWriteSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize)
  83. {
  84.     uint8_t spiBuf[15] = {0x00};
  85.     uint8_t recBuffer[15] = {0x00};
  86.     spiBuf[0] = registerAddr | 0x80;
  87.  
  88.     _BMI160_CS_ENABLE();
  89.  
  90.     HAL_StatusTypeDef opstatus = HAL_SPI_TransmitReceive(&hspi1, spiBuf, recBuffer, bufferSize, 50);
  91.  
  92.     _BMI160_CS_DISABLE();
  93.  
  94.     for(uint8_t i=0; i<(bufferSize); i++){
  95.         *(bufferPtr + i) = recBuffer[i+1];
  96.     }
  97.  
  98.     return opstatus;
  99. }

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.

  1. static HAL_StatusTypeDef BMI160_WriteRegister(uint8_t address, uint8_t cmd);
  2. static HAL_StatusTypeDef BMI160_ReadRegister(uint8_t address, uint8_t dataSize, uint8_t *rec);
  3. static uint8_t BMI160_ReadRegBits(uint8_t reg, unsigned pos, unsigned len);
  4. 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:

  1. static HAL_StatusTypeDef BMI160_WriteSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize);
  2. static HAL_StatusTypeDef BMI160_ReadSPI(const uint8_t registerAddr, uint8_t *bufferPtr, const uint8_t bufferSize);
  3. static HAL_StatusTypeDef BMI160_SPI_WriteRegisterBits(uint8_t reg, uint8_t data, unsigned pos, unsigned len);
  4. static uint8_t BMI160_SPI_ReadRegBits(uint8_t reg, unsigned pos, unsigned len);
  5. 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. 

  1. static HAL_StatusTypeDef BMI160_SPI_WriteRegister(uint8_t address, uint8_t cmd)
  2. {
  3.     uint8_t buff[1] ={cmd};
  4.     return BMI160_WriteSPI(address, &buff[0], 1);
  5. }
  6.  
  7. static HAL_StatusTypeDef BMI160_SPI_ReadRegister(uint8_t address, uint8_t dataSize, uint8_t *rec)
  8. {
  9.     uint8_t array[20] = {0xA5};
  10.  
  11.     HAL_StatusTypeDef opStatus = BMI160_ReadSPI(address, &array[0], dataSize);
  12.  
  13.     if(opStatus != HAL_OK) {return HAL_ERROR;}
  14.  
  15.     for(uint8_t i=0; i<dataSize; i++)
  16.     {
  17.         *(rec + i) = array[i];
  18.     }
  19.  
  20.     return opStatus;
  21. }

Teraz należy zdefiniować wskaźniki na funckję:

  1. HAL_StatusTypeDef (*BMI160_WriteToRegister)(uint8_t, uint8_t);
  2. 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:

  1. HAL_StatusTypeDef BMI160_SPI_Initialize(void)
  2. {
  3.     HAL_StatusTypeDef opStatus = HAL_ERROR;
  4.  
  5.     BMI160_WriteToRegister = BMI160_SPI_WriteRegister;
  6.     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:

  1. #if defined(BMI160_SPI)
  2.     BMI160_WriteToRegister = BMI160_SPI_WriteRegister;
  3.     BMI160_ReadFromRegister = BMI160_SPI_ReadRegister;
  4. #elif defined(BMI160_I2C)
  5.     BMI160_WriteToRegister = BMI160_I2C_WriteRegister;
  6.     BMI160_ReadFromRegister = BMI160_I2C_ReadRegister;
  7. #else
  8.     #error "No selected interface for BMI160"
  9. #endif
 
lub bezpośrednio w funkcjach np. w taki sposób:

  1. static HAL_StatusTypeDef BMI160_SPI_WriteRegister(uint8_t address, uint8_t cmd)
  2. {
  3. #if defined(BMI160_SPI)
  4.     uint8_t buff[1] ={cmd};
  5.     return BMI160_WriteSPI(address, &buff[0], 1);
  6. #elif defined (BMI_I2C)
  7.     uint8_t data[2] = {0x00};
  8.     data[0] = address;
  9.     data[1] = cmd;
  10.     return HAL_I2C_Master_Transmit(&hi2c1, BMI160_I2C_ADDR, data, 2, 10);
  11. #else
  12.     #error "No selected interface for BMI160"
  13. #endif
  14. }

Bibliotekę można pobrać z dysku Google pod tym linkiem.

Brak komentarzy:

Prześlij komentarz