一、SPI协议介绍:

1、【SPI基础知识简介】

SPI全称是串行外设接口(Serial Peripheral Interface),是由Motorola提出的一种全双工同步串行通信接口,通信波特率可以高达5Mbps,但具体速度大小取决于SPI硬件。SPI接口具有全双工操作,操作简单,数据传输速率较高的优点,但也存在没有指定的流控制,没有应答机制确认是否接收到数据的缺点。SPI总线只需四条线就可以完成MCU与各种外围器件的通讯:

SCLK:Serial Clock,(串行)时钟
MISO:Master In Slave Out,主设备输入,从设备输出
MOSI:Master Out  Slave In,主设备输出,从设备输入
SS:        Slave Select,选中从设备,片选,片选的其他别称[① CS(Chip Select)芯片选择、②CE(Chip Enable)芯片使能]

2、【SPI相关的缩写或说法】

先简单说一下,关于SPI中一些常见的说法:
SPI的极性Polarity和相位Phase,最常见的写法是CPOL和CPHA,不过也有一些其他写法,简单总结如下:
(1) CKPOL (Clock Polarity) = CPOL = POL = Polarity = (时钟)极性
(2) CKPHA (Clock Phase)   = CPHA = PHA = Phase = (时钟)相位
(3) SCK=SCLK=SPI的时钟
(4) Edge=边沿,即时钟电平变化的时刻,即上升沿(rising edge)或者下降沿(falling edge)
对于一个时钟周期内,有两个edge,分别称为:
Leading edge=前一个边沿=第一个边沿,对于开始电压是1,那么就是1变成0的时候,对于开始电压是0,那么就是0变成1的时候;
Trailing edge=后一个边沿=第二个边沿,对于开始电压是1,那么就是0变成1的时候(即在第一次1变成0之后,才可能有后面的0变成1),对于开始电压是0,那么就是1变成0的时候;

3、【SPI总线通信】

①、SPI是[单主设备(single-master )]通信协议,这意味着总线中的只有一支中心设备能发起通信。当SPI主设备想读/写[从设备]时,它首先拉低[从设备]对应的SS线(SS是低电平有效),接着开始发送工作脉冲到时钟线上,在相应的脉冲时间上,[主设备]把信号发到MOSI实现“写”,同时可对MISO采样而实现“读”

②、SPI模块为了和外设进行数据交换,根据外设工作要求,其输出串行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响,SPI主模块和与之通信的外设音时钟相位和极性应该一致。
如果CPOL=0,串行同步时钟的空闲状态为低电平; 
如果CPOL=1,串行同步时钟的空闲状态为高电平; 
时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。 
如果CPHA=0,在串行同步时钟的第一个跳变沿(上升或下降)数据被采样; 
如果CPHA=1,在串行同步时钟的第二个跳变沿(上升或下降)数据被采样;

4、【数据传输】

在一个SPI时钟周期内,会完成如下操作:

1) 主设备通过MOSI线发送1位数据,从设备通过该线读取这1位数据;

2) 从设备通过MISO线发送1位数据,主设备通过该线读取这1位数据。

这是通过移位寄存器来实现的。如图所示,主设备和从设备各有一个移位寄存器,且二者连接成环。随着时钟脉冲,数据按照从高位到低位的方式依次移出主设备寄存器和从机寄存器,并且依次移入从设备寄存器和主设备寄存器。当寄存器中的内容全部移出时,相当于完成了两个寄存器内容的交换。

5、【内部工作机制】

SSPSR 是 SPI 设备内部的移位寄存器(Shift Register). 它的主要作用是根据 SPI 时钟信号状态, 往 SSPBUF 里移入或者移出数据, 每次移动的数据大小由Bus-Width 以及 Channel-Width 所决定。

二、树莓派40Pin引脚对照图:

三、本次SPI编程要实现的结果:

用排线短接树莓派的19脚和21脚,实现树莓派spi通信的自发自收

四、SPI编程实现:

