目录
一、SPI 简介
二、硬件原理
ECSPI3_SCLK
ECSPI3_MISO和ECSPI3_MOSI
ECSPI3_SS0
三、I.MX6U ECSPI 简介
ECSPIx_RXDATA
ECSPIx_TXDATA
ECSPIx_CONREG
ECSPIx_CONFIGREG
ECSPIx_PERIODREG编辑
ECSPIx_STATREG
四、ICM-20608 简介
五、代码编写
1、创建文件及文件夹
2、添加时钟配置
3、编写spi.h
4、编写spi.c
5、编写icm20608.h
6、编写icm20608.c
7、编写main.c
8、修改makefile
同 I2C 一样, SPI 是很常用的通信接口,也可以通过 SPI 来连接众多的传感器。相比 I2C 接
口, SPI 接口的通信速度很快, I2C 最多 400KHz,但是 SPI 可以到达几十 MHz。
一、SPI 简介
和 I2C 一样广泛使用的串行通信: SPI, SPI 全称是 Serial Perripheral Interface,也就是串行外围设备接口。 SPI 是 Motorola 公司推出的一种同步串行接口技术,是一种高速、全双工的同步通信总线, SPI 时钟频率相比 I2C 要高很多,最高可以工作在上百 MHz。 SPI 以主从方式工作,通常是有一个主设备和一个或多个从设备,一般 SPI 需要4 根线,但也可以使用三根线(单向传输)
这四根线如下:
①、 CS/SS, Slave Select/Chip Select,这个是片选信号线,用于选择需要进行通信的从设备。
I2C 主机是通过发送从机设备地址来选择需要进行通信的从机设备的, SPI 主机不需要发送从机
设备,直接将相应的从机设备片选信号拉低即可。
②、 SCK, Serial Clock,串行时钟,和 I2C 的 SCL 一样,为 SPI 通信提供时钟。
③、 MOSI/SDO, Master Out Slave In/Serial Data Output,简称主出从入信号线,这根数据线
只能用于主机向从机发送数据,也就是主机输出,从机输入。
④、 MISO/SDI, Master In Slave Out/Serial Data Input,简称主入从出信号线,这根数据线只能用户从机向主机发送数据,也就是主机输入,从机输出。SPI 通信都是由主机发起的,主机需要提供通信的时钟信号。
SPI 有四种工作模式,通过串行时钟极性(CPOL)和相位(CPHA)的搭配来得到四种工作模式:
①、 CPOL=0,串行时钟空闲状态为低电平。
②、 CPOL=1,串行时钟空闲状态为高电平,可以通过配置时钟相位(CPHA)选择具体的传输议。
③、 CPHA=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
④、 CPHA=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。
二、硬件原理
看六轴传感器 ICM-20608引脚
ECSPI3_SCLK
ECSPI3_SCLK接在 UART2_RXD
ECSPI3_MISO和ECSPI3_MOSI
ECSPI3_MOSI接在UART2_CTS;ECSPI3_MISO接在UART2_RTS
ECSPI3_SS0
ECSPI3_SS0接在UART2_TXD
这个需要注意,后续会将该引脚复用为上面的GPIO1_IO20(软件片选)
三、I.MX6U ECSPI 简介
I.MX6U 自带的 SPI 外设叫做 ECSPI,全称是 Enhanced Configurable Serial Peripheral Interface,别看前面加了个“EC”就以为和标准 SPI 有不同, 其实就是 SPI
I.MX6U 的 ECSPI 可以工作在主模式或从模式,这里使用主模式, I.MX6U 有 4 个ECSPI,每个 ECSPI 支持四个片选信号,因为硬件片选信号只能使用指定的片选 IO,使用硬件片选一个主接口只能接四个从器件;这里用软件片选,人为拉低cs,一个spi主接口就可以支持很多个spi外设,软件片选可以使用任意的 IO
数据手册寄存器如下
ECSPIx_RXDATA
RXDATA寄存器为接收到的数据
ECSPIx_TXDATA
TXDATA寄存器为发送数据寄存器
ECSPIx_CONREG
BURST_LENGTH(bit31:24): 突发长度,设置 SPI 的突发传输数据长度,在一次 SPI 发送中最大可以发送 2^12bit 数据。可以设置 0X000~0XFFF,分别对应 1~2^12bit。一般设置突发长度为一个字节,也就是 8bit, BURST_LENGTH=7。
CHANNEL_SELECT(bit19:18): SPI 通道选择,一个 ECSPI 有四个硬件片选信号,每个片选信号是一个硬件通道,虽然本章实验使用的软件片选,但是 SPI 通道还是要选择的。可设置为 0~3,分别对应通道 0~3。开发板上的 ICM-20608 的片选信号接的是ECSPI3_SS0,也就是 ECSPI3 的通道 0,所以实验设置为 0。
DRCTL(bit17:16): SPI 的 SPI_RDY 信号控制位,用于设置 SPI_RDY 信号,为 0 的话不关心 SPI_RDY 信号;为 1 的话 SPI_RDY 信号为边沿触发;为 2 的话 SPI_DRY 是电平触发。
PRE_DIVIDER(bit15:12): SPI 预分频,设置前级分频, ECSPI 时钟频率使用两步来完成分频,此位设置的是第一步,可设置 0~15,分别对应 1~16 分频。
POST_DIVIDER(bit11:8): SPI 分频值,设置2级分频, ECSPI 时钟频率的第二步分频设置,分频值为2^POST_DIVIDER,最大2^n分频,n=0-15
CHANNEL_MODE(bit7:4): SPI 通道主/从模式设置, CHANNEL_MODE[3:0]分别对应 SPI通道 3~0, 为 0 的话就是设置为从模式,如果为 1 的话就是主模式。比如设置为 0X01 的话就是设置通道 0 为主模式。
SMC(bit3):开始模式控制,此位只能在主模式下起作用,为 0 的话通过 XCH 位来开启 SPI突发访问,为 1 的话只要向 TXFIFO 写入数据就开启 SPI 突发访问。
XCH(bit2): 此位只在主模式下起作用,当 SMC 为 0 的话此位用来控制 SPI 突发访问的开启。
HT(bit1): HT 模式使能位, I.MX6ULL 不支持。
EN(bit0): SPI 使能位,为 0 的话关闭 SPI,为 1 的话使能 SPI。
ECSPIx_CONFIGREG
HT_LENGTH(bit28:24): HT 模式下的消息长度设置, I.MX6ULL 不支持。
SCLK_CTL(bit23:20):设置 SCLK 信号线空闲状态电平, SCLK_CTL[3:0]分别对应通道3~0,为 0 的话 SCLK 空闲状态为低电平,为 1 的话 SCLK 空闲状态为高电平。
DATA_CTL(bit19:16):设置 DATA 信号线空闲状态电平, DATA_CTL[3:0]分别对应通道3~0,为 0 的话 DATA 空闲状态为高电平,为 1 的话 DATA 空闲状态为低电平。
SS_POL(bit15:12): 设置 SPI 片选信号极性设置, SS_POL[3:0]分别对应通道 3~0,为 0 的话片选信号低电平有效,为 1 的话片选信号高电平有效。
SCLK_POL(bit7:4): SPI 时钟信号极性设置,也就是 CPOL, SCLK_POL[3:0]分别对应通道 3~0,为 0 的话 SCLK 高电平有效(空闲的时候为低电平),为 1 的话 SCLK 低电平有效(空闲
的时候为高电平)。
SCLK_PHA(bit3:0): SPI时钟相位设置,也就是CPHA, SCLK_PHA[3:0]分别对应通道3~0,
为 0 的话串行时钟的第一个跳变沿(上升沿或下降沿)采集数据,为 1 的话串行时钟的第二个跳
变沿(上升沿或下降沿)采集数据。
通过 SCLK_POL 和 SCLK_PHA 可以设置 SPI 的工作模式。
ECSPIx_PERIODREG
CSD_CTL(bit21:16): 片选信号延时控制位,用于设置片选信号和第一个 SPI 时钟信号之
间的时间间隔,范围为 0~63。
CSRC(bit15): SPI 时钟源选择,为 0 的话选择 SPI CLK 为 SPI 的时钟源,为 1 的话选择
32.768KHz 的晶振为 SPI 时钟源。一般选择 SPI CLK 作为 SPI 时钟源,打开时钟树找到eCSPI
看到eCSPI在粉红色线上,一直对应pll3_sw_clk,也就是480HMZ,从上一直下来到需要”/8“,也就是8分频,也就是480hmz需要除8,就是60mhz;然后到osc选择器,用于选择根时钟源,由寄存器 CSCDR2 的位 ECSPI_CLK_SEL 来控制,下面找到寄存器 CSCDR2的bit18
为 0 的话选择 pll3_60m 作为 ECSPI 根时钟源。为 1 的话选择 osc_clk 作为 ECSPI 时钟源。本章选择 pll3_60m 作为 ECSPI 根时钟源;时钟树继续往下到1分频,由寄存器 CSCDR2 的位ECSPI_CLK_PODF 来控制,也就是寄存器 CSCDR2的bit24-19,如下
这里设置为0,表示为1分频;最后进入的eCSPI时钟源为60mhz
ECSPIx_STATREG
ECSPI 的状态寄存器
TC(bit7):传输完成标志位,为 0 表示正在传输,为 1 表示传输完成。
RO(bit6): RXFIFO 溢出标志位,为 0 表示 RXFIFO 无溢出,为 1 表示 RXFIFO 溢出。
RF(bit5): RXFIFO 空标志位,为 0 表示 RXFIFO 不为空,为 1 表示 RXFIFO 为空。
RDR(bit4): RXFIFO 数据请求标志位,此位为 0 表示 RXFIFO 里面的数据不大于
RX_THRESHOLD,此位为 1 的话表示 RXFIFO 里面的数据大于 RX_THRESHOLD。
RR(bit3): RXFIFO 就绪标志位,为 0 的话 RXFIFO 没有数据,为 1 的话表示 RXFIFO 中
至少有一个字的数据。
TF(bit2): TXFIFO 满标志位,为 0 的话表示 TXFIFO 不为满,为 1 的话表示 TXFIFO 为满。
TDR(bit1): TXFIFO 数据请求标志位,为 0 表示 TXFIFO 中的数据大于 TX_THRESHOLD,
为 1 表示 TXFIFO 中的数据不大于 TX_THRESHOLD。
TE(bit0): TXFIFO 空标志位,为 0 表示 TXFIFO 中至少有一个字的数据,为 1 表示 TXFIFO
为空。
四、ICM-20608 简介
ICM-20608 是6 轴 MEMS 传感器,包括 3 轴加速度和 3 轴陀螺仪,并且支持 I2C 和 SPI 两种协议,使用 I2C 接口的话通信速度最高可以达到400KHz,使用 SPI 接口的话通信速度最高可达到 8MHz。
ICM-20608 也是通过读写寄存器来配置和读取传感器数据,使用 SPI 接口读写寄存器需要 16 个时钟或者更多(如果读写操作包括多个字节的话),第一个字节包含要读写的寄存器地址,寄存器地址最高位是读写标志位,如果是读的话寄存器地址最高位要为 1,如果是写的话寄存器地址最高位要为 0,剩下的 7 位才是实际的寄存器地址,寄存器地址后面跟着的就是读写的数据。
在数据手册可以看到
最高频率为8mhz
注意这个寄存器配置,芯片默认会睡眠,需要关闭
部分寄存器如下
寄存器 地址 | 位 | 寄存器功能 | 描述 |
0X19 | SMLPRT_DIV[7:0] | 输出速率设置 | 设置输出速率,输出速率计算公式如下: SAMPLE_RATE=INTERNAL_SAMPLE_RATE/ (1 + SMPLRT_DIV) |
0X1A | DLPF_CFG[2:0] | 芯片配置 | 设置陀螺仪低通滤波。可设置 0~7。 |
0X1B | FS_SEL[1:0] | 陀螺仪量程设置 | 0:±250dps; 1:±500dps; 2:±1000dps 3:±2000dps |
0X1C | ACC_FS_SEL[1:0] | 加速度计量程设置 | 0:±2g; 1:±4g; 2:±8g; 3:±16g |
0X1D | A_DLPF_CFG[2:0] | 加速度计低通滤波设置 | 设置加速度计的低通滤波,可设置 0~7 |
0X1E | GYRO_CYCLE[7] | 陀螺仪低功耗使能 | 0:关闭陀螺仪的低功耗功能。 1:使能陀螺仪的低功耗功能 |
0X23 | TEMP_FIFO_EN[7] | FIFO 使能控制 | 1:使能温度传感器 FIFO。 0:关闭温度传感器 |
XG_FIFO_EN[6] | 1:使能陀螺仪 X 轴 FIFO。 0:关闭陀螺仪 X 轴 FIFO。 | ||
YG_FIFO_EN[5] | 1:使能陀螺仪 Y 轴 FIFO。 0:关闭陀螺仪 Y 轴 FIFO。 | ||
ZG_FIFO_EN[4] | 1:使能陀螺仪 Z 轴 FIFO。 0:关闭陀螺仪 Z 轴 FIFO。 | ||
ACCEL_FIFO_EN[3] | 1:使能加速度计 FIFO。 0:关闭加速度计 FIFO。 | ||
0X3B | ACCEL_XOUT_H[7:0] | 数据寄存器 | 加速度 X 轴数据高 8 位 |
0X3C | ACCEL_XOUT_L[7:0] | 加速度 X 轴数据低 8 位 | |
0X3D | ACCEL_YOUT_H[7:0] | 加速度 Y 轴数据高 8 位 | |
0X3E | ACCEL_YOUT_L[7:0] | 加速度 Y 轴数据低 8 位 | |
0X3F | ACCEL_ZOUT_H[7:0] | 加速度 Z 轴数据高 8 位 | |
0X40 | ACCEL_ZOUT_L[7:0] | 加速度 Z 轴数据低 8 位 | |
0X41 | TEMP_OUT_H[7:0] | 温度数据高 8 位 | |
0X42 | TEMP_OUT_L[7:0] | 温度数据低 8 位 | |
0X43 | GYRO_XOUT_H[7:0] | 陀螺仪 X 轴数据高 8 位 | |
0X44 | GYRO_XOUT_L[7:0] | 陀螺仪 X 轴数据低 8 位 | |
0X45 | GYRO_YOUT_H[7:0] | 陀螺仪 Y 轴数据高 8 位 | |
0X46 | GYRO_YOUT_L[7:0] | 陀螺仪 Y 轴数据低 8 位 | |
0X47 | GYRO_ZOUT_H[7:0] | 陀螺仪 Z 轴数据高 8 位 | |
0X48 | GYRO_ZOUT_L[7:0] | 陀螺仪 Z 轴数据低 8 位 | |
0X6B | DEVICE_RESET[7] SLEEP[6] | 电源管理寄存器 1 | 1:复位 ICM-20608。 0:退出休眠模式; 1,进入休眠模式 |
0X6C | STBY_XA[5] | 电源管理寄存器 2 | 0:使能加速度计 X 轴。1:关闭加速度计 X 轴。 |
STBY_ZA[3] | 0:使能加速度计 Z 轴。 1:关闭加速度计 Z 轴。 | ||
STBY_XG[2] | 0:使能陀螺仪 X 轴。 1:关闭陀螺仪 X 轴。 | ||
STBY_YG[1] | 0:使能陀螺仪 Y 轴。 1:关闭陀螺仪 Y 轴。 | ||
STBY_ZG[0] | 0:使能陀螺仪 Z 轴。 1:关闭陀螺仪 Z 轴。 | ||
0X75 | WHOAMI[7:0] | ID 寄存器, ICM-20608G 的 ID 为 0XAF,ICM-20608D 的 ID 为0XAE。 |
五、代码编写
1、创建文件及文件夹
2、添加时钟配置
在时钟配置文件上添加ECSOI的时钟配置
3、编写spi.h
#ifndef __BSP_SPI_H
#define __BSP_SPI_H
#include "imx6ul.h"
/*函数声明*/
void spi_init(ECSPI_Type *base);
unsigned char spich0_readwrite_byte(ECSPI_Type *base,
unsigned char txdata);
#endif
4、编写spi.c
#include "bsp_spi.h"
/*spi初始化*/
void spi_init(ECSPI_Type *base)
{
/* 配置CONREG寄存器
* bit0 : 1 使能ECSPI
* bit3 : 1 当向TXFIFO写入数据以后立即开启SPI突发。
* bit[7:4] : 0001 SPI通道0主模式,根据实际情况选择,
* 开发板上的ICM-20608接在SS0上,所以设置通道0为主模式
* bit[19:18]: 00 选中通道0(其实不需要,因为片选信号我们我们自己控制)
* bit[31:20]: 0x7 突发长度为8个bit。
*/
base->CONREG = 0;/*清零*/
/* 配置CONREG寄存器 */
base->CONREG |= (1<<0) | (1<<3) | (1<<4) | (7<<20);
/*
* ECSPI通道0设置,即设置CONFIGREG寄存器
* bit0: 0 通道0 PHA为0
* bit4: 0 通道0 SCLK高电平有效
* bit8: 0 通道0片选信号 当SMC为1的时候此位无效
* bit12: 0 通道0 POL为0
* bit16: 0 通道0 数据线空闲时高电平
* bit20: 0 通道0 时钟线空闲时低电平
*/
/* 设置通道寄存器 */
base->CONFIGREG = 0;
/*
* ECSPI通道0设置,设置采样周期
* bit[14:0] : 0X2000 采样等待周期,比如当SPI时钟为10MHz的时候
* 0X2000就等于1/10000 * 0X2000 = 0.8192ms,也就是连续
* 读取数据的时候每次之间间隔0.8ms
* bit15 : 0 采样时钟源为SPI CLK
* bit[21:16]: 0 片选延时,可设置为0~63
*/
/* 设置采样周期寄存器 */
base->PERIODREG = 0x2000;
/*
* ECSPI的SPI时钟配置,SPI的时钟源来源于pll3_sw_clk/8=480/8=60MHz
* 通过设置CONREG寄存器的PER_DIVIDER(bit[11:8])和POST_DIVEDER(bit[15:12])来
* 对SPI时钟源分频,获取到我们想要的SPI时钟:
* SPI CLK = (SourceCLK / PER_DIVIDER) / (2^POST_DIVEDER)
* 比如我们现在要设置SPI时钟为6MHz,那么PER_DIVEIDER和POST_DEIVIDER设置如下:
* PER_DIVIDER = 0X9。
* POST_DIVIDER = 0X0。
* SPI CLK = 60000000/(0X9 + 1) = 60000000=6MHz
*/
/*SPI时钟,ICM20608的SPI最高8mhz将SPI CLK设置为6MHZ*/
base->CONREG &= ~((0xF << 12) | (0XF << 8));/*先将bit15:12和bit11:8清理*/
base->CONREG |= (9 << 12);/*1级十分频, 60/10 = 6mhz*/
}
/*SPI发送、接收函数*/
unsigned char spich0_readwrite_byte(ECSPI_Type *base,
unsigned char txdata)
{
uint32_t spirxdata = 0;
uint32_t spitxdata = txdata;
/*选择通道0*/
base->CONREG &= ~(3 << 18); /*清零*/
base->CONREG |= (0 << 18);
/*数据发送*/
while((base->STATREG &(1 << 0)) == 0);/* 等待发送FIFO为空 */
base->TXDATA = spitxdata;
/*数据接收*/
while((base->STATREG &(1 << 3)) == 0);/* 等待接收FIFO有数据 */
spirxdata = base->RXDATA;
return spirxdata;
}
5、编写icm20608.h
#ifndef __BSP_ICM20608_H
#define __BSP_ICM20608_H
#include "imx6ul.h"
#include "bsp_gpio.h"
#include "bsp_spi.h"
#include "stdio.h"
#include "bsp_delay.h"
/*片选宏*/
#define ICM20608_CSN(n) (n ? gpio_pinwrite(GPIO1 , 20 ,1) : gpio_pinwrite(GPIO1 , 20 ,0))
/*id值*/
#define ICM20608G_ID (0XAF)
#define ICM20608D_ID (0XAE)
/* ICM20608寄存器
*复位后所有寄存器地址都为0,除了
*Register 107(0X6B) Power Management 1 = 0x40
*Register 117(0X75) WHO_AM_I = 0xAF或0xAE
*/
/* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */
#define ICM20_SELF_TEST_X_GYRO 0x00
#define ICM20_SELF_TEST_Y_GYRO 0x01
#define ICM20_SELF_TEST_Z_GYRO 0x02
#define ICM20_SELF_TEST_X_ACCEL 0x0D
#define ICM20_SELF_TEST_Y_ACCEL 0x0E
#define ICM20_SELF_TEST_Z_ACCEL 0x0F
/* 陀螺仪静态偏移 */
#define ICM20_XG_OFFS_USRH 0x13
#define ICM20_XG_OFFS_USRL 0x14
#define ICM20_YG_OFFS_USRH 0x15
#define ICM20_YG_OFFS_USRL 0x16
#define ICM20_ZG_OFFS_USRH 0x17
#define ICM20_ZG_OFFS_USRL 0x18
#define ICM20_SMPLRT_DIV 0x19
#define ICM20_CONFIG 0x1A
#define ICM20_GYRO_CONFIG 0x1B
#define ICM20_ACCEL_CONFIG 0x1C
#define ICM20_ACCEL_CONFIG2 0x1D
#define ICM20_LP_MODE_CFG 0x1E
#define ICM20_ACCEL_WOM_THR 0x1F
#define ICM20_FIFO_EN 0x23
#define ICM20_FSYNC_INT 0x36
#define ICM20_INT_PIN_CFG 0x37
#define ICM20_INT_ENABLE 0x38
#define ICM20_INT_STATUS 0x3A
/* 加速度输出 */
#define ICM20_ACCEL_XOUT_H 0x3B
#define ICM20_ACCEL_XOUT_L 0x3C
#define ICM20_ACCEL_YOUT_H 0x3D
#define ICM20_ACCEL_YOUT_L 0x3E
#define ICM20_ACCEL_ZOUT_H 0x3F
#define ICM20_ACCEL_ZOUT_L 0x40
/* 温度输出 */
#define ICM20_TEMP_OUT_H 0x41
#define ICM20_TEMP_OUT_L 0x42
/* 陀螺仪输出 */
#define ICM20_GYRO_XOUT_H 0x43
#define ICM20_GYRO_XOUT_L 0x44
#define ICM20_GYRO_YOUT_H 0x45
#define ICM20_GYRO_YOUT_L 0x46
#define ICM20_GYRO_ZOUT_H 0x47
#define ICM20_GYRO_ZOUT_L 0x48
#define ICM20_SIGNAL_PATH_RESET 0x68
#define ICM20_ACCEL_INTEL_CTRL 0x69
#define ICM20_USER_CTRL 0x6A
#define ICM20_PWR_MGMT_1 0x6B
#define ICM20_PWR_MGMT_2 0x6C
#define ICM20_FIFO_COUNTH 0x72
#define ICM20_FIFO_COUNTL 0x73
#define ICM20_FIFO_R_W 0x74
#define ICM20_WHO_AM_I 0x75
/* 加速度静态偏移 */
#define ICM20_XA_OFFSET_H 0x77
#define ICM20_XA_OFFSET_L 0x78
#define ICM20_YA_OFFSET_H 0x7A
#define ICM20_YA_OFFSET_L 0x7B
#define ICM20_ZA_OFFSET_H 0x7D
#define ICM20_ZA_OFFSET_L 0x7E
/*icm20608数据结构体*/
struct icm20608_dev_struc
{
signed int gyro_x_adc; /* 陀螺仪X轴原始值 */
signed int gyro_y_adc; /* 陀螺仪Y轴原始值 */
signed int gyro_z_adc; /* 陀螺仪Z轴原始值 */
signed int accel_x_adc; /* 加速度计X轴原始值 */
signed int accel_y_adc; /* 加速度计Y轴原始值 */
signed int accel_z_adc; /* 加速度计Z轴原始值 */
signed int temp_adc; /* 温度原始值 */
/* 下面是计算得到的实际值,扩大100倍方便计算 */
signed int gyro_x_act; /* 陀螺仪X轴实际值 */
signed int gyro_y_act; /* 陀螺仪Y轴实际值 */
signed int gyro_z_act; /* 陀螺仪Z轴实际值 */
signed int accel_x_act; /* 加速度计X轴实际值 */
signed int accel_y_act; /* 加速度计Y轴实际值 */
signed int accel_z_act; /* 加速度计Z轴实际值 */
signed int temp_act; /* 温度实际值 */
};
struct icm20608_dev_struc icm20608_dev; /* icm20608设备 */
unsigned char icm20608_init();
unsigned char icm20608_read_reg(unsigned char reg);
void icm20608_write_reg(unsigned char reg,unsigned char value);
void icm20608_read_len(unsigned char reg ,
unsigned char *buf,
unsigned char len);
void icm20608_getdata();
float icm20608_gyro_scaleget(void);
unsigned short icm20608_accel_scaleget(void);
#endif
6、编写icm20608.c
#include "bsp_icm20608.h"
#include "bsp_delay.h"
#include "bsp_spi.h"
#include "stdio.h"
struct icm20608_dev_struc icm20608_dev; /* icm20608设备 */
/*
* @description : 初始化ICM20608
* @param : 无
* @return : 0 初始化成功,其他值 初始化失败
*/
unsigned char icm20608_init(void)
{
unsigned char regvalue;
gpio_pin_config_t cs_config;
/* 1、ESPI3 IO初始化
* ECSPI3_SCLK -> UART2_RXD
* ECSPI3_MISO -> UART2_RTS
* ECSPI3_MOSI -> UART2_CTS
*/
IOMUXC_SetPinMux(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0);
IOMUXC_SetPinMux(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0);
IOMUXC_SetPinMux(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0);
/* 配置SPI SCLK MISO MOSI IO属性
*bit 16: 0 HYS关闭
*bit [15:14]: 00 默认100K下拉
*bit [13]: 0 keeper功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 驱动能力为R0/6
*bit [0]: 1 高转换率
*/
IOMUXC_SetPinConfig(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0x10B1);
IOMUXC_SetPinConfig(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0x10B1);
IOMUXC_SetPinConfig(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0x10B1);
IOMUXC_SetPinMux(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0);
IOMUXC_SetPinConfig(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0X10B0);
cs_config.direction = kGPIO_DigitalOutput;
cs_config.outputLogic = 0;
gpio_init(GPIO1, 20, &cs_config);
/* 2、初始化SPI */
spi_init(ECSPI3);
icm20608_write_reg(ICM20_PWR_MGMT_1, 0x80); /* 复位,复位后为0x40,睡眠模式 */
delayms(50);
icm20608_write_reg(ICM20_PWR_MGMT_1, 0x01); /* 关闭睡眠,自动选择时钟 */
delayms(50);
regvalue = icm20608_read_reg(ICM20_WHO_AM_I);
printf("icm20608 id = %#X\r\n", regvalue);
if(regvalue != ICM20608G_ID && regvalue != ICM20608D_ID)
return 1;
icm20608_write_reg(ICM20_SMPLRT_DIV, 0x00); /* 输出速率是内部采样率 */
icm20608_write_reg(ICM20_GYRO_CONFIG, 0x18); /* 陀螺仪±2000dps量程 */
icm20608_write_reg(ICM20_ACCEL_CONFIG, 0x18); /* 加速度计±16G量程 */
icm20608_write_reg(ICM20_CONFIG, 0x04); /* 陀螺仪低通滤波BW=20Hz */
icm20608_write_reg(ICM20_ACCEL_CONFIG2, 0x04); /* 加速度计低通滤波BW=21.2Hz */
icm20608_write_reg(ICM20_PWR_MGMT_2, 0x00); /* 打开加速度计和陀螺仪所有轴 */
icm20608_write_reg(ICM20_LP_MODE_CFG, 0x00); /* 关闭低功耗 */
icm20608_write_reg(ICM20_FIFO_EN, 0x00); /* 关闭FIFO */
return 0;
}
/*
* @description : 写ICM20608指定寄存器
* @param - reg : 要读取的寄存器地址
* @param - value: 要写入的值
* @return : 无
*/
void icm20608_write_reg(unsigned char reg, unsigned char value)
{
/* ICM20608在使用SPI接口的时候寄存器地址
* 只有低7位有效,寄存器地址最高位是读/写标志位
* 读的时候要为1,写的时候要为0。
*/
reg &= ~0X80;
ICM20608_CSN(0); /* 使能SPI传输 */
spich0_readwrite_byte(ECSPI3, reg); /* 发送寄存器地址 */
spich0_readwrite_byte(ECSPI3, value); /* 发送要写入的值 */
ICM20608_CSN(1); /* 禁止SPI传输 */
}
/*
* @description : 读取ICM20608寄存器值
* @param - reg : 要读取的寄存器地址
* @return : 读取到的寄存器值
*/
unsigned char icm20608_read_reg(unsigned char reg)
{
unsigned char reg_val;
/* ICM20608在使用SPI接口的时候寄存器地址
* 只有低7位有效,寄存器地址最高位是读/写标志位
* 读的时候要为1,写的时候要为0。
*/
reg |= 0x80;
ICM20608_CSN(0); /* 使能SPI传输 */
spich0_readwrite_byte(ECSPI3, reg); /* 发送寄存器地址 */
reg_val = spich0_readwrite_byte(ECSPI3, 0XFF); /* 读取寄存器的值 */
ICM20608_CSN(1); /* 禁止SPI传输 */
return(reg_val); /* 返回读取到的寄存器值 */
}
/*
* @description : 读取ICM20608连续多个寄存器
* @param - reg : 要读取的寄存器地址
* @return : 读取到的寄存器值
*/
void icm20608_read_len(unsigned char reg, unsigned char *buf, unsigned char len)
{
unsigned char i;
/* ICM20608在使用SPI接口的时候寄存器地址,只有低7位有效,
* 寄存器地址最高位是读/写标志位读的时候要为1,写的时候要为0。
*/
reg |= 0x80;
ICM20608_CSN(0); /* 使能SPI传输 */
spich0_readwrite_byte(ECSPI3, reg); /* 发送寄存器地址 */
for(i = 0; i < len; i++) /* 顺序读取寄存器的值 */
{
buf[i] = spich0_readwrite_byte(ECSPI3, 0XFF);
}
ICM20608_CSN(1); /* 禁止SPI传输 */
}
/*
* @description : 获取陀螺仪的分辨率
* @param : 无
* @return : 获取到的分辨率
*/
float icm20608_gyro_scaleget(void)
{
unsigned char data;
float gyroscale;
data = (icm20608_read_reg(ICM20_GYRO_CONFIG) >> 3) & 0X3;
switch(data) {
case 0:
gyroscale = 131;
break;
case 1:
gyroscale = 65.5;
break;
case 2:
gyroscale = 32.8;
break;
case 3:
gyroscale = 16.4;
break;
}
return gyroscale;
}
/*
* @description : 获取加速度计的分辨率
* @param : 无
* @return : 获取到的分辨率
*/
unsigned short icm20608_accel_scaleget(void)
{
unsigned char data;
unsigned short accelscale;
data = (icm20608_read_reg(ICM20_ACCEL_CONFIG) >> 3) & 0X3;
switch(data) {
case 0:
accelscale = 16384;
break;
case 1:
accelscale = 8192;
break;
case 2:
accelscale = 4096;
break;
case 3:
accelscale = 2048;
break;
}
return accelscale;
}
/*
* @description : 读取ICM20608的加速度、陀螺仪和温度原始值
* @param : 无
* @return : 无
*/
void icm20608_getdata(void)
{
float gyroscale;
unsigned short accescale;
unsigned char data[14];
icm20608_read_len(ICM20_ACCEL_XOUT_H, data, 14);
gyroscale = icm20608_gyro_scaleget();
accescale = icm20608_accel_scaleget();
icm20608_dev.accel_x_adc = (signed short)((data[0] << 8) | data[1]);
icm20608_dev.accel_y_adc = (signed short)((data[2] << 8) | data[3]);
icm20608_dev.accel_z_adc = (signed short)((data[4] << 8) | data[5]);
icm20608_dev.temp_adc = (signed short)((data[6] << 8) | data[7]);
icm20608_dev.gyro_x_adc = (signed short)((data[8] << 8) | data[9]);
icm20608_dev.gyro_y_adc = (signed short)((data[10] << 8) | data[11]);
icm20608_dev.gyro_z_adc = (signed short)((data[12] << 8) | data[13]);
/* 计算实际值 */
icm20608_dev.gyro_x_act = ((float)(icm20608_dev.gyro_x_adc) / gyroscale) * 100;
icm20608_dev.gyro_y_act = ((float)(icm20608_dev.gyro_y_adc) / gyroscale) * 100;
icm20608_dev.gyro_z_act = ((float)(icm20608_dev.gyro_z_adc) / gyroscale) * 100;
icm20608_dev.accel_x_act = ((float)(icm20608_dev.accel_x_adc) / accescale) * 100;
icm20608_dev.accel_y_act = ((float)(icm20608_dev.accel_y_adc) / accescale) * 100;
icm20608_dev.accel_z_act = ((float)(icm20608_dev.accel_z_adc) / accescale) * 100;
icm20608_dev.temp_act = (((float)(icm20608_dev.temp_adc) - 25 ) / 326.8 + 25) * 100;
}
7、编写main.c
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"
#include "bsp_rtc.h"
#include "bsp_icm20608.h"
#include "bsp_spi.h"
#include "stdio.h"
/*
* @description : 指定的位置显示整数数据
* @param - x : X轴位置
* @param - y : Y轴位置
* @param - size: 字体大小
* @param - num : 要显示的数据
* @return : 无
*/
void integer_display(unsigned short x, unsigned short y, unsigned char size, signed int num)
{
char buf[200];
lcd_fill(x, y, x + 50, y + size, tftlcd_dev.backcolor);
memset(buf, 0, sizeof(buf));
if(num < 0)
sprintf(buf, "-%d", -num);
else
sprintf(buf, "%d", num);
lcd_show_string(x, y, 50, size, size, buf);
}
/*
* @description : 指定的位置显示小数数据,比如5123,显示为51.23
* @param - x : X轴位置
* @param - y : Y轴位置
* @param - size: 字体大小
* @param - num : 要显示的数据,实际小数扩大100倍,
* @return : 无
*/
void decimals_display(unsigned short x, unsigned short y, unsigned char size, signed int num)
{
signed int integ; /* 整数部分 */
signed int fract; /* 小数部分 */
signed int uncomptemp = num;
char buf[200];
if(num < 0)
uncomptemp = -uncomptemp;
integ = uncomptemp / 100;
fract = uncomptemp % 100;
memset(buf, 0, sizeof(buf));
if(num < 0)
sprintf(buf, "-%d.%d", integ, fract);
else
sprintf(buf, "%d.%d", integ, fract);
lcd_fill(x, y, x + 60, y + size, tftlcd_dev.backcolor);
lcd_show_string(x, y, 60, size, size, buf);
}
/*
* @description : 使能I.MX6U的硬件NEON和FPU
* @param : 无
* @return : 无
*/
void imx6ul_hardfpu_enable(void)
{
uint32_t cpacr;
uint32_t fpexc;
/* 使能NEON和FPU */
cpacr = __get_CPACR();
cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
| (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
__set_CPACR(cpacr);
fpexc = __get_FPEXC();
fpexc |= 0x40000000UL;
__set_FPEXC(fpexc);
}
/*
* @description : main函数
* @param : 无
* @return : 无
*/
int main(void)
{
unsigned char state = OFF;
imx6ul_hardfpu_enable(); /* 使能I.MX6U的硬件浮点 */
int_init(); /* 初始化中断(一定要最先调用!) */
imx6u_clkinit(); /* 初始化系统时钟 */
delay_init(); /* 初始化延时 */
clk_enable(); /* 使能所有的时钟 */
led_init(); /* 初始化led */
beep_init(); /* 初始化beep */
uart_init(); /* 初始化串口,波特率115200 */
lcd_init(); /* 初始化LCD */
tftlcd_dev.forecolor = LCD_RED;
lcd_show_string(50, 10, 400, 24, 24, (char*)"ALPHA-IMX6UL SPI TEST"); /* 显示字符串 */
lcd_show_string(50, 40, 200, 16, 16, (char*)"MY@4384");
lcd_show_string(50, 60, 200, 16, 16, (char*)"2023/2/21");
while(icm20608_init()) /* 初始化ICM20608 */
{
lcd_show_string(50, 100, 200, 16, 16, (char*)"ICM20608 Check Failed!");
delayms(500);
lcd_show_string(50, 100, 200, 16, 16, (char*)"Please Check! ");
delayms(500);
}
lcd_show_string(50, 100, 200, 16, 16, (char*)"ICM20608 Ready");
lcd_show_string(50, 130, 200, 16, 16, (char*)"accel x:");
lcd_show_string(50, 150, 200, 16, 16, (char*)"accel y:");
lcd_show_string(50, 170, 200, 16, 16, (char*)"accel z:");
lcd_show_string(50, 190, 200, 16, 16, (char*)"gyro x:");
lcd_show_string(50, 210, 200, 16, 16, (char*)"gyro y:");
lcd_show_string(50, 230, 200, 16, 16, (char*)"gyro z:");
lcd_show_string(50, 250, 200, 16, 16, (char*)"temp :");
lcd_show_string(50 + 181, 130, 200, 16, 16, (char*)"g");
lcd_show_string(50 + 181, 150, 200, 16, 16, (char*)"g");
lcd_show_string(50 + 181, 170, 200, 16, 16, (char*)"g");
lcd_show_string(50 + 181, 190, 200, 16, 16, (char*)"o/s");
lcd_show_string(50 + 181, 210, 200, 16, 16, (char*)"o/s");
lcd_show_string(50 + 181, 230, 200, 16, 16, (char*)"o/s");
lcd_show_string(50 + 181, 250, 200, 16, 16, (char*)"C");
tftlcd_dev.forecolor = LCD_BLUE;
while(1)
{
icm20608_getdata();
integer_display(50 + 70, 130, 16, icm20608_dev.accel_x_adc);
integer_display(50 + 70, 150, 16, icm20608_dev.accel_y_adc);
integer_display(50 + 70, 170, 16, icm20608_dev.accel_z_adc);
integer_display(50 + 70, 190, 16, icm20608_dev.gyro_x_adc);
integer_display(50 + 70, 210, 16, icm20608_dev.gyro_y_adc);
integer_display(50 + 70, 230, 16, icm20608_dev.gyro_z_adc);
integer_display(50 + 70, 250, 16, icm20608_dev.temp_adc);
decimals_display(50 + 70 + 50, 130, 16, icm20608_dev.accel_x_act);
decimals_display(50 + 70 + 50, 150, 16, icm20608_dev.accel_y_act);
decimals_display(50 + 70 + 50, 170, 16, icm20608_dev.accel_z_act);
decimals_display(50 + 70 + 50, 190, 16, icm20608_dev.gyro_x_act);
decimals_display(50 + 70 + 50, 210, 16, icm20608_dev.gyro_y_act);
decimals_display(50 + 70 + 50, 230, 16, icm20608_dev.gyro_z_act);
decimals_display(50 + 70 + 50, 250, 16, icm20608_dev.temp_act);
#if 0
printf("accel x = %d\r\n",icm20608_dev.accel_x_adc);
printf("accel y = %d\r\n",icm20608_dev.accel_y_adc);
printf("accel z = %d\r\n",icm20608_dev.accel_z_adc);
printf("gyrp x = %d\r\n",icm20608_dev.gyro_x_adc);
printf("gyro y = %d\r\n",icm20608_dev.gyro_y_adc);
printf("gyro z = %d\r\n",icm20608_dev.gyro_z_adc);
printf("temp = %d\r\n",icm20608_dev.temp_adc);
#endif
delayms(120);
state = !state;
led_switch(LED0,state);
}
return 0;
}
在main里面开启了硬件浮点运算,需要在编译文件的时候添加进去
8、修改makefile
添加编译c文件的指令
$(CC) -Wall -march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard -Wa,-mimplicit-it=thumb -nostdlib -fno-builtin -c -O2 $(INCLUDE) -o $@ $<
编译后烧写在sd卡插上开发板就能看到屏幕有关6 轴 MEMS传感器的数据了