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。 #### 心得 謝謝助教的幫忙。