1、编程思路:

①、使能内核SPI驱动模块抽象出spi设备
②、利用open系统调用打开spi设备 "/dev/spidev0.0"
③、利用ioctl设置spi设备各项参数
④、进行读写操作

2、实际操作与代码部分:

①、使能内核SPI驱动
pi@raspberrypi:~ $ sudo raspi-config     弹出系统配置对话框,使能spi,重新启动树莓派,内核加载成功

重启之后可以确认系统已自动加载SPI驱动

pi@raspberrypi:~ $ ls /dev/spidev0.*
/dev/spidev0.0      /dev/spidev0.1

这里抽象出两个spi接口设备,但树莓派只引出来一组spi(引脚19、21、23、24),对应的设备文件为/dev/spidev0.0

②树莓派自发自收代码

/*********************************************************************************
*      Copyright:  (C) 2018 wangtao
*                  All rights reserved.
*
*       Filename:  spi_own.c
*    Description:  This file
*
*        Version:  1.0.0(11/07/2018)
*         Author:  WangTao <TAlicer@163.com>
*      ChangeLog:  1, Release initial version on "11/07/2018 17:15:56 PM"
*
********************************************************************************/#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "SPISet.h"int initSPI()
{int spiFd;spiFd=SPISetup(0,500000); //初始化SPI通道0,并设置为最大速度500000hzif(spiFd==-1){printf("init spi failed!\n");}
}int main()
{char tx_Data[10]={1,2,3,4,5,6,7,8,9,10}; //定义读写的数据char rx_Data[10]={0,0,0,0,0,0,0,0,0,0};int i=0;initSPI(); //spi的初始化while(1){SPIDataRW(0,tx_Data,rx_Data,7); //向总线中写入7个数据printf("read spi_rx_data is:\n"); //读出总线的数据,引脚19与21短接打印【1,2,3,4,5,6,7,0,0,0】// 引脚19与21不短接打印【0,0,0,0,0,0,0,0,0,0】  for(i=0;i<10;i++){printf("%d\n",rx_Data[i]);}printf("\n");sleep(1);}return 0;
}
/*********************************************************************************
*      Copyright:  (C) 2018 wangtao
*                  All rights reserved.
*
*       Filename:  SPISet.c
*    Description:  This file
*
*        Version:  1.0.0(11/07/2018)
*         Author:  WangTao <TAlicer@163.com>
*      ChangeLog:  1, Release initial version on "11/07/2018 17:15:56 PM"
*
********************************************************************************/#include <stdint.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <asm/ioctl.h>
#include <linux/spi/spidev.h>
#include "SPISet.h"static const char   *spiDev0  = "/dev/spidev0.0" ;
static const char   *spiDev1  = "/dev/spidev0.1" ;
static  uint8_t     spiBPW   = 8 ;
static  uint16_t    spiDelay = 0 ;static uint32_t     spiSpeeds [2] ;
static int          spiFds [2] ;/*
* SPIDataRW:
*    Write and Read a block of data over the SPI bus.
*    Note the data ia being read into the transmit buffer, so will
*    overwrite it!
*    This is also a full-duplex operation.
*********************************************************************************
*********************************************************************************/
int SPIDataRW (int channel, unsigned char *tx_data, unsigned char *rx_data, int len)
{int i = 0;struct spi_ioc_transfer spi ;channel &= 1 ;memset (&spi, 0, sizeof (spi)) ;spi.tx_buf        = (unsigned long)tx_data ;spi.rx_buf        = (unsigned long)rx_data ;spi.len           = len ;spi.delay_usecs   = spiDelay ;spi.speed_hz      = spiSpeeds [channel] ;spi.bits_per_word = spiBPW ;return ioctl (spiFds [channel], SPI_IOC_MESSAGE(1), &spi) ; //SPI_IOC_MESSAGE(1)的1表示spi_ioc_transfer的数量
}/*
* SPISetupMode:
*    Open the SPI device, and set it up, with the mode, etc.
*********************************************************************************
*********************************************************************************/int SPISetupMode (int channel, int speed, int mode)
{int fd ;if ((fd = open (channel == 0 ? spiDev0 : spiDev1, O_RDWR)) < 0){printf("Unable to open SPI device: %s\n", strerror (errno)) ;return -1;}spiSpeeds [channel] = speed ;spiFds    [channel] = fd ;/*
* 设置spi的读写模式:
*  Mode 0: CPOL=0, CPHA=0
*  Mode 1: CPOL=0, CPHA=1
*  Mode 2: CPOL=1, CPHA=0
*  Mode 3: CPOL=1, CPHA=1
*  这里我们默认设置为模式0
*********************************************************************************
*/if (ioctl (fd, SPI_IOC_WR_MODE, &mode) < 0)                     {                                                               printf("Can't set spi mode: %s\n", strerror (errno)) ;         return -1;                                                    }                                                               if (ioctl (fd, SPI_IOC_RD_MODE, &mode) < 0)                     {                                                               printf("Can't get spi mode: %s\n", strerror (errno)) ;        return -1;                                                 }    /*
* spi的读写bit/word设置可写
*    这里设置为8个位为一个字节
*********************************************************************************
*/if (ioctl (fd, SPI_IOC_WR_BITS_PER_WORD, &spiBPW) < 0)          {                                                               printf("Can't set bits per word: %s\n", strerror (errno))  ;  return -1;                                                    }                                                              if (ioctl (fd, SPI_IOC_RD_BITS_PER_WORD, &spiBPW) < 0)          {                                                               printf("Can't get bits per word: %s\n", strerror (errno))  ;  return -1;                                                   }   /*
* 设置spi读写速率
*********************************************************************************
*/if (ioctl (fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed) < 0){printf("Can't set max speed hz: %s\n", strerror (errno));return -1;}if (ioctl (fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed) < 0){printf("Can't get max speed hz: %s\n", strerror (errno));return -1;}return fd ;
}/*
* SPISetup:
*    Open the SPI device, and set it up, etc. in the default MODE 0
*********************************************************************************
*********************************************************************************/int SPISetup (int channel, int speed)
{return SPISetupMode (channel, speed, 0) ;
}
/*********************************************************************************
*      Copyright:  (C) 2018 wangtao
*                  All rights reserved.
*
*       Filename:  SPISet.h
*    Description:  This file
*
*        Version:  1.0.0(11/07/2018)
*         Author:  WangTao <TAlicer@163.com>
*      ChangeLog:  1, Release initial version on "11/07/2018 17:15:56 PM"
*
********************************************************************************/#ifdef __cplusplus
extern "C" {
#endifint SPIDataRW    (int channel, unsigned char *tx_data,unsigned char *rx_data, int len) ;
int SPISetupMode (int channel, int speed, int mode) ;
int SPISetup     (int channel, int speed) ;#ifdef __cplusplus
}
#endif

