Taiyou Kuo
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    4
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    ###### tags: `Embedded` # NUCLEO-H755ZI-Q 開發紀錄 [TOC] CPU: STM32H755ZI (Cortex(R) M7+M4) Flash: 2MBytes RAM: 1MB With L1 cache ![](https://i.imgur.com/dumYKfE.jpg) ## Architecture <!-- ![](https://i.imgur.com/e34dOLJ.png) --> ![](https://i.imgur.com/LQeYXSY.png) ### Performance Implication STM32H755 的架構十分複雜,我只針對我的應用紀錄可能的效能問題,詳細可參考 *AN4891* 和 *RM0399*。 當 M7 核心要存取位在 D2 Domain 的 SRAM1 時,他必須經過 **D1-to-D2 AHB bus** (上圖藍底標註位置),D1-to-D2 bus 是 D2 AHB bus 的其中一個 bus master,當 M7 核心和 M4 核心同時要存取 SRAM1 時,AHB bus 必須做出仲裁,仲裁演算法是使用 round robin,所以有可能 M7 先存取 SRAM1,也有可能 M4 先存取。 以下為手冊的描述 > #### AHB bus matrices in D2 and D3 domains > The AHB bus matrices in D2 and D3 domains ensure and arbitrate concurrent accesses from multiple masters to multiple slaves. This allows efficient simultaneous operation of high-speed peripherals. > The arbitration uses a **round-robin algorithm**. > > #### Cortex®-M4 D-bus >The Cortex®-M4 CPU uses the 32-bit D-bus, **via AHB bus matrix**, for literal load and debug access to memories containing code or data and mapped on addresses below 0x20000000: the internal SRAM1, SRAM2, SRAM3, and the internal Flash memory. > > [name=RM0399] 因此當 M4 在存取同為 D2 Domain 的 SRAM1 時,如果沒有 M7 同時存取 SRAM1,那此時速度會是最快的。當 M7 同時也要存取 SRAM1 時,M4 就會感受到存取速度變慢,因為有一部分的存取時間被 M7 (更準確說是 D1-to-D2 AHB bus) 搶佔了! ## MPU 參考 *AN4838* 因為 Cortex-M7 有 cache,因此設定適當的 MPU 參數十分重要,尤其是當使用: 1. FMC 2. Inter Core Communication 此時須確保兩個核心做記憶體區間是不被快取的 ## Bootloader 參考 *AN2606* ## MDMA 嘗試用 MDMA 寫入用 FMC 驅動的 LCD-TFT,~~但目前並不成功~~,已成功,關鍵是使用 repeated block transfer - 確認執行 `HAL_MDMA_Start_IT` 後 `HAL_MDMA_IRQHandler` 有被觸發 - 確認使用者註冊的 callback 函式 `MDMA_XferCpltCallback` 有被呼叫 ~~- 預期螢幕上會顯示黑色區塊,但未成功 <---- 現在在這裡 :cry:~~ [time=Mon, Sep 6, 2021 9:59 PM] - 使用 **repeated block transfer**,將 RAM_D1 的資料傳輸到 FMC,關鍵是每一個 block 不可超過 64KB (65536 byte),可參考我在 ST Community 詢問的 [MDMA 問題](https://community.st.com/s/question/0D53W000023XrPTSA0/problem-using-memtomem-dma-from-sram-to-fmc-on-stm32h755) ```c hmdma_mdma_channel40_sw_0.Init.Request = MDMA_REQUEST_SW; ``` ```c lcd.c extern MDMA_HandleTypeDef hmdma_mdma_channel40_sw_0; static void MDMA_XferCpltCallback(MDMA_HandleTypeDef *handle) { printf("hello %d\n", handle->State); } void StartlcdTask_MDMA(void *argument) { static uint16_t color_p[100 - 50 + 1][200 - 100 + 1] = {0}; lcd_init(); lcd_fill_screen(0x4243); HAL_MDMA_RegisterCallback(&hmdma_mdma_channel40_sw_0, HAL_MDMA_XFER_CPLT_CB_ID, MDMA_XferCpltCallback); lcd_prepare_to_write(100, 50, 200, 100); HAL_MDMA_Start_IT(&hmdma_mdma_channel40_sw_0, color_p, TFT_RAM, (200 - 100 + 1) * 2, (100 - 50 + 1)); while (1) { HAL_Delay(10); } } ``` 依據 STM32Cube Expansion Package for STM32H7 Series MDMA (AN5001) >### MDMA with external SDRAM >In order to move the image on the LCD, it is displayed and managed by MDMA as a regular memory-to-memory word copy. The start address of the SDRAM is configured in the LCD-TFT display controller and hardware decoded with the flexible memory controller (FMC). Each line is composed of pixels. Each pixel is 4 bytes in this case. Lines are not contiguous in the memory area. For this reason, a line is a block and there is an offset to go to the next line. A 320 × 240 regular image represents 240 blocks, each block being composed of 320 words. The first pixel of the screen (top-left corner) corresponds to the start address (the lowest address value) of the external memory. The screen has a resolution of 640 × 480 allowing the image to be moved inside the entire screen. ## TIM ### N-Pulse Generation 如何產生 N 個 pulse?答案是使用 Timer 的 One Pulse Mode 搭配 Repetition Counter (RCR) Repetition Counter 的值 + 1 就等於產生的 Pulse 數 首先呼叫以下函式啟用 One Pulse Mode (以 TIM15 為例) ```c HAL_TIM_OnePulse_Start(&htim15, TIM_CHANNEL_1); ``` 事實上 One Pulse Mode 多半為硬體觸發,但我想寫在軟體裡,因此使用以下函式觸發一次 One Pulse Mode,等到 repetition counter underflow 時會自動停止 Timer。 ```c __HAL_TIM_ENABLE(&htim15); ``` :::warning ~~`HAL_TIM_OnePulse_Start` 只適用於 Channel 1 和 Channel 2,但其他 Channel 也可使用 One Pulse Mode,這時候需用 `HAL_TIM_PWM_Start`。~~ ``` HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_3); ``` 使用 RCR 控制輸出 N pulses 時,建議直接操作 TIM 暫存器而不是使用 HAL 函式庫,因為可以避免不必要的 Bug 產生 可參考 AN4776 > [time=Sat, Feb 17, 2024 2:15 AM] ::: #### 其他設定 ![](https://i.imgur.com/yVpAQto.png) ![](https://i.imgur.com/qkXiikJ.png) - ==PWM mode 2==: TIMx_CNT < TIMx_CCR1 時通道 1 為 inactive,否則為 active - CH Polarity High: active 定義為高電平 - Pulse: Duty Cycle 下圖為範例,大方波週期為 2 Sec,小方波週期為 0.9 sec,當大方波為 rising edge 時觸發小方波 兩個方波 duty cycle 都為 50% ![](https://i.imgur.com/9Ft60pM.jpg) ### ==Trigger Another Timer== 如果要用的 TIM 沒有提供 RCR Register (如 TIM2~5),如何產生 N 個 Pulse 呢?答案是使用拿有 RCR Register 的 TIM 來觸發 TIM2~5。 下圖為 TIMx internal trigger connection,Slave TIM 為我們想觸發的 TIM,而 ITRx 為觸發源的 TIM,這張表格列出了哪些 TIM 可以串接再一起 ![](https://i.imgur.com/Ez9SgJQ.png) 由兩個重要的暫存器控制 - TS[4:0]: Trigger selection - SMS[3:0]: Slave mode selection - 0110: Trigger Mode - The counter starts at a ==rising edge== of the trigger TRGI (but it is not reset). Only the start of the counter is controlled ### Motor Control https://www.aliexpress.com/item/1005002927091186.html ### Reference - auto-reload preload: https://www.cnblogs.com/birdBull/p/15469791.html - https://www.digikey.tw/en/maker/projects/getting-started-with-stm32-timers-and-timer-interrupts/d08e6493cefa486fb1e79c43c0b08cc6 - https://www.youtube.com/watch?v=QMAgD9SS5_E&ab_channel=STMicroelectronics ## I2C https://www.ti.com/lit/an/slva704/slva704.pdf ## LVGL 中文字體 ![](https://i.imgur.com/xljFbs5.jpg) 原本 lvgl 預設支援的中文字體小於一千個 (中日韓文共一千字),遇到出現頻率較少的字就無法顯示,例如 "疫苗" 兩個字在 lvgl 就無法顯示出來,而且字體大小只有 8, 16 兩種選擇,加上字體本身也不好看,所以必須要自訂字體。 lvgl 的官網就提供工具可以產生所需要的 bitmap https://lvgl.io/tools/fontconverter 選擇你要的字體,我選擇 Noto Sans TC,==Name== 設定為 notosans_24,大小為 24,Bpp 設定 2bit-per-pixel,最後一欄 ==Symbols== 則是最重要的,要填入支援的字 我使用 github 上有人分享的常用中文字,總共 3760 個字,應付一般應用已經綽綽有餘了。 https://github.com/kaienfr/Font/blob/master/learnfiles/chinese%E7%AE%80%E7%B9%81%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8.txt 所有可印出的 ascii 字元: https://gist.github.com/alexlauerman/8569945 而產生的 .c 檔怎麼使用呢,如果使用 STM32CudeIDE 可以丟入 Core/Src 中,並修改 lv_conf.h 的內容: ```c /*Optionally declare custom fonts here. *You can use these fonts as default font too and they will be available *globally. E.g. #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(my_font_1) *LV_FONT_DECLARE(my_font_2)*/ #define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(notosans_24); /*Always set a default font*/ #define LV_FONT_DEFAULT &notosans_24 ``` 重新編譯就能使用了 :::danger :warning: 中文字體庫會佔 flash 不少空間,在我的板子上就用掉了大約 700KBytes。 ::: 這時候可能有些 awesome font 定義的 icon 不能使用 可以用這個 ttf https://github.com/lvgl/lvgl/files/6082668/Font.Awesome.5.Free.for.LVGL.zip ## LVGL PC Simulator on **Windows** ### :arrow_right: **請直接參考這篇: [lvgl 使用記錄](https://hackmd.io/@st9540808/ByxcMmx_s)** 用了一下 lvgl **強力推薦**的 code block 開發環境真的覺得有夠難用的,不但在設定編輯器字體直接給我當掉,而且還沒辦法看到 console 輸出,一氣之下決定改用 Eclipse。 首先有幾個步驟 - 先到 [lv_sim_eclipse_sdl](https://github.com/lvgl/lv_sim_eclipse_sdl) 把專案 clone 下來 - 安裝 Eclipse,我是選 Eclipse IDE 2021‑06,安裝時選 C/C++ development - 安裝 mingw,我選擇 [MSYS2](**https://**) - 原本 mingw installer 到 2013 就沒更新了,而且只支援 gcc 6,MSYS2 支援到 gcc 10 - 用 MSYS2 要安裝額外的套件也很方便,簡單輸入個命令就可以了 - 用 `pacman -S <package name>` 可查看此套件所有支援的硬體架構 :heavy_check_mark: 很方便 - 可參考以下安裝方法 ```shell pacman -S mingw-w64-x86_64-gcc pacman -S mingw-w64-x86_64-libpng pacman -S mingw-w64-x86_64-SDL2 ``` - 安裝 MSYS2 把路徑加到 PATH 環境變數,在我的機器上是以下路徑 ``` C:\msys64\mingw64\bin ``` - 開啟 Eclipse,把剛剛 clone 下來的專案匯入到 Eclipse 中 - Project ➔ Properties ➔ C/C++ Build ➔ Tool Chain Editor 中的 Current toolchain 選擇 MingGW GCC - 按下 :hammer: 按鈕組建專案,這時候應該會遇到 lv_drivers/display/fbdev.c 編譯錯誤,簡單把它從專案排除 (右鍵 Resource Configurations ➔ Exclude from Build) 就可以了 :::info 可略過這步 - 編譯完成後按下 Run 箭頭符號,這時候應該會跟你說找不到 lv_sim_eclipse_sdl,因為本專案是給 Linux 使用的,在 Windows 會變成 lv_sim_eclipse_sdl.exe。 - 首先 refresh 這個專案應該就能看到 lv_sim_eclipse_sdl.exe - 修改 pc_simulator.launch 第五行 ```xml=5 <stringAttribute key="org.eclipse.cdt.launch.PROGRAM_NAME" value="Debug/lv_sim_eclipse_sdl.exe"/> ``` 參考: https://github.com/lvgl/lv_sim_eclipse_sdl/issues/97 ::: :warning: Eclipse 內建的 console 輸出好像會卡在緩衝區,直到程式結束才印出來,因為我需要看即時的 console 的輸出,所以我都是在**資料夾直接點擊執行**。 執行結果 ![](https://i.imgur.com/zLEk8Bg.png) :warning: 另外如果遇到中文沒辦法正常顯示,需要把 Encoding 改成 UTF-8 ## TIM :::info 先看 - [General-purpose timer cookbook for STM32 microcontrollers]( https://www.st.com/resource/en/application_note/dm00236305-generalpurpose-timer-cookbook-for-stm32-microcontrollers-stmicroelectronics.pdf) - [STM32 cross-series timer overview](https://www.st.com/resource/en/application_note/an4013-stm32-crossseries-timer-overview-stmicroelectronics.pdf) ::: 好難用 參考: - https://micromouseonline.com/2016/02/03/tim3-arr-regular-interrupts-stm32f4/ - https://www.cnblogs.com/milton/p/15028413.html ### Encoder Mode ![](https://i.imgur.com/8dmFf7m.jpg) 我需要分辨 A 相和 B 相是誰領先 90 度,就必須用 Timer Encoder Mode 解決。 參考: - http://elastic-notes.blogspot.com/p/cubemx-stm32-encoder-interface.html - https://deepbluembedded.com/stm32-timer-encoder-mode-stm32-rotary-encoder-interfacing/ - https://blog.csdn.net/qq_17525633/article/details/116719309 > [time=Sun, Mar 6, 2022 5:29 PM] ## Inter Core Communication 如何在兩個核心之間 (M7 & M4) 溝通呢 (AKA 異核通訊)?最簡單的方式是使用 Shared Memory 模型,我們可直接拿 CMSIS RTOSv2 提供的 Message Queue 來用。 ### Native Message Queue - 優點:使用簡單,能快速上手 - 缺點:因為是使用輪詢檢查 Queue 中是否有資料,會造成 CPU 資源浪費 :warning: 要先設定適當的 MPU 參數,否則可能會拿到非預期的資料 ![](https://i.imgur.com/no9waIa.png) 將 Message Queue 的緩衝區位址定義在 RAM_D3 中 (D3 Domain),這必須要修改 linker script (.ld 檔)。 來看看怎麼修改 ld 檔囉,首先開啟 CM7 和 CM4 的 STM32H755ZITX_FLASH.ld,在檔案最下面加入這幾行 ```diff /DISCARD/ : { libc.a ( * ) libm.a ( * ) libgcc.a ( * ) } + .RAM_D3 : + { + . = ALIGN(4); + KEEP(*(.RAM_D3)) + KEEP(*(.RAM_D3*)) + } >RAM_D3 ``` M4 預設沒有定義 RAM_D3,因此要額外定義 ```diff MEMORY { FLASH (rx) : ORIGIN = 0x08100000, LENGTH = 1024K RAM (xrw) : ORIGIN = 0x10000000, LENGTH = 288K + RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K } ``` Common\msgqueue.h ```c #ifndef INC_MSGQUEUE_H_ #define INC_MSGQUEUE_H_ extern uint32_t cb_mem[40]; extern uint32_t mq_mem[2]; extern osMessageQueueAttr_t queue_attr; #endif /* INC_MSGQUEUE_H_ */ ``` Common\msgqueue.c ```c #include "cmsis_os2.h" #include "FreeRTOS.h" #include "queue.h" #include "msgqueue.h" /* put buffer inside D3 Domain (SRAM4) */ uint32_t cb_mem[40] __attribute__((section(".RAM_D3.cb"))); uint32_t mq_mem[80] __attribute__((section(".RAM_D3.mq"))); osMessageQueueAttr_t queue_attr = { .name = "led_queue", .attr_bits = 0, .cb_mem = cb_mem, .cb_size = sizeof(cb_mem), .mq_mem = mq_mem, .mq_size = sizeof(mq_mem), }; ``` #### Application Code 以下示範如何在兩個核心之間交換資料,CM7負責傳資料而CM4負責接收,使用的是 cmsis os (FreeRTOS) 的 Message Queue,傳送資料的大小及格式需自行定義,在這裡為 `hmi_write_rq_t` 的結構體。 CM7 ```c #include "cmsis_os2.h" #include "msgqueue.h" osMessageQueueId_t msgq_hmi_write_rq; hmi_write_rq_t write_rq; void Startm7Task(void *argument) { write_rq.dm_start = 38; write_rq.length = 2; msgq_hmi_write_rq = osMessageQueueNew(10, sizeof(hmi_write_rq_t), &queue_attr); while (1) { write_rq.dm_start++; osMessageQueuePut(msgq_hmi_write_rq, &write_rq, 0, osWaitForever); osDelay(50); } } ``` CM4 ```c osMessageQueueId_t msgq_hmi_write_rq; hmi_write_rq_t write_rq; void Startm4Task(void *argument) { static_assert(sizeof(hmi_write_rq_t) == 8, "sizeof(hmi_write_rq_t) != 8"); osStatus_t ret; msgq_hmi_write_rq = osMessageQueueNew(10, sizeof(hmi_write_rq_t), &queue_attr); while (1) { ret = osMessageQueueGet(msgq_hmi_write_rq, &write_rq, NULL, 0); if (ret == osOK) { debug_print("[%s] dm_start: %u, length: %u", __func__, write_rq.dm_start, write_rq.length); } osDelay(1); } } ``` 比較好的做法是當沒有資料時處理器能進入低功耗模式,當有資料時觸發中斷從啟動處理器並拿資料。 實作上可使用 ==Hardware Semaphore== (HSEM) ### HSEM 此為比較進階的功能,使用方法待補... ### Linker Scripts References - https://stackoverflow.com/questions/38239722/how-to-place-a-symbol-at-certain-address-via-the-linker-script - http://www.math.utah.edu/docs/info/ld_3.html#SEC16 ## EEPROM 使用別人已寫好的程式碼 https://github.com/nimaltd/ee24 ### Reference - [AT24C04](https://ww1.microchip.com/downloads/en/DeviceDoc/doc0180.pdf) - [AT24C256](https://ww1.microchip.com/downloads/en/DeviceDoc/AT24C128C-AT24C256C-Data-Sheet-DS20006270B.pdf) - tutorial: https://www.youtube.com/watch?v=-tV2pPXZ4VM&ab_channel=ControllersTech - 範例程式 https://controllerstech.com/eeprom-and-stm32/ - https://www.cnblogs.com/firege/p/9372029.html ## EtherCat Master 使用 SOEM 開源軟體實作 > [time=Fri, Dec 17, 2021 8:01 PM] 重新啟動 :1234: 檢查 ethernet link 是否上線 ```c // Check Link Status do { osDelay(50); HAL_ETH_ReadPHYRegister(&heth, 0, 1, &val); } while (!(val & (1 << 2))); ``` ## AWS Iot Core 來玩玩 AWS 雲端囉,首先要先能傳資料到 AWS IoT Core,才能進行接下來的工作 連線到 Iot Core,首先有幾部份要確認 - 在 AWS 上 - 確認 certificate 啟用 - 確認有適當的 policy - 在本機或板子上 - 把 private key, certifcate 寫入板子 - 把 endpoint 寫入板子 - 確認網路連線 https://docs.aws.amazon.com/freertos/latest/userguide/freertos-prereqs.html ``` StartDefaultTask() -> DEMO_RUNNER_RunDemos() runDemoTask() -> ``` <!-- ```javascript= const collection = "塔塔加管理中心" var AWS = require('aws-sdk'); const dynamo = new AWS.DynamoDB.DocumentClient(); exports.handler = async (event) => { // TODO implement var now = new Date().toLocaleString('zh-TW', { year:'numeric', month:'2-digit', day:'2-digit', hour:'2-digit', minute:'2-digit', second:'2-digit', hour12: false }); const params = { TableName: "ESUN_Tataka_Traffic", Item: { "site": collection, "timestamp": now, } }; dynamo.put(params, function(err, data) { if (err) { console.error("Unable to add device. Error JSON:", JSON.stringify(err, null, 2)); return {"message": "error"} } else { console.log(data) console.log("Data saved:", JSON.stringify(params, null, 2)); return {"message": "Item created in DB"} } }); }; ``` - aws demo clock rate? tim6/tim7 tim2~5 APB1 ```c if (determined) { big_iteration++; small_iteration++; } else { big_state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_6); small_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_15); if (big_state == GPIO_PIN_RESET && big_iteration == 0) { // start counting HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); big_iteration = 1; } if (small_state == GPIO_PIN_RESET && small_iteration == 0) { // start counting HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin); small_iteration = 1; } // 大型車 if (big_state == GPIO_PIN_RESET && small_state == GPIO_PIN_RESET) { large_car++; determined = true; } // 中型車 if (big_state == GPIO_PIN_RESET && small_state == GPIO_PIN_SET && big_iteration > 5) { medium_car++; determined = true; } // 機車 if (big_iteration == 0 && small_state == GPIO_PIN_SET) { motorbike++; determined = true; } } // Check if we need to reset if (big_iteration > 60 || small_iteration > 30) { big_iteration = 0; small_iteration = 0; IotLogInfo("{\"機車\": %d, \"中小型車\": %d, \"大型車\": %d}", motorbike, medium_car, large_car); determined = false; } ``` ```c // full iteration: 1.5sec, small iteration: 0.75sec while (1) { if (small_iteration) { small_iteration++; } else { small_state = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_15); if (small_state == GPIO_PIN_RESET) { small_iteration = 1; HAL_GPIO_TogglePin(LD3_GPIO_Port, LD3_Pin); } } if (big_iteration) { big_iteration++; } else { big_state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_6); if (big_state == GPIO_PIN_RESET) { // start counting HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin); big_iteration = 1; } } if (small_iteration > 30) { small_iteration = 0; } vTaskDelay(pdMS_TO_TICKS(25UL)); } ``` ```c while (1) { if (xSemaphoreTake(CountingSem, 50)) { if (LARGE_IS_ON) { vTaskDelay(pdMS_TO_TICKS(50UL)); if (LARGE_IS_ON) { xTaskCreate(buttom_large_triggered, "LARGE BOTTON", 1500, NULL, 3, &th[th_idx]); th_idx = (th_idx + 1) % 3; } else xSemaphoreGive(CountingSem); } else xSemaphoreGive(CountingSem); } if (iter++ > 1) { iter = 0; IotLogInfo("Semaphore: %d", uxSemaphoreGetCount(CountingSem)); } vTaskDelay(pdMS_TO_TICKS(50UL)); } ``` --> ## STM32 Modbus 使用 https://github.com/alejoseb/Modbus-STM32-HAL-FreeRTOS 這個函式庫,此函式庫支援 STM32,且能用 RS485 作為底層通訊。STM32H7 的 UART 已提供 hardware RS485 DE 控制腳位,所以不需要自行用 GPIO 控制讀或寫,啟用方式如下圖。 ![](https://i.imgur.com/ug9KBiJ.png) 接著根據驅動器參數設定 baud rate, parity bit, 等等,就能使用函式庫發送 modbus 封包,modbus 初始化方式如下: ```c modbusHandler_t ModbusH; uint16_t ModbusDATA[8]; ModbusH.uModbusType = MB_MASTER; ModbusH.port = &huart4; ModbusH.u8id = 0; ModbusH.u16timeOut = 1000; ModbusH.EN_Port = NULL; ModbusH.u16regs = ModbusDATA; ModbusH.u16regsize = sizeof(ModbusDATA) / sizeof(ModbusDATA[0]); ModbusH.xTypeHW = USART_HW; ModbusInit(&ModbusH); ModbusStart(&ModbusH); ``` - `EN_Port`: 因為使用 hardware RS485 DE,所以此設定為 `NULL` - `u16regs`: 使用要自行分配一段記憶體區間,當要發送或接收,都會把資料寫進這裡 ### 讀取一個暫存器 來試試看用 modbus 讀暫存器囉 ```c modbus_t telegram; telegram.u8id = 1; telegram.u8fct = 3; telegram.u16RegAdd = 0x0; telegram.u16CoilsNo = 1; telegram.u16reg = ModbusDATA; ModbusQuery(&ModbusH, telegram); u32NotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); if (u32NotificationValue != ERR_OK_QUERY) { HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET); } ``` - `u8id`: slave number - `u8fct`: function code, 3 代表讀 - `u16RegAdd`: register number - `u16CoilsNo`: number of register to read or write ### 寫入暫存器 來寫入暫存器囉,試試看寫入編號為 5 的暫存器,寫入的值為 45。 ```c modbus_t telegram; telegram.u8id = 1; telegram.u8fct = 16; telegram.u16RegAdd = 0x5; telegram.u16CoilsNo = 1; telegram.u16reg = ModbusDATA; ModbusDATA[0] = 45; ModbusQuery(&ModbusH, telegram); u32NotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); if (u32NotificationValue != ERR_OK_QUERY) { HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET); } ``` ### References - https://www.ni.com/zh-tw/innovations/white-papers/14/the-modbus-protocol-in-depth.html - https://github.com/alejoseb/Modbus-STM32-HAL-FreeRTOS - https://electronics.stackexchange.com/questions/384306/stm32f4-how-are-preemption-priorities-and-sub-priorities-used - https://vkinngworld.blogspot.com/2016/04/plcmodbus.html ## FreeRTOS > Low priority numbers denote low priority tasks. The idle task has priority zero (tskIDLE_PRIORITY). Timer 的 callback 能不能設定優先權?根據 FreeRTOS 上關於 [Software Timers](https://www.freertos.org/Configuring-a-real-time-RTOS-application-to-use-software-timers.html) 的敘述,`configTIMER_TASK_PRIORITY` 可設定優先值 ## Cortex-M7 Priority 紀錄一下心得 ![](https://i.imgur.com/2V6FRXj.png) :::warning 上圖的 Priority 為 ST 定義的,實際 Exception Number 是從 1 開始,有 15 個是給 Built in Exceptions (0x0000_0004 到 0x0000_003c+4,共 60 個 byte) ::: - Exception Number: Cortex-M 為每個 exception 都定義了一個數值,這個數值無法被修改,==Exception Number 結合 Priority/Sub-Priority 的數值共同決定這個 exception 的優先權==,Priority 較高的 exception 會先執行,如果兩個 exception 的 Priority 數值相等,則 exception 數值較低的會先執行 - Priority/Sub-Priority: 每個 exception 都有一個大小為 4bit 的 Priority/Sub-Priority 值,預設是 Priority 佔 4bit 而 Priority 佔 0bit,但這是可以調整的 參考: https://interrupt.memfault.com/blog/arm-cortex-m-exceptions-and-nvic ## WIFI/BLE 使用介面為 AT Command https://docs.espressif.com/projects/esp-at/en/latest/AT_Command_Set/Basic_AT_Commands.html#cmd-uartc ## 中斷延遲 開啟 FPU 有可能增加中斷的延遲時間,因為系統需要把 FPU 暫存器的值存到 stack 上,因此我設計一項實驗看我從觸發中斷,到中斷實際執行所花的時間 直接看結論,設定 M4 Clock 為 240MHz,在我的板子上 (NUCLEO-H755ZI-Q),測出的中斷延遲如下表 | Floating-point ABI | Compiler Optimization | latency (ns) | | -------- | -------- | -------- | | -mfloat-abi=hard | -O0 | ~300ns | | -mfloat-abi=softfp | -O0 | ~300ns | | -mfloat-abi=hard | -O1 | ~160ns | `-O1` 的延遲約為 39 個 Clock Cycle 實驗方式如下,我用 LD1 的 GPIO 當作訊號源打到 EXTI0_IRQn,當 LD1 訊號為正緣或負緣則會觸發中斷 (EXTI0_IRQHandler),因此看 LD1 和 INTR_CH2 的時間差即可知道中斷延遲 ```c void delay_us(uint16_t steps) { // 1/240 microseconds per step __HAL_TIM_SET_COUNTER(&htim6,0); // set the counter value a 0 while (__HAL_TIM_GET_COUNTER(&htim6) < steps); // wait for the counter to reach the us input in the parameter } while (1) { HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin); delay_us(500); } ... void EXTI0_IRQHandler(void) { /* USER CODE BEGIN EXTI0_IRQn 0 */ // HAL_GPIO_WritePin(LD1_GPIO_Port, LD1_Pin, GPIO_PIN_RESET); HAL_GPIO_TogglePin(INTR_CH2_GPIO_Port, INTR_CH2_Pin); /* USER CODE END EXTI0_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); /* USER CODE BEGIN EXTI0_IRQn 1 */ /* USER CODE END EXTI0_IRQn 1 */ } ``` ### `-O0` 的實驗結果 ![](https://i.imgur.com/mwXCYwA.jpg) ### `-O1` 的實驗結果 ![](https://i.imgur.com/X495f2v.jpg)

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully