AHL_RISCV汇编程序

zh3g 发布于 2025-12-05 342 次阅读


文章目录
  • 重点讲新知识点 ——.align 4(四字节对齐)
  • 对比关 / 开中断的代码(一眼分清)
  • 新手怎么选寄存器?(通用原则)
  • 动态演示(0-6 循环的完整流程)
  • 明确sp的本质:栈指针是 “RAM 级别的临时存储管家”
  • 项目名字:GPIO-ASM-CH573-251119_zzz

    文末附上最后的项目代码

    .include "includes.inc" /* 包含汇编总头文件(需定义灯引脚/状态宏) */
    比较负责 回头看源代码详细研究
    
    /* 定义数据段(RAM区域,变量使用)=================== */
    .section .data
    hello_information: /* 功能提示字符串 */
    .ascii "\n"
    .ascii "------------------------------------------------------\n"
    .ascii "★金葫芦提示★ \n"
    .ascii "【工程名称】RISC-V三色灯组合颜色控制 \n"
    .ascii "【程序功能】实现红/绿/蓝单色、二组合、三组合颜色切换 \n"
    .ascii "【硬件连接】红/绿/蓝灯分别接对应GPIO引脚(见user.inc) \n"
    .ascii "【操作说明】观察硬件板上的三色灯,串口打印当前颜色组合 \n"
    .ascii "------------------------------------------------------\n\0"
    
    /* printf格式符定义(仅保留新逻辑需要的) */
    printf_format_count: /* 颜色切换次数提示 */
    .ascii "颜色切换次数mColorCount = %d\n\0"
    printf_format_color: /* 当前颜色组合提示 */
    .ascii "当前显示颜色:%s\n\0"
    
    /* 颜色名称字符串(与状态值一一对应) */
    color_name_red: .ascii "Red (single)\0"
    color_name_green: .ascii "Green (single)\0"
    color_name_blue: .ascii "Blue (single)\0"
    color_name_yellow: .ascii "Red+Green (yellow)\0"
    color_name_purple: .ascii "Red+Blue (purple)\0"
    color_name_cyan: .ascii "Green+Blue (cyan)\0"
    color_name_white: .ascii "Red+Green+Blue (white)\0"
    
    .align 4 /* 四字节对齐 */
    mMainLoopCount: /* 主循环计数(达到阈值切换颜色) */
    .word 0
    mColorChangeCount: /* 颜色切换总次数 */
    .word 0
    mColorState: /* 颜色状态标志(0-6对应7种颜色组合) */
    .byte 0 /* 0=红 1=绿 2=蓝 3=黄 4=紫 5=青 6=白 */

    .section .data  /*给汇编器发 “分区指令” —— 告诉汇编器:“接下来我写的内容,要放到程序运行时内存的「指定区域」里”。*/

    1. 标签:hello_information:

    汇编里的标签格式是「标签名 + 冒号」,作用是标记这段字符串在内存里的起始位置

    2. 伪指令:.ascii

    补充个小知识点:RISC-V 里和字符相关的伪指令还有.asciz(z=zero,会自动在字符串末尾加\0)、.byte(存单个字节)

    3. 注释:/* 功能提示字符串 */

    用/* */包裹的内容是 “多行注释”
    /* printf格式符定义(仅保留新逻辑需要的) */
    printf_format_count:      /* 颜色切换次数提示 */
        .ascii "颜色切换次数mColorCount = %d\n\0"
    printf_format_color:      /* 当前颜色组合提示 */
        .ascii "当前显示颜色:%s\n\0"
    标签printf_format_color::给 “当前颜色的打印模板” 贴名字,标记模板的起始位置。
    riscv
    .ascii "当前显示颜色:%s\n\0"
    → 定义颜色提示的打印模板,拆解:
    • 当前显示颜色::固定文字(不变);
    • %s:字符串占位符 → 程序运行时,把实际的颜色名称(比如 “红色”“绿蓝组合”“红绿蓝组合”)填到这个位置;
    • \n:换行符 → 打印完颜色后换行;
    • \0:结束符 → 标记模板结束。
    color_name_cyan:           .ascii "Green+Blue (cyan)\0"
    color_name_white:          .ascii "Red+Green+Blue (white)\0"
    .align 4                  /* 四字节对齐 */

    重点讲新知识点 ——.align 4(四字节对齐)

    先抛核心结论:.align 4告诉汇编器:把后续要存放的数据,放到 “4 的整数倍” 的内存地址上(比如地址 0x1000、0x1004、0x1008…,而不是 0x1001、0x1002)。

    .align 4的语法和规则

    • 格式:.align + 数字(数字是 2 的幂次,比如.align 4表示 2⁴?不!新手记:.align n表示 “按 2ⁿ字节对齐”)

      ✅ 纠正:.align 4的正确含义是 “按 4 字节(2²)对齐”(很多教材会简化说 “数字直接表示字节数”,新手这么记就行);

      ✅ 常见用法:.align 2(2 字节对齐)、.align 4(4 字节对齐,最常用)、.align 8(8 字节对齐)

    • 作用范围:只影响紧跟在它后面的代码 / 数据,对前面的颜色字符串无影响。

    这段颜色字符串后面,大概率会定义 “灯状态变量”“GPIO 引脚配置” 等4 字节的整数型数据(比如color_state是 32 位整数,占 4 字节),加.align 4能保证这些数据存到对齐的地址上,CPU 访问时更快、更稳定

    mMainLoopCount:       /* 主循环计数(达到阈值切换颜色) */
        .word 0
    mColorChangeCount:    /* 颜色切换总次数 */
        .word 0
    mColorState:          /* 颜色状态标志(0-6对应7种颜色组合) */
        .byte 0              /* 0=红 1=绿 2=蓝 3=黄 4=紫 5=青 6=白 */
    .word/.byte 的读写指令区别:
    • 读.word变量:用lw 寄存器, 标签(lw=Load Word,加载 4 字节);
    • 写.word变量:用sw 寄存器, 标签(sw=Store Word,存储 4 字节);
    • 读.byte变量:用lb 寄存器, 标签(lb=Load Byte,加载 1 字节); 2**(1*8)=256 (范围0~255)
    • 写.byte变量:用sb 寄存器, 标签(sb=Store Byte,存储 1 字节);

    算了  这汇编等设备好了再学吧 设备被我搞废了

    行了 按了六下开关 将设备重置了 现在又能运行了

    /* 定义代码段(Flash区域,存放代码)=========================== */
    .section  .text
    .type main function
    .global main
    .align 2
    这段代码的核心就是配置 main 函数的 “存储位置” 和 “入口身份”:
    • .section .text:定位置(Flash 代码区);
    • .type main function:定类型(函数);
    • .global main:定入口(CPU 启动先执行);
    • .align 2:做优化(4 字节对齐,读指令更快)。
    /* 主函数入口 */
    main:
        /*【1】启动初始化========================================== */
        /* 1.1 申请栈空间 */
        addi sp,sp,-48
        /* 1.2 关总中断(修正LI为li) */
        li t0, 0x8
        csrc mstatus, t0

    【第一部分:申请栈空间】

    addi是咱们最早学的 “立即数加法指令”,格式:addi 目标寄存器, 源寄存器, 立即数 → 这里就是:sp = sp + (-48) → 等价于 sp = sp - 48

    • 初始时sp指向一个高地址(比如 0x80010000);
    • 执行addi sp,sp,-48后,sp会往低地址挪 48 字节(比如到 0x8000FFD0);
    • 这 “挪出来的 48 字节区间(0x8000FFD0 ~ 0x80010000)” 就是程序申请的 “临时工作台”,后续 main 函数执行时,临时变量、保存的寄存器值都存在这里。
    1.关键术语
    • mstatus:RISC-V 的机器状态寄存器(Machine Status Register)——CPU 的 “核心状态开关”,里面有多个二进制位,控制 CPU 的关键行为,比如 “是否允许中断”;
    • MIE位:mstatus 寄存器的第 3 位(bit3)(二进制从 0 开始数),是 “总中断使能位”:
      • MIE=1 → 开总中断(CPU 会响应各种中断信号);
      • MIE=0 → 关总中断(CPU 无视所有中断信号);
    • csrc:RISC-V 的特权指令(只能在机器态执行),全称 “Clear Status Register”—— 作用是 “把指定寄存器的位,清 0 到目标状态寄存器里”(简单说:用 t0 里的值当 “掩码”,把 mstatus 里对应位清 0)。
    2. 逐行拆解
    • li t0, 0x8:li是 “加载立即数”,把 0x8(二进制1000)装进 t0 寄存器;

      ✅ 关键:0x8 的二进制只有第 3 位是 1,刚好对应 mstatus 的 MIE 位(总中断使能位);

    • csrc mstatus, t0:用 t0 里的 0x8 当掩码,把 mstatus 寄存器的第 3 位(MIE 位)清 0 → 关闭总中断。
    3. 为什么初始化时要关总中断?
    初始化阶段(比如配置 GPIO、初始化串口、设置变量初始值)是程序 “最脆弱” 的阶段 —— 如果此时有中断(比如定时器中断、串口中断),CPU 会暂停初始化,跑去处理中断,导致:
    • GPIO 还没配置好,中断处理函数里操作 GPIO 会出错;
    • 变量还没初始化(比如 mColorState 还是随机值),中断里读变量会乱套;
    • 栈空间刚申请好,中断处理函数用栈时可能覆盖 main 函数的栈数据;
    等所有初始化完成(比如 GPIO 配好、变量设好初始值),程序会再执行csrs mstatus, t0(置位 MIE 位),重新打开总中断。

    RISC-V 汇编里,指令大小写不敏感(LI和li都可以),但规范写法是小写li

        /* 串口打印工程提示 */
        la a0,hello_information
        call printf

    用 la 指令把纯提示字符串的地址传给 printf 的第一个参数(a0),再用 call 指令调用 printf 函数,完成串口打印

    /* 1.3 初始化三色灯GPIO(红/绿/蓝均设为输出,初始灭) */
        /* 初始化红灯 */
        li a0,LIGHT_RED
        li a1,GPIO_OUTPUT
        li a2,LIGHT_OFF
    这段代码的核心是按 RISC-V 传参规则,给 GPIO 初始化函数传递三个参数:
    1. a0:要配置的引脚(红灯);
    2. a1:引脚模式(输出);
    3. a2:初始状态(灭);
    这是嵌入式 RISC-V 控制硬件的 “标配操作”—— 先初始化引脚(定模式、定初始状态),后续才能通过修改引脚电平来控制灯的亮灭~
        /* 1.4 开总中断(修正LI为li) */
        li t0, 0x8
        csrs mstatus, t0

    核心指令:csrs(Set Status Register,“状态寄存器置位指令”)—— 和关中断的csrc(Clear,清位)是反义词:

    对比关 / 开中断的代码(一眼分清)

    操作 代码 核心指令 作用 执行时机
    关总中断 li t0,0x8; csrc mstatus,t0 csrc 清 0 MIE 位,关中断 初始化开始(main 开头)
    开总中断 li t0,0x8; csrs mstatus,t0 csrs 置 1 MIE 位,开中断 初始化完成后

    嵌入式 RISC-V 程序的 “标准流程”:关中断→初始化→开中断→进入业务逻辑,既保证初始化稳定,又能响应外部事件

    /*【2】主循环(计数→切换颜色→控制灯亮灭)=================================== */
    main_loop:
        /* 2.1 主循环计数+1,判断是否达到阈值(MAINLOOP_COUNT在user.inc定义) */
        la a5,mMainLoopCount    /* 读取主循环计数 */
        lw t1,0(a5)
        addi t1,t1,1
        sw t1,0(a5)
        li t2,MAINLOOP_COUNT    /* 判断是否达到阈值 */
        bltu t1,t2,main_loop    /* 未到阈值,继续主循环 */
    这段代码的核心是实现 “计数 - 判断 - 循环” 的定时逻辑:
    1. 读mMainLoopCount的当前值→+1→写回;
    2. 对比当前值和阈值MAINLOOP_COUNT;
    3. 没到阈值就跳回循环起点,到了就退出循环去切换颜色。
    指令 全称 核心逻辑 通俗解释 三色灯场景举例
    beq Branch if Equal 寄存器 1 == 寄存器 2 → 跳转 两个数相等,就跳去标签 beq t0, t1, reset_color

    (t0 = 当前颜色状态,t1=6(白灯),相等则跳去 reset_color 重置为 0)

    bne Branch if Not Equal 寄存器 1 != 寄存器 2 → 跳转 两个数不等,就跳去标签 bne t0, zero, light_on

    (t0 = 灯状态,zero=0,不等则跳去 light_on 开灯)

    指令 全称 核心逻辑 通俗解释 适用场景
    blt Branch if Less Than 寄存器 1 < 寄存器 2 → 跳转(有符号) 有符号数,前者小于后者则跳 温度 < 0℃,触发加热
    bge Branch if Greater or Equal 寄存器 1 ≥ 寄存器 2 → 跳转(有符号) 有符号数,前者≥后者则跳 电压≥3.3V,触发断电保护
    指令 全称 核心逻辑 通俗解释 三色灯场景举例
    bltu Branch if Less Than Unsigned 寄存器 1 < 寄存器 2 → 跳转(无符号) 无符号数,前者小则跳 你之前的bltu t1,t2,main_loop(计数 < 阈值则循环)
    bgeu Branch if Greater or Equal Unsigned 寄存器 1 ≥ 寄存器 2 → 跳转(无符号) 无符号数,前者≥后者则跳 bgeu t1,t2,change_color(计数≥阈值则切换颜色)

    新手怎么选寄存器?(通用原则)

    1. 临时计算 / 判断 → 优先用 t0~t6;
    2. 函数传参 → 优先用 a0~a3;
    3. 临时存地址 / 非核心参数 → 用 a4~a7;
    4. 长期保留的值 → 用 s0~s11;
    5. 绝对不动 ra/sp/zero。
        /* 2.4 读取当前颜色状态,切换到下一个状态(0-6循环) */
        la a5,mColorState
        lb t0,0(a5)            /* t0=当前状态值(0-6) */
        addi t0,t0,1
        li t1,7
        bltu t0,t1,state_ok    /* 未到7,状态有效 */
        li t0,0                /* 超过6,重置为0(循环) */
    state_ok:
        sb t0,0(a5)            /* 保存新状态 */

    动态演示(0-6 循环的完整流程)

    用 “状态 = 6(白灯)” 举例,看代码怎么把它切回 0:
    1. lb t0,0(a5) → t0=6;
    2. addi t0,t0,1 → t0=7;
    3. li t1,7 → t1=7;
    4. bltu t0,t1,state_ok → 7 < 7?不成立,不跳转;
    5. li t0,0 → t0=0;
    6. state_ok: → 执行到这;
    7. sb t0,0(a5) → mColorState=0(红灯)。
    再用 “状态 = 5(青灯)” 举例:
    1. lb t0,0(a5) → t0=5;
    2. addi t0,t0,1 → t0=6;
    3. li t1,7 → t1=7;
    4. bltu t0,t1,state_ok → 6 < 7?成立,跳去 state_ok;
    5. 跳过li t0,0,直接执行sb t0,0(a5) → mColorState=6(白灯)。
        /* 2.5 根据新状态控制三色灯亮灭 */
        /* 先全灭所有灯(避免残留状态) */
        li a0,LIGHT_RED
        li a1,LIGHT_OFF
        call gpio_set
        li a0,LIGHT_GREEN
        li a1,LIGHT_OFF
        call gpio_set
        li a0,LIGHT_BLUE
        li a1,LIGHT_OFF
        call gpio_set

    1. gpio_set和gpio_init的区别(避免混淆)

    函数 核心作用 执行时机 传参(简化)
    gpio_init 设置引脚模式(输入 / 输出) 程序初始化阶段(只 1 次) a0 = 引脚,a1 = 模式,a2 = 初始状态
    gpio_set 设置引脚电平(亮 / 灭) 每次切换颜色(多次) a0 = 引脚,a1 = 亮 / 灭状态
        /* 分支判断:不同状态对应不同灯亮灭 */
        la a5,mColorState
        lb t0,0(a5)
        li t1,0
        beq t0,t1,color_red     /* 0=红 */
        li t1,1
        beq t0,t1,color_green   /* 1=绿 */
        li t1,2
        beq t0,t1,color_blue    /* 2=蓝 */
        li t1,3…………
    这段代码的核心是用beq指令实现 “状态值→颜色标签” 的一一映射:
    1. 读状态值→逐个对比 0/1/2/3…;
    2. 匹配则跳转到对应颜色标签,点亮目标灯;
    3. 逻辑直观、易维护,适配 7 种颜色的简单状态机;
    4. 结合之前 “先全灭所有灯” 的操作,能精准点亮单一 / 组合色,无混色 bug。
    /* 颜色分支:单色-红 */
    color_red:
        li a0,LIGHT_RED
        li a1,LIGHT_ON
        call gpio_set
        la a1,color_name_red    /* 颜色名称地址 */
        j print_color_info      /* 打印颜色信息 */
    /* 打印颜色切换信息 */
    print_color_info:
        /* 打印切换次数 */
        la a0,printf_format_count
        la a5,mColorChangeCount
        lw a1,0(a5)
        call printf
        /* 打印当前颜色组合 */
        la a0,printf_format_color
        call printf              /* a1已存放颜色名称地址,直接调用 */
        /* 回到主循环 */
        j main_loop

    上面的代码有明显的错误 不知道各位有没有看出来
    正确的应该是这样的

    /* 打印颜色切换信息 */
    print_color_info:
    /* 保存到栈指针 */
    sw a1,0(sp)
        /* 打印切换次数 */
        la a0,printf_format_count
        la a5,mColorChangeCount
        lw a1,0(a5)
        call printf
        /* 打印当前颜色组合 */
        la a0,printf_format_color
        lw a1,0(sp)
        call printf /* a1已存放颜色名称地址,直接调用 */
        /* 回到主循环 */
        j main_loop
    这里解释一下为啥用栈指针

    明确sp的本质:栈指针是 “RAM 级别的临时存储管家”

    sp(Stack Pointer,栈指针寄存器)的核心作用是指向程序在 RAM 中申请的栈空间,栈空间的特性是:
    1. 持久存储:栈在 RAM 中(而非 CPU 内部寄存器),写入的值除非主动改写,否则不会丢失;
    2. 先进后出:适合 “暂存 - 恢复” 的场景(先存进去,用完再取出来);
    3. 规范分配:main 函数开头已通过addi sp,sp,-48申请了 48 字节栈空间,有足够的空间存放寄存器值(1 个寄存器占 4 字节)。

    下面是RGB灯光变换代码

    /*=====================================================================
    //文件名称:main.s
    //功能概要:汇编语言编程,调用构件GPIO,实现三色灯单色/二组合/三组合颜色切换
    //版权所有:苏大嵌入式(sumcu.suda.edu.cn)
    //版本更新:20241204
    //=====================================================================*/
    .include "includes.inc"    /* 包含汇编总头文件(需定义灯引脚/状态宏) */
    /* 定义数据段(RAM区域,变量使用)=================== */
    .section .data
    hello_information:   /* 功能提示字符串 */
        .ascii "\n"
        .ascii "------------------------------------------------------\n"
        .ascii "★金葫芦提示★                                         \n"
        .ascii "【工程名称】RISC-V三色灯组合颜色控制                     \n"
        .ascii "【程序功能】实现红/绿/蓝单色、二组合、三组合颜色切换      \n"
        .ascii "【硬件连接】红/绿/蓝灯分别接对应GPIO引脚(见user.inc)    \n"
        .ascii "【操作说明】观察硬件板上的三色灯,串口打印当前颜色组合    \n"
        .ascii "------------------------------------------------------\n\0"
    /* printf格式符定义(仅保留新逻辑需要的) */
    printf_format_count:      /* 颜色切换次数提示 */
        .ascii "颜色切换次数mColorCount = %d\n\0"
    printf_format_color:      /* 当前颜色组合提示 */
        .ascii "当前显示颜色:%s\n\0"
    /* 颜色名称字符串(与状态值一一对应) */
    color_name_red:       .ascii "Red (single)\0"
    color_name_green:     .ascii "Green (single)\0"
    color_name_blue:      .ascii "Blue (single)\0"
    color_name_yellow:    .ascii "Red+Green (yellow)\0"
    color_name_purple:    .ascii "Red+Blue (purple)\0"
    color_name_cyan:           .ascii "Green+Blue (cyan)\0"
    color_name_white:          .ascii "Red+Green+Blue (white)\0"
    .align 4                  /* 四字节对齐 */
    mMainLoopCount:       /* 主循环计数(达到阈值切换颜色) */
        .word 0
    mColorChangeCount:    /* 颜色切换总次数 */
        .word 0
    mColorState:          /* 颜色状态标志(0-6对应7种颜色组合) */
        .byte 0              /* 0=红 1=绿 2=蓝 3=黄 4=紫 5=青 6=白 */
    /* 定义代码段(Flash区域,存放代码)=========================== */
    .section  .text
    .type main function
    .global main
    .align 2
    /* 主函数入口 */
    main:
        /*【1】启动初始化========================================== */
        /* 1.1 申请栈空间 */
        addi sp,sp,-48
        /* 1.2 关总中断(修正LI为li) */
        li t0, 0x8
        csrc mstatus, t0
        /* 串口打印工程提示 */
        la a0,hello_information
        call printf
        /* 1.3 初始化三色灯GPIO(红/绿/蓝均设为输出,初始灭) */
        /* 初始化红灯 */
        li a0,LIGHT_RED
        li a1,GPIO_OUTPUT
        li a2,LIGHT_OFF
        call gpio_init
        /* 初始化绿灯 */
        li a0,LIGHT_GREEN
        li a1,GPIO_OUTPUT
        li a2,LIGHT_OFF
        call gpio_init
        /* 初始化蓝灯 */
        li a0,LIGHT_BLUE
        li a1,GPIO_OUTPUT
        li a2,LIGHT_OFF
        call gpio_init
        /* 1.4 开总中断(修正LI为li) */
        li t0, 0x8
        csrs mstatus, t0
    /*【2】主循环(计数→切换颜色→控制灯亮灭)=================================== */
    main_loop:
        /* 2.1 主循环计数+1,判断是否达到阈值(MAINLOOP_COUNT在user.inc定义) */
        la a5,mMainLoopCount    /* 读取主循环计数 */
        lw t1,0(a5)
        addi t1,t1,1
        sw t1,0(a5)
        li t2,MAINLOOP_COUNT    /* 判断是否达到阈值 */
        bltu t1,t2,main_loop    /* 未到阈值,继续主循环 */
        /* 2.2 达到阈值,重置主循环计数 */
        la a5, mMainLoopCount
        li t1,0
        sw t1,0(a5)
        /* 2.3 颜色切换次数+1 */
        la a5,mColorChangeCount
        lw t1,0(a5)
        addi t1,t1,1
        sw t1,0(a5)
        /* 2.4 读取当前颜色状态,切换到下一个状态(0-6循环) */
        la a5,mColorState
        lb t0,0(a5)            /* t0=当前状态值(0-6) */
        addi t0,t0,1
        li t1,7
        bltu t0,t1,state_ok    /* 未到7,状态有效 */
        li t0,0                /* 超过6,重置为0(循环) */
    state_ok:
        sb t0,0(a5)            /* 保存新状态 */
        /* 2.5 根据新状态控制三色灯亮灭 */
        /* 先全灭所有灯(避免残留状态) */
        li a0,LIGHT_RED
        li a1,LIGHT_OFF
        call gpio_set
        li a0,LIGHT_GREEN
        li a1,LIGHT_OFF
        call gpio_set
        li a0,LIGHT_BLUE
        li a1,LIGHT_OFF
        call gpio_set
        /* 分支判断:不同状态对应不同灯亮灭 */
        la a5,mColorState
        lb t0,0(a5)
        li t1,0
        beq t0,t1,color_red     /* 0=红 */
        li t1,1
        beq t0,t1,color_green   /* 1=绿 */
        li t1,2
        beq t0,t1,color_blue    /* 2=蓝 */
        li t1,3
        beq t0,t1,color_yellow  /* 3=黄(红绿) */
        li t1,4
        beq t0,t1,color_purple  /* 4=紫(红蓝) */
        li t1,5
        beq t0,t1,color_cyan    /* 5=青(绿蓝) */
        li t1,6
        beq t0,t1,color_white   /* 6=白(红绿蓝) */
    /* 颜色分支:单色-红 */
    color_red:
        li a0,LIGHT_RED
        li a1,LIGHT_ON
        call gpio_set
        la a1,color_name_red    /* 颜色名称地址 */
        j print_color_info      /* 打印颜色信息 */
    /* 颜色分支:单色-绿 */
    color_green:
        li a0,LIGHT_GREEN
        li a1,LIGHT_ON
        call gpio_set
        la a1,color_name_green
        j print_color_info
    /* 颜色分支:单色-蓝 */
    color_blue:
        li a0,LIGHT_BLUE
        li a1,LIGHT_ON
        call gpio_set
        la a1,color_name_blue
        j print_color_info
    /* 颜色分支:二组合-黄(红绿) */
    color_yellow:
        li a0,LIGHT_RED
        li a1,LIGHT_ON
        call gpio_set
        li a0,LIGHT_GREEN
        li a1,LIGHT_ON
        call gpio_set
        la a1,color_name_yellow
        j print_color_info
    /* 颜色分支:二组合-紫(红蓝) */
    color_purple:
        li a0,LIGHT_RED
        li a1,LIGHT_ON
        call gpio_set
        li a0,LIGHT_BLUE
        li a1,LIGHT_ON
        call gpio_set
        la a1,color_name_purple
        j print_color_info
    /* 颜色分支:二组合-青(绿蓝) */
    color_cyan:
        li a0,LIGHT_GREEN
        li a1,LIGHT_ON
        call gpio_set
        li a0,LIGHT_BLUE
        li a1,LIGHT_ON
        call gpio_set
        la a1,color_name_cyan
        j print_color_info
    /* 颜色分支:三组合-白(红绿蓝) */
    color_white:
        li a0,LIGHT_RED
        li a1,LIGHT_ON
        call gpio_set
        li a0,LIGHT_GREEN
        li a1,LIGHT_ON
        call gpio_set
        li a0,LIGHT_BLUE
        li a1,LIGHT_ON
        call gpio_set
        la a1,color_name_white
        j print_color_info
    /* 打印颜色切换信息 */
    print_color_info:
    /* 保存到栈指针 */
    sw a1,0(sp)
        /* 打印切换次数 */
        la a0,printf_format_count
        la a5,mColorChangeCount
        lw a1,0(a5)
        call printf
        /* 打印当前颜色组合 */
        la a0,printf_format_color
        lw a1,0(sp)
        call printf /* a1已存放颜色名称地址,直接调用 */
        /* 回到主循环 */
        j main_loop
    /*【3】栈恢复(兜底,运行不到但保留规范)=========================================== */
        addi sp, sp, 48
        ret
    最后更新于 2025-12-05