Funpack4-3 - 用MAX32655FTHR实现 LED 纯色/彩虹/呼吸灯
上电后串口先看到 CM4 的启动提示,随后 RV32 输出灯效演示信息。LED 默认进入纯色模式,按 BUTTON2 可以切换颜色;按 BUTTON1 切换到彩虹模式后,RGB 会连续渐变,BUTTON2 可快速改变渐变速度。
标签
ADI
LED
MAX32655FTHR
MAX32655FTHR 点灯 LED
vla
更新2026-02-25
19

1 项目和设计思路介绍

内容

- 项目目标:在 MAX32655FTHR 板上展示“双核协作 + 人机交互 + 可视化灯效”,让新手

能一眼理解 CM4 启动 RV32、RV32 处理交互与灯效的完整链路。

- 系统分工:CM4 侧仅做启动加载与提示信息;RV32 侧专注按键扫描与 RGB 灯效渲染,

体现“主核负责系统启动、协处理核负责应用逻辑”的清晰边界。

- 设计原则:

- 可见性:用 RGB LED 作为直观输出,按钮作为直观输入。

- 可解释性:关键算法(软件 PWM、按键去抖、彩虹/呼吸状态机)都有可调参数与易

读流程。

- 易修改:将“模式、速度、颜色”等定义为常量数组,方便快速改造。

- 关键流程:上电后 CM4 打印提示并启动 RV32;RV32 初始化外设后进入主循环,执

行“PWM 输出 + 按键采样 + 灯效更新”的节拍调度。


2 硬件介绍(控制器及使用外设)

内容

- 控制器:MAX32655(双核 SoC)

- CM4(Arm Cortex-M4F):复位后最先运行,负责启动 RV32。

- RV32(RISC-V):运行应用逻辑(按键读取 + RGB 灯效)。

- 开发板:MAX32655FTHR

- 板载 DAPLink(MAX32625):提供 SWD 调试与 UART0 虚拟串口。

- USB 供电即可运行。

- 本项目使用的外设与引脚

- RGB LED(D1):P0.18(R) / P0.19(G) / P0.26(B)

- BUTTON1/2:P0.2 / P0.3

- UART0:P0.0(RX) / P0.1(TX),用于串口日志输出

- 其他说明

- 灯效输出使用 GPIO 软件 PWM 实现亮度变化。

- 通过 DAPLink 的虚拟串口观察运行状态(115200 8-N-1)。


3 软件代码介绍(主要代码片段)

主要代码片段(扩展版)


1) CM4 启动 RV32(m4/src/main.c)

printf("ARM: RV_ARM_Loader\r\n");
MXC_Delay(MXC_DELAY_SEC(2));
MXC_SYS_RISCVRun();

说明:CM4 侧只负责提示与启动 RV32,业务逻辑全部交给 RV32;启动后 CM4 进入空循

环,不参与灯效与交互。


2) RV32 初始化与可调参数(riscv/src/main.c)

static const uint32_t kPwmPeriodUs = 2000;    // 500Hz
static const uint32_t kButtonSampleUs = 5000; // 5ms
static const uint32_t kEffectTickUs = 20000; // 20ms

说明:三组时间常量分别控制 PWM 频率、按键采样周期、灯效刷新节拍;Board_Init()

负责 GPIO/LED/按键的板级初始化,Console_Init() 确保串口可用。


3) 颜色/亮度处理基础(gamma + 缩放)


static uint8_t gamma_u8(uint8_t x)

{

uint16_t xx = (uint16_t)x * (uint16_t)x;

return (uint8_t)((xx + 127u) / 255u);

}



static rgb8_t rgb_scale(rgb8_t c, uint8_t scale)

{

rgb8_t out = {

.r = (uint8_t)(((uint16_t)c.r * scale + 127u) / 255u),

.g = (uint8_t)(((uint16_t)c.g * scale + 127u) / 255u),

.b = (uint8_t)(((uint16_t)c.b * scale + 127u) / 255u),

};

return out;

}

