目录一.背景SPI四种工作模式三种协议对比ADXL345传感器二.寄存器conconfigstart三.实现1.初始化2.读写原理ADC调用一.背景SPI 是英语 Serial Peripheral interface(串行外设接口) 的缩写顾名思义就是串行外围设备接口。是 Motorola首先在其 MC68HCXX 系列处理器上定义的。SPI 接口主要应用在 EEPROMFLASH实时时钟AD 转换器还有数字信号处理器和数字信号解码器之间。SPI 是一种高速的全双工同步的串行通信总线并且在芯片的管脚上只占用四根线节约了芯片的管脚同时为 PCB 的布局上节省空间提供方便正是出于这种简单易用的特性现在越来越多的芯片集成了这种通信协议。标准SPI采用四线组网方式如下图SCLK时钟信号线。与I2C类似的所有设备都都需要时钟信号线进行同步该信号由主机负责提供➢MOSI主出从入相当于主机的数据发送线➢MISO主入从出相当于主机的数据接收线➢CS片选信号与I2C不同SPI设备没有所谓的从机地址连接在总线上的设备通过CS片选信号选择。在同一时刻总线上只有一个外设被选择CS信号线通常是低电平有效的。以上的四线方式构成了SPI全双工串行同步通信方式。需要注意的是对于MOSI和MISO来说不是必须存在的可以只有MOSI或者只有MISO。当然这样的话就只能实现单工通信方式了这样的SPI成为三线SPI。至于到底采用三线方式还是四线方式取决于外设本身。例如OLCD只需要主机发送数据而不需要从机发送数据就可以使用三线SPI的连接方式。SPI一主机多从机通过拉低CS片选信号选择设备。SPI的数据传输的时序要理解SPI的通信时序。需要搞清楚SPI的两个重要概念。时钟极性(Clock Polarity 简称CPOL)和时钟相位(Clock Phase简称CPHA)。注意这两个概念都是跟时钟信号线相关的。CPOL规定了时钟信号线在空闲时的状态CPOL0表示时钟信号线在空闲时为低电平CPOL1表示时钟信号线在空闲是为高电平CPHA与I2C不同SPI的数据接收方并不是在时钟高或者低电平时采样的而是在时钟信号处于沿时至于是在上升沿还是下降沿取决于CPHA。CPHA0表示是在时钟信号变化的第一个沿时采样CPHA1表示是在时钟信号变化的第二个沿时采样。因此不难理解这样的排列组合总共能够组合 出四种方式具体使用哪种方式是由外设厂家规定的因此在使用SPI之前就不得不读懂外设的说明文档。一下就是四种方式的时序图来自《IMX6ULL参考手册.pdf》P794页。SPI四种工作模式CPOL1, CPHA0:空闲时SCLK为高电平SPI在下降沿采样CPOL 1,CPHA1:空闲时SCLK为高电平SPI在上升沿采样CPOL0, CPHA0:空闲时SCLK为低电平SPI在上升沿采样CPOL0, CPHA1:空闲时SCLK为低电平SPI在下降沿采样无论哪种工作方式SPI这种串行通信方式都是MSB先行的。三种协议对比UARTI2CSPI全双工、异步、串行半双工、同步、串行全双工、同步、串行两根线 TXD RXD两根线 SCL SDA四根线 SCLK MOSI MISO CS 5MHZ100KHZ - 3.4MHZ1M - 100MHZ点对点多主多从一主多从低位先行高位先行高位先行ADXL345传感器测量X,Y,Z三轴的重力加速度写时寄存器地址最高位为低电平读时为高电平。INT1/2中断引脚里面有寄存器可以设置XYZ三轴的阈值当超过阈值触发中断SDO-MISOSDA-MOSICS-片选CS接17号SCL接18号MISO/SDO接19号MOSI/SDA接20号引脚二.寄存器con将60MHZ分频为1MHZ四位将哪一位置1就是将该通道设置为主机模式三种启动方式1.切换通道2.片选线拉低3.SMC位向发送缓冲区写数据自动开启EN在配置其他寄存器之前将此位置1configSCLK_CTL置1默认高电平SS_POL片选极性极性即默认电平的高低上面引脚接线图将CS接到GPIO引脚所以可通过GPIO设置此处默认即可。SS_CTL置0对应上面reg寄存器的发送数据就开始通信置1说明片选线拉低才通信。SCLK_POL:表示空闲时为高还是低电平。SCLK_PHA即CPHA,表示第几个沿发送数据时钟极性和相位都应置1.startRR:置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调用spi写主机发送寄存器地址-主机发送要写入的数据spi读主机发送寄存器地址-发送占位字节Dummy(0xFF 0x00) -从机回复数据// 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; }
阅读完成 · 觉得有帮助?