---
# System prepended metadata

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

---

2021 嵌入式作業系統分析與實做 Lab5 report
===
[Lab5](https://docs.google.com/presentation/d/1DXcgOGfFjXAtjCBJNEZ9rX9eH_8PEqDY9kw91_gjlpg/edit#slide=id.gd7a91f09cc_0_99)
contribute by `Liao712148`
###### tags: `FreeRTOS`
### Lab要求
* Step1:
    * Import the project

* Step2:
    * Implement printf(lab5_0)
    * 修改printf.h使得專案可以直接使用printf輸出結果到console(uart tx)

* Step3:
    * Install the sd card device to developement board(lab5_1) 
* Step4:
    * Read and write file by multi-task(lab5_2)
* Step5:
    * Playback the wav file to DAC(lab5_3, lab5_4)
    * 請disable掉task1與task2 並且enable task3
    * 請在task3中加入適當的函式 使得wav檔案可以順利播放(都在waveplayer.c中)(lab5_3)
    * 請放入多個wav檔案 並且利用藍色的push button實做按下時 可以播放下一首歌(lab5_4)

### function介紹

FRESULT f_open (FIL* FileObject, const XCHAR* FileName, BYTE ModeFlags );

引數：
*    FileObject :將被建立的檔案物件結構的指標。
*    XCHAR :要被操作(打開或建立)的檔案名稱。
*    ModeFlags :描述被打開後的檔案，應該要如何操作。
    *    FA_READ :指定讀訪物件,可以從檔案中讀取資料。
    *    FA_WRITE :從buffer中打資料寫入該檔案。
    *    FA_OPEN_EXISTING :開啟檔案,如果檔案不存在則開啟失敗。
    *    FA_OPEN_ALWAYS :如果檔案存在則開啟，否則創立檔案。
    *    FA_CREATE_NEW :創立一個新的檔案，如果該檔案已經存在了則建立失敗。
    *    FA_CREATE_ALWAYS :創立一個新的檔案，如果檔案已經存在就將其覆蓋。

FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw )

引數：
*    fp :要被操作(寫入)的檔案。
*    buff :把要寫入的資料放在buffery，再從buffer寫入fp。
*   btw :要寫入檔案的資料數目。

FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br)

引數：
*    fp :要被操作(讀取)的檔案。
*    buff :把檔案中的資料讀取到buffer中。
*    btr :要從檔案中讀取的資料數目。

FRESULT f_close (FIL* FileObject)

引數：
*    FileObject :把檔案關閉。



