如何在LibreComputer RK3328(Renegade)上编写CPP程序实现SPI通信?
实现LibreComputer Renegade与STM32F4的SPI通信方案
一、解决Renegade上的SPI启用问题
Linux系统下无需直接操作寄存器,通过系统工具和设备树配置启用SPI:
- 启用SPI硬件overlay:编辑
/boot/uEnv.txt,添加/修改overlay_prefix=rockchip,再追加overlays=spi0(使用SPI1则写spi1),重启系统后用ls /dev/spidev*确认设备节点(如/dev/spidev0.0)是否存在。 - 排查设备树冲突:若节点未出现,用
dtc -I fs /proc/device-tree查看SPI节点状态,确认对应引脚未被其他外设占用。
二、基于spidev的CPP CLI程序实现
利用Linux用户空间驱动spidev实现SPI读写,无需编写内核驱动:
1. SPI操作封装类
#include <fcntl.h> #include <unistd.h> #include <sys/ioctl.h> #include <linux/spi/spidev.h> #include <cstdint> #include <stdexcept> class SPI { private: int fd; const char* device; uint8_t mode; uint32_t speed; uint8_t bits; public: SPI(const char* dev, uint8_t m, uint32_t s, uint8_t b) : device(dev), mode(m), speed(s), bits(b) { fd = open(device, O_RDWR); if (fd < 0) throw std::runtime_error("Failed to open SPI device"); if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) { close(fd); throw std::runtime_error("Failed to set SPI mode"); } if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) { close(fd); throw std::runtime_error("Failed to set SPI speed"); } if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) { close(fd); throw std::runtime_error("Failed to set bits per word"); } } ~SPI() { if (fd >= 0) close(fd); } int transfer(uint8_t* tx_buf, uint8_t* rx_buf, size_t len) { struct spi_ioc_transfer tr = { .tx_buf = (unsigned long)tx_buf, .rx_buf = (unsigned long)rx_buf, .len = len, .speed_hz = speed, .bits_per_word = bits, }; return ioctl(fd, SPI_IOC_MESSAGE(1), &tr); } };
2. CLI控制逻辑
解析命令行参数,实现运动指令发送、状态读取功能:
#include <iostream> #include <cstring> int main(int argc, char* argv[]) { if (argc < 2) { std::cout << "Usage: " << argv[0] << " <cmd> [params]\n"; std::cout << "Cmds:\n move <x> <y> <z> - Send inverse kinematics results to STM32\n status - Read status from STM32\n"; return 1; } try { SPI spi("/dev/spidev0.0", SPI_MODE_0, 1000000, 8); // 匹配STM32的SPI模式、速率、位宽 if (strcmp(argv[1], "move") == 0) { if (argc != 5) { std::cout << "Move requires x, y, z params\n"; return 1; } // 打包逆运动学计算后的指令帧(示例格式) uint8_t tx_buf[5] = {0x01, static_cast<uint8_t>(atoi(argv[2]) & 0xFF), static_cast<uint8_t>(atoi(argv[3]) & 0xFF), static_cast<uint8_t>(atoi(argv[4]) & 0xFF), 0x00}; // 可选校验位 uint8_t rx_buf[5] = {0}; spi.transfer(tx_buf, rx_buf, 5); rx_buf[0] == 0x0A ? std::cout << "Move command sent successfully\n" : std::cout << "Move failed, response: " << static_cast<int>(rx_buf[0]) << "\n"; } else if (strcmp(argv[1], "status") == 0) { uint8_t tx_buf[1] = {0x02}; // 读取状态指令 uint8_t rx_buf[4] = {0}; // 状态帧:位置、报错码等 spi.transfer(tx_buf, rx_buf, 4); std::cout << "Status: pos=" << static_cast<int>(rx_buf[0]) << ", error=" << static_cast<int>(rx_buf[1]) << "\n"; } else { std::cout << "Unknown command\n"; return 1; } } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << "\n"; return 1; } return 0; }
3. 编译与运行
编译命令:
g++ -o spi_controller spi_controller.cpp
运行前赋予权限:将用户加入spi组(sudo usermod -aG spi $USER,重启生效),或直接用sudo ./spi_controller执行。
三、STM32F4端SPI通信要点
- 配置SPI为从机模式,模式、速率、位宽与Renegade端完全匹配(如SPI_MODE_0)。
- 配置对应GPIO为SPI功能,硬件片选NSS设为输入下拉模式。
- 实现SPI中断接收:收到指令帧后解析并执行步进电机控制,完成后返回状态帧。
- 定义固定通信协议:比如帧头+指令类型+数据+校验位,避免通信丢包或误解析。
四、调试排障
- 用逻辑分析仪检查SCK、MOSI引脚信号是否正常输出。
- 先测试单向通信:Renegade发送固定数据,STM32接收后打印,确认物理连接无误。
- 降低SPI速率(如先100kHz)排查信号失真问题。
- 用
dmesg | grep spi查看内核日志,确认SPI驱动加载状态。
内容的提问来源于stack exchange,提问作者PlushyCascade82
相关产品推荐
相关产品推荐

