全志D1s RISC-V裸机开发入门指南
2026/7/17 6:27:43 网站建设 项目流程

1. 全志D1s裸机开发环境初探

第一次接触全志D1s这颗RISC-V芯片时,我手里拿着百问网的DongshanPI-D1S开发板,这块板子最吸引我的地方是集成了CKLink调试器——这意味着不需要额外购买J-Link或者ST-Link就能开始调试。板载的OTG烧录功能也很实用,用Type-C线连接电脑就能直接烧写程序,对新手特别友好。

开发环境搭建有几个关键点需要注意:

  • 工具链选择:官方推荐使用平头哥的Xuantie-900系列工具链
  • 调试器配置:由于板载CKLink,不需要额外驱动,但需要安装OpenOCD
  • 串口终端:建议使用MobaXterm这类集成工具,方便同时查看调试输出和串口日志

我第一次搭建环境时犯了个典型错误——直接用了Ubuntu默认的gcc工具链,结果编译出来的程序根本无法运行。后来才发现必须使用专门的RISC-V交叉编译工具链,这个坑浪费了我整整一个下午。

2. 裸机程序的基本框架解析

裸机程序与常规嵌入式开发最大的区别在于:没有任何操作系统支持,所有硬件操作都要自己实现。对于D1s来说,一个最简单的裸机程序需要包含以下几个关键部分:

2.1 启动文件(startup.s)

这个汇编文件是芯片上电后执行的第一段代码,主要完成:

.section .init .global _start _start: la sp, _stack_end // 设置栈指针 call main // 跳转到C语言的main函数 j . // 无限循环

2.2 链接脚本(mem.ld)

定义内存布局非常重要,D1s的内存映射比较特殊:

MEMORY { RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 64K } SECTIONS { .text : { *(.init) *(.text*) } > RAM .data : { *(.data*) } > RAM .bss : { *(.bss*) } > RAM _stack_end = ORIGIN(RAM) + LENGTH(RAM); }

2.3 最小化的main.c

第一个程序我建议就点亮LED,这能验证整个工具链是否正常工作:

#define GPIO_BASE 0x02000000 void main() { volatile unsigned int *gpio_ctrl = (unsigned int*)(GPIO_BASE + 0x30); *gpio_ctrl = 0x01; // 设置GPIO模式 volatile unsigned int *gpio_data = (unsigned int*)(GPIO_BASE + 0x34); while(1) { *gpio_data ^= 0x01; // LED闪烁 for(int i=0; i<1000000; i++); // 简单延时 } }

3. 编译与烧录实战

有了代码后,编译过程需要特别注意:

3.1 交叉编译命令

riscv64-unknown-elf-gcc -march=rv64imafdc -mabi=lp64d -nostartfiles \ -T mem.ld -o firmware.elf startup.s main.c

3.2 生成二进制文件

riscv64-unknown-elf-objcopy -O binary firmware.elf firmware.bin

3.3 烧录到开发板

DongshanPI-D1S支持USB烧录,连接Type-C线后:

sudo sunxi-fel -v spl sun20i-d1-spl.bin sudo sunxi-fel -v write 0x40000000 firmware.bin sudo sunxi-fel -v exec 0x40000000

我第一次烧录时遇到了权限问题,解决方法是在/etc/udev/rules.d/下添加规则文件:

SUBSYSTEM=="usb", ATTR{idVendor}=="1f3a", MODE="0666"

4. 调试技巧与常见问题

4.1 OpenOCD调试配置

创建openocd.cfg文件:

adapter driver cmsis-dap transport select jtag source [find target/xuanTie_c910.cfg]

启动调试会话:

openocd -f openocd.cfg

4.2 GDB连接

riscv64-unknown-elf-gdb firmware.elf (gdb) target remote :3333 (gdb) load (gdb) b main (gdb) c

4.3 常见问题排查

  1. 程序跑飞:检查启动文件是否正确设置了栈指针
  2. LED不亮:确认GPIO引脚号是否正确,D1s的GPIO映射与全志其他系列不同
  3. 烧录失败:确保先烧录SPL引导程序,再烧录主程序
  4. 调试断点无效:检查编译时是否带了-g选项

我在调试时发现一个有趣的现象:如果忘记在启动文件中设置栈指针,程序会随机崩溃。这是因为C函数调用需要使用栈空间,没有正确初始化栈指针会导致不可预知的行为。

5. 进阶开发建议

当第一个LED程序成功运行后,可以尝试以下进阶实验:

5.1 添加串口输出

#define UART0_BASE 0x02500000 void uart_putc(char c) { volatile unsigned int *uart_tx = (unsigned int*)(UART0_BASE + 0x00); while(!(*uart_tx & 0x40000000)); // 等待发送就绪 *uart_tx = c; } void main() { // 初始化UART volatile unsigned int *uart_clk = (unsigned int*)(UART0_BASE + 0x24); *uart_clk = 0x0d; // 设置时钟分频 while(1) { uart_putc('H'); uart_putc('i'); uart_putc('\r'); uart_putc('\n'); } }

5.2 使用中断

D1s的中断控制器比较特殊,需要先配置PLIC:

#define PLIC_BASE 0x10000000 void enable_interrupt() { // 设置外部中断优先级 volatile unsigned int *plic_prio = (unsigned int*)(PLIC_BASE + 0x2000); *plic_prio = 1; // 使能中断 volatile unsigned int *plic_enable = (unsigned int*)(PLIC_BASE + 0x2080); *plic_enable = 0x01; // 设置全局中断使能 asm volatile("csrs mie, %0" :: "r"(0x888)); }

5.3 优化代码大小

裸机开发时Flash空间有限,可以添加编译选项优化:

-Os -ffunction-sections -fdata-sections -Wl,--gc-sections

从第一个LED程序到能够处理中断、串口通信,这个过程让我对RISC-V架构有了更深的理解。D1s作为一款性价比极高的RISC-V芯片,其裸机开发体验既充满挑战又令人兴奋。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询