#### Step 1:
一般要開發使用像 `STM32` 這類的單片機，如果要做 Debug 將訊息往外輸出 最常用的就是 UART 串口。根據LAB3 , LAB4都是利用`HAL_UART_Transmit()`將字串傳送，在透過USB to TTL的RX與開發板的TX相連。這樣就可以透過console，即可監看要輸出的資料。所以根據此概念，因此將改寫printf.h中的內容
```cpp=
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the EVAL_COM1 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart4, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}
```
結果如下
![](https://i.imgur.com/Q75hafn.png)

[參考資料](http://www.makdev.net/2018/06/printf-uart.html)


#### Step 2:
**請接板子後面的針腳 接正面容易鬆脫**
*    pe4請接到sd卡板子的cs
*    ttl的gnd與sd卡板子的gnd接到開發板gnd
*    sd卡板子的vcc接到開發板5v
*    sd卡板子的sck接到開發板的PA5
*    sd卡板子的MISO接到開發板的PA6
*    sd卡板子的MOSI接到開發板的PA7
*    ttl的RX接到開發板的TX

#### Step 3:
Task1 是負責將buffer中的資料寫入到檔案(TEST.TXT)中，而Task2 是負責將檔案中(TEST.TXT)的資料讀入到buffer中，所以如果一個task在使用檔案資料時，也有另外一個task要對該檔案的內容做操作，就會發生錯誤。
所以在某個task在操作檔案時，會希望其他task不能對其檔案做修改，因此可以用`taskENTER_CRITICAL()`,`taskEXIT_CRITICAL()`,來讓task是讀寫檔案時不會被其他task打斷。也可以用`mutex()`,`vTaskSuspendAll()`,`xTaskResumeAll()`，來確保使用檔案這個動作是`atmoic`。

```cpp=
void Task1(void *pvParameters) {
	uint8_t count=0;
	for (;;) {
		FIL test;
		if ( xSemaphoreTake(xMutex, 0xffff) == pdTRUE ) {
		    if (f_open(&test, "TEST.TXT", FA_WRITE) != 0) {
			    printf("open file err in task1\r\n");
		    } else {
			    printf("file open ok in task1\r\n");
		    }
		    char buff[13];
		    memset(buff, 0, 13);
		    if(count%2){
			    sprintf(buff, "%s", "DataWriteT");
		    }else{
			    sprintf(buff, "%s", "DataWriteF");
		    }
		    UINT byteswrite;
		    f_write(&test, buff, 12, &byteswrite);
		    f_close(&test);
		}
		xSemaphoreGive(xMutex);
		count++;
		vTaskDelay(1);
	}
}
```
```cpp=
void Task2(void *pvParameters) {
	while (1) {
		FIL test_1;
		if ( xSemaphoreTake(xMutex, 0xffff) == pdTRUE ) {
		    if (f_open(&test_1, "TEST.TXT", FA_READ) != 0) {
			    printf("open file err in task2\r\n");
		    } else {
			    printf("file open ok in task2\r\n");
		    }
		    char buff[11];
		    memset(buff, 0, 11);
		    UINT bytesread;
		    f_read(&test_1, buff, 11, &bytesread);
		    printf("data read is %s in task 2\r\n", buff);
		    f_close(&test_1);
		}
		xSemaphoreGive(xMutex);
		vTaskDelay(1);
	}
}
```
有把critical section保護好的話(一次只有一個task去讀取SD卡)，的話會出現以下畫面。
![](https://i.imgur.com/GQhRrQB.png)


### critical section , mutex, vTaskSuspendAll 比較
critical section : 只能用在同一個process下產生的不同的threads，不能在process之間使用，Critical Section本身不是核心物件，相關函式的呼叫一般都在使用者模式內執行，執行時間較快。可以視為是light weight mutex。如果有設立`configMAX_SYSCALL_INTERRUPT_PRIORITY`，會把priority低於 `configMAX_SYSCALL_INTERRUPT_PRIORITY`的interrupt給disable掉。context switch也是受interrupt trigger，所以在cirtical section 中的task不會被preemptive掉。

mutex : 可以在不同的processs之間使用，Mutex 是核心物件，相關函式的執行需要使用者模式（User Mode）到核心模式（Kernel Mode）的轉換，需要用到system call。所以叫耗費時間。也有priority inheritence的機制可以盡量避免priority inversion。

vTaskSuspendAll : 會suspend scheduler 所以之後就不會有context switcht產生。但是不會disable interrupt 所以依舊有可能跳去執行ISR，可是如過 interrupt是要去呼叫某個task，該task不會馬上被執行，要等到scheduler恢復後才會被調用。

[參考資料](https://stackoverflow.com/questions/800383/what-is-the-difference-between-mutex-and-critical-section)
[參考資料](https://lionrex.pixnet.net/blog/post/51358802)
[參考資料](https://www.itread01.com/content/1549795324.html)

#### Step 4:
```cpp=
void Task3(void *pvParameters) {
	AUDIO_PLAYER_Start(0);
	for (;;) {
		AUDIO_PLAYER_Process(TRUE);
	}
}
```
第`2`行 :參數是0表示會從檔案中第0個index開始播放。
第`4`行 :參數是TRUE表示當播完最後一個audoio file播完時會回到第一個audoio file開始播放。循環播放的意思

#### Step 5:
按下藍色的button會去trigger 一個 interrupt，而它的ISR會去修改AudioState，這個變數被定義在`waveplayer.c`中，是一個type為AUDIO_PLAYBACK_StateTypeDef的一個類似狀態機功能的變數，當中的狀態有AUDIO_STATE_NEXT(切換到下一個audio file),AUDIO_STATE_PREVIOUS(切換到上一個audio file),AUDIO_STATE_STOP(停止),AUDIO_STATE_RESUME(恢復),...,狀態。根據lab要求，我們會希望在按下blue buttom時會去切換到下一首。所以會透過ISR將AudioState的狀態修改為AUDIO_STATE_NEXT。
```cpp=
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
	BaseType_t xHigherPriorityTaskWoken;
	if (GPIO_Pin == GPIO_PIN_0) {
		xHigherPriorityTaskWoken = pdFALSE;
		AudioState = AUDIO_STATE_NEXT;/*修改AudioState的狀態*/
		if (flag == pdTRUE) {
			xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken);
		} else {
			//do nothing
		}

		HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_14);
		uint8_t data_1 = 0x5f | 0x80;
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_RESET);
		HAL_SPI_Transmit(&hspi1, &data_1, 1, 10);
		////	HAL_Delay(10);
		HAL_SPI_Receive(&hspi1, &data_1, 1, 10);
		HAL_GPIO_WritePin(GPIOE, GPIO_PIN_3, GPIO_PIN_SET);
		portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
	}

}
```


#### 心得
謝謝助教的幫忙。
SD卡常常讀不到，很擔心是不是做錯。從新插進SD卡板，==有可能==就可以讀的到。
記得耳機要插在開發板上，而不是PC上。
耳機不能是有麥克風功能的(三環)，有的話要轉接。