1. Milk-V Duo S开发板与Arduino开发环境概述Milk-V Duo S是一款基于RISC-V架构的双核开发板采用大小核设计大核运行Linux系统小核运行实时操作系统。这种架构设计使得开发者能够同时利用Linux系统的丰富生态和实时系统的确定性响应能力。对于嵌入式开发者和物联网爱好者来说这款开发板提供了极具性价比的开发平台。Arduino作为开源硬件平台的代表其简洁的编程方式和丰富的库资源大大降低了嵌入式开发的门槛。Milk-V Duo S通过支持Arduino开发环境让开发者能够使用熟悉的Arduino IDE和API来开发小核应用程序这为快速原型开发提供了极大便利。提示Milk-V Duo S的小核运行的是经过优化的RTOS系统专门为Arduino环境进行了适配因此可以完美兼容大部分Arduino库和示例代码。2. 开发环境搭建与配置2.1 硬件准备在开始开发前需要准备以下硬件设备Milk-V Duo S开发板建议使用V1.2及以上版本支持Arduino的固件镜像可从Milk-V官网下载Micro SD卡至少8GB容量用于烧录系统镜像USB Type-C数据线用于供电和串口通信可选USB转TTL串口模块用于调试输出2.2 软件安装与配置首先需要安装最新版的Arduino IDE当前推荐2.3.2版本。安装完成后按照以下步骤添加Milk-V Duo S支持打开Arduino IDE进入文件→首选项在附加开发板管理器网址中添加以下URLhttps://github.com/kubuds/sophgo-arduino/releases/download/v0.2.5/package_sg200x_index.json保存设置后进入工具→开发板→开发板管理器搜索SG200X并安装最新版本的支持包2.3 系统固件烧录由于Milk-V Duo S采用双核设计需要特别注意固件版本的选择从Milk-V官网下载带有arduino标记的专用固件使用balenaEtcher等工具将固件镜像烧录到Micro SD卡将SD卡插入开发板通过USB Type-C接口供电启动注意首次启动后建议通过串口终端执行以下命令禁用默认的LED闪烁脚本mv /mnt/system/blink.sh /mnt/system/blink.sh_backup sync reboot3. 开发板引脚分配与功能详解Milk-V Duo S的GPIO引脚分为两个区域分别位于RJ45网口侧(J3)和USB-A接口侧(J4)。这两组引脚有不同的逻辑电平引脚区域逻辑电平主要功能J3排针3.3VGPIO, UART, I2C, PWMJ4排针1.8V音频接口,MIPI DSI3.1 常用功能引脚映射以下是开发中最常用的引脚功能分配GPIO控制J3区域的20号引脚SG2000编号B20是常用的通用GPIO板载LED连接到J3区域的0号引脚串口通信UART2J3区域的11(TX)、13(RX)引脚UART3J4区域的50(TX)、48(RX)引脚注意1.8V电平PWM输出PWM4J3区域的9号引脚PWM5J3区域的7号引脚PWM6J3区域的22号引脚I2C接口I2C1J3区域的11(SCL)、12(SDA)引脚I2C4J3区域的4(SCL)、5(SDA)引脚特别注意J4区域的引脚使用1.8V逻辑电平直接连接3.3V设备可能会造成损坏使用时需要电平转换电路。4. 基础功能开发示例4.1 GPIO控制 - 点亮LED下面是一个完整的LED闪烁示例展示了基本的GPIO操作#define LED_PIN 0 // 板载LED连接的引脚 void setup() { pinMode(LED_PIN, OUTPUT); // 设置引脚为输出模式 } void loop() { digitalWrite(LED_PIN, HIGH); // LED亮 delay(1000); // 延时1秒 digitalWrite(LED_PIN, LOW); // LED灭 delay(1000); // 延时1秒 }上传此程序后可以看到板载LED以1秒间隔闪烁。如果需要使用外部LED应注意LED正极通过限流电阻通常220Ω-1kΩ连接GPIO引脚LED负极连接开发板的GND引脚电流不要超过单个GPIO引脚的最大驱动能力通常20mA4.2 串口通信开发Milk-V Duo S提供了多个UART接口最常用的是UART23.3V电平和UART31.8V电平。以下是UART2的基本使用示例void setup() { Serial.begin(115200); // 初始化调试串口 Serial2.begin(115200); // 初始化UART2引脚11(TX)、13(RX) } void loop() { if(Serial2.available()) { // 检查是否有数据到达 String received Serial2.readString(); Serial.print(Received: ); Serial.println(received); } Serial2.println(Hello from Duo S); // 发送数据 delay(1000); }实际使用时需要注意确保通信双方的波特率设置一致连接时注意TX与RX要交叉连接1.8V电平的UART3连接3.3V设备时需要电平转换4.3 PWM输出控制PWM脉宽调制常用于控制电机速度、LED亮度等。以下是呼吸灯效果的实现代码#define PWM_PIN 6 // 使用支持PWM的引脚 void setup() { pinMode(PWM_PIN, OUTPUT); } void loop() { // 亮度渐增 for(int brightness 0; brightness 255; brightness) { analogWrite(PWM_PIN, brightness); delay(10); } // 亮度渐减 for(int brightness 255; brightness 0; brightness--) { analogWrite(PWM_PIN, brightness); delay(10); } }关键点说明analogWrite()函数的第二个参数范围是0-255对应0%-100%的占空比不同引脚支持的PWM通道有限使用时需查阅引脚分配表同一个PWM通道不能同时用于多个引脚5. 外设与传感器集成5.1 I2C设备驱动 - OLED显示屏SSD1306驱动的OLED屏是常见的I2C设备以下是驱动示例#include Wire.h #include Adafruit_SSD1306.h #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, Wire, OLED_RESET); void setup() { Wire.begin(); // 初始化I2C使用默认引脚 if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(SSD1306 allocation failed); for(;;); } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.println(Hello, Milk-V Duo S!); display.display(); } void loop() { // 可以添加动态显示内容 }接线说明OLED的SCL引脚 → J3区域的11号引脚OLED的SDA引脚 → J3区域的12号引脚OLED的VCC → 3.3V电源OLED的GND → 开发板GND5.2 超声波测距传感器HC-SR04超声波模块是常用的距离测量传感器使用示例#define TRIG_PIN 20 // 触发引脚 #define ECHO_PIN 21 // 回波引脚 void setup() { Serial.begin(115200); pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT); } void loop() { // 发送10μs的触发脉冲 digitalWrite(TRIG_PIN, LOW); delayMicroseconds(2); digitalWrite(TRIG_PIN, HIGH); delayMicroseconds(10); digitalWrite(TRIG_PIN, LOW); // 测量回波脉冲宽度 long duration pulseIn(ECHO_PIN, HIGH); // 计算距离声速343m/s除以2因为是往返距离 float distance duration * 0.0343 / 2; Serial.print(Distance: ); Serial.print(distance); Serial.println( cm); delay(500); }使用注意事项确保Trig和Echo引脚正确连接测量距离通常在2cm-400cm之间避免测量吸音材料如布料、泡沫等5.3 舵机控制使用Servo库控制舵机的示例代码#include Servo.h Servo myServo; int pos 0; void setup() { myServo.attach(4); // 使用J3区域的4号引脚支持PWM } void loop() { // 从0度转到180度 for(pos 0; pos 180; pos 1) { myServo.write(pos); delay(15); } // 从180度转回0度 for(pos 180; pos 0; pos - 1) { myServo.write(pos); delay(15); } }关键参数说明标准舵机控制信号周期为20ms50Hz脉宽0.5ms-2.5ms对应0-180度位置确保电源能够提供足够的电流单个舵机可能需要数百mA6. 高级功能与双核通信6.1 Mailbox双核通信机制Milk-V Duo S的大小核之间通过Mailbox机制进行通信。以下是小核接收大核消息的示例#include mailbox.h struct cmdqu_t { uint8_t ip_id; uint8_t cmd_id : 7; uint8_t block : 1; unsigned short mstime; unsigned int param_ptr; } __attribute__((packed)); void showmsg(MailboxMsg msg) { cmdqu_t *cmdq (cmdqu_t *)msg.data; Serial.print(Received command from Linux: ); Serial.print(ID); Serial.print(cmdq-cmd_id); Serial.print(, Param0x); Serial.println(cmdq-param_ptr, HEX); *(msg.data) 0; // 清除消息 } void setup() { Serial.begin(115200); mailbox_init(false); mailbox_register(0, showmsg); mailbox_enable_receive(0); Serial.println(Mailbox Ready); } void loop() { // 主循环可以处理其他任务 }大核Linux端可以通过以下命令发送消息echo 123 /proc/mailbox6.2 多任务处理虽然Arduino环境通常使用单线程但可以通过以下方式实现简单的多任务unsigned long previousMillis1 0; unsigned long previousMillis2 0; const long interval1 500; // 任务1间隔500ms const long interval2 1000; // 任务2间隔1000ms void task1() { // 任务1的代码 digitalWrite(LED_PIN, !digitalRead(LED_PIN)); } void task2() { // 任务2的代码 Serial.println(Task2 executed); } void setup() { pinMode(LED_PIN, OUTPUT); Serial.begin(115200); } void loop() { unsigned long currentMillis millis(); if(currentMillis - previousMillis1 interval1) { previousMillis1 currentMillis; task1(); } if(currentMillis - previousMillis2 interval2) { previousMillis2 currentMillis; task2(); } // 这里可以添加其他非延时代码 }这种方法避免了使用delay()造成的阻塞使得多个任务可以同时执行。7. 性能优化与调试技巧7.1 内存优化策略Milk-V Duo S的小核内存有限开发时应注意尽量减少全局变量的使用使用PROGMEM存储不变的常量数据动态内存分配要谨慎避免内存碎片对于大型数组考虑使用外部存储示例代码#include avr/pgmspace.h const char largeData[] PROGMEM This is a large string...; void setup() { Serial.begin(115200); // 从Flash读取数据 char buffer[50]; strncpy_P(buffer, largeData, sizeof(buffer)); Serial.println(buffer); } void loop() { }7.2 串口调试技巧高效的串口调试可以大大提升开发效率使用条件编译控制调试输出#define DEBUG 1 #if DEBUG #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) #endif使用带时间戳的调试信息void debugPrint(const char* message) { Serial.print(millis()); Serial.print(: ); Serial.println(message); }使用二进制数据输出void printBinary(uint8_t value) { for(int i7; i0; i--) { Serial.print(bitRead(value, i) ? 1 : 0); } }7.3 常见问题排查程序无法上传检查开发板选择是否正确Duo Dev Module确认串口端口选择正确确保已安装pyserial库pip install pyserial外设不工作确认引脚连接正确检查外设供电是否足够验证引脚是否被其他功能占用系统不稳定检查电源是否提供足够电流建议5V/2A以上确保固件版本是最新的Arduino专用版本检查是否有短路或接线错误通过以上方法和技巧可以充分发挥Milk-V Duo S的开发潜力实现各种有趣的物联网和嵌入式项目。