【ARM 裸机开发 (IMX6ULL-mini)】SPI 协议与ADXL345 三轴加速度传感器

文章目录

前言

在【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)**和 64 32 个发送 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相关寄存器配置成我们想要的模式。

c 复制代码
void spi3_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);
    //MISO
    IOMUXC_SetPinMux(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0);
    IOMUXC_SetPinConfig(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0x10B1);
    //MOSI
    IOMUXC_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读即是写,写即是读的特点,我们可以写出这样一个读写函数:

c 复制代码
unsigned char spi3_read_write(unsigned char data)
{
    while((ECSPI3->STATREG & (1 << 0)) == 0);
    ECSPI3->TXDATA = data;

    while((ECSPI3->STATREG & (1 << 3)) == 0);
    return ECSPI3->RXDATA;
}

七、ADXL345与IMX6ULL通信

根据ADXL345参考手册寄存器配置:0x31=0x0B 开启满分辨率 ,±16g,0.0039g/LSB 换算公式。原始数据拼接:低字节0x32在前,高字节0x33左移 8 位,CS 软件控制片选等,可以写出ADXL345的读函数、写函数、初始化函数、数据获取函数等。

c 复制代码
struct ADXL345_Data {
    short x;
    short y;
    short z;
    float x_g;
    float y_g;
    float z_g;
};

extern void adxl345_write(unsigned char reg_addr, unsigned char data);
extern unsigned char adxl345_read(unsigned char reg_addr);
extern void adxl345_init(void);
extern struct ADXL345_Data adxl345_read_data(void);
c 复制代码
void adxl345_write(unsigned char reg_addr, unsigned char data)
{
    // CS low
    gpio_set_value(GPIO1, 20, 0);
    //主机发送寄存器地址
    spi3_read_write(reg_addr);
    //主机发送数据
    spi3_read_write(data);
    // CS high
    gpio_set_value(GPIO1, 20, 1);
}

unsigned char adxl345_read(unsigned char reg_addr)
{
    unsigned char data;
    // CS low
    gpio_set_value(GPIO1, 20, 0);
    //主机发送读取的寄存器地址
    spi3_read_write(reg_addr | 0x80);
    //主机接收数据
    data = spi3_read_write(0x00);
    // CS high
    gpio_set_value(GPIO1, 20, 1);
    //返回数据
    return data;
}

void adxl345_init(void)
{
    //关闭中断
    adxl345_write(0x2E, 0x08);
    //设置满分辨率和满量程
    adxl345_write(0x31, 0x0B);
    //设置低功耗模式+配置采样速率
    adxl345_write(0x2C, 0x08);
    //配置测量和唤醒模式
    adxl345_write(0x2D, 0x0B);
}

struct ADXL345_Data adxl345_read_data(void)
{
    struct ADXL345_Data data;

    //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;

    return data;
}

八、UART、I2C与SPI对比

相关推荐
susplus3 小时前
【ARM 裸机开发 (IMX6ULL-mini)】RGB-LCD 驱动与时序详解
arm·rgb_lcd
额额额对了5 小时前
嵌入式 ADC 从入门到实战:基于 i.MX6ULL 的裸机驱动开发
驱动开发·嵌入式硬件·arm
幼儿园蛋小黑17 小时前
ARM 中断机制详解
arm
晊晌_h2 天前
IMX6ULL裸机开发:定时器(GPT/EPIT)与UART串口
arm·定时器·arm裸机开发
幼儿园蛋小黑7 天前
ARM 时钟与定时器详解:从原理到实践
arm
幼儿园蛋小黄7 天前
从定时器到时钟:ARM 嵌入式学习总结
arm
祖力557 天前
ARM时钟
嵌入式硬件·arm·时钟·imx6ull
koi77u7 天前
嵌入式学习——ARM(3)
arm
学linux的QQ蛋7 天前
i.MX6ULL GIC 中断整体知识点总结和ARM时钟重点总结
arm·时钟·中断