五、程序运行结果:

1、MISO与MOSI短接                                      2、MISO与MOSI未短接

                      

关于Linux下ioctl的理解推荐这篇博客 https://www.cnblogs.com/tdyizhen1314/p/4896689.html    
关于spi的ioctl的理解推荐这两篇博客  https://blog.csdn.net/borntox/article/details/51871480       
                                                            http://www.cnblogs.com/sankye/p/3955630.html

Linux下树莓派spi编程相关推荐

  1. C——Linux下的串口编程

    原 C--Linux下的串口编程 2017年06月06日 19:30:50 C_Aya 阅读数:11537 <span class="tags-box artic-tag-box&qu ...

  2. linux c语言工具,Linux下C语言编程环境的工具.doc

    Linux下C语言编程环境的工具 Linux下C语言编程环境的工具 Linux下C语言编程环境的工具 要想在Linux下进行C语言编程,首先得搭建好一个编程环境.这里分别说明一下几个非常有用的软件包. ...

  3. Linux下C语言编程-进程的创建

    Linux下C语言编程-进程的创建 作者:hoyt 1.进程的概念 Linux操作系统是面向多用户的.在同一时间可以有许多用户向操作系统发出各种命令.那么操作系统是怎么实现多用户的环境呢?在现代的操作 ...

  4. linux下的c编程

    vi编译器简介 vi的三种模式,分别为命令行模式,插入模式以及底行模式,这里游客去看,这里提供好几个小技巧:G表示移动到文件末尾nG表示移动到第几行 gcc优化选项 gcc可以对代码进行优化,他可以通 ...

  5. linux+下c语言编程项目,精通UNIX下C语言编程与项目实践

    cc -I  //include 目录 -L //静态库目录?动态也可以 -l //小写L,接静态库名称?动态也可以 -DXXX='"XXFF"' //-D直接定义宏 -c 只编译 ...

  6. 您知道Linux下C语言编程的一些注意事项吗_教育中国

    您知道Linux下C语言编程的一些注意事项吗_教育中国 云风的 BLOG: 一个 C 接口设计的问题 一个 C 接口设计的问题 C 语言在本质上,参数传递都是值传递.不像 Pascal 和 C++ 可 ...

  7. Linux下TCP网络编程-创建服务器与客户端

    一.前言 互联网概念诞生于20世纪60年代末,从9几年中国接入互联网开始到现在,生活的每个角落都能看到网络的使用.现在物联网时代.共享经济的到来,生活中不仅仅电脑.手机可以接入网络,身边的各个设备也能 ...

  8. c 调用 linux驱动程序,Linux下的C编程实战(五)――驱动程序设计

    Linux下的C编程实战(五) ――驱动程序设计 1.引言 设备驱动程序是操作系统内核和机器硬件之间的接口,它为应用程序屏蔽硬件的细节,一般来说,Linux的设备驱动程序需要完成如下功能: (1)初始 ...

  9. linux c语言 ppt,Linux下C语言编程.ppt

    Linux下C语言编程 Linux中C语言的重要性 Linux和C天生有不解之缘. Linux操作系统的内核主要是用C写的,另外Linux下的很多软件也是用C写的,特别是一些著名的服务软件,比如MyS ...

