środa, 23 lutego 2022

[47] STM32 - Discovery - LIS3DSH HAL

W tym poście chciałbym opisać obsługę akcelerometru LIS3DSH zamontowanej na płytce STM32 Discovery w różnych trybach.

[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]
Układ był już opisywany na moim blogu, natomiast tym razem chciałem bardzie rozwinąć temat obsługi z wykorzystaniem przerwań oraz DMA.

Opis układu:


LIS3DSH jest to 3-osiowy akcelerometr, który umożliwia pomiar przyśpieszenia (w zakresie +/- 2g, 4g, 8g, 16g). W czujnik wbudowany została maszyna stanu oraz funkcje auto testu.

Inicjalizacja:


Przed inicjalizacją czujnika można odczytać rejestry INFO1, INFO2 oraz WHO_AM_I:


Zgodnie z dokumentacją rejestr INFO1 powinien mieć wartość 33(0x21), INFO2 0, WHO_AM_I 63(0x3F).

  1. uint8_t LIS3DSH_ReadId(LIS3DSH_SensorId_TypeDef *id_ptr){
  2.     uint8_t id_reg_val[3] = {0x00};
  3.     HAL_StatusTypeDef opStatus = LIS3DSH_ReadSPI(LIS3DSH_INFO1, &id_reg_val[0], 3);
  4.  
  5.     if(opStatus == 0) {
  6.         id_ptr->info1  = id_reg_val[0];
  7.         id_ptr->info2  = id_reg_val[1];
  8.         id_ptr->whoami = id_reg_val[2];
  9.        
  10.         if(id_ptr->info1 == 33 &&
  11.            id_ptr->info2 == 0 &&
  12.            id_ptr->whoami == 63)
  13.         {
  14.             return 0;
  15.         }
  16.         return 0xFF;
  17.     }
  18.     return (uint8_t)opStatus;
  19. }

Inicjalizacja czujnika wykonuje się w kilku funkcjach. Na samym początku należy wprowadzić podstawowe parametry konfiguracyjne do struktury:

  1. typedef struct {
  2.     LIS3DSH_DataRate_Enum_TypeDef dataRateSetting;
  3.     LIS3DSH_FullScale_Enum_TypeDef fullScaleSetting;
  4.     LIS3DSH_AntiAliasing_Enum_TypeDef antiAliasingSetting;
  5.     LIS3DSH_Axis_Enum_TypeDef axisSetting;
  6.     LIS3DSH_Interrupt_Enum_TypeDef interruptSetting;
  7.     float sensiLevelSetting;
  8. }LIS3DSH_ParaTypeDef;
  9.  
  10. void LIS3DSH_Initial_DataStructure(LIS3DSH_ParaTypeDef *lis3dsh_str_ptr, const LIS3DSH_DataRate_Enum_TypeDef dataRateSetting,
  11.         const LIS3DSH_FullScale_Enum_TypeDef fullScaleSetting, const LIS3DSH_AntiAliasing_Enum_TypeDef antiAliasingSetting,
  12.         const LIS3DSH_Axis_Enum_TypeDef axisSetting, const LIS3DSH_Interrupt_Enum_TypeDef interruptSetting)
  13. {
  14.     lis3dsh_str_ptr->dataRateSetting = dataRateSetting;
  15.     lis3dsh_str_ptr->fullScaleSetting = fullScaleSetting;
  16.     lis3dsh_str_ptr->antiAliasingSetting = antiAliasingSetting;
  17.     lis3dsh_str_ptr->axisSetting = axisSetting;
  18.     lis3dsh_str_ptr->interruptSetting = interruptSetting;
  19. }
  20.  
  21. void LIS3DSH_Initial_Default_DataStructure(LIS3DSH_ParaTypeDef *lis3dsh_str_ptr)
  22. {
  23.     lis3dsh_str_ptr->dataRateSetting = DATARATE_25_EN;
  24.     lis3dsh_str_ptr->fullScaleSetting = FULLSCALE_4_EN;
  25.     lis3dsh_str_ptr->antiAliasingSetting = FILTER_50_EN;
  26.     lis3dsh_str_ptr->axisSetting = XYZ_AXIS_ENABLE;
  27.     lis3dsh_str_ptr->interruptSetting = INTERRUPT_EN;
  28. }

