4. SPI通信

本章配套视频介绍:

../../../_images/video.png

《32-在鲁班猫上使用SPI接口》

https://www.bilibili.com/video/BV1X14y1U7bW/

1
2
3
4
5
6
#如之前有获取则可跳过
#获取仓库
git clone https://gitee.com/LubanCat/lubancat_rk_code_storage.git

#代码所在的位置
lubancat_rk_code_storage/spi

本章主要围绕带有40Pin引脚的LubanCat系列的板卡,如下:

  • LubanCat-Zero-W

  • LubanCat- Zero-N

  • LubanCat-1

  • LubanCat-1N

  • LubanCat-2

  • LubanCat-2N

40pin引脚中只有一组spi接口SCK、MOSI、MISO,有两个默认片选信号CS0、CS1。

4.1. SPI引脚关系

40pin接口的spi引脚,如下图:

未找到图片

其中SPI的引脚关系如下表所示:

SPI

引脚

功能

MOSI

19

主设备输出/从设备输入

MISO

21

主设备输入/从设备输出

CLOCK

23

时钟信号线

CS0

24

片选信号线0

CS1

26

片选信号线1

其中spidev3.0控制CS0,spidev3.1控制CS1。

4.2. 使能SPI通信接口

SPI接口在默认情况是关闭状态的,需要使能才能使用, SPI接口的设备树插件有两个:

  1. xxx-spi3-m1-overlay.dtbo

  2. xxx-spi3-m1-gpio-cs-overlay.dtbo

  • xxx-spi3-m1-overlay.dtbo是使用了SPI_MOSI、SPI_MISO、SPI_CLOCK、SPI_CS0这四个引脚的。

  • xxx-spi3-m1-gpio-cs-overlay.dtbo是使用了SPI_MOSI、SPI_MISO、SPI_CLOCK、SPI_CS0、SPI_CS1这五个引脚。

两个插件不能同时开启,下面以开启xxx-spi3-m1-overlay.dtbo为例子。

4.2.1. 方法一

1
2
3
4
5
#进入工具配置
sudo fire-config

#移动光标到下图的位置
#按确认键进入配置
未找到图片

打开SPI通信接口和SPI片选接口:

  1. 使用方向键移动光标到 SPI

  2. “空格键” 选中SPI-CS(出现 “*” ),如下图。

  3. “确认键” 进行设置。

  4. “Esc键” 退出到终端,运行 sudo reboot 进行重启应用。

未找到图片

4.2.2. 方法二

可以通过打开 /boot/uEnv/uEnv.txt ,查看是否启用了spi相关设备设备树插件。

编辑文件,将带有 xxx-spi3-m1-overlay.dtbo 的两行的注释符号去掉,如下图:

未找到图片

配置完成后重启板卡激活配置。

注解

如果是直接拔电源的方式重启,会有可能出现文件没能做出修改 (原因:文件未能及时从内存同步到存储设备中,解决方法,在终端上输入 “sync” 再拔电关机)。

4.3. 检查SPI设备

使能spi设备树插件之后重新启动板卡,通过SPI设备文件来判断spi驱动是否加载成功。 SPI_3对应的设备文件是spidev3.0,如果使用了xxx-spi3-m1-gpio-cs-overlay.dtbo就会出现spidev3.0和spidev3.1。

如下所示:

1
2
3
4
5
#查看spi设备
ls /dev/spi*

#信息输出如下
/dev/spidev3.0  /dev/spidev3.1

spidev3.0和spidev3.1的区别在于片选信号的不同,spidev3.0使用CS0 , spidev3.1使用CS1。

4.4. 回环测试程序

根据ioctl相关参数即可编写spi相关测试程序,此处为了简单仅做回环测试实验, 只需要将SPI3的 MIOS与MOSI引脚(板卡上的19和21)使用跳线帽短接即可。

如下图所示:

未找到图片spi_show

代码位置如下:

代码位置
1
quick_start/spi/spi_selftest.c

代码较长复制粘贴容易乱序,可以下载我们提供的源码 spi_selftest.c

quick_start/spi/spi_selftest.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <linux/spi/spidev.h>

#define SPI_DEV_PATH "/dev/spidev3.0"

/*SPI 接收 、发送 缓冲区*/
unsigned char tx_buffer[100] = "hello the world !";
unsigned char rx_buffer[100];


int fd;                  					// SPI 控制引脚的设备文件描述符
static unsigned  mode = SPI_MODE_2;         //用于保存 SPI 工作模式
static uint8_t bits = 8;        			// 接收、发送数据位数
static uint32_t speed = 10000000; 			// 发送速度
static uint16_t delay;          			//保存延时时间

void transfer(int fd, uint8_t const *tx, uint8_t const *rx, size_t len)
{
	int ret;

	struct spi_ioc_transfer tr = {
		.tx_buf = (unsigned long)tx,
		.rx_buf = (unsigned long)rx,
		.len = len,
		.delay_usecs = delay,
		.speed_hz = speed,
		.bits_per_word = bits,
		.tx_nbits = 1,
		.rx_nbits = 1
	};

	ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
	if (ret < 1)
		printf("can't send spi message\n");
}

void spi_init(int fd)
{
	int ret = 0;

	// spi mode 设置SPI 工作模式
	ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
	if (ret == -1)
		printf("can't set spi mode\n");

	//  bits per word  设置一个字节的位数
	ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
	if (ret == -1)
		printf("can't set bits per word\n");

	//  max speed hz  设置SPI 最高工作频率
	ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
	if (ret == -1)
		printf("can't set max speed hz\n");

	// 打印
	printf("spi mode: 0x%x\n", mode);
	printf("bits per word: %d\n", bits);
	printf("max speed: %d Hz (%d KHz)\n", speed, speed / 1000);
}

int main(int argc, char *argv[])
{
	int fd;
	
	if(argc < 2){
	printf("Wrong use !!!!\n");
		printf("Usage: %s [dev]\n",argv[0]);
		return -1;
	}
	/*打开 SPI 设备*/
	fd = open(argv[1], O_RDWR); // open file and enable read and  write
	if (fd < 0){
		printf("Can't open %s \n",argv[1]); // open i2c dev file fail
		exit(1);
	}

	/*初始化SPI */
	spi_init(fd);

	/*执行发送*/
	transfer(fd, tx_buffer, rx_buffer, sizeof(tx_buffer));

	/*打印 tx_buffer 和 rx_buffer*/
	printf("tx_buffer: \n %s\n ", tx_buffer);
	printf("rx_buffer: \n %s\n ", rx_buffer);

	close(fd);

	return 0;
}

4.4.1. 编译运行

1
2
3
4
5
#在板卡使用gcc编译
gcc spi_selftest.c -o spi_selftest

#运行
sudo ./spi_selftest /dev/spidev3.x

效果如下图所示:

未找到图片spi_show

如果mosi和mosi不进行连接就运行程序开始测试,会打印乱码或者空白内容。

4.5. 更多

更多关于rk-spi驱动以及把spi从master模式切换为salve模式等可以查看: rk开源手册/Common/SPI。

4.6. 参考资料

本文档主要提供有一定经验的使用者快速入门使用,详细资料可阅读以下文档:

《imx6ull : SPI通信》

《LubanCat RK系列 : SPI通信》

rk开源资料(github): 《开源手册》