跳到主要内容

sidebar_position: 3 slug: /basic_applications/Pin_Applications

SPI 使用说明

本文档介绍如何使用SPI

模块介绍

SPI(Serial Peripheral Interface) 是一种 SoC 与外设之间的串行通信接口,仅支持 x1 模式。SPI 有主设备(Master)和从设备(Slave)两种模式,通常为一个主设备控制一个或多个从设备进行通信。主设备通过拉低片选线选择一个从设备进行通信,完成数据交互。主设备负责提供时钟,并发起读写操作。K1 SPI 当前仅支持主设备模式。

//引脚功能
SCLK:时钟信号线
MISO:master input slave output,从主通信
MOSI:master output slave input,主从通信
CS: chip select或SS(slave select),从设备片选信号线

引脚说明

参考《引脚定义说明》,确认开发板支持的SPI引脚

以MUSE-Pi-Pro为例,支持引脚如下

查看设备

  • 查看系统SPI总线设备和驱动信息
 ls /sys/bus/spi

spi--
|-- devices //spi总线上的设备
|-- drivers //spi总线上注册的设备驱动
|-- drivers_autoprobe
|-- drivers_probe
  • 查看指定SPI总线设备的信息
ls -la /sys/bus/spi/devices/spi3.0
  • 查看指定SPI设备的模态(设备名称)
cat /sys/bus/spi/devices/spi3.0/modalias
  • 查看SPI设备的速度模式
cat /sys/bus/spi/devices/spi3.0/of_node/spi-max-frequency

启用SPI

SPI引脚GPIO75、76、77、78与其他设备复用,因此使用之前需要在dts 禁用其他冲突的设备

** 这里提供手动修改编译dts和直接获取dtb的方法,二选一即可 **

  • dtb路径
wget https://archive.spacemit.com/ros2/prebuilt/brdk_libs/spi/k1-x_MUSE-Pi-Pro.dtb 

下载到/boot/spacemit/6.6.63/下,以替换旧的dtb
  • 获取开发板设备树dts文件
cd /boot/spacemit/6.6.63/       //设备树所在分区

sudo apt update
sudo apt install device-tree-compiler //编译工具

sudo dtc -I dtb -O dts -o k1-x_MUSE-Pi-Pro.dts k1-x_MUSE-Pi-Pro.dtb //反编译

ls
//出现k1-x_MUSE-Pi-Pro.dts反编译成功

  • 修改设备树

    • 找到spi@d420c000节点
      • 失能节点和子节点flash 即修改status = "disabled"
      • 添加spi@3节点
    • 找到spi@d401c000节点
      • 添加spi@3节点
spi@d420c000 {
compatible = "spacemit,k1x-qspi";
#address-cells = <0x01>;
#size-cells = <0x00>;
reg = <0x00 0xd420c000 0x00 0x1000 0x00 0xb8000000 0x00 0xc00000>;
reg-names = "qspi-base\0qspi-mmap";
k1x,qspi-sfa1ad = <0x4000000>;
k1x,qspi-sfa2ad = <0x100000>;
k1x,qspi-sfb1ad = <0x100000>;
k1x,qspi-sfb2ad = <0x100000>;
clocks = <0x03 0x8f 0x03 0x90>;
clock-names = "qspi_clk\0qspi_bus_clk";
resets = <0x1d 0x4e 0x1d 0x4f>;
reset-names = "qspi_reset\0qspi_bus_reset";
k1x,qspi-pmuap-reg = <0xd4282860>;
k1x,qspi-mpmu-acgr-reg = <0xd4051024>;
k1x,qspi-freq = <0x1945ba0>;
k1x,qspi-id = <0x04>;
power-domains = <0x20 0x00>;
cpuidle,pm-runtime,sleep;
interrupts = <0x75>;
interrupt-parent = <0x1e>;
k1x,qspi-tx-dma = <0x01>;
k1x,qspi-rx-dma = <0x01>;
dmas = <0x21 0x2d 0x01>;
dma-names = "tx-dma";
interconnects = <0x22>;
interconnect-names = "dma-mem";
status = "disabled";
pinctrl-names = "default";
pinctrl-0 = <0x5b>;

flash@0 {
compatible = "jedec,spi-nor";
reg = <0x00>;
spi-max-frequency = <0x1945ba0>;
m25p,fast-read;
broken-flash-reset;
status = "disabled";
};
spi@3 {
compatible = "cisco,spi-petra";
reg = <0x0>;
spi-max-frequency = <6400000>;
status = "okay";
};
};