说明:gamma_u8() 做简易 gamma≈2.0 的视觉校正,让低亮度变化更平滑;rgb_scale()

用于呼吸亮度的比例缩放,避免溢出并做四舍五入。


4) 彩虹颜色轮(HSV→RGB 简化版)


static rgb8_t color_wheel(uint16_t hue)

{

rgb8_t c = { 0, 0, 0 };

uint16_t h = hue % 1536u;

uint8_t region = (uint8_t)(h >> 8);

uint8_t offset = (uint8_t)(h & 0xFF);



switch (region) {

case 0: c.r = 255; c.g = offset; c.b = 0; break;

case 1: c.r = (uint8_t)(255 - offset); c.g = 255; c.b = 0; break;

case 2: c.r = 0; c.g = 255; c.b = offset; break;

case 3: c.r = 0; c.g = (uint8_t)(255 - offset); c.b = 255; break;

case 4: c.r = offset; c.g = 0; c.b = 255; break;

default: c.r = 255; c.g = 0; c.b = (uint8_t)(255 - offset); break;

}

return c;

}

说明:将 hue 划分为 6 个 256 级分段(总 1536 级),每段线性插值一个颜色分量,

用极少运算实现平滑彩虹过渡。


5) 事件驱动的软件 PWM(riscv/src/main.c)


uint32_t on_r = ((uint32_t)rgb.r * period_us) / 255u;

uint32_t on_g = ((uint32_t)rgb.g * period_us) / 255u;

uint32_t on_b = ((uint32_t)rgb.b * period_us) / 255u;



if (on_r > 0) { LED_On(LED_RED); } else { LED_Off(LED_RED); }

if (on_g > 0) { LED_On(LED_GREEN); } else { LED_Off(LED_GREEN); }

if (on_b > 0) { LED_On(LED_BLUE); } else { LED_Off(LED_BLUE); }



uint32_t off_r = (on_r > 0 && on_r < period_us) ? on_r : period_us;

uint32_t off_g = (on_g > 0 && on_g < period_us) ? on_g : period_us;

uint32_t off_b = (on_b > 0 && on_b < period_us) ? on_b : period_us;



uint32_t elapsed = 0;

while (1) {

uint32_t next = period_us;

if (off_r < next) next = off_r;

if (off_g < next) next = off_g;

if (off_b < next) next = off_b;

if (next == period_us) break;

if (next > elapsed) { MXC_Delay(next - elapsed); }

if (off_r == next) { LED_Off(LED_RED); off_r = period_us; }

if (off_g == next) { LED_Off(LED_GREEN); off_g = period_us; }

if (off_b == next) { LED_Off(LED_BLUE); off_b = period_us; }

elapsed = next;

}

if (elapsed < period_us) { MXC_Delay(period_us - elapsed); }

说明:将每个通道的“关灯时刻”作为事件进行调度:同一周期内只关心“下一次关断时

间”,避免三路计时器并行,实现清晰的分段延时。


6) 按键去抖(移位寄存器法)

 
static bool button_filter_pressed_event(button_filter_t *btn, bool

raw_pressed)

{

btn->history = (uint8_t)((btn->history << 1) | (raw_pressed ? 1u : 0u));

if (btn->history == 0xFF && !btn->stable_pressed) {

btn->stable_pressed = true;

return true;

}

if (btn->history == 0x00 && btn->stable_pressed) {

btn->stable_pressed = false;

}

return false;

}

说明:用 8 位移位寄存器记录连续采样结果:8 个 1 才触发“按下事件”;8 个 0 才认

为释放,能有效滤除按键抖动。


7) 主循环节拍调度(PWM → 按键 → 灯效)


rgb8_t pwm_rgb = {

.r = gamma_u8(target.r),

.g = gamma_u8(target.g),

.b = gamma_u8(target.b),

};

rgb_pwm_cycle(pwm_rgb, kPwmPeriodUs);

now_us += kPwmPeriodUs;



