文章目录
- 前言
- 一、SPI基础概念
- 二、SPI的时序
- 三、IMX6ULL上的SPI
- 3.1 概念及原理框图
- 3.2 相关寄存器
- RXDATA
- TXDATA
- CONREG
- CONFIGREG
- STATREG
- 四、ADXL345加速度传感器
- 五、SPI初始化
- 六、SPI读写函数
- 七、ADXL345与IMX6ULL通信
- 八、UART、I2C与SPI对比
前言
在【ARM 裸机开发 (IMX6ULL-mini)】I2C协议详解这篇文章中详细讲解了IMX6ULL上的I2C协议,这篇文章将讲解另一个常用的通信协议SPI。两者在许多外设中,常常都能够使用,但还是有很多差异的,在这篇文章最后,会总结两者的区别。除此以外,本篇文章还会介绍ADXL345加速度传感器与IMX6ULL的SPI通信。
一、SPI基础概念
SPI:Serial Peripheral interface(串行外设接口)
SPI 接口主要应用在 EEPROM,FLASH,实时时钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI 是一种高速的,全双工,同步的串行通信总线。SPI数据发送与I2C一样是高位先行。
标准SPI采用四线组网方式,如下图:
➢SCLK:时钟信号线。与I2C类似的,所有设备都都需要时钟信号线进行同步,该信号由主机负责提供;
➢MOSI:主出从入,相当于主机的数据发送线;
➢MISO:主入从出,相当于主机的数据接收线;
➢CS:片选信号,与I2C不同,SPI设备没有所谓的从机地址,连接在总线上的设备通过CS片选信号选择。在同一时刻,总线上只有一个外设被选择,CS信号线通常是低电平有效的。
二、SPI的时序
SPI的通信时序与I2C不同,I2C的时序是固定的,时钟线(SCL)上的信号为高电平期间,数据线(SDA)上的数据必须保持稳定。只有在时钟线(SCL)上的信号为低电平期间,数据线(SCL)上的高电平或低电平状态才允许变化。但SPI的时序是取决于**时钟极性(Clock Polarity 简称CPOL)和时钟相位(Clock Phase简称CPHA)**的。
➢CPOL:规定了时钟信号线在空闲时的状态,CPOL=0表示时钟信号线在空闲时为低电平;CPOL=1表示时钟信号线在空闲是为高电平;
➢CPHA:与I2C不同,SPI的数据接收方并不是在时钟高或者低电平时采样的,而是在时钟信号处于沿时,至于是在上升沿还是下降沿取决于CPHA。CPHA=0表示是在时钟信号变化的第一个沿时采样,CPHA=1表示是在时钟信号变化的第二个沿时采样。
因此,我们可以得到这样的时序图:
无论哪种工作方式,SPI这种串行通信方式都是MSB先行的。
和I2C类似,大多数情况下与SPI外设的通信其实就是读写外设寄存器,这些寄存器同样也有着各自的地址。由于四线制SPI是全双工通信方式,在发送一个字节数据的同时,其实也就接收到了一个字节,因此,对于SPI来说,发送其实也就是接收。IMX6ULL有一个发送缓冲寄存器,同时也有一个接收缓冲寄存器,发送完一个字节之后,接收缓冲寄存器也会收到一个字节。
三、IMX6ULL上的SPI
3.1 概念及原理框图
IMX6ULL 自带的 SPI 外设叫做 ECSPI,全称是 Enhanced Configurable Serial Peripheral Interface,增强的SPI。ECSPI 有 6432 个接收**FIFO(RXFIFO)**和 6432 个发送FIFO(TXFIFO)。
IMX6ULL中一共有3个SPI。其中SPI3是引出了引脚。
IMX6ULL参考手册中的原理框图:
| 名称 | 全称 | 作用 |
|---|---|---|
| TXDATA | 发送FIFO | TX FIFO,64×32bit,CPU 往这里写待发送数据,写进去就进发送缓冲区 |
| RXDATA | 接收FIFO | RX FIFO,64×32bit,移位寄存器收到的数据存入这里;CPU 读这个寄存器拿收到的数据 |
| STATREG | 状态寄存器 | 标记 FIFO 满 / 空、传输完成、错误标志位 |
| INTREG | 中断寄存器 | 中断使能、中断标志控制,产生Interrupt Request |
| DMAREG | DMA 寄存器 | DMA 相关配置,产生DMA Requests |
| PERIODREG | 周期寄存器 | 硬件片选延时 |
| TESTREG | 测试寄存器 | 模块自测,调试用 |
| CONREG | 控制寄存器 | 核心配置:主 / 从模式、CPOL/CPHA、MSB/LSB、数据位宽、硬件 SS 使能 |
3.2 相关寄存器
在一般的SPI通信中我们主要用到RXDATA、TXDATA、CONREG、CONFIGREG、STATREG这五个寄存器:
RXDATA
TXDATA
CONREG
CONFIGREG
STATREG
四、ADXL345加速度传感器
ADXL345是一款三轴加速度传感器,主要特点如下:
➢ 测量范围:±2g、±4g、±8g、±16g设置;
➢ 分辨率:最高13位(3.9mg/LSB);
➢ 工作电压:2.0V至3.6V;
➢ 低功耗:测量模式仅0.23mA,待机模式40μA;
➢ 核心功能:1、三轴加速度测量(X、Y、Z);2、静态重力加速度测量; 3、动态加速度(震动、运动)测量; 4、自由落体检测; 5、单击/双击检测; 6、活动/非活动监测;
➢ 接口支持:SPI(3线和4线模式)、I2C数字接口
➢ 特色功能:1、内置32级FIFO缓冲; 2、可编程中断功能; 3、自动休眠和唤醒功能;
➢ 应用场景:1、移动设备方向检测; 2、游戏和虚拟现实控制器;3、运动检测和计步器;4、防盗系统的运动检测;5、工业振动监测;6、医疗设备姿态检测
时序图:
通过时序图可见ADXL345采用的是SPI通信模式3,即CPOL=1.CPHA=1。
这里需要注意一个细节,ADXL345在向寄存器写入和读取时,寄存器的最高位是不一样的,读的时候最高位为1,写的时候最高位为0。例如写数据到0x20寄存器时,先发0x20再发要写入的数据;读取时则需要先发0xA0,之后进入读取时序。
具体操作也是根据ADXL345参考手册操作相关寄存器。
五、SPI初始化
SPI3的四条线都是GPIO复用引脚,因此,我们需要将其配置为SPI模式。然后对SPI相关寄存器配置成我们想要的模式。
voidspi3_channel0_init(void){//片选IOMUXC_SetPinMux(IOMUXC_UART2_TX_DATA_GPIO1_IO20,0);IOMUXC_SetPinConfig(IOMUXC_UART2_TX_DATA_GPIO1_IO20,0x10B1);//时钟IOMUXC_SetPinMux(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK,0);IOMUXC_SetPinConfig(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK,0x10B1);//MISOIOMUXC_SetPinMux(IOMUXC_UART2_RTS_B_ECSPI3_MISO,0);IOMUXC_SetPinConfig(IOMUXC_UART2_RTS_B_ECSPI3_MISO,0x10B1);//MOSIIOMUXC_SetPinMux(IOMUXC_UART2_CTS_B_ECSPI3_MOSI,0);IOMUXC_SetPinConfig(IOMUXC_UART2_CTS_B_ECSPI3_MOSI,0x10B1);//通信前,CS拉高gpio_init(GPIO1,20,&(GPIO_Type_t){1,gpio_output});//SPI寄存器设置ECSPI3->CONREG=0;ECSPI3->CONREG|=(7<<20)|(0B1110<<12)|(2<<8)|(1<<4)|(1<<3)|(1<<0);ECSPI3->CONFIGREG=0;ECSPI3->CONFIGREG|=(1<<20)|(1<<4)|(1<<0);}六、SPI读写函数
在完成初始化后,根据SPI读即是写,写即是读的特点,我们可以写出这样一个读写函数:
unsignedcharspi3_read_write(unsignedchardata){while((ECSPI3->STATREG&(1<<0))==0);ECSPI3->TXDATA=data;while((ECSPI3->STATREG&(1<<3))==0);returnECSPI3->RXDATA;}七、ADXL345与IMX6ULL通信
根据ADXL345参考手册寄存器配置:0x31=0x0B开启满分辨率,±16g,0.0039g/LSB换算公式。原始数据拼接:低字节0x32在前,高字节0x33左移 8 位,CS 软件控制片选等,可以写出ADXL345的读函数、写函数、初始化函数、数据获取函数等。
structADXL345_Data{shortx;shorty;shortz;floatx_g;floaty_g;floatz_g;};externvoidadxl345_write(unsignedcharreg_addr,unsignedchardata);externunsignedcharadxl345_read(unsignedcharreg_addr);externvoidadxl345_init(void);externstructADXL345_Dataadxl345_read_data(void);voidadxl345_write(unsignedcharreg_addr,unsignedchardata){// CS lowgpio_set_value(GPIO1,20,0);//主机发送寄存器地址spi3_read_write(reg_addr);//主机发送数据spi3_read_write(data);// CS highgpio_set_value(GPIO1,20,1);}unsignedcharadxl345_read(unsignedcharreg_addr){unsignedchardata;// CS lowgpio_set_value(GPIO1,20,0);//主机发送读取的寄存器地址spi3_read_write(reg_addr|0x80);//主机接收数据data=spi3_read_write(0x00);// CS highgpio_set_value(GPIO1,20,1);//返回数据returndata;}voidadxl345_init(void){//关闭中断adxl345_write(0x2E,0x08);//设置满分辨率和满量程adxl345_write(0x31,0x0B);//设置低功耗模式+配置采样速率adxl345_write(0x2C,0x08);//配置测量和唤醒模式adxl345_write(0x2D,0x0B);}structADXL345_Dataadxl345_read_data(void){structADXL345_Datadata;//x轴数据data.x=adxl345_read(0x32)|adxl345_read(0x33)<<8;//y轴数据data.y=adxl345_read(0x34)|adxl345_read(0x35)<<8;//z轴数据data.z=adxl345_read(0x36)|adxl345_read(0x37)<<8;//获得三轴加速度data.x_g=data.x*0.0039;data.y_g=data.y*0.0039;data.z_g=data.z*0.0039;returndata;}