Kolejnym krokiem jest wpisanie danych z struktury do czujnika:

  1. uint8_t LIS3DSH_Initialization(LIS3DSH_ParaTypeDef *lis3dsh_str_ptr)
  2. {
  3.     uint8_t tempData = 0;
  4.     uint8_t opStatus = 0xFF;
  5.  
  6.     lis3dsh_InitCalibrationData();
  7.  
  8.     tempData |= (lis3dsh_str_ptr->axisSetting & 0x07);
  9.     tempData |= (lis3dsh_str_ptr->dataRateSetting & 0xF0);
  10.  
  11.     opStatus = LIS3DSH_WriteSPI(LIS3DSH_CTRL_REG4, &tempData, 1);
  12.  
  13.     if(opStatus != 0) { return opStatus; }
  14.  
  15.     tempData = 0;
  16.     tempData |= (lis3dsh_str_ptr->antiAliasingSetting & 0xC0);
  17.     tempData |= (lis3dsh_str_ptr->fullScaleSetting & 0x38);
  18.  
  19.     opStatus = LIS3DSH_WriteSPI(LIS3DSH_CTRL_REG5, &tempData, 1);
  20.  
  21.     if(opStatus != 0) { return opStatus; }
  22.  
  23.     opStatus = lis3dsh_setInterruptInt1(lis3dsh_str_ptr->interruptSetting);
  24.  
  25.     if(opStatus != 0x00) { return opStatus; }
  26.  
  27.     lis3dsh_str_ptr->sensiLevelSetting = lis3dsh_SetSensiLevel_BasedFullscale(lis3dsh_str_ptr->fullScaleSetting);
  28.  
  29.     return 0;
  30. }

Polling:


W przypadku pobierania danych w ciągiem należy czekać na gotowość czujnika do przesłania kolejnych informacji:

  1. uint8_t LIS3DSH_Poll_Data(void)
  2. {
  3.     uint32_t startTick = HAL_GetTick();
  4.  
  5.     if(lis3dsh_readStatusReg() == 1) {
  6.         return true;
  7.     }
  8.  
  9.     return false;
  10. }

  1. static uint8_t lis3dsh_readStatusReg(void)
  2. {
  3.     uint8_t statReg = 0;
  4.     LIS3DSH_ReadSPI(LIS3DSH_STATUS_ADDR, &statReg, 1);
  5.  
  6.     if(statReg & 0x07) {
  7.         return 1;
  8.     }
  9.     return 0;
  10. }

Wartość 0x07 w rejestrze status (0x27) oznacza nowe dane dla osi X, Y, Z.

Można też wykorzystać odliczanie czasu w celu zmniejszenia ilości danych pobieranych z czujnika:

  1. typedef struct {
  2.     uint8_t start_Count_Flag;
  3.     uint32_t start_Tick_Value;
  4. }LIS3DSH_Timeout_Typedef;
  5.  
  6. uint8_t LIS3DSH_PollData_Timeout(LIS3DSH_Timeout_Typedef *timeoutStruct_Ptr, uint32_t timeout)
  7. {
  8.     if(lis3dsh_timeout((LIS3DSH_Timeout_Typedef *)&timeoutStruct_Ptr, timeout) == 1)
  9.     {
  10.         if(lis3dsh_readStatusReg() == 1) {
  11.             timeoutStruct_Ptr->start_Count_Flag = 0;
  12.             return true;
  13.         }
  14.     }
  15.  
  16.     return false;
  17. }
  18.  
  19. static uint8_t lis3dsh_timeout(LIS3DSH_Timeout_Typedef *timeoutStruct_Ptr, uint32_t timeout_ms)
  20. {
  21.     if(timeoutStruct_Ptr->start_Count_Flag == 0) {
  22.         timeoutStruct_Ptr->start_Tick_Value = HAL_GetTick();
  23.         timeoutStruct_Ptr->start_Count_Flag = 1;
  24.     }
  25.  
  26.     if((HAL_GetTick() - timeoutStruct_Ptr->start_Tick_Value) < timeout_ms)
  27.     {
  28.         return 1;
  29.     }
  30.     return 0;
  31. }

W tym przypadku przekroczeniu zadanego czasu sprawdzana jest możliwość pobrania danych z czujnika. Dopiero po zatwierdzeniu z obu funkcji przechodzę do odczytu danych z czujnika.

  1. while (1)
  2. {
  3.     if(LIS3DSH_PollData_Timeout(1000) == 1)
  4.     {
  5.         LIS3DSH_ScaledData_t = LIS3DSH_GetReadScaledData();
  6.         HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_12);
  7.     }
  8. }

Przerwania:


W celu uruchomienia przerwania należy ustawić odpowiednią wartość w rejestrze konfiguracyjnym CTRL_REG3 (0x23):


