i.MX6ULL(十二) SPI 协议

一 SPI 简介

1.1 SPI

SPI 全称是 Serial
Perripheral Interface,也就是串行外围设备接口。 SPI 是 Motorola 公司推出的一种同步串行接口
技术,是一种高速、全双工的同步通信总线, SPI 时钟频率相比 I2C 要高很多,最高可以工作
上百 MHz。 SPI 以主从方式工作,通常是有一个主设备和一个或多个从设备,一般 SPI 需要
4 根线,但是也可以使用三根线(单向传输),本章我们讲解标准的 4 线 SPI,

i.MX6ULL(十二) SPI 协议_第1张图片

①、 CS/SS, Slave Select/Chip Select,这个是片选信号线,用于选择需要进行通信的从设备。
I2C 主机是通过发送从机设备地址来选择需要进行通信的从机设备的, SPI 主机不需要发送从机
设备,直接将相应的从机设备片选信号拉低即可。
②、 SCK, Serial Clock,串行时钟,和 I2C 的 SCL 一样,为 SPI 通信提供时钟。
③、 MOSI/SDOMaster Out Slave In/Serial Data Output,简称主出从入信号线,这根数据线
只能用于主机向从机发送数据,也就是主机输出,从机输入。
④、 MISO/SDI, Master In Slave Out/Serial Data Input,简称主入从出信号线,这根数据线只
能用户从机向主机发送数据,也就是主机输入,从机输出
 

CSn片选引脚

1.2 工作模式

SPI 有四种工作模式,通过串行时钟极性(CPOL)和相位(CPHA)的搭配来得到四种工作模式:
①、 CPOL=0,串行时钟空闲状态为低电平。
②、 CPOL=1,串行时钟空闲状态为高电平,此时可以通过配置时钟相位(CPHA)来选择具
体的传输协议。
③、 CPHA=0,串行时钟的第一个跳变沿(上升沿或下降沿)采集数据。
④、 CPHA=1,串行时钟的第二个跳变沿(上升沿或下降沿)采集数据。
这四种工作模式如图 27.1.1.2 所示:

i.MX6ULL(十二) SPI 协议_第2张图片

SPI 的时序图很简单,不像 I2C 那样还要分为读时序和写时序,因
为 SPI 是全双工的,所以读写时序可以一起完成。图 27.1.1.3 中, CS 片选信号先拉低,选中要
通信的从设备,然后通过 MOSI 和 MISO 这两根数据线进行收发数据, MOSI 数据线发出了
0XD2 这个数据给从设备,同时从设备也通过 MISO 线给主设备返回了 0X66 这个数据
。这个就
是 SPI 时序图。

二 I.MX6U ECSPI 简介

2.1 特性及工作模式

I.MX6U 自带的 SPI 外设叫做 ECSPI,全称是 Enhanced Configurable Serial Peripheral Interface,
别看前面加了个“EC”就以为和标准 SPI 有啥不同的, 其实就是 SPI。 ECSPI 有 64*32 个接收
FIFO(RXFIFO)和 64*32 个发送 FIFO(TXFIFO), ECSPI 特性如下:


①、全双工同步串行接口。
②、可配置的主/从模式。
③、四个片选信号,支持多从机。 CHANNEL_SELECT
④、发送和接收都有一个 32x64 的 FIFO。
⑤、片选信号 SS/CS,时钟信号 SCLK 极性可配置。
⑥、支持 DMA。          SPI&&DMA   STM32的SPI采用DMA方式传输测试 – 源码巴士


I.MX6U 的 ECSPI 可以工作在主模式或从模式,本章我们使用主模式, I.MX6U 有 4 个
ECSPI,??其他在哪 每个 ECSPI 支持四个片选信号,也就说,如果你要使用 ECSPI 的硬件片选信号的话,
一个 ECSPI 可以支持 4 个外设。如果不使用硬件的片选信号就可以支持无数个外设,本章实验
我们不使用硬件片选信号,因为硬件片选信号只能使用指定的片选 IO,软件片选的话可以使用
任意的 IO。 

i.MX6ULL(十二) SPI 协议_第3张图片

ICM2068

i.MX6ULL(十二) SPI 协议_第4张图片

2.2 时钟

i.MX6ULL(十二) SPI 协议_第5张图片
 