最新文章

  1. C#进行Visio二次开发之判断图纸是否有设备
  2. python从安装开始加粉_安装python
  3. Java字符串就该这样设计
  4. 开发环境和运行环境的区别_生产环境 VS 开发环境,关于Kubernetes的四大认识误区...
  5. MySQL5.5的安装,连接和操作
  6. C语言代码规范(四)命名规则
  7. 第十章 动态选路协议
  8. Windows配置Gtkmm开发环境(with codeblocks)
  9. 电商无线页面设计手机移动端的设计模板
  10. Zookeeper应用场景
  11. React Native运行原理解析
  12. MFC消息映射及消息处理函数原型
  13. 美食网页设计作品html,美食网页设计与制作.doc
  14. 2014年度江西省高等学校科技落地计划项目立项名单
  15. 调研分析-全球与中国工业电源插头和插座市场现状及未来发展趋势
  16. matlab对三维矩阵求和,【求助】多维矩阵求和运算!!
  17. 成年人的崩溃,是从借钱开始的
  18. tsv文件 java_将选定的行从tsv文件导入neo4j
  19. css实现立体长方柱
  20. pc端不同分辨率适配

热门文章

  1. [数据分析干货]四种简单常用的数据分析方法,学完立马升职加薪!
  2. Flat Lattice 代码
  3. Mac related cmd
  4. 城市公交线路查询系统
  5. 解决matlab文件导出为EPS文件,AI打不开的问题
  6. 功能机和Andorid 语言支持
  7. android日记 设计说明,基于Android的掌上校园系统的设计与实现毕业论文.doc
  8. 食堂饭卡刷卡原理及吐槽
  9. python制作连点器_python群聊工具实现(上)
  10. 树莓派教程二-网络配置