https://linux-sunxi.org/SPIdev

The SPI bus (or Serial Peripheral Interface bus) is a synchronous serial data link originally created by motorola.
For more information about SPI please refer to this link: http://en.wikipedia.org/wiki/Serial_Peripheral_Interface

In the linux kernel the SPI works only in master mode.
There is a way of using the spi kernel driver to work as a device in the userspace. It's called SPIdev.

Contents

[hide

Configuring your kernel

For using it you will have to enable this options in your defconfig or manually in your kernel:

CONFIG_SPI_SUN4I=y
CONFIG_SPI_SUN6I=y
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
CONFIG_EXPERIMENTAL=y
CONFIG_SPI_SPIDEV=y

More information

For more information about using SPIdev in the userspace please refer to (Documentation/spi/): http://lxr.free-electrons.com/source/Documentation/spi/

You will find there:

  • spidev (contains the documentantion about the spidev)

In tools/spi (http://lxr.free-electrons.com/source/tools/spi/) you will find:

  • spidev_fdx.c (contains a simple example in C of a full duplex communication)
  • spidev_test.c (contains a simple example in C of a half duplex communication)

Configuring your FEX

It is important to configure your .fex file to be able to do so:

(if you are using spi0)

[spi0_para]
spi_used = 1
spi_cs_bitmap = 1
spi_cs0 = port:PI10<2><default><default><default>
spi_sclk = port:PI11<2><default><default><default>
spi_mosi = port:PI12<2><default><default><default>
spi_miso = port:PI13<2><default><default><default>

(here you will specify the number of spi devices your card will have, if you plan only to use the spidev just put 1):

[spi_devices]
spi_dev_num = 1

(here you will have to put in the modalias "spidev")

[spi_board0]
modalias = "spidev"
max_speed_hz = 12000000
bus_num = 0
chip_select = 0
mode = 0
full_duplex = 1
manual_cs = 0

For more information about editing the fex file: http://linux-sunxi.org/Fex_Guide

Configuring your device-tree (mainline)

For the most boards SPI is disabled by default. To enable it you have to modify device-tree of your board.

Example for pcDuino3

As an example, we will enable SPI0 for this board. 
We will have to modify arch/arm/boot/dts/sun7i-a20-pcduino3.dts.
First of all, for aesthetic reasons, we want spi0 to appear as /sys/class/spi_master/spi0 and not as ls /sys/class/spi_master/spi32766, therefore we add spi0 = &spi0; in the aliases section.
Second - we enable spi0 by adding +&spi0 section. In example below spidev is also enabled, so that /dev/spidev0.0 could be accessible from the userspace (please note, that you must also enable CONFIG_SPI_SPIDEV in kernel configuration). If you don't need that functionality, you can omit spidev@0x00 section.

--- a/arch/arm/boot/dts/sun7i-a20-pcduino3.dts
+++ b/arch/arm/boot/dts/sun7i-a20-pcduino3.dts
@@ -, +, @@ aliases {
serial0 = &uart0;
+ spi0 = &spi0;
}; chosen {
@@ -, +, @@
regulator-name = "avcc";
}; +&spi0 {
+ pinctrl-names = "default";
+ pinctrl- = <&spi0_pins_a>,
+ <&spi0_cs0_pins_a>;
+ status = "okay";
+
+ spidev@0x00 {
+ compatible = "spidev";
+ spi-max-frequency = <>;
+ reg = <>;
+ };
+};
+
&reg_usb1_vbus {
status = "okay";
};

Example for A10s Olinuxino Micro UEXT connector

--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -, +, @@
clocks = <&apb1_gates >;
status = "disabled";
};
+
+ spi2: spi@01c17000 {
+ compatible = "allwinner,sun4i-a10-spi";
+ reg = <0x01c17000 0x1000>;
+ interrupts = <>;
+ clocks = <&ahb_gates >, <&spi2_clk>;
+ clock-names = "ahb", "mod";
+ dmas = <&dma SUN4I_DMA_DEDICATED >,
+ <&dma SUN4I_DMA_DEDICATED >;
+ dma-names = "rx", "tx";
+ status = "disabled";
+ #address-cells = <>;
+ #size-cells = <>;
+ };
};
}; @@ -, +, @@
allwinner,drive = <SUN4I_PINCTRL_30_MA>;
allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
};
+
+ spi2_pins_a: spi2@ {
+ allwinner,pins = "PB11", "PB12", "PB13", "PB14";
+ allwinner,function = "spi2";
+ allwinner,drive = <SUN4I_PINCTRL_10_MA>;
+ allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+ };
+
+ spi2_pins_b: spi2@ {
+ allwinner,pins = "PE00", "PE01", "PE02", "PE03";
+ allwinner,function = "spi2";
+ allwinner,drive = <SUN4I_PINCTRL_10_MA>;
+ allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+ };
}; --- a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
@@ -, +, @@
status = "okay";
}; +&spi2 {
+ pinctrl-names = "default";
+ pinctrl- = <&spi2_pins_a>;
+ status = "okay";
+};
+
&ohci0 {
status = "okay";
}; --- a/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun5i-a10s-olinuxino-micro.dts
@@ -, +, @@
pinctrl-names = "default";
pinctrl- = <&spi2_pins_a>;
status = "okay";
+ spi2_0 {
+ #address-cells = <>;
+ #size-cells = <>;
+
+ compatible = "spidev";
+
+ reg = <>;
+ spi-max-frequency = <>;
+ };
}; &ohci0 {

Using the SPI bus

In the user space

Once you will have this set you can boot your sunxi device and you will have in your dev in /dev/spidevn.0

Transfer size is limited to 64 bytes on sun4i and 128 bytes on sun6i. You have to loop over longer messages in your code. Some SPI devices may require that you prefix each message fragment with a header, other may not. YMMV. Look up transfer diagrams in device datasheet. 
Known problems: Using the spidev_test.c example you will receive [spi]: drivers/spi/spi_sunxi.c(L1025) cpu tx data time out!
Using the spidev_fdx.c method it works like a charm! :)

