---
# System prepended metadata

title: lab2-sensor_and_interrupt
tags: [嵌入式作業系統分析與實作]

---

---
tags: 嵌入式作業系統分析與實作
---

# lab2-sensor_and_interrupt
contributed by <`RusselCK` >

###### tags: `RusselCK`


## 作業要求
* Must use sensor interrupt : motion detection.
    * When you shake your board, it will trigger the interrupt.
* Please use the deferred interrupt handling task.
    * Use semephore.
    * ISR will give the semephore and the handler task enters the running state.
* At the beginning,the green LED blinking,then shake the board, the red LED triggered（switch state） by ISR and the orange LED blinking five times in handler task.
* When orange LED blinking, you should not trigger the sensor interrupt if you shake the board.

## Pinout & Configuration

![](https://i.imgur.com/3pW6PMY.png)

![](https://i.imgur.com/gbjV0hv.png)

![](https://i.imgur.com/zhj2Mce.png)

![](https://i.imgur.com/wTP9PdO.png)
![](https://i.imgur.com/b2ug5Dt.png)


## 程式架構

![](https://i.imgur.com/cCCXbeC.png)

`vLab02_task_1` : the Green LED blinking
`ISR` : the Red LED triggered (switch state)
`vLab02_Handler_task` : the Orange LED blinking 5 times

**Priority** : `ISR` > `vLab02_Handler_task` > `vLab02_task_1`
## 程式碼與解說

### Include, Macro and Variables
```c 
/* USER CODE BEGIN Includes */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* USER CODE END Includes */
```

```c 
/* Private macro -----------------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define Green_LED_High  HAL_GPIO_WritePin(Green_LED_GPIO_Port, GPIO_PIN_12, GPIO_PIN_SET)
#define Green_LED_Low   HAL_GPIO_WritePin(Green_LED_GPIO_Port, GPIO_PIN_12, GPIO_PIN_RESET)
#define Orange_LED_High HAL_GPIO_WritePin(Red_LED_GPIO_Port, GPIO_PIN_13, GPIO_PIN_SET)
#define Orange_LED_Low  HAL_GPIO_WritePin(Red_LED_GPIO_Port, GPIO_PIN_13, GPIO_PIN_RESET)
#define Red_LED_High    HAL_GPIO_WritePin(Red_LED_GPIO_Port, GPIO_PIN_14, GPIO_PIN_SET)
#define Red_LED_Low     HAL_GPIO_WritePin(Red_LED_GPIO_Port, GPIO_PIN_14, GPIO_PIN_RESET)
#define Red_LED_Toggle  HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14)
/* USER CODE END PM */
```
為了方便之後程式碼的閱讀，我們將會用到的 GPIO Read/Write/Toggle 指令換成可讀性較高的 Macro

```c  
/* USER CODE BEGIN PV */
SemaphoreHandle_t xSemaphore;
/* USER CODE END PV */
```
我們先 declare 之後會用到的 SemaphoreHandle

### Motion Sensor Interrupt
- [ ] [MEMS digital output motion sensor: ultra-low-power high-performance three-axis “nano” accelerometer](https://www.st.com/resource/en/datasheet/lis3dsh.pdf)
- [ ] [LIS3DSH: 3-axis digital output accelerometer](https://www.st.com/resource/en/application_note/dm00026768-lis3dsh-3axis-digital-output-accelerometer-stmicroelectronics.pdf)

```c 
void MEMS_Write(uint8_t address,uint8_t data){
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi1,&address,1,10);
    HAL_SPI_Transmit(&hspi1,&data,1,10);
    HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3,GPIO_PIN_SET);
}

void vLab02_sensor_init()
{
    MEMS_Write(0x21,0x01);
    MEMS_Write(0x23,0x48);
    MEMS_Write(0x20,0x67);
    MEMS_Write(0x24,0x00);
    MEMS_Write(0x57,0x55);
    MEMS_Write(0x40,0x05);
    MEMS_Write(0x41,0x11);
    MEMS_Write(0x59,0xFC);
    MEMS_Write(0x5A,0xFC);
    MEMS_Write(0x5B,0x01);
}
```
![](https://i.imgur.com/hxaEiqd.png)

![](https://i.imgur.com/TwSkbfM.png)

![](https://i.imgur.com/nlUS4kZ.png)

只要參照文件設置 Registers 的 Values，我們就可以達成「晃動板子便有 interrupt signal」的效果

### `vLab02_task_1()`

```c 
void vLab02_task_1( void *pvParameters )
{
    while(1)
    {
    	/* Green LED blinking */
    	Green_LED_High;    // Green LED ON
    	vTaskDelay(500);
    	Green_LED_Low;     // Green LED OFF
    	vTaskDelay(500);
    }
}
```
Task1 要做的事情是「綠燈不斷閃爍」，從程式碼的註解可以很直觀的明白每一行程式碼在做的事情

### ISR

```c 
/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN)
{
    /* switch Red LED state */
    Red_LED_Toggle;

    /* semaphore give */
    BaseType_t xHigherPriorityTaskWoken;
    xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR( xSemaphore,
                           &xHigherPriorityTaskWoken );
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/* USER CODE END 4 */
```
> `EXIT` stand for External Interrupt

ISR 要做的事情就是「改變 Red LED state」 和「give a semaphore」

#### Interrupt handler
`Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c`
```c 
/**
  * @brief  This function handles EXTI interrupt request.
  * @param  GPIO_Pin Specifies the pins connected EXTI line
  * @retval None
  */
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

/**
  * @brief  EXTI line detection callbacks.
  * @param  GPIO_Pin Specifies the pins connected EXTI line
  * @retval None
  */
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}
```

根據原生程式碼的提示，我們需要將 ISR 的內容寫在 `main.c` 的 `HAL_GPIO_EXTI_Callback()` 裡頭



### `vLab02_Handler_task()`

```c 
#define LONG_TIME 0xFFFF

void vLab02_Handler_task(void *pvParameters)
{
    while(1)
    {
        if(xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE)
        {
            /* Orange LED blinking 5 times */
            for (int i = 0; i < 5; ++i)
            {
                Orange_LED_High;
                vTaskDelay(500);
                Orange_LED_Low;
                vTaskDelay(500);
    	    }
        }
    }
}
```
當 `vLab02_Handler_task()` 成功接收到 semephore 時，Handler task 就會執行「橘燈閃爍 5 次」

## 使用方式

```c 
void vLab02_start (void *pvParameters)
{
    vLab02_sensor_init();
    xSemaphore = xSemaphoreCreateBinary();

    xTaskCreate(vLab02_task_1, "Lab02_task_1", 1000, NULL, 3,  NULL);
    xTaskCreate(vLab02_Handler_task, "Lab02_Handler_task", 1000, NULL, 2, NULL);

    vTaskDelete(NULL);
}

int main (void)
{
    ...
    
    /* USER CODE BEGIN 2 */
    xTaskCreate(vLab02_start, "Lab02_Start", 1000, NULL, 1,  NULL);
    vTaskStartScheduler();
    /* USER CODE END 2 */
    
    ...
}
```

## 問題與解決

### Q1. Sensor interrup 只會執行一次

將 `vLab02_sensor_init();` 加到 `ISR` 、 `vLab02_Handler_task` 或 `vLab02_task_1` 之中，達到 sensor register reinitialization 的效果。

### Q2. 如何在橘燈閃爍的途中，避免 sensor interrup 的執行？

等橘燈閃爍完之後再初始化 sensor register

```c 
void vLab02_Handler_task(void *pvParameters)
{
    while(1)
    {
        if(xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE)
        {
            /* Orange LED blinking 5 times */
            for (int i = 0; i < 5; ++i)
            {
                Orange_LED_High
                vTaskDelay(500);
                Orange_LED_Low
                vTaskDelay(500);
    	    }
            /* reset motion sensor registers */
            vLab02_sensor_init();
        }
    }
}
```