正点原子阿尔法ARM开发板-IMX6ULL(十一)——IIC协议和SPI协议--AP3216C环境光传感器和ICM20608六轴传感器

news2024/11/28 6:39:05

文章目录

  • 一、前言
  • 二、 IIC协议
    • 2.1 协议解读
      • 2.1.1 起始位、停止位、数据传输
      • 2.1.2 写时序
      • 2.1.3 读时序
    • 2.2 代码分析
    • 2.3 AP3216C环境光传感器的代码分析
  • 三、SPI协议
    • 3.1 协议解读
    • 3.2 代码分析
    • 3.3 ICM-20608六轴传感器代码分析

一、前言

看了IIC,我之前毕设用过这个协议,SDA、SCL,也就是一个数据线一个时钟线,嗯,还需要设置波特率,不过当时就是调调库。
不多说了,看看协议,再看代码即可

二、 IIC协议

2.1 协议解读

2.1.1 起始位、停止位、数据传输

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.1.2 写时序

在这里插入图片描述

1)、开始信号。
2)、发送 I2C 设备地址,每个 I2C 器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个 I2C 器件。这是一个 8 位的数据,其中高 7 位是设备地址,最后 1 位是读写位,为1 的话表示这是一个读操作,为 0 的话表示这是一个写操作。
3)、 I2C 器件地址后面跟着一个读写位,为 0 表示写操作,为 1 表示读操作。
4)、从机发送的 ACK 应答信号。
5)、重新发送开始信号。
6)、发送要写写入数据的寄存器地址。
7)、从机发送的 ACK 应答信号。
8)、发送要写入寄存器的数据。
9)、从机发送的 ACK 应答信号。
10)、停止信号。

写:发送设备地址、发送要写入的寄存器地址、发送数据

2.1.3 读时序

在这里插入图片描述
1)、主机发送起始信号。
2)、主机发送要读取的 I2C 从设备地址。
3)、读写控制位,因为是向 I2C 从设备发送数据,因此是写信号。
4)、从机发送的 ACK 应答信号。
5)、重新发送 START 信号。
6)、主机发送要读取的寄存器地址。
7)、从机发送的 ACK 应答信号。
8)、重新发送 START 信号。
9)、重新发送要读取的 I2C 从设备地址。
10)、读写控制位,这里是读信号,表示接下来是从 I2C 从设备里面读取数据。
11)、从机发送的 ACK 应答信号。
12)、从 I2C 器件里面读取到的数据。
13)、主机发出 NO ACK 信号,表示读取完成,不需要从机再发送 ACK 信号了。
14)、主机发出 STOP 信号,停止 I2C 通信。

读:发送设备地址(写)、发送要写入的寄存器地址、发送设备地址(读)、发送数据

2.2 代码分析

#include "bsp_i2c.h"
#include "bsp_delay.h"
#include "stdio.h"

/*
 * @description		: 初始化I2C,波特率100KHZ
 * @param - base 	: 要初始化的IIC设置
 * @return 			: 无
 */
void i2c_init(I2C_Type *base)
{
	/* 1、配置I2C */
	base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C */

    /* 设置波特率为100K
     * I2C的时钟源来源于IPG_CLK_ROOT=66Mhz
 	 * IC2 时钟 = PERCLK_ROOT/dividison(IFDR寄存器)
	 * 设置寄存器IFDR,IFDR寄存器参考IMX6UL参考手册P1260页,表29-3,
	 * 根据表29-3里面的值,挑选出一个还是的分频数,比如本例程我们
	 * 设置I2C的波特率为100K, 因此当分频值=66000000/100000=660.
	 * 在表29-3里面查找,没有660这个值,但是有640,因此就用640,
	 * 即寄存器IFDR的IC位设置为0X15
	 */
	base->IFDR = 0X15 << 0;

	/*
     * 设置寄存器I2CR,开启I2C
     * bit[7] : 1 使能I2C,I2CR寄存器其他位其作用之前,此位必须最先置1
	 */
	base->I2CR |= (1<<7);
}

