---
title: Documents of My work
tags: Research
---
# 開發環境
- OS of PC: ubuntu 16.04
- Discovery kit: STM32H745I-DISCO
- IDE: STM32CubeIDE
## 安裝環境
:::info
- 以下操作有 `$` 開頭的表示在 terminal 執行的指令
- 所有用到的指令跟參數如果不知道什麼意思的話,都去 google 一下,常用就記起來了
:::
1. 灌好一台 ubuntu 16.04 英文版
2. 安裝`中文輸入, git, vim `
1. 中文輸入怎麼安裝自己上網搜尋 `ubuntu 16.04 安裝新酷音`
2. 安裝 git `$ sudo apt-get install git`
3. 安裝 vim `$ sudo apt-get install vim`
3. 安裝 STM32CubeIDE
1. google 搜尋 `stm32cubeide`
2. 沒意外的話會到這個[網站](https://www.st.com/en/development-tools/stm32cubeide.html#get-software)
3. 註冊一個帳號並登入,下載檔案需要登入
4. 回到那個[網站](https://www.st.com/en/development-tools/stm32cubeide.html#get-software)
5. 選 `STM32CubeIDE-DEB STM32CubeIDE Debian Linux Installer`
1. 現在是用 1.4.0 版本,以後可能有新版本,如果擔心新版本會有問題可以指定版本
2. 點選 `Get Software`
3. 應該會下載到一個檔名很長的 zip 檔
```
en.st-stm32cubeide_1.4.0_7511_20200720_0928_amd64.deb_bundle_sh.zip
```
6. 解壓縮檔案得到一個 shell script
```
st-stm32cubeide_1.4.0_7511_20200720_0928_amd64.deb_bundle.sh
```
7. 用終端機開啟檔案所在的位置,增加執行權限
```
$ chmod +x st-stm32cubeide_1.4.0_7511_20200720_0928_amd64.deb_bundle.sh
```
8. 執行 shell script
```
$ sudo ./st-stm32cubeide_1.4.0_7511_20200720_0928_amd64.deb_bundle.sh
```
9. 過程中會看同意書還有一些選項,都選 `y`
10. 試試看能不能執行
:::info
如果視窗開不起來,有幾個可能
1. 沒有安裝 jre,試試看執行 `$ sudo apt-get install -f`
2. 執行檔權限怪怪的,解決辦法要應當時情況決定
3. 但正常情況我都沒有遇過這些問題
:::
1. 先在家目錄家創一個資料夾,等等用來裝專案檔案
```
$ mkdir -p ~/STM32Cube/lab1
```
2. 按一下 `ctrl` 跟 `alt` 中間那顆按鍵
3. 打 `stm32`,就會跳出來了,執行它
4. Workspace 選剛剛創的資料夾,路徑應該長這樣(apple 是你的 ubuntu 使用者帳號)
```
/home/apple/STM32Cube/lab1
```
5. 開始一個新的 STM32 project,選 `Start new STM32 project`
6. 它會要你選 MCU,我們是用開發板,所以上面 tab 先切到 `Board Selector`
7. 在 Commercial Part Number 上面打 `STM32H745I-DISCO`
8. 右邊只會有一張板子可以選,選了之後按 `Next`
9. Project Name 輸入 `lab1`,其他都不用動,按 `Finish`
10. 它會問你要不要初始化(initialize)週邊裝置(peripheral),選 `No`,因為我們不會用到所有週邊裝置
11. 它會問你要不要開起 configuration tool 的視窗,選 `Yes` 就可以了
12. 第一次開他要下載一些驅動裝置的 source code,等它一下
13. 成功開起來會面應該會像這樣

14. 到這邊表示成功了,可以先關掉視窗,lab1 會從這個地方繼續往下做。
4. Clone 我的 work 的專案 repo,裡面有原始碼和所有可能會用到的教學文件
```
$ mkdir -p ~/git
$ cd ~/git/
$ git clone https://github.com/apple11361/Tensorflow_Lite_Inference_on_Edge_Device.git
```
# 硬體開發板相關資訊
- Discovery kit: [STM32H745I-DISCO](https://www.st.com/en/evaluation-tools/stm32h745i-disco.html)
- MCU: [STM32H745XI
](https://www.st.com/en/microcontrollers-microprocessors/stm32h745xi.html)
- Flash: 2 Mbytes
- RAM: 1 Mbytes
- CPU: Cortex M4, Cortex M7
- little endian
- 240MHz
- 480MHz
- [MPU and cache](http://news.eeworld.com.cn/mcu/ic486426.html)
- 4-Gbyte on-board eMMC
## Memory map


## System architecture

# 如何創造一個 Model
:::info
我們實驗的 workload 就是各式各樣的 machine learning mode,所以必須要會自己創造 model。如果只是單純要看 inference performance 不需要管 training 的品質,但要有能力調整 model 結構,例如層數、input image size。
我們只用的工具是 Tensorflow 的 python API,從使用 Keras 設定好模型結構到輸出成 tflite 的 flatbuffer 格式,最後利用我們自己的 model converter 工具,轉換成我們的 work 可以使用的 model format。
目前我們有的 workload 如下:
1. Person detection
- Model size: 236,072 bytes
- Input image size: 96\*96\*1 bytes
- TFLite for Microcontrollers benchmark model
2. Image classification
- Model size:
- Input image size: 32\*32\*3 bytes
- MobileNet_v2_0.25_quant
3. Image classification
- Model size:
- Input image size: 32\*32\*3 bytes
- Evaluation model in CMSIS paper
4. Image classification
- Model size:
- Input image size: 32\*32\*3 bytes
- Evaluation model in CMSIS paper with quantization
:::
## 安裝環境
:::info
以下內容是參考[這篇教學](https://medium.com/@lufor129/%E5%82%BB%E7%93%9C%E5%BC%8Ftensorflow-keras%E5%AE%89%E8%A3%9D%E6%95%99%E5%AD%B8-730b235275d)
$ 開頭代表 terminal 指令
:::
1. 使用我們最一開始灌好的一台 ubuntu 16.04 英文版
2. 到 anaconda 的[網站](https://www.anaconda.com/products/individual)下載 Anaconda Installers for x86 linux
3. 下載下來是一個 shell script,執行 shell script
```
$ bash ~/Downloads/Anaconda3-2020.07-Linux-x86_64.sh
```
4. 按下 `Enter` 繼續安裝,看完同意書,輸入 `yes`
5. 安裝完後會問你要不要直接初始化 Anaconda3,選 `yes`
6. 重開一次 terminal 才能完成安裝(.bashrc 的問題)
7. 關閉自動載入 base environment(讓你的終端機平常在一般狀態)
```
$ conda config --set auto_activate_base False
```
8. 驗證安裝成功,輸入
```
$ conda list
```
應該要顯示出安裝的套件跟版本,確認 conda 是最新版本
```
$ conda update -n base -c defaults conda
```
9. 建立 conda 環境
```
$ conda create python=3.6 -n model_builder
```
10. 進入環境
```
$ conda activate model_builder
```
如果要退出環境,輸入下面指令
```
$ conda deactivate
```
11. 安裝需要的相關套件
```
$ conda install jupyter
$ conda install ipykernel
$ conda install numpy
$ conda install keras
$ pip install tensorflow==2.3.0
$ pip install tensorflow-model-optimization
$ pip install tflearn
$ pip install sklearn
$ python -m ipykernel install --user --name model_builder --display-name "model builder"
```
## 開始創造我們需要的 model
:::info
程式碼內容參考別人的 [open source](https://github.com/super13579/CNN_revolution)
:::
1. 創建一個資料夾來放我們寫的 python code
```
$ mkdir -p ~/git/Tensorflow_Lite_Inference_on_Edge_Device/5.My_Work/model_builder/vgg16
$ cd ~/git/Tensorflow_Lite_Inference_on_Edge_Device/5.My_Work/model_builder/vgg16
```
2. 執行 jupyter
```
$ jupyter notebook
```
3. 右上角 new 一個新的檔案,選剛剛裝的環境 model_builder
4. 開始寫程式,`shift+enter` 可以執行,結果會直接顯示在網頁界面
## 使用 PC 當作一個 model server 讓開發版下載 model
:::info
我們使用 netcat 來完成這件事,詳細 netcat 的使用可以上網搜尋
:::
1. 使用方法
```
nc -l [port] < [file_path]
```
2. 例如現在要讓開發版下載 mobilenet_v2.model,開發版中的程式碼會連線到 PC 的 IP 及 port 2996
```
nc -l 2996 < ./mobilenet_v2.model
```
# Labs
## lab1
- 實驗目標:練習連接開發板,看懂開機程式,使用 IDE 編譯、燒錄、Debug
- 實驗步驟:
1. 接續安裝環境創建的 lab1 專案檔
2. 用 micro USB 連接線,連接開發板的 ST-Link 跟 PC 的 USB 接口
3. 依照 git repo 中,[這份文件](https://github.com/apple11361/Tensorflow_Lite_Inference_on_Edge_Device/blob/master/0.Document/3.Getting%20started%20with%20projects%20based%20on%20dual-core%20STM32H7%20microcontrollers.pdf)的 3.1 節去做,在看的時候要細心一點,每一個步驟都不能做錯。(3.1.1 跟 3.1.2可以不用看)
4. 第一次燒錄的時候它會問你要不要更新 firmware,選 yes。
1. 按 `Open in update mode`
2. 按 `Upgrade`
3. 更新完之後關掉更新的視窗,繼續依照文件燒錄
5. 依照上面設定好之後,就可以燒錄執行了
1. 記得,Debug 啟動的順序一定是先 CM7,再 CM4
2. 開始執行的順序一定是先 CM4, 再 CM7
3. 原因是 Debug 啟動 CM7 會燒錄 CM7 跟 CM4 的程式碼
4. 執行是因為 CM7 裡面的開機程式會將 CM4 從 deep sleep mode 喚醒。如果順序顛倒,CM7 要喚醒 CM4 的時候 CM4 不在 deep sleep mode,就會 timeout 錯誤。
6. 兩個 core 都成功執行後,最後都會進入 while 迴圈,那表示 lab1 成功了。
7. 最後你可以嘗試先自己去理解兩個 core 裡面,從 main 開頭到進入 while 迴圈中間經過的步驟。註解都有寫清楚那行程式碼是作什麼的,不用 trace code 到最裡面,但是可以了解一下 dual core 怎麼開機的。
- 驗收方式:實際展示可以利用 IDE 從創建專案到讓兩個 core 執行到 while 迴圈,並簡單報告 main 做了什麼。
## lab2
- 實驗目標:練習寫簡單的開發版程式、使用 GPIO 及 code generator。
- 實驗步驟:
1. 依照之前的方式創建新專案檔案
1. 先在家目錄家創一個資料夾,等等用來裝專案檔案
`$ mkdir -p ~/STM32Cube/lab2`
2. Workspace 選剛剛創的資料夾,路徑應該長這樣(apple 是你的 ubuntu 使用者帳號)
```
/home/apple/STM32Cube/lab2
```
3. 開始一個新的 STM32 project,選 `Start new STM32 project`
4. 它會要你選 MCU,我們是用開發板,所以上面 tab 先切到 `Board Selector`
5. 在 Commercial Part Number 上面打 `STM32H745I-DISCO`
6. 右邊只會有一張板子可以選,選了之後按 `Next`
7. Project Name 輸入 `lab2`,其他都不用動,按 `Finish`
8. 它會問你要不要初始化(initialize)週邊裝置(peripheral),選 `No`,因為我們不會用到所有週邊裝置
9. 它會問你要不要開起 configuration tool 的視窗,選 `Yes` 就可以了
2. 在 configuration tool 的視窗切換到 GPIO,設定 PJ2(Port J Pin 2 的意思,microcontroller 的 PJ2 是連接到開發版的一顆 LED 燈)
1. `Pin Context Assignment` 設定 CM7

2. 關掉 lab2.ioc 視窗,他會問你要不要存檔及自動產生程式碼,都選 yes
3. 依照先前的 Debug 方式燒錄及執行程式,會看到開發版正面的綠色 LED 燈會亮。
4. 稍微閱讀一下 `main.c` 的 `static void MX_GPIO_Init(void)` 裡面的內容
5. 在 main() 裡面利用迴圈及 `HAL_GPIO_WritePin()` 讓 LED 閃爍。
- 驗收方式:實際展示可以讓 LED 閃爍,並簡單報告 GPIO 的初始化做了什麼。
## lab3
- 實驗目標:練習使用 HAL(Hardware Abstraction Layer) library,嘗試使用 UART 輸出 log 到電腦終端機。
- 實驗步驟:
1. 依照之前的方式創建新專案檔案
1. 先在家目錄家創一個資料夾,等等用來裝專案檔案
`$ mkdir -p ~/STM32Cube/lab3`
2. Workspace 選剛剛創的資料夾,路徑應該長這樣(apple 是你的 ubuntu 使用者帳號)
```
/home/apple/STM32Cube/lab3
```
3. 開始一個新的 STM32 project,選 `Start new STM32 project`
4. 它會要你選 MCU,我們是用開發板,所以上面 tab 先切到 `Board Selector`
5. 在 Commercial Part Number 上面打 `STM32H745I-DISCO`
6. 右邊只會有一張板子可以選,選了之後按 `Next`
7. Project Name 輸入 `lab3`,其他都不用動,按 `Finish`
8. 它會問你要不要初始化(initialize)週邊裝置(peripheral),選 `No`,因為我們不會用到所有週邊裝置
9. 它會問你要不要開起 configuration tool 的視窗,選 `Yes` 就可以了
2. 在 configuration tool 的視窗切換到 `Connectivity->USART3`
1. 將 Cortex-M7 打勾
2. Mode 選 `Asynchronous`

3. 關掉 lab3.ioc 視窗,他會問你要不要存檔及自動產生程式碼,都選 yes
3. 查詢 HAL library 看看 API 的使用方法,檔案位置在
```
/home/apple/STM32Cube/lab3/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_uart.c
```
4. 稍微瞭解一下 main.c 中 `MX_USART3_UART_Init()` 程式碼的內容
5. 使用 `HAL_UART_Transmit()` 撰寫程式輸出 `Hello World!\r\n` 到電腦終端機
6. 電腦終端機接收方法:
1. 基本的 `UART`, `USART`, `serial port`, `baud rate` 這些專有名詞的解釋不知道的話先去維基百科看一下
2. 開發版的 USART3 透過 ST-Link 界面 使用 USB 線連接到電腦,所以不需要另外再接一條線
3. 在 Linux 下我們使用 `screen` 這個軟體來接收 serial port 的輸入
4. 在終端機輸入 `$ sudo screen /dev/ttyACM0 115200`
5. 如果你的電腦有插其他裝置的話,其實不一定是 ttyACM0
6. 115200 是 baud rate,可以在程式碼看到我們有設定是 115200,電腦要用一樣的 baud rate 來接收,否則接收到的會是亂碼
7. 基本上,這樣執行之後,如果開發版有送東西來,會直接顯示在畫面上
7. 依照先前的 Debug 方式燒錄及執行程式,會看到電腦終端機收到開發版傳送的訊息。
- 驗收方式:實際展示將 `Hello World!` 顯示在電腦終端機上
## lab4
- 實驗目標:練習使用 HAL library,學習操作開發版上的 eMMC(Embedded Multi Media Card)。
- 實驗步驟:
1. 依照之前的方式創建新專案檔案
1. 先在家目錄家創一個資料夾,等等用來裝專案檔案
`$ mkdir -p ~/STM32Cube/lab4`
2. Workspace 選剛剛創的資料夾,路徑應該長這樣(apple 是你的 ubuntu 使用者帳號)
```
/home/apple/STM32Cube/lab4
```
3. 開始一個新的 STM32 project,選 `Start new STM32 project`
4. 它會要你選 MCU,我們是用開發板,所以上面 tab 先切到 `Board Selector`
5. 在 Commercial Part Number 上面打 `STM32H745I-DISCO`
6. 右邊只會有一張板子可以選,選了之後按 `Next`
7. Project Name 輸入 `lab4`,其他都不用動,按 `Finish`
8. 它會問你要不要初始化(initialize)週邊裝置(peripheral),選 `No`,因為我們不會用到所有週邊裝置
9. 它會問你要不要開起 configuration tool 的視窗,選 `Yes` 就可以了
2. 依照 lab3 的步驟設定 UART(之後的 lab 都會使用 UART)
3. 在 configuration tool 的視窗切換到 `Connectivity->SDMMC1`
1. 將 Cortex-M7 打勾
2. Mode 選 MMC 8 bits wide bus
3. SDMMC1 global interrupt 打勾

4. 關掉 lab4.ioc 視窗,他會問你要不要存檔及自動產生程式碼,都選 yes
4. 查詢 HAL library 看看 API 的使用方法,檔案位置在
```
/home/apple/STM32Cube/lab4/Drivers/STM32H7xx_HAL_Driver/Src/stm32h7xx_hal_mmc.c
```
5. 稍微瞭解一下 main.c 中 `MX_SDMMC1_MMC_Init()` 和 stm32h7xx_it.c 中 `SDMMC1_IRQHandler()` 的程式碼的內容
7. 在 `int main()` 中宣告以下變數
```=c
/* Because the block size is 512 bytes */
uint8_t pucTestString[512];
uint8_t pucStringInMMC[512];
```
7. 修改 linker script `RAM` 的位置以及 `stack` 起始位置
編輯 `STM32H745XIHX_FLASH.ld`,如下圖,必須這麼修改的原因是因為這張板子的記憶體架構

8. 使用 `HAL_MMC_ReadBlocks_DMA`, `HAL_MMC_WriteBlocks_DMA`, `HAL_MMC_GetCardState`,完成以下程式
```=c
strcpy(pucTestString, "Test eMMC\r\n");
/* Write the pucTestString to eMMC block 0 */
...
/* Read the eMMC block and store to pucStringInMMC */
...
/* Print pucStringInMMC to terminal */
HAL_UART_Transmit(&huart3, pucStringInMMC, strlen((char *)pucStringInMMC), ulTimeout);
```
9. 為了完成上述程式碼,你可能會需要實做 `HAL_MMC_TxCpltCallback()` 跟 `HAL_MMC_RxCpltCallback()`,`stm32h7xx_hal_mmc.c` 中的 `HAL_MMC_Init()` 會去 reference 上述兩個 callback functions
- 驗收方式:實際展示 `Test eMMC` 顯示在電腦終端機上,並解釋程式流程還有為什麼一定要改 linker script
## lab5
- 實驗目標:練習使用 FreeRTOS task API
- 實驗步驟:
1. 先熟讀[這份文件](https://github.com/apple11361/Tensorflow_Lite_Inference_on_Edge_Device/blob/master/0.Document/9.freertos-kernel-developer-guide.pdf)的 3.1~3.8 還有 3.12
2. 依照先前的步驟創建 `lab5`
1. 一樣要使用 UART
2. 還要在 configuration tool 的視窗切換到 `Middleware->FREERTOS_M7`,interface 選 CMSIS_V1
3. 然後 configuration tool 的視窗切換到 `System core->SYS`,`Timebase Source` 選 `TIM1`
4. 跟之前一樣關掉,儲存,產生程式碼(做很多次了,所以就不貼截圖了,有什麼問題可以直接問我)
3. 將 `StartDefaultTask()`刪掉改成以下兩個 task(記得上面的 function declaration 也要改)
```= c
/**
* @brief Task to print "I'm task1".
* @param argument: Not used
* @retval None
*/
void prvTask1(void *argument)
{
TickType_t ticks = 500 / portTICK_PERIOD_MS;
char *str = "I'm task1\r\n";
while(1)
{
vTaskDelay(ticks); /* Minimum delay = 1 tick */
HAL_UART_Transmit(&huart3, (uint8_t*)str, strlen(str), HAL_MAX_DELAY);
}
}
/**
* @brief Task to print "I'm task2".
* @param argument: Not used
* @retval None
*/
void prvTask2(void *argument)
{
TickType_t ticks = 500 / portTICK_PERIOD_MS;
char *str = "I'm task2\r\n";
while(1)
{
vTaskDelay(ticks); /* Minimum delay = 1 tick */
HAL_UART_Transmit(&huart3, (uint8_t*)str, strlen(str), HAL_MAX_DELAY);
}
}
```
4. 把 CMSIS 的 API 都改成 FreeRTOS 的 API
1. 將 `osKernelStart()` 改成 `vTaskStartScheduler()`
2. 將 `osThreadDef()` 跟 `defaultTaskHandle()` 改成 `xTaskCreate()`
- 驗收方式:看到類似以下輸出

有空的話可以閱讀 FreeRTOS source code,對你以後有幫助,只是要花很多時間。