Należy ustawić flagę DR_EN oraz INT1_EN.

  1. static uint8_t lis3dsh_setInterruptInt1(LIS3DSH_Interrupt_Enum_TypeDef intEnStat)
  2. {
  3.     uint8_t opStatus = 0;
  4.  
  5.     if(intEnStat == INTERRUPT_EN)
  6.     {
  7.         uint8_t tempData = 0x88;
  8.         opStatus = LIS3DSH_WriteSPI(LIS3DSH_CTRL_REG3_ADDR, &tempData, 1);
  9.  
  10.         if(opStatus != 0) { return opStatus; }
  11.     }
  12.     return opStatus;
  13. }

Przerwanie jest przypisane do pinu PE0 dla INT1 układu LIS3DSH. PE1 obsługuje pin INT2. 

Przerwanie zostaje ustawione gdy są gotowe dane do przesłania. W obsłudze przerwania tak aby było ono najszybciej wykonane najłatwiej ustawić flagę. 

  1. volatile uint8_t lis3dsh_dataReady_irq_flag = 0;
  2.  
  3. void EXTI0_IRQHandler(void)
  4. {
  5.   HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
  6. }
  7.  
  8. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  9. {
  10.     if(GPIO_Pin == GPIO_PIN_0)
  11.     {
  12.       lis3dsh_dataReady_irq_flag = 1;
  13.     }
  14. }
  15.  
  16. //w main ...
  17. while(1) {
  18.     if(lis3dsh_dataReady_irq_flag == 1)
  19.     {
  20.         lis3dsh_dataReady_irq_flag = 0;
  21.         LIS3DSH_ScaledData_t = LIS3DSH_GetReadScaledData(&LIS3DSH_Config, &LIS3DSH_BiasScaleData_t);
  22.     }
  23. }

Jeśli przerwanie zostało wywołane i flaga została ustawiona na 1, to w pętli while nastąpi odczyt danych z czujnika.

DMA:


W przykładzie opisującym DMA wykorzystam przerwanie INT1 inicjalizujące odczyt danych.

Na samym początku należy zmienić kolejność wywoływania bibliotek inicjalizujących DMA oraz SPI:

  1. //Z
  2. MX_SPI1_Init();
  3. MX_DMA_Init();
  4. //NA
  5. MX_DMA_Init();
  6. MX_SPI1_Init();

Wygenerowana inicjalizacja SPI z DMA wygląda następująco:

  1. void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
  2. {
  3.   GPIO_InitTypeDef GPIO_InitStruct = {0};
  4.   if(hspi->Instance==SPI1)
  5.   {
  6.     __HAL_RCC_SPI1_CLK_ENABLE();
  7.  
  8.     __HAL_RCC_GPIOA_CLK_ENABLE();
  9.     /**SPI1 GPIO Configuration
  10.     PA5     ------> SPI1_SCK
  11.     PA6     ------> SPI1_MISO
  12.     PA7     ------> SPI1_MOSI
  13.     */
  14.     GPIO_InitStruct.Pin = SPI1_SCK_Pin|SPI1_MISO_Pin|SPI1_MOSI_Pin;
  15.     GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  16.     GPIO_InitStruct.Pull = GPIO_NOPULL;
  17.     GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  18.     GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
  19.     HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  20.  
  21.     /* SPI1 DMA Init */
  22.     /* SPI1_RX Init */
  23.     hdma_spi1_rx.Instance = DMA2_Stream0;
  24.     hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
  25.     hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
  26.     hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
  27.     hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
  28.     hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  29.     hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  30.     hdma_spi1_rx.Init.Mode = DMA_NORMAL;
  31.     hdma_spi1_rx.Init.Priority = DMA_PRIORITY_HIGH;
  32.     hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  33.     if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
  34.     {
  35.       Error_Handler();
  36.     }
  37.  
  38.     __HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx);
  39.  
  40.     /* SPI1_TX Init */
  41.     hdma_spi1_tx.Instance = DMA2_Stream3;
  42.     hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
  43.     hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  44.     hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  45.     hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
  46.     hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  47.     hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  48.     hdma_spi1_tx.Init.Mode = DMA_NORMAL;
  49.     hdma_spi1_tx.Init.Priority = DMA_PRIORITY_HIGH;
  50.     hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  51.     if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
  52.     {
  53.       Error_Handler();
  54.     }
  55.  
  56.     __HAL_LINKDMA(hspi,hdmatx,hdma_spi1_tx);
  57.   }
  58. }