spi@d401c000 {
compatible = "spacemit,k1x-spi";
reg = <0x00 0xd401c000 0x00 0x34>;
k1x,ssp-id = <0x03>;
k1x,ssp-clock-rate = <0x186a000>;
dmas = <0x21 0x14 0x01 0x21 0x13 0x01>;
dma-names = "rx\0tx";
power-domains = <0x20 0x00>;
cpuidle,pm-runtime,sleep;
interrupt-parent = <0x1e>;
interrupts = <0x37>;
clocks = <0x03 0x58>;
resets = <0x1d 0x18>;
#address-cells = <0x01>;
#size-cells = <0x00>;
interconnects = <0x22>;
interconnect-names = "dma-mem";
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <0x2e>;
k1x,ssp-disable-dma;

spi@3 {
compatible = "cisco,spi-petra";
reg = <0x0>;
spi-max-frequency = <6400000>;
status = "okay";
};

};


  • 编译新设备树
sudo dtc -I dts -O dtb -o k1-x_MUSE-Pi-Pro.dtb k1-x_MUSE-Pi-Pro.dts

  • 替换镜像文件
//获取
wget https://archive.spacemit.com/ros2/prebuilt/brdk_libs/spi/vmlinuz-6.6.63

移动或复制到/boot/目录下,覆盖旧的镜像vmlinuz-6.6.63
  • 重启开发板
sudo reboot

重启后在 /dev/下可查看spi3.0设备

示例

  • 连接任意一款spi设备到开发板
连接方式
主设备 (Master) <===> 从设备 (Slave)
SCK --------------> SCK
MOSI --------------> MOSI (或 SDI)
MISO <-------------- MISO (或 SDO)
CS0 --------------> CS (或 nSS)
  • 查看设备
ls /dev/spi*
//输出
spi3
  • 代码
//spi_master.c

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <string.h>
#include <stdint.h>

#define SPI_DEV "/dev/spidev3.0"
#define BUF_SIZE 32

static uint32_t mode = SPI_MODE_0;
static uint8_t bits = 8;
static uint32_t speed = 1000000;

int spi_init()
{
int fd = open(SPI_DEV, O_RDWR);
if (fd < 0) {
perror("无法打开SPI设备");
return -1;
}

if (ioctl(fd, SPI_IOC_WR_MODE, &mode) < 0) {
perror("无法设置SPI模式");
goto error;
}

if (ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits) < 0) {
perror("无法设置字长");
goto error;
}

if (ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0) {
perror("无法设置SPI速度");
goto error;
}

return fd;

error:
close(fd);
return -1;
}

int spi_transfer(int fd, uint8_t *tx_buf, uint8_t *rx_buf, int 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,
.delay_usecs = 0,
};

if (ioctl(fd, SPI_IOC_MESSAGE(1), &tr) < 1) {
perror("SPI传输失败");
return -1;
}

return 0;
}

int main()
{
int fd = spi_init();
if (fd < 0) return 1;

uint8_t tx_buf[BUF_SIZE] = {0};
uint8_t rx_buf[BUF_SIZE] = {0};

tx_buf[0] = 0x9f;
spi_transfer(fd, tx_buf, rx_buf, strlen((char *)tx_buf) + 3);
printf("从设备响应: %x%x%x%x\n", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);


while (1)
{
printf("Master> ");
fgets((char *)tx_buf, BUF_SIZE, stdin);


if (strncmp((char *)tx_buf, "quit", 4) == 0) break;


if (spi_transfer(fd, tx_buf, rx_buf, strlen((char *)tx_buf) + 3) == 0) {
printf("从设备响应: %x%x%x%x\n", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3]);
}

memset(tx_buf, 0, BUF_SIZE);
memset(rx_buf, 0, BUF_SIZE);
}

close(fd);
return 0;
}
  • 在主机上交叉编译此文件
riscv64-unknown-linux-gnu-gcc -O2 -mcpu=spacemit-x60 -march=rv64gc_zba_zbb_zbc_zbs spi_master.c -o spi_master

编译工具下载及使用方法见交叉编译工具手册

  • 复制可执行文件到开发板
scp spi_master bianbu@10.0.91.35:/home/bianbu

// scp - ssh远程复制命令
// spi_master - 可执行文件名
// bianbu - 开发板用户名
// 10.0.91.35 - 开发板ip(hostname -I查看自己的板子ip)
// /home/bianbu - 存储路径(自定义)

注意:使用scp命令需要板子和主机在同一局域网下

  • 在开发板上运行该程序
sudo ./spi_master                                                                    
//运行结果
从设备响应: 0000
Master> 9f
从设备响应: 0000