/*
 * @description			: 发送重新开始信号
 * @param - base 		: 要使用的IIC
 * @param - addrss		: 设备地址
 * @param - direction	: 方向
 * @return 				: 0 正常 其他值 出错
 */
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address,  enum i2c_direction direction)
{
	/* I2C忙并且工作在从模式,跳出 */
	if(base->I2SR & (1 << 5) && (((base->I2CR) & (1 << 5)) == 0))		
		return 1;

	/*
     * 设置寄存器I2CR
     * bit[4]: 1 发送
     * bit[2]: 1 产生重新开始信号
	 */
	base->I2CR |=  (1 << 4) | (1 << 2);

	/*
     * 设置寄存器I2DR
     * bit[7:0] : 要发送的数据,这里写入从设备地址
     *            参考资料:IMX6UL参考手册P1249
	 */ 
	base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
	
	return 0;
}

/*
 * @description			: 发送开始信号
 * @param - base 		: 要使用的IIC
 * @param - addrss		: 设备地址
 * @param - direction	: 方向
 * @return 				: 0 正常 其他值 出错
 */
unsigned char i2c_master_start(I2C_Type *base, unsigned char address,  enum i2c_direction direction)
{
	if(base->I2SR & (1 << 5))			/* I2C忙 */
		return 1;

	/*
     * 设置寄存器I2CR
     * bit[5]: 1 主模式
     * bit[4]: 1 发送
	 */
	base->I2CR |=  (1 << 5) | (1 << 4);

	/*
     * 设置寄存器I2DR
     * bit[7:0] : 要发送的数据,这里写入从设备地址
     *            参考资料:IMX6UL参考手册P1249
	 */ 
	base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
	return 0;
}

/*
 * @description		: 检查并清除错误
 * @param - base 	: 要使用的IIC
 * @param - status	: 状态
 * @return 			: 状态结果
 */
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status)
{
	/* 检查是否发生仲裁丢失错误 */
	if(status & (1<<4))
	{
		base->I2SR &= ~(1<<4);		/* 清除仲裁丢失错误位 			*/

		base->I2CR &= ~(1 << 7);	/* 先关闭I2C 				*/
		base->I2CR |= (1 << 7);		/* 重新打开I2C 				*/
		return I2C_STATUS_ARBITRATIONLOST;
	} 
	else if(status & (1 << 0))     	/* 没有接收到从机的应答信号 */
	{
		return I2C_STATUS_NAK;		/* 返回NAK(No acknowledge) */
	}
	return I2C_STATUS_OK;
}

/*
 * @description		: 停止信号
 * @param - base	: 要使用的IIC
 * @param			: 无
 * @return 			: 状态结果
 */
unsigned char i2c_master_stop(I2C_Type *base)
{
	unsigned short timeout = 0xffff;

	/*
	 * 清除I2CR的bit[5:3]这三位
	 */
	base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));

	/* 等待忙结束 */
	while((base->I2SR & (1 << 5)))
	{
		timeout--;
		if(timeout == 0)	/* 超时跳出 */
			return I2C_STATUS_TIMEOUT;
	}
	return I2C_STATUS_OK;
}

/*
 * @description		: 发送数据
 * @param - base 	: 要使用的IIC
 * @param - buf		: 要发送的数据
 * @param - size	: 要发送的数据大小
 * @param - flags	: 标志
 * @return 			: 无
 */
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size)
{
	/* 等待传输完成 */
	while(!(base->I2SR & (1 << 7))); 
	
	base->I2SR &= ~(1 << 1); 	/* 清除标志位 */
	base->I2CR |= 1 << 4;		/* 发送数据 */
	
	while(size--)
	{
		base->I2DR = *buf++; 	/* 将buf中的数据写入到I2DR寄存器 */
		
		while(!(base->I2SR & (1 << 1))); 	/* 等待传输完成 */	
		base->I2SR &= ~(1 << 1);			/* 清除标志位 */

		/* 检查ACK */
		if(i2c_check_and_clear_error(base, base->I2SR))
			break;
	}
	
	base->I2SR &= ~(1 << 1);
	i2c_master_stop(base); 	/* 发送停止信号 */
}