Do tego należy uruchomić przerwania od 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_16;
  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.  
  20.   HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
  21.   HAL_NVIC_EnableIRQ(SPI1_IRQn);
  22. }

Jak wspomniałem wcześniej do obsługi wykorzystywane są przerwanie od INT1 informujące o możliwości odczytu danych. W tym przerwaniu rozpoczynam odczyt danych z czujnika (funkcja HAL_SPI_TransmitReceive_DMA). Bufory z danymi zapisanymi i odczytanymi są buforami globalnymi. Zakończenie odbierania danych wywołuje przerwania od SPI1 gdzie obrabiam otrzymane wyniki i ustawiam flagę odczytu w celu obrobienia danych w pętli głównej programu.

  1. void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
  2. {
  3.     if(GPIO_Pin == GPIO_PIN_0 && LIS3DSH_t.lis3dsh_dataReady_irq_flag == 0)
  4.     {
  5.         LIS3DSH_DMA_StartRead();
  6.         LIS3DSH_t.lis3dsh_dataReady_irq_flag = 1;
  7.         HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);
  8.     }
  9. }
  10.  
  11. void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
  12.     if(hspi->Instance == SPI1 && LIS3DSH_t.lis3dsh_dataReady_irq_flag == 1)
  13.     {
  14.         LIS3DSH_t.lis3dsh_dataReady_irq_flag = 0;
  15.         LIS3DSH_DMA_ReadComplete(&LIS3DSH_t.LIS3DSH_RawData_t);
  16.         LIS3DSH_t.convertData_From_Global = 1;
  17.         HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_13);
  18.     }
  19. }

  1.   while (1)
  2.   {
  3.     /* USER CODE END WHILE */
  4.  
  5.     /* USER CODE BEGIN 3 */
  6.       if(LIS3DSH_t.convertData_From_Global == 1)
  7.       {
  8.           LIS3DSH_t.convertData_From_Global = 0;
  9.           LIS3DSH_t.LIS3DSH_ScaledData_t = LIS3DSH_DMA_ConvertData(&LIS3DSH_t.LIS3DSH_RawData_t,
  10.                   &LIS3DSH_t.LIS3DSH_Config_t,
  11.                   &LIS3DSH_t.LIS3DSH_BiasScaleData_t);
  12.       }
  13.   }

Poniżej wszystkie funkcje obsługujące DMA w LIS3DSH:

  1. uint8_t LIS3DSH_DMA_StartRead(void)
  2. {
  3.     LIS3DSH_DMA_Buffers_t.glob_spiBuf[0] = LIS3DSH_OUT_X_L | 0x80;
  4.  
  5.     _LIS3DHS_CS_ENBALE;
  6.  
  7.     HAL_StatusTypeDef opStatus = HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t *)&LIS3DSH_DMA_Buffers_t.glob_spiBuf[0], (uint8_t *)&LIS3DSH_DMA_Buffers_t.glob_buffer[0], 8);
  8.  
  9.     if(opStatus == HAL_OK)
  10.     {
  11.         return 1;
  12.     } else{
  13.         _LIS3DHS_CS_DISABLE;
  14.         return 0;
  15.     }
  16. }
  17.  
  18. void LIS3DSH_DMA_ReadComplete(volatile LIS3DSH_RawData_TypeDef *rawData_ptr)
  19. {
  20.     _LIS3DHS_CS_DISABLE;
  21.  
  22.     LIS3DSH_t.LIS3DSH_RawData_t.x = ((LIS3DSH_DMA_Buffers_t.glob_buffer[2] << 8) + LIS3DSH_DMA_Buffers_t.glob_buffer[1]);
  23.     LIS3DSH_t.LIS3DSH_RawData_t.y = ((LIS3DSH_DMA_Buffers_t.glob_buffer[4] << 8) + LIS3DSH_DMA_Buffers_t.glob_buffer[3]);
  24.     LIS3DSH_t.LIS3DSH_RawData_t.z = ((LIS3DSH_DMA_Buffers_t.glob_buffer[6] << 8) + LIS3DSH_DMA_Buffers_t.glob_buffer[5]);
  25. }
  26.  
  27. LIS3DSH_ScaleData_TypeDef LIS3DSH_DMA_ConvertData(volatile LIS3DSH_RawData_TypeDef *rawData_ptr, LIS3DSH_ParaTypeDef *lis3dsh_str_ptr, LIS3DSH_BiasScaleData_TypeDef *lis3dsh_bias_scale_ptr)
  28. {
  29.     LIS3DSH_ScaleData_TypeDef tempScaledData;
  30.  
  31.     tempScaledData.x = (rawData_ptr->x * lis3dsh_str_ptr->sensiLevelSetting * lis3dsh_bias_scale_ptr->X_ScaleData) + 0.0f - lis3dsh_bias_scale_ptr->X_BiasData;
  32.     tempScaledData.y = (rawData_ptr->y * lis3dsh_str_ptr->sensiLevelSetting * lis3dsh_bias_scale_ptr->Y_ScaleData) + 0.0f - lis3dsh_bias_scale_ptr->Y_BiasData;
  33.     tempScaledData.z = (rawData_ptr->z * lis3dsh_str_ptr->sensiLevelSetting * lis3dsh_bias_scale_ptr->Z_ScaleData) + 0.0f - lis3dsh_bias_scale_ptr->Z_BiasData;
  34.  
  35.     return tempScaledData;
  36. }