if (now_us - last_button_sample_us >= kButtonSampleUs) {

last_button_sample_us += kButtonSampleUs;

bool b1_event = button_filter_pressed_event(&btn1, PB_Get(BUTTON1) ?

true : false);

bool b2_event = button_filter_pressed_event(&btn2, PB_Get(BUTTON2) ?

true : false);

/* ... */

}



if (now_us - last_effect_tick_us >= kEffectTickUs) {

last_effect_tick_us += kEffectTickUs;

/* ... */

}

说明:以 PWM 周期作为“最小时间片”,用 now_us 累计运行时间;按键采样与灯效更新

分别用“上次时间戳 + 周期”方式调度,避免漂移。


8) 彩虹/呼吸状态机(节拍更新)


if (mode == EFFECT_RAINBOW) {

uint16_t step = rainbow_steps[rainbow_speed_level - 1];

hue = (uint16_t)((hue + step) % 1536u);

target = color_wheel(hue);

} else if (mode == EFFECT_BREATH) {

breath_level += (int16_t)(breath_dir * breath_step);

/* ... */

target = rgb_scale(kBreathColors[breath_idx], (uint8_t)breath_level);

}

说明:彩虹模式通过色相步进遍历颜色轮,速度由 rainbow_steps 定义;呼吸模式用三

角波控制亮度(breath_level 与 breath_dir),再用 rgb_scale() 叠加颜色。


主要代码片段

CM4 启动 RV32(m4/src/main.c)

  printf("ARM: RV_ARM_Loader\r\n");

MXC_Delay(MXC_DELAY_SEC(2));

MXC_SYS_RISCVRun();

说明:CM4 侧只负责提示与启动 RV32,业务逻辑全部交给 RV32。


事件驱动的软件 PWM(riscv/src/main.c)


static void rgb_pwm_cycle(rgb8_t rgb, uint32_t period_us)

{

uint32_t on_r = ((uint32_t)rgb.r * period_us) / 255u;

uint32_t on_g = ((uint32_t)rgb.g * period_us) / 255u;

uint32_t on_b = ((uint32_t)rgb.b * period_us) / 255u;

...

}

说明:用“事件调度”的方式在一个周期内统一处理 RGB 的开关时刻,避免为每个通道单

独计时,逻辑清晰且易解释。


按键去抖(移位寄存器法)


static bool button_filter_pressed_event(button_filter_t *btn, bool

raw_pressed)

{

btn->history = (uint8_t)((btn->history << 1) | (raw_pressed ? 1u : 0u));

if (btn->history == 0xFF && !btn->stable_pressed) {

btn->stable_pressed = true;

return true;

}

if (btn->history == 0x00 && btn->stable_pressed) {

btn->stable_pressed = false;

}

return false;

}

说明:连续 8 次采样稳定才认定为“按下”,能有效滤除抖动。


彩虹/呼吸状态机(节拍更新)


if (mode == EFFECT_RAINBOW) {

hue = (uint16_t)((hue + step) % 1536u);

target = color_wheel(hue);

} else if (mode == EFFECT_BREATH) {

breath_level += (int16_t)(breath_dir * breath_step);

...

target = rgb_scale(kBreathColors[breath_idx], (uint8_t)breath_level);

}

说明:灯效以固定 tick 更新,保证动画节奏稳定;参数可调,易于演示和拓展。


4 硬件实现功能展示

展示要点

- 上电后串口打印:

- ARM: RV_ARM_Loader

- RV32: MAX32655FTHR RGB 灯效演示

- BUTTON1: 切换模式 | BUTTON2: 调整参数

- 默认模式为“纯色”,BUTTON2 依次切换预置颜色(含关灯)。

- 按下 BUTTON1 切换到“彩虹”模式,LED 连续渐变;BUTTON2 切换速度(慢/中/快)。

- 再次按 BUTTON1 切换到“呼吸”模式,LED 亮度循环变化;BUTTON2 切换呼吸颜色(红/

绿/蓝/白)。

附件下载
led 代码归档.7z
团队介绍
个人
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号