esp32筆記[18]-使用匯編在riscv架構的esp32c3點燈

qsBye發表於2024-09-07

摘要

使用esp-idf工具鏈編譯彙編程式實現在riscv架構的esp32c3點燈.

Abstract

Compiling an assembly program using the esp-idf toolchain to blink an LED on the RISC-V based ESP32-C3.

原理簡介

esp32c3的riscv架構

[https://www.bilibili.com/read/cv21025938/]
[https://smist08.wordpress.com/tag/assembly-language/]
[https://www.elektor.com/risc-v-assembly-language-programming-using-esp32-c3-and-qemu-e-book]
[https://github.com/bigmagic123/esp32c3_bare_metal]
[https://esp32.com/viewtopic.php?t=27734]
[https://github.com/TimSchulzRC/ESP32C3_Assembly_blink]
Focusing on the CPU resource, Espressif states that the ESP32-C3 device supports the RV32IMC ISA. Therefore, the following base and extensions are supported:
• I - Base Integer Instruction Set, 32-bit
• M - Standard Extension for Integer Multiplication and Division
• C - Standard Extension for Compressed Instructions

專注於CPU資源,Espressif公司給出了ESP32-C3裝置支援RV32IMC指令集架構。因此,ESP32-C3支援RV32IMC ISA,即以下基礎和擴充套件:
• I - 32位基本整數指令集
• M - 整數乘法和除法的標準擴充套件
• C - 壓縮指令的標準擴充套件

(來源:Warren Gay - RISC-V Assembly Language Programming. Using ESP32-C3 and QEMU-Elektor International Media (2022)(Z-Lib.io))

esp32c3時脈頻率

  • ESP32-C3 只支援 40MHz 晶振(精度為 ± 10 p p m ),匹配電容 C15 和 C17 的取值需要經過測試之後再行確定。
  • 每個ESP32定時器都是使用APB時鐘(一般是80MHz)作為基礎時鐘。
  • 該定時器使用APB_CLK 時鐘源(通常為80 MHz),時脈頻率偏差小於±10 ppm,時間解析度為1 μs。

執行二進位制的一般過程

編譯->連結->執行,這裡使用匯編程式碼,但是還是需要使用esp-idf工具鏈進行編譯以生成正確的app.bin(blink.bin)檔案.

esp-idf生成的二進位制檔案

  1. bootloader.bin : 載入程式
  2. partition.bin : 分割槽地址
  3. blink.bin : 應用程式(反編譯生成的blink.dump.s和源程式main.s不一致)

實現

git clone https://github.com/TimSchulzRC/ESP32C3_Assembly_blink.git
cd ESP32C3_Assembly_blink
  1. 修改彙編程式指向正確的io口
    main/main.s
.data
LED_PIN: .word 12              # 定義一個名為LED_PIN的變數,值為12,通常代表LED所連線的GPIO編號
C3_GPIO: .word 0x60004000      # 定義一個名為C3_GPIO的變數,值為0x60004000,通常這是GPIO暫存器的基址

.text
.global app_main               # 宣告app_main函式為全域性可見
app_main:                      # app_main函式開始
        lw      a0, C3_GPIO    # 將C3_GPIO的值載入到暫存器a0,即GPIO暫存器的基址
        lw      a1, LED_PIN    # 將LED_PIN的值載入到暫存器a1,即LED的GPIO編號

        # Aktiviere den Output
        li      t0, 1          # 將立即數1載入到暫存器t0
        sll     t0, t0, a1     # 將t0左移a1位,構建出LED對應的位掩碼
        sw      t0, 32(a0)     # 將位掩碼寫入到GPIO輸出使能暫存器,使能LED對應的GPIO輸出

toggle_led:                    # toggle_led標籤,用於切換LED狀態
        # Schalte LED an oder aus
        lw      t4, 4(a0)      # 從GPIO輸出暫存器載入當前狀態到t4
        xor     t4, t4, t0     # 透過異或操作切換LED對應的位狀態
        sw      t4, 4(a0)      # 將新的狀態寫回GPIO輸出暫存器,切換LED

        li      t5, 0          # 將立即數0載入到暫存器t5,用於計數
        li      t6, 10000000   # 將立即數10000000載入到暫存器t6,作為延遲計數

loop:                          # loop標籤,用於建立延遲
        addi    t5, t5, 1      # 將t5加1
        blt     t5, t6, loop   # 如果t5小於t6,則跳轉到loop,繼續延遲
        j       toggle_led     # 跳轉到toggle_led,切換LED狀態
  1. 編譯
docker pull espressif/idf:release-v4.4
alias esp-idf='docker run --rm --privileged -v $PWD:/project -w /project -it espressif/idf:release-v4.4 bash -c'
esp-idf "cd /project && idf.py build"
  1. 燒錄
esptool.py --chip auto --port /dev/cu.wchusbserial56910187941 -b 460800 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 80m 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/blink.bin
  1. (可選)反編譯
/Applications/riscv-gnu-toolchain/xpack-riscv-none-elf-gcc-14.2.0-1/bin/riscv-none-elf-objdump -d build/blink.elf > build/blink.dump.s
cat build/blink.dump.s

效果

io12的燈閃爍

聯絡方式

如果對本文有疑問或者提出建議可評論區留言或者傳送郵件到2557877116@qq.com.

相關文章