目录
一.背景
SPI四种工作模式
三种协议对比
ADXL345传感器
二.寄存器
con
config
start
三.实现
1.初始化
2.读写
原理
ADC调用
一.背景
- CPOL:规定了时钟信号线在空闲时的状态,CPOL=0表示时钟信号线在空闲时为低电平;CPOL=1表示时钟信号线在空闲是为高电平;
- CPHA:与I2C不同,SPI的数据接收方并不是在时钟高或者低电平时采样的,而是在时钟信号处于沿时,至于是在上升沿还是下降沿取决于CPHA。CPHA=0表示是在时钟信号变化的第一个沿时采样
SPI四种工作模式
CPOL= 1,CPHA=1:空闲时SCLK为高电平,SPI在上升沿采样
CPOL=0, CPHA=0:空闲时SCLK为低电平,SPI在上升沿采样
CPOL=0, CPHA=1:空闲时SCLK为低电平,SPI在下降沿采样
三种协议对比
| UART | I2C | SPI |
|---|---|---|
| 全双工、异步、串行 | 半双工、同步、串行 | 全双工、同步、串行 |
| 两根线 TXD RXD | 两根线 SCL SDA | 四根线 SCLK MOSI MISO CS |
| < 5MHZ | 100KHZ - 3.4MHZ | 1M - 100MHZ |
| 点对点 | 多主多从 | 一主多从 |
| 低位先行 | 高位先行 | 高位先行 |
ADXL345传感器
测量X,Y,Z三轴的重力加速度
写时寄存器地址最高位为低电平,读时为高电平。
INT1/2中断引脚,里面有寄存器可以设置XYZ三轴的阈值,当超过阈值触发中断
SDO->MISO
SDA->MOSI
CS->片选
CS接17号,SCL接18号,MISO/SDO接19号,MOSI/SDA接20号引脚
二.寄存器
con
将60MHZ分频为1MHZ
四位,将哪一位置1就是将该通道设置为主机模式
三种启动方式:
1.切换通道
2.片选线拉低
3.SMC位,向发送缓冲区写数据自动开启
EN:在配置其他寄存器之前,将此位置1
config
SCLK_CTL置1,默认高电平
SS_POL:片选极性,(极性即默认电平的高低),上面引脚接线图将CS接到GPIO引脚,所以可通过GPIO设置,此处默认即可。
SS_CTL:置0对应上面reg寄存器的发送数据就开始通信,置1说明片选线拉低才通信。
SCLK_POL:表示空闲时为高还是低电平。
SCLK_PHA即CPHA,表示第几个沿发送数据,时钟极性和相位都应置1.
start
RR:置1表示超过一位字节数据在接收缓冲区
TE同理。
三.实现
1.初始化
// spi3_channel0初始化 void spi3_channel0_init(void) { // 1. spi引脚复用功能 IOMUXC_SetPinMux(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0); // CS IOMUXC_SetPinMux(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0); // SCLK IOMUXC_SetPinMux(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0); // MISO IOMUXC_SetPinMux(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0); // MOSI // 2. spi引脚电气属性 IOMUXC_SetPinConfig(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0x10B1); IOMUXC_SetPinConfig(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0x10B1); IOMUXC_SetPinConfig(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0x10B1); IOMUXC_SetPinConfig(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0x10B1); // 3. 通信前,将CS拉高 GPIO_Type_t t = { .direction = gpio_output, .default_value = 1 }; gpio_init(GPIO1, 20, &t); // 4. SPI寄存器 ECSPI3->CONREG = 0; ECSPI3->CONREG |= (7 << 20) | (0x0E << 12) | (2 << 8) | (1 << 4) | (1 << 3) | (1 << 0); ECSPI3->CONFIGREG = 0; ECSPI3->CONFIGREG |= (1 << 20) | (1 << 4) | (1 << 0); };电气属性更改高压摆率即可,其余和点灯功能相同。
2.读写
原理
SPI是全双工移位通信:主从两侧各有一个移位寄存器。每个时钟沿到来时,主机通过 MOSI 往外“移出”1位,同时从 MISO “移入”1位。也就是——每“写”一位的同时必然“读”到一位。
写即是读:你往总线“写”一个字节(移出8位),同一过程里也会“读”回从机在MISO上的8(可能是无效/占位字节)。
读即是写:你想从从机“读”数据,必须提供时钟,所以主机必须同时“写”出一个占位字节(通常是0xFF或0x00),用这些时钟把从机的数据“移入”。
// spi读写 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; }ADC调用
// adxl345写 void adxl345_write(unsigned char reg_addr, unsigned char data) { // 拉低CS gpio_set_value(GPIO1, 20, 0); // 发送寄存器地址 spi3_read_write(reg_addr);//发完会回复一个字节,无用不管 // 发送数据 spi3_read_write(data); // 拉高CS gpio_set_value(GPIO1, 20, 1); } // adxl345读 unsigned char adxl345_read(unsigned char reg_addr) { unsigned char ret = 0; // 拉低CS gpio_set_value(GPIO1, 20, 0); // 发送寄存器地址 spi3_read_write(reg_addr | 0x80); // 发送Dummy ret = spi3_read_write(0xFF); // 拉高CS gpio_set_value(GPIO1, 20, 1); return ret; }struct ADXL345_Data { short x; short y; short z; float xg; float yg; float zg; }; // adxl345初始化 void adxl345_init(void) { // 关闭中断 adxl345_write(0x2E, 0x08); // 设置满分辨率 + 设置满量程 adxl345_write(0x31, 0x0B); // 低功耗模式 + 速率 adxl345_write(0x2C, 0x08); // 配置测量模式 + 唤醒模式 adxl345_write(0x2D, 0x0B); } // 获得adxl345三轴加速度 struct ADXL345_Data adxl345_collect(void) { struct ADXL345_Data ret; // 读取x轴数据 ret.x = adxl345_read(0x32); ret.x |= adxl345_read(0x33) << 8; // 读取y轴数据 ret.y = adxl345_read(0x34); ret.y |= adxl345_read(0x35) << 8; // 读取z轴数据 ret.z = adxl345_read(0x36); ret.z |= adxl345_read(0x37) << 8; // 获得x y z三轴加速度 ret.xg = ret.x * 0.0039; ret.yg = ret.y * 0.0039; ret.zg = ret.z * 0.0039; return ret; }