①、这是一个选择器,用于选择根时钟源,由寄存器 CSCDR2 的位 ECSPI_CLK_SEL 来控
制,为 0 的话选择 pll3_60m 作为 ECSPI 根时钟源。为 1 的话选择 osc_clk 作为 ECSPI 时钟源。
本章我们选择 pll3_60m 作为 ECSPI 根时钟源。
②、 ECSPI 时钟分频值,由寄存器 CSCDR2 的位 ECSPI_CLK_PODF 来控制,分频值为
2^ECSPI_CLK_PODF。本章我们设置为 0,也就是 1 分频。
③、最终进入 ECSPI 的时钟,也就是 SPI CLK=60MHz。
 

2.3 寄存器

同其他设备一样 也有控制寄存器 配置寄存器 状态寄存器  这三种寄存器似乎已经成为标配

ECSPI主要寄存器 有 

ECSPIx_CONREG(x=1~4) 、ECSPIx_TXDATA、ECSPIx_RXDATA、ECSPIx_CONFIGRE
ECSPIx_STATREG 

1、RXDATA寄存器为接收到的数据。 64 x 32 

2、TXDATA寄存器为发送数据寄存器。

3、CONREG寄存器为配置寄存器,

i.MX6ULL(十二) SPI 协议_第6张图片

同其他外设控制寄存器一样  具有 使能 时钟分频 选择 模式等通用控制及其它专有控制功能  

i.MX6ULL(十二) SPI 协议_第7张图片

CHANNEL_SELECT(bit19:18): SPI 通道选择,一个 ECSPI 有四个硬件片选信号,每个片
选信号是一个硬件通道,虽然我们本章实验使用的软件片选,但是 SPI 通道还是要选择的。可
设置为 0~3,分别对应通道 0~3。 I.MX6U-ALPHA 开发板上的 ICM-20608 的片选信号接的是
ECSPI3_SS0,也就是 ECSPI3 的通道 0,所以本章实验设置为 0。
 

bit0置1,使能SPI。Bit3置1,表示当向TXFIFO写入数据以后马上开启SPI突发访问,也就是发送数据。Bit7:4设置SPI通道主从模式,bit7为通道3,bit4为通道0,我们使用到了SS0,也就是通道0,因此需要设置bit4为1。Bit19:18设置为00,我们使用到SS0,也就是通道0。Bit31:30设置突发访问长度,我们设置为7,也就是8bit突发长度,一个字节。

4、CONFIGREG寄存器

 控制相关引脚电器属性等

bit0为PHA,设置为0,表示 串行时钟的第一个跳变沿开始采集数据。设置bit4为PO,设置为0,表示SCLK空闲的时候为低电平。Bit8设置0。Bit12设置 为0。Bit16设置为0,表示空闲的时候数据线为高。Bit20设置为0,表示SCLK空闲的时候为低。

5、STATREG寄存器,

i.MX6ULL(十二) SPI 协议_第8张图片

i.MX6ULL(十二) SPI 协议_第9张图片

bit0表示TXFIFO为空,我们在发送数据之前要等待TXFIFO为空,(??)也就是等待bit0为1。Bit3表示RXFIFO是否有数据,为1的时候示RXFIFO至少有1个字的数据,我们在接收数据的时候要等到bit3为1。

i.MX6ULL(十二) SPI 协议_第10张图片

6、PERIODREG寄存器,bit14:0设置wait states时间,我们设置为0X2000。Bit15设置wait states的时钟源为SPI CLK,将此位设置0。Bit21:16表示片选信号的延时,可设置0-63,这里设置为0.

3 ICM-20608 简介

 ICM-20608 是 InvenSense 出品的一款 6 轴 MEMS 传感器,包括 3 轴加速度和 3 轴陀螺仪

有D/G 两种型号 ID \

 ICM-20608G的 “WHO_AM_I ”寄存器,地址为 0X75,默认值是0XAF。这个寄存器保存了器件的ID

I.MX6U-ALPHA 开发板上的 ICM-20608 通过 SPI 接口和 I.MX6U 连接在一起。

3.1 特性:

①、陀螺仪支持 X,Y 和 Z 三轴输出,内部集成 16 位 ADC,测量范围可设置:±250,±
500,±1000 和±2000° /s。
②、加速度计支持 X,Y 和 Z 轴输出,内部集成 16 位 ADC,测量范围可设置:±2g,±4g,
±4g,±8g 和±16g。
③、用户可编程中断。
④、内部包含 512 字节的 FIFO。
⑤、内部包含一个数字温度传感器。
⑥、耐 10000g 的冲击。
⑦、支持快速 I2C,速度可达 400KHz。
⑧、支持 SPI,速度可达 8MHz  
 