Testy:


W tej części chciałbym opisać na jakie elementy warto zwrócić uwagę podczas wykonywania programu oraz w jaki sposób przetestować funkcjonalność.

Korzystanie z liczb typu float:


Układ stm32 jest wyposażony w jednostkę FPU (Float Pointing Unit), która znacząco skraca czas potrzebny na wykonanie operacji na liczbach zmiennoprzecinkowych.



Jak można zaobserwować w tabelce powyżej większość operacji na liczbach zmiennoprzecinkowych odbywa się w pojedynczych instrukcjach.

Aby ją uruchomić należy ustawić odpowiednią flagę kompilatora (-mfpu=fpv4-sp-d16). W CubeIDE ustawiana jest ona w konfiguracji projektu.


Dodatkowo należy zwrócić uwagę na sposób pisania programu, tak aby nie korzystał on z typu double.

  1. -mfpu=fpv4-sp-d16

Inicjalizacja czujnika:


Przed uruchomieniem urządzenia warto sprawdzić czy komunikujemy się z poprawnym modułem. Tutaj najprostszym sposobem jest skorzystanie z danych z rejestru WHOAMI i odczytanie ID układu (Funkcja i rejestr opisany w części inicjalizacji)

Komunikacja:


Cały proces komunikacji jest obsługiwany przez biblioteki HAL. W związku z tym najlepszym sposobem sprawdzania działania jest obsługiwanie wartości zwracanych podczas zapisu/odczytu danych. Jeśli otrzymane wyniki są różne od HAL_OK, wtedy proces komunikacji nie przeszedł poprawnie i trzeba wykonać odpowiednie działania np. ponowne wysłanie komendy zapisu/odczytu danych, ponowna inicjalizacja czujnika, reset procesora, wywołanie informacji o błędnym działaniu czujnika.

Testy jednostkowe:


Jedynym elementem jaki można przetestować w testach jednostkowych jest funkcja obrabiające otrzymane wyniki pomiaru. I tutaj jest to dosyć naciągane, ponieważ funkcje obliczające wynik nie będą ulegały zmianie i ich implementacja przez cały czas życia projektu pozostanie niezmieniona i raczej nie będzie poprawiana. Pozostałe elementy zależą od elementów sprzętowych jak pobieranie danych z SPI, zakłócenia na tych liniach itp. itd. 

Podobnie wygląda sytuacja np. z taką funkcją:

  1. static float lis3dsh_SetSensiLevel_BasedFullscale(const LIS3DSH_FullScale_Enum_TypeDef fullScaleSetting)
  2. {
  3.     if(fullScaleSetting == FULLSCALE_2_EN) { return LIS3DSH_SENSI_0_06G; }
  4.     else if(fullScaleSetting == FULLSCALE_4_EN) { return LIS3DSH_SENSI_0_12G; }
  5.     else if(fullScaleSetting == FULLSCALE_6_EN) { return LIS3DSH_SENSI_0_18G; }
  6.     else if(fullScaleSetting == FULLSCALE_8_EN) { return LIS3DSH_SENSI_0_24G; }
  7.     else if(fullScaleSetting == FULLSCALE_16_EN) { return LIS3DSH_SENSI_0_73G; }
  8.     else { return LIS3DSH_SENSI_0_12G; }
  9. }

gdzie na podstawie jednego parametru zwracamy drugi. W przypadku takich funkcji napisanie testów jednostkowych będzie całkowicie bezsensowne.

Jeśli powstała by funkcja wykonująca różne obliczenia, czy różne akcje w zależności od odczytanych wyników czy kilku parametrów wejściowych to napisanie testów sprawdzających jej działanie względem różnych warunków wejściowych byłoby pomocne.

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