I've made a user friendlier library (C functions) to comunicate using SPIdev:
(Note, this library supose the read and write address to be 2 bytes)

/*
spidevlib.c - A user-space program to comunicate using spidev.
Gustavo Zamboni
*/
#include <stdint.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <linux/spi/spidev.h> char buf[];
char buf2[];
extern int com_serial;
extern int failcount; struct spi_ioc_transfer xfer[]; //////////
// Init SPIdev
//////////
int spi_init(char filename[])
{
int file;
__u8 mode, lsb, bits;
__u32 speed=; if ((file = open(filename,O_RDWR)) < )
{
printf("Failed to open the bus.");
/* ERROR HANDLING; you can check errno to see what went wrong */
com_serial=;
exit();
} ///////////////
// Verifications
///////////////
//possible modes: mode |= SPI_LOOP; mode |= SPI_CPHA; mode |= SPI_CPOL; mode |= SPI_LSB_FIRST; mode |= SPI_CS_HIGH; mode |= SPI_3WIRE; mode |= SPI_NO_CS; mode |= SPI_READY;
//multiple possibilities using |
/*
if (ioctl(file, SPI_IOC_WR_MODE, &mode)<0) {
perror("can't set spi mode");
return;
}
*/ if (ioctl(file, SPI_IOC_RD_MODE, &mode) < )
{
perror("SPI rd_mode");
return;
}
if (ioctl(file, SPI_IOC_RD_LSB_FIRST, &lsb) < )
{
perror("SPI rd_lsb_fist");
return;
}
//sunxi supports only 8 bits
/*
if (ioctl(file, SPI_IOC_WR_BITS_PER_WORD, (__u8[1]){8})<0)
{
perror("can't set bits per word");
return;
}
*/
if (ioctl(file, SPI_IOC_RD_BITS_PER_WORD, &bits) < )
{
perror("SPI bits_per_word");
return;
}
/*
if (ioctl(file, SPI_IOC_WR_MAX_SPEED_HZ, &speed)<0)
{
perror("can't set max speed hz");
return;
}
*/
if (ioctl(file, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < )
{
perror("SPI max_speed_hz");
return;
} printf("%s: spi mode %d, %d bits %sper word, %d Hz max\n",filename, mode, bits, lsb ? "(lsb first) " : "", speed); //xfer[0].tx_buf = (unsigned long)buf;
xfer[].len = ; /* Length of command to write*/
xfer[].cs_change = ; /* Keep CS activated */
xfer[].delay_usecs = , //delay in us
xfer[].speed_hz = , //speed
xfer[].bits_per_word = , // bites per word 8 //xfer[1].rx_buf = (unsigned long) buf2;
xfer[].len = ; /* Length of Data to read */
xfer[].cs_change = ; /* Keep CS activated */
xfer[].delay_usecs = ;
xfer[].speed_hz = ;
xfer[].bits_per_word = ; return file;
} //////////
// Read n bytes from the 2 bytes add1 add2 address
// 这种2个spi_ioc_transfer一前以后的方式,前面的永远是master发送,后面的永远是slave的发送,这种方式,相当于spi总线里的 spi_write_then_read 函数的实现,这种方式只适用于小量数据的传输,即一次收发小于32字节的数据。(应该是这样的,但还是有待进一步考证)。只有在同一个spi_ioc_transfer结构里的tx和rx才是同时收发的。
//////////
char * spi_read(int add1,int add2,int nbytes,int file)
{
int status; memset(buf, , sizeof buf);
memset(buf2, , sizeof buf2);
buf[] = 0x01;
buf[] = add1;
buf[] = add2;
xfer[].tx_buf = (unsigned long)buf;
xfer[].len = ; /* Length of command to write*/
xfer[].rx_buf = (unsigned long) buf2;
xfer[].len = nbytes; /* Length of Data to read */
status = ioctl(file, SPI_IOC_MESSAGE(), xfer);
if (status < )
{
perror("SPI_IOC_MESSAGE");
return;
}
//printf("env: %02x %02x %02x\n", buf[0], buf[1], buf[2]);
//printf("ret: %02x %02x %02x %02x\n", buf2[0], buf2[1], buf2[2], buf2[3]); com_serial=;
failcount=;
return buf2;
} //////////
// Write n bytes int the 2 bytes address add1 add2
//////////
void spi_write(int add1,int add2,int nbytes,char value[],int file)
{
unsigned char buf[], buf2[];
int status; memset(buf, , sizeof buf);
memset(buf2, , sizeof buf2);
buf[] = 0x00;
buf[] = add1;
buf[] = add2;
if (nbytes>=) buf[] = value[];
if (nbytes>=) buf[] = value[];
if (nbytes>=) buf[] = value[];
if (nbytes>=) buf[] = value[];
xfer[].tx_buf = (unsigned long)buf;
xfer[].len = nbytes+; /* Length of command to write*/
status = ioctl(file, SPI_IOC_MESSAGE(), xfer);
if (status < )
{
perror("SPI_IOC_MESSAGE");
return;
}
//printf("env: %02x %02x %02x\n", buf[0], buf[1], buf[2]);
//printf("ret: %02x %02x %02x %02x\n", buf2[0], buf2[1], buf2[2], buf2[3]); com_serial=;
failcount=;
}

Usage example:

char *buffer;
char buf[]; file=spi_init("/dev/spidev0.0"); //dev buf[] = 0x41;
buf[] = 0xFF;
spi_write(0xE6,0x0E,,buf,file); //this will write value 0x41FF to the address 0xE60E buffer=(char *)spi_read(0xE6,0x0E,,file); //reading the address 0xE60E close(file);

For info it is possible to use all the 12000000 Hz frequency limit transfers, however bear in mind, that frequency will not scale linearly. There are fixed frequencies you can select from, especially at the higher end:

  • 100.00 MHz
  • 50.00 MHz
  • 33.33 MHz
  • 25.00 MHz
  • 20.00 MHz
  • 16.66 MHz
  • 14.28 MHZ
  • 12.50 MHz
  • 11.11 MHz
  • 10.00 MHz
  • 9.09 MHz
  • 8.33 MHz
  • 7.69 MHz
  • 7.14 MHz
  • 6.66 MHz
  • 6.25 MHz
  • 5.88 MHz
  • 5.55 MHz
  • 5.26 MHz
  • 5.00 MHz
  • 4.76 MHz
  • 4.54 MHz
  • 4.34 MHz
  • 4.16 MHz
  • 3.84 MHz
  • ...

In the kernel space

If you are coding a driver for a SPI device, it makes most sense to code it as a kernel module. Instead of using /dev/spidevX.X you should register a new (slave) device and exchange data through it. If you are wondering what bus number you should use, you can find available buses by listing /sys/class/spi_master. There should be nodes like spi0, spi1... Number after spi is bus number. What number gets spi master depends on device-tree configuration.

Here is an example of module, that writes 0x00 to SPI when module is initialized and 0xff when uninitialized. It is using bus number 0 and communicating at the speed of 1Hz:

#include <linux/init.h>
#include <linux/module.h>
#include <linux/spi/spi.h> #define MY_BUS_NUM 0
static struct spi_device *spi_device; static int __init spi_init(void)
{
int ret;
unsigned char ch = 0x00;
struct spi_master *master; //Register information about your slave device:
struct spi_board_info spi_device_info = {
.modalias = "my-device-driver-name",
.max_speed_hz = , //speed your device (slave) can handle
.bus_num = MY_BUS_NUM,
.chip_select = ,
.mode = ,
}; /*To send data we have to know what spi port/pins should be used. This information
can be found in the device-tree. */
master = spi_busnum_to_master( spi_device_info.bus_num );
if( !master ){
printk("MASTER not found.\n");
return -ENODEV;
} // create a new slave device, given the master and device info
spi_device = spi_new_device( master, &spi_device_info ); if( !spi_device ) {
printk("FAILED to create slave.\n");
return -ENODEV;
} spi_device->bits_per_word = ; ret = spi_setup( spi_device ); if( ret ){
printk("FAILED to setup slave.\n");
spi_unregister_device( spi_device );
return -ENODEV;
} spi_write(spi_device, &ch, sizeof(ch)); return ;
} static void __exit spi_exit(void)
{
unsigned char ch = 0Xff; if( spi_device ){
spi_write(spi_device, &ch, sizeof(ch));
spi_unregister_device( spi_device );
}
} module_init(spi_init);
module_exit(spi_exit); MODULE_LICENSE("GPL");
MODULE_AUTHOR("Piktas Zuikis <[email protected]>");
MODULE_DESCRIPTION("SPI module example");

SPI NOR Flash

The Allwinner Boot Rom can boot from NOR flash packaged over an SPI interface. Booting devices from SPI flash is covered in this other article.

SPI bus 的收发编程的更多相关文章

  1. linux SPI bus demo hacking

    /********************************************************************** * linux SPI bus demo hacking ...

  2. Linux内核中SPI总线驱动分析

    本文主要有两个大的模块:一个是SPI总线驱动的分析 (研究了具体实现的过程): 另一个是SPI总线驱动的编写(不用研究具体的实现过程). 1 SPI概述 SPI是英语Serial Peripheral ...

  3. spi子系统之驱动SSD1306 OLED

    spi子系统之驱动SSD1306 OLED 接触Linux之前,曾以为读源码可以更快的学习软件,于是前几个博客都是一边读源码一边添加注释,甚至精读到每一行代码,实际上效果并不理想,看过之后就忘记了.主 ...

  4. SPI总线

    一.概述. SPI, Serial Perripheral Interface, 串行外围设备接口, 是 Motorola 公司推出的一种同步串行接口技术. SPI 总线在物理上是通过接在外围设备微控 ...

  5. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

  6. spi驱动框架全面分析,从master驱动到设备驱动

    内核版本:linux2.6.32.2  硬件资源:s3c2440 参考:  韦东山SPI视频教程 内容概括:     1.I2C 驱动框架回顾     2.SPI 框架简单介绍     3.maste ...

  7. Linux内核SPI支持概述

    1. 什么是SPI? Serial Peripheral Interface是一种同步4线串口链路,用于连接传感器.内存和外设到微控制器.他是一种简单的事实标准,还不足以复杂到需要一份正式的规范.SP ...

  8. Arduino SPI + SPI Flash芯片W25Q80BV

    W25Q80BV是台湾华邦电子(Winbond)生产的8M-bit串行flash芯片.主要特性有: 工作电压:2.5 ~ 3.6 V 功耗:读写(active)时4mA,低功耗(power-down) ...

  9. 第24章 SPI—读写串行FLASH—零死角玩转STM32-F429系列

    第24章     SPI—读写串行FLASH 全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/ ...

随机推荐

  1. Hive学习笔记——parse

    Hive是如何解析SQL的呢,首先拿hive的建表语句来举例,比如下面的建表语句 create table test(id int,name string)row format delimited f ...

  2. 发布微信小程序体验版

    小程序这么火,一直没有做过.因为公司有个业务需要做小程序就顺带学习了一把. 1)本次是采用<微信开发者工具 Stable v1.02.1904090>进行的开发: 2)前端使用的是微信官方 ...

  3. General VDPConfig对讲配置工具

    General VDPConfig大华楼宇对讲配置工具是ConfigTool的一个对讲模块,主要服务对象为可视对讲的门口机和室内机,大华可视对讲在独户应用中,利用了自身在视频监控领域的优势,充分整合了 ...

  4. VisualVM使用

    sualVM是JDK自带的一个用于Java程序性能分析的工具 在JDK安装目录的bin文件夹下名称为 jvisualvm.exe 在左侧选择应用 (1)概述 应用程序和运行时环境的基本信息 基本参数 ...

  5. 1 linux性能优化之平均负载uptime

    不知道onenote的笔记复制出来就是图片了...

  6. Android MVP模式简单介绍:以一个登陆流程为例

    老的项目用的MVC的模式,最近完成了全部重构成MVP模式的工作,虽然比较麻烦,好处是代码逻辑更加清楚.简洁,流程更加清晰,对于后续版本迭代维护都挺方便.对于一些想要学习MVP模式的同学来讲,百度搜出来 ...

  7. [转帖]Linux超级用户root口令忘记怎么办?

    Linux超级用户root口令忘记怎么办? 2010-05-10 12:15:00 monkey_d_meng 阅读数 5535  收藏 更多 分类专栏: Linux   版权声明:本文为博主原创文章 ...

  8. Resharper速度慢解决办法

    Reshaper很好用,但是安装后速度特别慢,大部分情况下,我们只需要使用一些插件功能,代码自动分析功能可以关闭,如图:取消Code analysis即可.

  9. 老司机的自信,让 CDN 加速再加速

    CDN 的存在,加快了用户的访问速度,使用户可以在不提升网速下,获得更好的访问体验.购物时,可以更快的显示商品图片:看电影时,可以随意的拖拽浏览.如果把数据资源比作是一件件包裹,那么用户的带宽就像是一 ...

  10. pytest_html报告报错截图+失败重跑

    前言 做web自动化的小伙伴应该都希望在html报告中展示失败后的截图,提升报告的档次,pytest-html也可以生成带截图的报告. conftest.py 1.失败截图可以写到conftest.p ...