TinkerBoardR支持两路SPI,通过IO口引出。如下图所示,分别为SPI1和SPI5
源码采用的Linux内核版本为4.4,以下是Linux 4.4 spi驱动支持的一些特性:
标准的SPI有四个引脚,分别为:
SPI一共有四种工作模式,如下表,主机与从机需要工作在相同模式下才可以正常通信,实际上采用比较多的是模式0和模式3
SPI模式 | CPOL | CPHA | 空闲时SCK时钟 | 采样时刻 |
---|---|---|---|---|
0 | 0 | 0 | 低电平 | 奇数边沿 |
1 | 0 | 1 | 低电平 | 偶数边沿 |
2 | 1 | 0 | 高电平 | 奇数边沿 |
3 | 1 | 1 | 高电平 | 偶数边沿 |
下面我们来分析4种模式的时序图
CPOL=0:时钟线空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿
CPHA=0:数据在第1个跳变沿(上升沿)采样
时序图如下:
CPOL=0:时钟线空闲时是低电平,第1个跳变沿是上升沿,第2个跳变沿是下降沿
CPHA=1:数据在第2个跳变沿(下降沿)采样
时序图如下:
CPOL=1:时钟线空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿
CPHA=0:数据在第1个跳变沿(下降沿)采样
时序图如下:
CPOL=1:时钟线空闲时是高电平,第1个跳变沿是下降沿,第2个跳变沿是上升沿
CPHA=1:数据在第2个跳变沿(上升沿)采样
时序图如下:
这里测试使用的是SPI1,默认在设备树中是关闭的,需要手动在设备树中打开它。注意这里不能和UART4同时打开,因为是复用的。
&spi1 {
status = "okay";
max-freq = <48000000>; /* spi internal clk, don't modify */
spi_dev@0 {
compatible = "rockchip,spidev";
reg = <0>;
spi-max-frequency = <48000000>;
spi-lsb-first;
};
};
&uart4 {
status = "disabled";
};
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#define SPI_PATH "/dev/spidev32766.0"
/*SPI 接收 、发送 缓冲区*/
static unsigned char tx_buffer[128] = "TinkerBoardR Test SPI!";
static unsigned char rx_buffer[128];
static int fd; // SPI 控制引脚的设备文件描述符
static uint32_t mode = SPI_MODE_2; //用于保存 SPI 工作模式
static uint8_t bits = 8; // 接收、发送数据位数
static uint32_t speed = 500000; // 发送速度
static uint16_t delay; //保存延时时间
//spi初始化
int spi_init(void)
{
int ret;
fd = open(SPI_PATH,O_RDWR);
if(fd < 0)
{
perror("/dev/spidev32766.0");
return -1;
}
//设置spi工作模式
ret = ioctl(fd,SPI_IOC_RD_MODE,&mode);
if( ret == -1)
{
printf("SPI_IOC_RD_MODE error......\n ");
goto fd_close;
}
ret = ioctl(fd,SPI_IOC_WR_MODE,&mode);
if( ret == -1)
{
printf("SPI_IOC_WR_MODE error......\n ");
goto fd_close;
}
//设置SPI通信的字长
ret = ioctl(fd,SPI_IOC_RD_BITS_PER_WORD,&bits);
if( ret == -1)
{
printf("SPI_IOC_RD_BITS_PER_WORD error......\n ");
goto fd_close;
}
ret = ioctl(fd,SPI_IOC_WR_BITS_PER_WORD,&bits);
if( ret == -1)
{
printf("SPI_IOC_WR_BITS_PER_WORD error......\n ");
goto fd_close;
}
//设置SPI最高工作频率
ret = ioctl(fd,SPI_IOC_WR_MAX_SPEED_HZ,&speed);
if( ret == -1)
{
printf("SPI_IOC_WR_MAX_SPEED_HZ error......\n ");
goto fd_close;
}
ret = ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ,&speed);
if( ret == -1)
{
printf("SPI_IOC_RD_MAX_SPEED_HZ error......\n ");
goto fd_close;
}
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);
return 0;
fd_close:
close(fd);
return -1;
}
//spi传输数据
int spi_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,
};
ret = ioctl(fd,SPI_IOC_MESSAGE(1),&tr);
if( ret == -1 )
{
return -1;
}
return 0;
}
int main(int argc,char * argv[])
{
int ret;
ret = spi_init();
if( -1 == ret )
{
printf("spi_init error\n");
exit(-1);
}
ret = spi_transfer(fd, tx_buffer, rx_buffer, sizeof(tx_buffer));
if (-1 == ret )
{
printf("transfer error...\n");
}
/*打印 tx_buffer 和 rx_buffer*/
printf("tx_buffer: \n %s\n ", tx_buffer);
printf("rx_buffer: \n %s\n ", rx_buffer);
return 0;
}