2021 嵌入式作業系統分析與實做 Lab2 report
===
[Lab2](https://docs.google.com/presentation/d/1J0XgXx4LPXY-uybhQQt-9vBDyMNAwS4tl3ItCDovBIw/edit#slide=id.gccfe1e9b3d_0_941)
contribute by `Liao712148`
###### tags: `FreeRTOS`
### Lab要求
* 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.
#### 實做流程
##### Step.1
因為是透過**FreeRTOS**來實做所以要先
```cpp=
#include "FreeRTOS.h"
```
接著**FreeRTOS**是以task的方式來實行funcion所以要
```cpp=
#include "task.h"
```
兩者得include的順序不能對調,否則會報錯。
##### Step.2
因為是透過外在裝置,來觸發**interrupt**所以需要根據[Motion sensor](https://www.st.com/resource/en/datasheet/lis3dsh.pdf)對sensor的register做初始化。
```cpp=
#define LIS3DSH_WHO_AM_I_ADDR 0x0F
#define LIS3DSH_STAT_ADDR 0x18
#define LIS3DSH_CTRL_REG4_ADDR 0x20
#define LIS3DSH_CTRL_REG1_ADDR 0x21
#define LIS3DSH_CTRL_REG2_ADDR 0x22
#define LIS3DSH_CTRL_REG3_ADDR 0x23
#define LIS3DSH_CTRL_REG5_ADDR 0x24
#define LIS3DSH_CTRL_REG6_ADDR 0x25
#define LIS3DSH_STATUS_ADDR 0x27
#define LIS3DSH_OUT_X_L_ADDR 0x28
#define LIS3DSH_OUT_X_H_ADDR 0x29
#define LIS3DSH_OUT_Y_L_ADDR 0x2A
#define LIS3DSH_OUT_Y_H_ADDR 0x2B
#define LIS3DSH_OUT_Z_L_ADDR 0x2C
#define LIS3DSH_OUT_Z_H_ADDR 0x2D
#define LIS3DSH_ST1_1_ADDR 0x40
#define LIS3DSH_ST1_2_ADDR 0x41
#define LIS3DSH_THRS1_1_ADDR 0x57
#define LIS3DSH_MASK1_B_ADDR 0x59
#define LIS3DSH_MASK1_A_ADDR 0x5A
#define LIS3DSH_SETT1_ADDR 0x5B
/*根據SPI的概念
CS (Chip Select)。傳輸要開始的第一步,就是把對應的 CS 拉低(reset)。過程大致上像這樣:
master 把 slave 所對應的 CS 拉低。
開始傳資料:master 用 MOSI 傳資料給 slave; 而 slave 用 MISO 傳給 master。
結束時把 slave 對應的 CS 拉高(set)。*/
void MEMS_Write(uint8_t address,uint8_t data){
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);//因為三軸加速器的CS pin腳為PE3,所以要把PE3拉低
HAL_SPI_Transmit(&hspi1,&address,1,10);
HAL_SPI_Transmit(&hspi1,&data,1,10);
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3,GPIO_PIN_SET);
}
int main( void)
{
/* ... */
MEMS_Write(LIS3DSH_CTRL_REG1_ADDR,0x01);
MEMS_Write(LIS3DSH_CTRL_REG3_ADDR,0x48);
MEMS_Write(LIS3DSH_CTRL_REG4_ADDR,0x67);
MEMS_Write(LIS3DSH_CTRL_REG5_ADDR,0x00);
MEMS_Write(LIS3DSH_THRS1_1_ADDR,0x55);
MEMS_Write(LIS3DSH_ST1_1_ADDR,0x05);
MEMS_Write(LIS3DSH_ST1_2_ADDR,0x11);
MEMS_Write(LIS3DSH_MASK1_B_ADDR,0xFC);
MEMS_Write(LIS3DSH_MASK1_A_ADDR,0xFC);
MEMS_Write(LIS3DSH_SETT1_ADDR,0x01);
/* ... */
for(;;);
}
```
##### Step.3
* 為了能避免ISR持續occupy處理器太長的時間,所以我們會將原先ISR要執行的操作,份配給一個vHandlerTask,這樣能更加充分的利用處理器。
* 實現方式是利用ISR發出binary sempahore給一個高priority的vHandlerTask,vHandlerTask負責ISR要做的操作,vHandlerTask接收到binary semaphore後,就會從原先的block state進入到ready state,由於其priority較高,所以會pre-empt原先priority較低的task,優先被處理器被執行。
因為要讓ISR送出semaphore所以會呼叫[xSemaphoreGiveFromISR](https://www.freertos.org/a00124.html),需要先
```cpp=
#include "semphr.h"
```
HAL_GPIO_EXTI_Callback原先被定義在Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c
```cpp=
__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
*/
}
```
這個function是用weak修飾的,在linker的時候,global variable 和function同屬於strong,所以如果有相同名稱且引數相同的function的話會出現錯誤,所以用weak修飾function可以避免上述的情況。
在main.c當中定義
```cpp=
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken=pdFALSE;
xSemaphoreGiveFromISR(xSemaphore,&xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
```
在[xSemaphoreGiveFromISR](https://www.freertos.org/a00124.html)在成功寄出semaphore後,如果收到semaphore的task有比當前task還要高的priority則xHigherPriorityTaskWoken被改成pdTRUE。
portYIELD_FROM_ISR檢測xHigherPriorityTaskWoken若被改成pdTRUE,則會做context switch,不必等到tick interrupt發生。
##### Step.4
```cpp=
SemaphoreHandle_t xSemaphore =NULL;
int main( void)
{
/* ... */
xSemaphore=xSemaphoreCreateBinary();
/* ... */
for(;;);
}
```
利用[xSemaphoreCreateBinary](https://www.freertos.org/xSemaphoreCreateBinary.html)來產生binary semaphore,在FreeRTOS中,binary semaphore會拿來實現**synchorization**,而不能用來做~~mutual exclusion~~,因為會有[priority inverison](https://en.wikipedia.org/wiki/Priority_inversion)的問題。若要mutual exclusion要用[xSemaphoreCreateMutex](https://www.freertos.org/CreateMutex.html)。
##### Step.5
定義vHandlerTask
```cpp=
#define LONG_TIME 0xffff
void MEMS_Read(uint8_t address,uint8_t *data){
address |= 0x80;
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3,GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1,&address,1,10);
HAL_SPI_Receive(&hspi1,data,1,10);
HAL_GPIO_WritePin(GPIOE,GPIO_PIN_3,GPIO_PIN_SET);
}
static void vHandlerTask(void* pvParameters)
{
for(;;)
{
if(xSemaphoreTake(xSemaphore,LONG_TIME)==pdTRUE)
{
int data=0;
HAL_GPIO_TogglePin(Red_LED_GPIO_Port, GPIO_PIN_14);
for(int i=0;i<5;i++)
{
HAL_GPIO_WritePin(Orange_LED_GPIO_Port,GPIO_PIN_13,GPIO_PIN_SET);
vTaskDelay(1000);
HAL_GPIO_WritePin(Orange_LED_GPIO_Port,GPIO_PIN_13,GPIO_PIN_RESET);
vTaskDelay(1000);
}
MEMS_Read(0x5F,&data);
}
}
}
```
* 第`7`行:vHandlerTask會一直跟xSemaphore要binary semaphore,如果沒要到的話vHandlerTask會進入blocked state。直到拿到binary semaphore或是經過了LONG_TIME個Tick後才會回到ready state。
* [xSemaphoreTake](https://www.freertos.org/a00122.html)
* ==第`18`行:== 把motion sensor的register重新reset,否則無法產生下一個interrupt。
定義綠燈閃爍的task
```cpp=
static void vBackgorund(void)
{
for(;;)
{
HAL_GPIO_WritePin(Green_LED_GPIO_Port,GPIO_PIN_12,GPIO_PIN_SET);
vTaskDelay(1000);
HAL_GPIO_WritePin(Green_LED_GPIO_Port,GPIO_PIN_12,GPIO_PIN_RESET);
vTaskDelay(1000);
}
}
```
##### Step.6
初始化task
```cpp=
int main( void)
{
/* ... */
xTaskCreate(vBackgorund,"",1000,NULL,1,NULL);//較低的priority
xTaskCreate(vHandlerTask,"",1000,NULL,3,NULL);//較高的priority
vTaskStartScheduler();
while(1)
{
}
}
```
* 雖然vBackground的priority較低但是vHandlerTask需要等binary semphore所以會先進入到block state,此時ready list中只有vBackground,因此vBackground會被執行,綠燈閃爍。
* 當晃動開發板時,motion sensor會產生interrupt,隨後ISR會發出binary semaphore到xSemaphore。
* vHandlerTask拿到binary semaphore,所以從block state回到ready state。
* 由於vHandlerTask的priority較高,所以會pre-empt vHandlerTask,處理器改成執行vHandlerTask。
#### 心得
謝謝助教的幫忙。