SPI (serial peripheral interface),以下是 linux 4.4 SPI 驱动支持的一些特性︰
默认采用摩托罗拉 SPI 协议
支持 8 位和 16 位
软件可编程时钟频率和传输速率高达 50MHz
支持 SPI 4 种传输模式配置
每个 SPI 控制器支持一个到两个片选
风火轮科技开发一款rk3588s的主板youyeetoo R1,该主板提供40pin扩展管脚,其中提供1路spi管脚使用。管脚分布如下图所示:
引脚接口
功能 | 管脚 |
---|---|
cs | 14脚 |
CLK | 23脚 |
MOSI | 21脚 |
MISO | 19脚 |
硬件连接
短接MOSI
和MISO
,如图:
如果要在Android系统里面使用shell命令,需要通过ADB进入Android的命令行窗口。ADB的使用教程请参考 adb调试 一章。通过adb打开Android系统命令行后,操作pwm管脚需要先把pwm设备导出到用户空间。
adb shell setprop persist.sys.root_access 3
adb root
adb remount
adb shell
cat >> /system/bin/setup.sh << EOF
chmod 666 /dev/spidev0.0
EOF
Android系统的权限管理是十分严格的,使用c++或者java操作Android根文件系统里的文件或资源需要对应的权限。这里编写的ndk程序是基于 system app的作为模板进行编写。system app请参考创建system app这一章。
#include <jni.h>
#include <string>
#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 "spidev.h"
#include <android/log.h>
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "Tag", __VA_ARGS__)
#define SPI_DEV0_0 "/dev/spidev0.0"
static char tx_buffer[100] = "hello the world !";
static char rx_buffer[100];
static int fd; // SPI 控制引脚的设备文件描述符
static unsigned int mode = SPI_MODE_2; //用于保存 SPI 工作模式
static unsigned char bits = 8; // 接收、发送数据位数
static unsigned int speed = 500000; // 发送速度
static unsigned short delay; //保存延时时间
//spi初始化
static int spi_init(void);
//spi发送数据
static int transfer(int fd, char *tx, char *rx, unsigned int len);
extern "C" JNIEXPORT jstring JNICALL
Java_com_youyeetoo_r1_1spi_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
int ret;
int i;
ret = spi_init();
if( -1 == ret )
{
return env->NewStringUTF("spi_init error\n");
}
ret = transfer(fd, tx_buffer, rx_buffer, sizeof(tx_buffer));
if (-1 == ret )
{
return env->NewStringUTF("transfer error...\n");
}
/*打印 tx_buffer 和 rx_buffer*/
return env->NewStringUTF(rx_buffer);
}
int transfer(int fd, char *tx, char *rx, unsigned int 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 )
{
LOGI("transfer fail\n");
return -1;
}
LOGI("transfer success\n");
return 0;
}
int spi_init(void)
{
int ret;
fd = open(SPI_DEV0_0, O_RDWR);
if(fd < 0)
{
perror("/dev/spidev0.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;
}
在native-lib.cpp
同级目录下面创建一个头文件spidev.h
,内容如下:
/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
/*
* include/linux/spi/spidev.h
*
* Copyright (C) 2006 SWAPP
* Andrea Paterniani <a.paterniani@swapp-eng.it>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef SPIDEV_H
#define SPIDEV_H
#include <linux/types.h>
#include <linux/ioctl.h>
/* User space versions of kernel symbols for SPI clocking modes,
* matching <linux/spi/spi.h>
*/
#define SPI_CPHA 0x01
#define SPI_CPOL 0x02
#define SPI_MODE_0 (0|0)
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04
#define SPI_LSB_FIRST 0x08
#define SPI_3WIRE 0x10
#define SPI_LOOP 0x20
#define SPI_NO_CS 0x40
#define SPI_READY 0x80
#define SPI_TX_DUAL 0x100
#define SPI_TX_QUAD 0x200
#define SPI_RX_DUAL 0x400
#define SPI_RX_QUAD 0x800
#define SPI_CS_WORD 0x1000
#define SPI_TX_OCTAL 0x2000
#define SPI_RX_OCTAL 0x4000
#define SPI_3WIRE_HIZ 0x8000
/*---------------------------------------------------------------------------*/
/* IOCTL commands */
#define SPI_IOC_MAGIC 'k'
/**
* struct spi_ioc_transfer - describes a single SPI transfer
* @tx_buf: Holds pointer to userspace buffer with transmit data, or null.
* If no data is provided, zeroes are shifted out.
* @rx_buf: Holds pointer to userspace buffer for receive data, or null.
* @len: Length of tx and rx buffers, in bytes.
* @speed_hz: Temporary override of the device's bitrate.
* @bits_per_word: Temporary override of the device's wordsize.
* @delay_usecs: If nonzero, how long to delay after the last bit transfer
* before optionally deselecting the device before the next transfer.
* @cs_change: True to deselect device before starting the next transfer.
* @word_delay_usecs: If nonzero, how long to wait between words within one
* transfer. This property needs explicit support in the SPI controller,
* otherwise it is silently ignored.
*
* This structure is mapped directly to the kernel spi_transfer structure;
* the fields have the same meanings, except of course that the pointers
* are in a different address space (and may be of different sizes in some
* cases, such as 32-bit i386 userspace over a 64-bit x86_64 kernel).
* Zero-initialize the structure, including currently unused fields, to
* accommodate potential future updates.
*
* SPI_IOC_MESSAGE gives userspace the equivalent of kernel spi_sync().
* Pass it an array of related transfers, they'll execute together.
* Each transfer may be half duplex (either direction) or full duplex.
*
* struct spi_ioc_transfer mesg[4];
* ...
* status = ioctl(fd, SPI_IOC_MESSAGE(4), mesg);
*
* So for example one transfer might send a nine bit command (right aligned
* in a 16-bit word), the next could read a block of 8-bit data before
* terminating that command by temporarily deselecting the chip; the next
* could send a different nine bit command (re-selecting the chip), and the
* last transfer might write some register values.
*/
struct spi_ioc_transfer {
unsigned long* tx_buf;
unsigned long* rx_buf;
unsigned int len;
unsigned int speed_hz;
unsigned short delay_usecs;
unsigned char bits_per_word;
unsigned char cs_change;
unsigned char tx_nbits;
unsigned char rx_nbits;
unsigned char word_delay_usecs;
unsigned char pad;
/* If the contents of 'struct spi_ioc_transfer' ever change
* incompatibly, then the ioctl number (currently 0) must change;
* ioctls with constant size fields get a bit more in the way of
* error checking than ones (like this) where that field varies.
*
* NOTE: struct layout is the same in 64bit and 32bit userspace.
*/
};
/* not all platforms use <asm-generic/ioctl.h> or _IOC_TYPECHECK() ... */
#define SPI_MSGSIZE(N) \
((((N)*(sizeof (struct spi_ioc_transfer))) < (1 << _IOC_SIZEBITS)) \
? ((N)*(sizeof (struct spi_ioc_transfer))) : 0)
#define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
/* Read / Write of SPI mode (SPI_MODE_0..SPI_MODE_3) (limited to 8 bits) */
#define SPI_IOC_RD_MODE _IOR(SPI_IOC_MAGIC, 1, unsigned char)
#define SPI_IOC_WR_MODE _IOW(SPI_IOC_MAGIC, 1, unsigned char)
/* Read / Write SPI bit justification */
#define SPI_IOC_RD_LSB_FIRST _IOR(SPI_IOC_MAGIC, 2, unsigned char)
#define SPI_IOC_WR_LSB_FIRST _IOW(SPI_IOC_MAGIC, 2, unsigned char)
/* Read / Write SPI device word length (1..N) */
#define SPI_IOC_RD_BITS_PER_WORD _IOR(SPI_IOC_MAGIC, 3, unsigned char)
#define SPI_IOC_WR_BITS_PER_WORD _IOW(SPI_IOC_MAGIC, 3, unsigned char)
/* Read / Write SPI device default max speed hz */
#define SPI_IOC_RD_MAX_SPEED_HZ _IOR(SPI_IOC_MAGIC, 4, unsigned int)
#define SPI_IOC_WR_MAX_SPEED_HZ _IOW(SPI_IOC_MAGIC, 4, unsigned int)
/* Read / Write of the SPI mode field */
#define SPI_IOC_RD_MODE32 _IOR(SPI_IOC_MAGIC, 5, unsigned int)
#define SPI_IOC_WR_MODE32 _IOW(SPI_IOC_MAGIC, 5, unsigned int)
#endif /* SPIDEV_H */