/*
 * @description		: 读取数据
 * @param - base 	: 要使用的IIC
 * @param - buf		: 读取到数据
 * @param - size	: 要读取的数据大小
 * @return 			: 无
 */
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size)
{
	volatile uint8_t dummy = 0;

	dummy++; 	/* 防止编译报错 */
	
	/* 等待传输完成 */
	while(!(base->I2SR & (1 << 7))); 
	
	base->I2SR &= ~(1 << 1); 				/* 清除中断挂起位 */
	base->I2CR &= ~((1 << 4) | (1 << 3));	/* 接收数据 */
	
	/* 如果只接收一个字节数据的话发送NACK信号 */
	if(size == 1)
        base->I2CR |= (1 << 3);

	dummy = base->I2DR; /* 假读 */
	
	while(size--)
	{
		while(!(base->I2SR & (1 << 1))); 	/* 等待传输完成 */	
		base->I2SR &= ~(1 << 1);			/* 清除标志位 */

	 	if(size == 0)
        {
        	i2c_master_stop(base); 			/* 发送停止信号 */
        }

        if(size == 1)
        {
            base->I2CR |= (1 << 3);
        }
		*buf++ = base->I2DR;
	}
}

/*
 * @description	: I2C数据传输,包括读和写
 * @param - base: 要使用的IIC
 * @param - xfer: 传输结构体
 * @return 		: 传输结果,0 成功,其他值 失败;
 */
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer)
{
	unsigned char ret = 0;
	 enum i2c_direction direction = xfer->direction;	

	base->I2SR &= ~((1 << 1) | (1 << 4));			/* 清除标志位 */

	/* 等待传输完成 */
	while(!((base->I2SR >> 7) & 0X1)){}; 

	/* 如果是读的话,要先发送寄存器地址,所以要先将方向改为写 */
    if ((xfer->subaddressSize > 0) && (xfer->direction == kI2C_Read))
    {
        direction = kI2C_Write;
    }

	ret = i2c_master_start(base, xfer->slaveAddress, direction); /* 发送开始信号 */
    if(ret)
    {	
		return ret;
	}

	while(!(base->I2SR & (1 << 1))){};			/* 等待传输完成 */

    ret = i2c_check_and_clear_error(base, base->I2SR);	/* 检查是否出现传输错误 */
    if(ret)
    {
      	i2c_master_stop(base); 						/* 发送出错,发送停止信号 */
        return ret;
    }
	
    /* 发送寄存器地址 */
    if(xfer->subaddressSize)
    {
        do
        {
			base->I2SR &= ~(1 << 1);			/* 清除标志位 */
            xfer->subaddressSize--;				/* 地址长度减一 */
			
            base->I2DR =  ((xfer->subaddress) >> (8 * xfer->subaddressSize)); //向I2DR寄存器写入子地址
  
			while(!(base->I2SR & (1 << 1)));  	/* 等待传输完成 */

            /* 检查是否有错误发生 */
            ret = i2c_check_and_clear_error(base, base->I2SR);
            if(ret)
            {
             	i2c_master_stop(base); 				/* 发送停止信号 */
             	return ret;
            }  
        } while ((xfer->subaddressSize > 0) && (ret == I2C_STATUS_OK));

        if(xfer->direction == kI2C_Read) 		/* 读取数据 */
        {
            base->I2SR &= ~(1 << 1);			/* 清除中断挂起位 */
            i2c_master_repeated_start(base, xfer->slaveAddress, kI2C_Read); /* 发送重复开始信号和从机地址 */
    		while(!(base->I2SR & (1 << 1))){};/* 等待传输完成 */

            /* 检查是否有错误发生 */
			ret = i2c_check_and_clear_error(base, base->I2SR);
            if(ret)
            {
             	ret = I2C_STATUS_ADDRNAK;
                i2c_master_stop(base); 		/* 发送停止信号 */
                return ret;  
            }
           	          
        }
    }	


    /* 发送数据 */
    if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0))
    {
    	i2c_master_write(base, xfer->data, xfer->dataSize);
	}

    /* 读取数据 */
    if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0))
    {
       	i2c_master_read(base, xfer->data, xfer->dataSize);
	}
	return 0;	
}

在这里插入图片描述

2.3 AP3216C环境光传感器的代码分析

AP3216C:环境光强度、接近距离和红外线强度三个参数检测。一般是用于检测是否有物体接近

#include "bsp_ap3216c.h"
#include "bsp_i2c.h"
#include "bsp_delay.h"
#include "cc.h"
#include "stdio.h"

/*
 * @description	: 初始化AP3216C
 * @param		: 无
 * @return 		: 0 成功,其他值 错误代码
 */
