摘要
使用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生成的二进制文件
- bootloader.bin : 引导程序
- partition.bin : 分区地址
- blink.bin : 应用程序(反编译生成的blink.dump.s和源程序main.s不一致)
实现
git clone https://github.com/TimSchulzRC/ESP32C3_Assembly_blink.git
cd ESP32C3_Assembly_blink
- 修改汇编程序指向正确的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 Outputli t0, 1 # 将立即数1加载到寄存器t0sll t0, t0, a1 # 将t0左移a1位,构建出LED对应的位掩码sw t0, 32(a0) # 将位掩码写入到GPIO输出使能寄存器,使能LED对应的GPIO输出toggle_led: # toggle_led标签,用于切换LED状态# Schalte LED an oder auslw t4, 4(a0) # 从GPIO输出寄存器加载当前状态到t4xor t4, t4, t0 # 通过异或操作切换LED对应的位状态sw t4, 4(a0) # 将新的状态写回GPIO输出寄存器,切换LEDli t5, 0 # 将立即数0加载到寄存器t5,用于计数li t6, 10000000 # 将立即数10000000加载到寄存器t6,作为延迟计数loop: # loop标签,用于创建延迟addi t5, t5, 1 # 将t5加1blt t5, t6, loop # 如果t5小于t6,则跳转到loop,继续延迟j toggle_led # 跳转到toggle_led,切换LED状态
- 编译
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"
- 烧录
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
- (可选)反编译
/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的灯闪烁 |
---|