---
# System prepended metadata

title: 2021 嵌入式作業系統分析與實做 Lab2 report
tags: [FreeRTOS]

---

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。



####  心得
謝謝助教的幫忙。











































































































