提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
第一章 :RK3566 内核的spi设备的使用和测试
前言
SPI是一种常见的设备通用通信协议。它有一个独特优势就是可以无中断传输数据,可以连续地发送或接收任意数量的位。而在I2C和 UART 中,数据以数据包的形式发送,有着限定位数。
一、SPI的优缺点
优点
SPI通讯无起始位和停止位,因此数据可以连续流传输而不会中断;没有像 I2C这样的复杂的从站寻址系统,数据传输速率比I2c更高(几乎快两倍)。独立的MISO 和 MOSI 线路,可以同时发送和接收数据。
缺点
SPI使用四根线(I2C和 UART 使用两根线),没有信号接收成功的确认(I2C拥有此功能),没有任何形式的错误检查(如 UART 中的奇偶校验位等)。
二、SPI的属性
1.SPI的特点
1,spi是一种高速,全双工,同步串行总线
2,spi有主从两种模式,通常由一个主设备或者多个从设备组成。spi不支持多主机。
3,spi通信至少需要四根线,分别是:
MISO(M:即主设备,I:即输入,S:即从设备,O:即输入,所以就是主设备输入,从设备输出),
MOSI(同理,即主设备输出,从设备输入),
SCLK(时钟信号),
CS/C(片选信号)。
连接方式:
2.SPI的通信原理
3.SPI的极性和相位
分别是 CPOL(Clock POlarity)和CPHA(Clock PHAse)。
- CPOL配置SPI总线的极性
- CPHA配置SPI总线的相位
3.1 SPI总线的极性
极性,会直接影响SPI总线空闲时的时钟信号是高电平还是低电平。
- CPOL=1:表示空闲时是高电平
- CPOL=0:表示空闲时是低电平
3.2 SPI总线的相位
一个时钟周期会有2个跳变沿。而相位,直接决定SPI总线从那个跳变沿开始采样数据。
-CPHA=0:表示从第一个跳变沿开始采样
-CPHA =1:表示从第二个跳变沿开始采样
3.3 SPI的四种工作模式
模式0
CPoL = 0:空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿
CPHA = 0:数据在第1个跳变沿(上升沿)采样
模式1
CPOL =0::空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿
CPHA =1:数据在第2个跳变沿(下降沿)采样
模式2
CPOL = 1:空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿
CPHA = 0:数据在第1个跳变沿(下降沿)采样
模式3
CPOL = 1:空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿
CPHA = 1:数据在第2个跳变沿(上升沿)采样
四种模式总图
4.SPI的通信过程
SPI总线在进行数据传输的时候,默认先传输高位,后传输低位。数据线高电平表示逻辑1,数据线低电平表示逻辑0,一个字节传输完成后无需应答信号即可开启下一个字节的传输。
SPI总线采用同步方式,在时钟线的第一个或者第二个跳变沿采集数据(主机侧读数据),然后在紧接着下一个跳变沿发数据。8个时钟周期即可完成一个字节的数据传输。
模式0为例:
三、添加内核自带SPI设备节点
在tspi-rk3566-user-v10-linux.dts下添加如下内容
spi3: spi@fe640000 {
compatible = "rockchip,rk3066-spi";
reg = <0x0 0xfe640000 0x0 0x1000>;
interrupts = <GIC_SPI 106 IRQ_TYPE_LEVEL_HIGH>;
#address-cells = <1>;
#size-cells = <0>;
clocks = <&cru CLK_SPI3>, <&cru PCLK_SPI3>;
clock-names = "spiclk", "apb_pclk";
dmas = <&dmac0 26>, <&dmac0 27>;
dma-names = "tx", "rx";
pinctrl-names = "default", "high_speed";
pinctrl-0 = <&spi3m0_cs0 &spi3m0_cs1 &spi3m0_pins>;
pinctrl-1 = <&spi3m0_cs0 &spi3m0_cs1 &spi3m0_pins_hs>;
status = "disabled";
};
&spi3{
status = "okay";
pinctrl-names = "default", "high_speed";
//开启spi3,指定使用的pinctrl节点和cs片选引脚
pinctrl-0 = <&spi3m1_cs0 &spi3m1_pins>;
pinctrl-1 = <&spi3m1_cs0 &spi3m1_pins_hs>;
//cs-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
spi_test@0 {
status = "okay";
compatible = "rockchip,spidev";
reg = <0>; //chip select 0:cs0 1:cs1
spi-max-frequency = <10000000>; //spi output clock
//dc_control_pin = <&gpio3 RK_PA7 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
//pinctrl-0 = <&spi_oled_pin>;
};
};
然后编译kernel烧录boot到开发板启动即可在 /dev/目录下看到spidev3.0这个设备
四、编译内核自带的spi工具进行测试
内核自带的SPI工具位于此目录下
SDK/kernel/tools/spi
在/kernel/tools/spi下,需要自己手动先make一下
接下来我们使用另外一个测试程序不使用官方这个,我们使用这个程序
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#define SPI_DEV_PATH "/dev/spidev3.0" //修改为你要测试的SPI设备
int fd;
static unsigned mode = SPI_MODE_0;
static uint8_t bits = 8;
static uint32_t speed = 1000000; // 设置SPI速度为1MHz
static uint16_t delay;
void transfer(int fd, uint8_t const *tx, uint8_t *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,
.cs_change = 0, // 设置为1以在每次传输前切换片选,这里不切换片选
};
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1) {
perror("SPI transfer failed");
}
}
void spi_init(void)
{
int ret;
// 打开 SPI 设备
fd = open(SPI_DEV_PATH, O_RDWR);
if (fd < 0) {
perror("Can't open SPI device");
exit(1);
}
// 设置 SPI 工作模式
ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
if (ret == -1) {
perror("Can't set SPI mode");
exit(1);
}
// 设置位数
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1) {
perror("Can't set bits per word");
exit(1);
}
// 设置SPI速度
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1) {
perror("Can't set max speed");
exit(1);
}
// 打印设置
printf("SPI mode: 0x%x\n", mode);
printf("Bits per word: %d\n", bits);
printf("Max speed: %d Hz\n", speed);
}
int main(int argc, char *argv[])
{
if (argc != 2) {
printf("Usage: %s <string_to_send>\n", argv[0]);
return 1;
}
char *tx_buffer = argv[1]; // 获取要发送的字符串作为命令行参数
// 初始化SPI接口
spi_init();
// 设置要接收数据的缓冲区
unsigned char rx_buffer[strlen(tx_buffer) + 1];
// 执行SPI数据传输
transfer(fd, tx_buffer, rx_buffer, strlen(tx_buffer));
// 打印发送和接收的数据
printf("Sent: %s\n", tx_buffer);
printf("Received: %s\n", rx_buffer);
// 关闭SPI设备
close(fd);
return 0;
}
接下来需要编译一下这个文件,用以下命令
sudo aarch64-linux-gnu-gcc spidev_test.c -o spidev_test
然后使用adb push上传到开发板上,进行测试并使用逻辑分析仪进行解析