unsigned char ap3216c_init(void)
{
	unsigned char data = 0;

	/* 1、IO初始化,配置I2C IO属性	
     * I2C1_SCL -> UART4_TXD
     * I2C1_SDA -> UART4_RXD
     */
	IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);
	IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);

	/* 
	 *bit 16:0 HYS关闭
	 *bit [15:14]: 1 默认47K上拉
	 *bit [13]: 1 pull功能
	 *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_UART4_TX_DATA_I2C1_SCL, 0x70B0);
	IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0X70B0);

	i2c_init(I2C1);		/* 初始化I2C1 */

	/* 2、初始化AP3216C */
	ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0X04);	/* 复位AP3216C 			*/
	delayms(50);													/* AP33216C复位至少10ms */
	ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0X03);	/* 开启ALS、PS+IR 		   	*/
	data = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG);	/* 读取刚刚写进去的0X03 */
	if(data == 0X03)
		return 0;	/* AP3216C正常 	*/
	else 
		return 1;	/* AP3216C失败 	*/
}

/*
 * @description	: 向AP3216C写入数据
 * @param - addr: 设备地址
 * @param - reg : 要写入的寄存器
 * @param - data: 要写入的数据
 * @return 		: 操作结果
 */
unsigned char ap3216c_writeonebyte(unsigned char addr,unsigned char reg, unsigned char data)
{
    unsigned char status=0;
    unsigned char writedata=data;
    struct i2c_transfer masterXfer;
	
    /* 配置I2C xfer结构体 */
   	masterXfer.slaveAddress = addr; 			/* 设备地址 				*/
    masterXfer.direction = kI2C_Write;			/* 写入数据 				*/
    masterXfer.subaddress = reg;				/* 要写入的寄存器地址 			*/
    masterXfer.subaddressSize = 1;				/* 地址长度一个字节 			*/
    masterXfer.data = &writedata;				/* 要写入的数据 				*/
    masterXfer.dataSize = 1;  					/* 写入数据长度1个字节			*/

    if(i2c_master_transfer(I2C1, &masterXfer))
        status=1;
        
    return status;
}

/*
 * @description	: 从AP3216C读取一个字节的数据
 * @param - addr: 设备地址
 * @param - reg : 要读取的寄存器
 * @return 		: 读取到的数据。
 */
unsigned char ap3216c_readonebyte(unsigned char addr,unsigned char reg)
{
	unsigned char val=0;
	
	struct i2c_transfer masterXfer;	
	masterXfer.slaveAddress = addr;				/* 设备地址 				*/
    masterXfer.direction = kI2C_Read;			/* 读取数据 				*/
    masterXfer.subaddress = reg;				/* 要读取的寄存器地址 			*/
    masterXfer.subaddressSize = 1;				/* 地址长度一个字节 			*/
    masterXfer.data = &val;						/* 接收数据缓冲区 				*/
    masterXfer.dataSize = 1;					/* 读取数据长度1个字节			*/
	i2c_master_transfer(I2C1, &masterXfer);

	return val;
}

/*
 * @description	: 读取AP3216C的数据,读取原始数据,包括ALS,PS和IR, 注意!
 *				: 如果同时打开ALS,IR+PS的话两次数据读取的时间间隔要大于112.5ms
 * @param - ir	: ir数据
 * @param - ps 	: ps数据
 * @param - ps 	: als数据 
 * @return 		: 无。
 */
void ap3216c_readdata(unsigned short *ir, unsigned short *ps, unsigned short *als)
{
    unsigned char buf[6];
    unsigned char i;

	/* 循环读取所有传感器数据 */
    for(i = 0; i < 6; i++)	
    {
        buf[i] = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_IRDATALOW + i);	
    }
	
    if(buf[0] & 0X80) 	/* IR_OF位为1,则数据无效 */
		*ir = 0;					
	else 				/* 读取IR传感器的数据   		*/
		*ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); 			
	
	*als = ((unsigned short)buf[3] << 8) | buf[2];	/* 读取ALS传感器的数据 			 */  
	
    if(buf[4] & 0x40)	/* IR_OF位为1,则数据无效 			*/
		*ps = 0;    													
	else 				/* 读取PS传感器的数据    */
		*ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F); 	
}

在这里插入图片描述

三、SPI协议

3.1 协议解读