结构框图

i.MX6ULL(十二) SPI 协议_第11张图片

 如果使用 IIC 接口的话 ICM-20608 的 AD0 引脚决定 I2C 设备从地址的最后一位,??如果 AD0
为 0 的话 ICM-20608 从设备地址是 0X68,如果 AD0 为 1 的话 ICM-20608 从设备地址为 0X69。

3.2 ICM-20608 重要寄存器
本章我们使用 SPI 接口
,跟AP3216C 一样, ICM-20608 也是通过读写寄存器来配置
和读取传感器数据,使用 SPI 接口读写寄存器需要 16 个时钟或者更多(如果读写操作包括多个
字节的话),第一个字节包含要读写的寄存器地址,寄存器地址最高位是读写标志位,如果是读
的话寄存器地址最高位要为 1,如果是写的话寄存器地址最高位要为 0
剩下的 7 位才是实际
寄存器地址,寄存器地址后面跟着的就是读写的数据。

i.MX6ULL(十二) SPI 协议_第12张图片

  

i.MX6ULL(十二) SPI 协议_第13张图片

i.MX6ULL(十二) SPI 协议_第14张图片

 四 代码例程

4.1 读取ICM-20608六轴传感器加速中及陀螺仪数据并显示

bsp_spi部分 

/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_spi.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : SPI驱动文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/17 左忠凯创建
***************************************************************/
#include "bsp_spi.h"
#include "bsp_gpio.h"
#include "stdio.h"

/*
 * @description		: 初始化SPI
 * @param - base	: 要初始化的SPI
 * @return 			: 无
 */
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; /* 先清除控制寄存器 */
	base->CONREG |= (1 << 0) | (1 << 3) | (1 << 4) | (7 << 20); /* 配置CONREG寄存器 */

	/*
     * 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
	 */
	base->CONREG &= ~((0XF << 12) | (0XF << 8));	/* 清除PER_DIVDER和POST_DIVEDER以前的设置 */
	base->CONREG |= (0X9 << 12);					/* 设置SPI CLK = 6MHz */
}

/*
 * @description		: SPI通道0发送/接收一个字节的数据
 * @param - base	: 要使用的SPI
 * @param - txdata	: 要发送的数据
 * @return 			: 无
 */
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;
}

ICM-20608

/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 bsp_icm20608.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : ICM20608驱动文件。
其他	   : 无
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/3/26 左忠凯创建
***************************************************************/
#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;
}


main.c

/**************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名	: 	 mian.c
作者	   : 左忠凯
版本	   : V1.0
描述	   : I.MX6U开发板裸机实验19 SPI实验
其他	   : SPI也是最常用的接口,ZERO开发板上有一个6轴传感器ICM20608,
		 这个六轴传感器就是SPI接口的,本实验就来学习如何驱动I.MX6U
		 的SPI接口,并且通过SPI接口读取ICM20608的数据值。
论坛 	   : www.wtmembed.com
日志	   : 初版V1.0 2019/1/17 左忠凯创建
**************************************************************/
#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-IMX6U SPI TEST");  
	lcd_show_string(50, 40, 200, 16, 16, (char*)"ICM20608 TEST");  
	lcd_show_string(50, 60, 200, 16, 16, (char*)"ATOM@ALIENTEK");  
	lcd_show_string(50, 80, 200, 16, 16, (char*)"2019/3/27");  
	
	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;
}

4.2 陀螺仪角速度及加速度计算方式

陀螺仪

如果陀螺仪所设置的分辨率范围为 ±250,即 -250~+250,也就是 500°/s。

ADC数据的位数为 16位,即 0~65535,也就是 65536。那么一度对应多大的数据呢?

65536/500 = 131.07

举例说明:

如果所设置的分辨率范围为 ±250,读取到的  ADC值是 1000,那么陀螺仪的角速度是多少?

当前陀螺仪的角速度为:

1000 / 131  = 7.6°/s

加速度
 

 举例说明:

如果加速度计设置的分辨率范围为 ±2,即 -2~+2,也就是 4。

ADC值的位数为 16位,即 0~65535,也就是 65536。一度则对应多大的 ADC值呢?

65536/4 = 16384

如果此时读取到的 ADC值为 2,则这时的加速度计的加速度是多少?

3 * 16384  = 32767
 

你可能感兴趣的:(单片机,嵌入式硬件)