esp32笔记[18]-使用汇编在riscv架构的esp32c3点灯

摘要

使用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.

posted @ 2024-09-07 00:20  qsBye  阅读(24)  评论(0编辑  收藏  举报