6. SPI操作指南

6.1. 操作准备

SPI的操作准备如下:

  • 使用SDK发布的内核以及文件系统。文件系统可使用SDK所发布的squashFS或ext4。也可透过本地文件系统再透过网络挂载至NFS。

6.2. 操作过程

  • 加载内核。把SPI相关模块全部编入内核,不需要再执行加载命令。

  • 在控制台下运行SPI读写命令或者自行在内核态或者用户态编写SPI读写程序,就可以对挂载在SPI控制器上的外围设备进行读写操作。

6.3. 操作示例

6.3.1. 内核态SPI读写程序示例:

此操作示例说明在内核态下如何通过SPI读写程序实现对SPI外围设备的读写操作。

步骤 1. 调用SPI核心层函数 spi_busnum_to_master(),以获取一个描述SPI控制器结构体:

master = spi_busnum_to_master(bus_num);

//bus_num为要读写的SPI外围设备所在的控制器号

//master为描述SPI控制器的spi_master结构体类型指针

步骤 2. 通过spi外围设备在核心层的名称调用SPI核心层函数取得挂载在SPI控制器上描述SPI外围设备的结构体:

snprintf(str, sizeof(str), “%s.%u”, dev_name(&master->dev), cs);

dev = bus_find_device_by_name(&spi_bus_type, NULL, str);

spi = to_spi_device(dev);

//spi_buf_type为描述SPI总线的bus_type结构体类型变量

//spi为描述SPI外围设备spi_device结构体类型指针

步骤 3. 调用SPI核心层函数将spi_transfer添加到spi_message队列中。

spi_message_init(&m)

spi_message_add_tail(&t, &m)

//t为spi_transfer结构体类型变量

//m为spi_message结构体类型变量

步骤 4. 调用SPI核心层停工的读写函数对外围设备进行读写操作

status = spi_sync(spi, &m);

status = spi_async(spi, &m)

//spi为描述SPI外围设备的spi_device结构体类型指针

//spi_sync函数为进行spi同步读写操作

//spi_async函数为进行spi异步读写操作

代码示例如下:

此段代码示例仅供参考,而非实际应用功能。

//传入SPI控制器总线号和片选号
static unsigned int busnum;module_param(busnum, uint, 0);
MODULE_PARM_DESC(busnum, "SPI busnumber (default=0)");

static unsigned int cs;
module_param(cs, uint, 0);
MODULE_PARM_DESC(cs, "SPI processor select (default=0)");

extern struct bus_typespi_bus_type;

//宣告SPI控制器的结构体
static struct spi_master *master;

//宣告SPI外围设备的结构体
static struct spi_device *spi_device;

static int __init spidev_init(void) {
   char *spi_name;
   struct device *spi;
   master =spi_busnum_to_master(busnum);
   spi_name = kzalloc(strlen(dev_name(&master->dev)), GFP_KERNEL);

   if (!spi_name)
      return -ENOMEM;

   snprintf(spi_name,sizeof(spi_name), "%s.%u", dev_name(&master->dev),cs);
   spi = bus_find_device_by_name(&spi_bus_type, NULL, spi_name);
   if (spi == NULL)
      return -EPERM;

   spi_device = to_spi_device(spi);
   if (spi_device ==NULL)
      return -EPERM;

   put_device(spi);
   kfree(spi_name);

   return 0;
}
int spi_dev_write(, void \*buf,unsigned long len, int buswidth)
{
   struct spi_device *spi = spi_device;
   struct spi_transfer t = {
      .speed_hz = 2000000,
      .tx_buf = buf,
      //buf里需依外围设备规范填入device addr, register addr, write data等资讯
      .len = len,
   };
   struct spi_message m;
   spi->mode = SPI_MODE_0;

   if (buswidth == 16)
      t.bits_per_word = 16;
      else
         t.bits_per_word = 8;
      if (!spi) {
         return -ENODEV;
      }
      spi_message_init(&m);
      spi_message_add_tail(&t, &m);
      return spi_sync(spi, &m);
}
int spi_dev_read(unsigned char devaddr, unsigned char reg_addr,void*buf, size_t len)
{
   struct spi_device *spi = spi_device;
   int ret;
   u8 txbuf[4] = { 0, };
   struct spi_transfer t = {
      .speed_hz = 2000000,
      .rx_buf =buf,
      .len = len,
      };
   struct spi_message m;
   spi->mode = SPI_MODE_0;

   if (!spi) {
      return -ENODEV;
   }
   txbuf[0] = devaddr;
   txbuf[1] = 0;
   txbuf[2] = reg_addr;//txbuf[1]&txbuf[2]需根据外围设备位宽来决定填写1 byte or 2 bytes,此范例为2     |bytes位宽
   t.tx_buf =txbuf;

   spi_message_init(&m);
   spi_message_add_tail(&t, &m);
   ret = spi_sync(spi, &m);

   return ret;
}

6.3.2. 用户态SPI读写程序示例:

此操作示例在用户态下实现对挂载在SPI控制器0上的SPI外围设备的读写操作。(具体实现可参考 tools/spi/spidev_test.c)

步骤1: 打开SPI总线对应的设备文件,获取文件描述符。

tatic const char *device = "/dev/spidev32766.0";
…
fd = open(device, O_RDWR);
if (fd < 0)
   pabort("can't open device");

备注: SPI控制器1上挂载的外围设备默认节点为 “dev/spidev32765.0”

SPI控制器2上挂载的外围设备默认节点为 “dev/spidev32764.0”

SPI控制器3上挂载的外围设备默认节点为 “dev/spidev32763.0”

仅需替换节点名称即可,其馀操作与SPI控制器0上挂载的外围设备相同。

步骤2: 通过ioctl设置SPI传输模式:

/*
*spi mode
*/
ret = ioctl(fd, SPI_IOC_WR_MODE32, &mode);
if (ret == -1)
   pabort("can't set spi mode");
ret = ioctl(fd, SPI_IOC_RD_MODE32, &mode);
if(ret == -1)
   pabort("can't get spi mode");

备注:mode 值配置请参考下图或是内核代码 include/linux/spi/spi.h,

Ex. mode = SPI_MODE_3 | SPI_LSB_FIRST;

#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)

步骤3: 通过ioctl设置SPI传输频宽:

/*
* bits per word
*/
ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
if (ret == -1)
   pabort("can't set bits per word");
ret = ioctl(fd,SPI_IOC_RD_BITS_PER_WORD, &bits);
if (ret == -1)
   pabort("can't get bits per word");

步骤4: 通过ioctl设置SPI传输速度 (一般建议speed = 25M):
/*
* max speed hz
*/
ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
if (ret == -1)
   pabort("can't set max speed hz");

ret = ioctl(fd,SPI_IOC_RD_MAX_SPEED_HZ, &speed);
if (ret == -1)
   pabort("can't get max speed hz");

步骤5: 使用ioctl进行数据读写:
ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
if (ret < 1)
   pabort("can't send spi message");

备注: tr传输一帧消息的spi_ioc结构体数组首地址。