1、SPI相比I2C最大的优势有两点:一个是速度快,最高可以大几十M,甚至上百MHz,第二个就是SPI是个全双工
2、SPI接口和I2C一样,一个SPI接口可以连接多个SPI外设,SPI通过CS引脚/数据线,片选引脚来选择和哪个SPI外设通信。SPI通信前先将指定的SPI外设对应的CS引脚拉低来选中此设备。
3、ALPHA开发板上通过ECSPI3接口连接了一个6轴传感器,引脚如下:
ECSPI3_SCLK UART2_RX
ECSPI3 MOSI:UART2 CTS
ECSPI3 SSO:UART2 TXD
ECSPI3 MISO:UART2 RTS.
6ULL一个SPI主接口有4个硬件片选,分别为SS0-SS3。
4、根据CPOL和CPHA可以设置四种工作模式,一般使用CPOL=0、CPHA=O。
在这里插入图片描述
一般就是四根线,一条控制CS拉低,则表示开始,一个时钟线,剩下就是全双工了。
在这里插入图片描述

3.2 代码分析

#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;
}

在这里插入图片描述

3.3 ICM-20608六轴传感器代码分析

#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;
}

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2227650.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vmware虚拟机解决摄像头无效,相机失效

问题&#xff1a; 使用vmware虚拟机&#xff0c;打开windows的虚拟机&#xff0c;发现找不到摄像头&#xff0c;打开自带的相机软件报错&#xff1a; 解决方法如下&#xff0c;依次点击vmware状态栏的 虚拟机-可移动设备-chicony integrated camera-连接&#xff08;断开与主…

MySQL用户权限管理属于SQL语句中的DCL语句

1.用户授权 语法&#xff1a;grant 权限&#xff0c;权限&#xff0c;on 库名&#xff0c;表名 to 用户名 [identified by 密码] MySQL5的版本&#xff0c;如果这个用户事先不存在&#xff0c;这个grant命令去给用户授权的时候&#xff0c;会将用户一起创建出来&#xff0…

已解决 django.db.utils.OperationalError: (1051, “Unknown table

报错信息&#xff1a; django.db.utils.OperationalError: (1051, "Unknown table bjybolg.tool_submission")python manage.py migrate --fake 命令用于告诉 Django 假装已经应用某个迁移&#xff0c;而不实际执行该迁移的操作。这通常在以下情况下非常有用&#x…

Linux shell编程学习笔记87:blkid命令——获取块设备信息

0 引言 在进行系统安全检测时&#xff0c;我们需要收集块设备的信息&#xff0c;这些可以通过blkid命令来获取。 1 blkid命令的安装 blkid命令是基于libblkid库的命令行工具&#xff0c;可以在大多数Linux发行版中使用。 如果你的Linux系统中没有安装blkid命令&#xff0c;…

堆的应用——堆排序和TOP-K问题

1.堆排序 想法⼀&#xff1a; 基于已有数组建堆、取堆顶元素完成排序。也就是利用写好的堆数据结构&#xff08;之前的文章有讲解&#xff09;&#xff0c;去实现排序。 void HeapSort(int* a, int n){HP hp;for(int i 0; i < n; i){HPPush(&hp,a[i]);}int i 0;whi…

HexForge:一款用于扩展安全汇编和十六进制视图的IDA插件

关于HexForge HexForge是一款用于扩展安全汇编和十六进制视图的IDA插件&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以方便地直接从 IDA Pro 界面数据解码、解密或执行安全数据审计任务。 功能介绍 1、从 IDA 的反汇编或十六进制视图复制原始十六进制&#xff1b;…

00 DSA-- 入门、实现动态数组、实现链表、栈和队列、环形数组、哈希表

两种代码模式 核心代码模式 核心代码模式&#xff1a;就是给你一个函数框架&#xff0c;你需要实现函数逻辑&#xff0c;这种模式一般称之为。 目前大部分刷题平台和技术面试/笔试场景都是核心代码模式。 比如力扣第一题两数之和&#xff0c;就是给出 twoSum 函数的框架如下…

Jmeter压力测试简单教程(包括服务器状态监控)

前段时间公司需要对服务器进行压力测试&#xff0c;包括登录前的页面和登录后的页面&#xff0c;主要目的是测试负载均衡的实现效果。不知道是不是因为Jmeter不如loadRunner火爆还是什么&#xff0c;网上关于Jmeter的资料有很多但是大多千篇一律&#xff0c;要么简单弄个页面测…

Android 开发 调节声音 SeekBar自定义样式

效果图 xml布局 mipmap/seekbar图片随意一张图都可以&#xff0c;这里我的图就不贴出来了 <SeekBarandroid:id"id/seekBar"android:layout_marginLeft"8dp"android:layout_width"377dp"android:layout_height"8dp"android:layou…

循序渐进丨openGauss / MogDB 数据库内存占用相关SQL

一、内存总体分布 数据库总体内存使用分布 select * from gs_total_memory_detail; 当dynamic_used_memory大于max_dynamic_memory就会报内存不足&#xff1b;如果此时dynamic_used_memory小于max_dynamic_memory&#xff0c;而dynamic_peak_memory大于max_dynamic_memory表…

基于vite和vue3、 eslint、prettier、stylelint、husky规范

前言 在现代的前端开发中&#xff0c;代码规范非常重要。它可以提高团队的协作效率&#xff0c;减少代码错误&#xff0c;使代码更易于维护。为了实现代码规范化&#xff0c;我们可以使用一些工具来辅助我们的开发流程&#xff0c;包括eslint、prettier、stylelint、husky&am…

MYSQL-SQL-03-DQL(Data Query Language,数据查询语言)(单表查询)

DQL&#xff08;数据查询语言&#xff09; DQL英文全称是Data Query Language(数据查询语言)&#xff0c;数据查询语言&#xff0c;用来查询数据库中表的记录。 查询关键字: SELECT 在一个正常的业务系统中&#xff0c;查询操作的频次是要远高于增删改的&#xff0c;当我们去访…

宇音天下最新力作 | VTX356语音识别合成芯片问世

北京宇音天下科技有限公司&#xff0c;依托在语音技术领域的丰富经验和技术积累&#xff0c;成功推出了一款具有里程碑意义的语音识别合成芯片——VTX356。这款芯片的问世&#xff0c;不仅彰显了公司在智能语音处理领域的专业实力&#xff0c;也预示着智能家居、车载电子、智能…

开始菜单增强工具 StartAllBack v3.7.10.4910 直装激活版

StartAllBack中文版(StartIsBack)是一款Win11开始菜单增强工具&#xff0c;可以为Windows11恢复经典样式的Windows7主题风格开始菜单和任务栏&#xff0c;功能包括了&#xff1a;自定义开始菜单样式和操作&#xff0c;个性化任务栏及资源管理器等。 详细功能 √ 全面更新中文语…

java中Scanner的nextLine和next方法

思考&#xff0c;输入1 2 3 4 5加上enter&#xff0c;输出什么 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int[][] m new int[2][2];for (int i 0; i < 2; i) {for (int j 0; j < 2;…

DataX数据同步

背景&#xff1a; 业务需求中&#xff0c;经常会有同步各种系统或者数仓的数据到自己的库进行使用。比如从oracle同步到自己的mysql&#xff0c;自己写代码如果数据量大需要考虑多线程并发等。最近使用了阿里的Datax项目&#xff0c;操作简单并高效。 Datax Datax概述&支…

如何使用的是github提供的Azure OpenAI服务

使用的是github提供的Azure OpenAI的服务gpt-4o 说明&#xff1a;使用的是github提供的Azure OpenAI的服务&#xff0c;可以无限薅羊毛。开源地址 进入&#xff1a; 地址 进入后点击 右上角“Get API key”按钮 点击“Get developer key” 选择Beta版本“Generate new to…

高可用HA软件

高可用HA&#xff08;High Availability&#xff09;软件在分布式系统架构设计中至关重要&#xff0c;它们能够减少系统停机时间&#xff0c;确保应用程序持久、不间断地提供服务。以下是四款常用的高可用HA软件介绍&#xff1a; Keepalived Keepalived起初是为LVS&#xff08;…

Python小白学习教程从入门到入坑------第十八课 异常模块与包【上】(语法基础)

一、异常 在Python中&#xff0c;异常&#xff08;Exception&#xff09;是一种用于处理在程序运行时可能发生的错误情况的机制 异常允许程序在检测到错误时不是简单地崩溃&#xff0c;而是能够优雅地处理这些错误&#xff0c;可能包括记录错误信息、清理资源、或者向用户提…

如何使用非官方的根组件

文章目录 1. 知识回顾2. 使用方法2.1 源码分析2.2 常用属性3. 示例代码4. 内容总结我们在上一章回中介绍了"Get包简介"相关的内容,本章回中将介绍GetMaterialApp组件.闲话休提,让我们一起Talk Flutter吧。 1. 知识回顾 我们在上一章回中已经介绍过GetMaterialApp组…