STM32---SPI基本功能(SPI与ICM20948通信)

SPI 简介

SPI 是英语 Serial Peripheral interface 的缩写,顾名思义就是串行外围设备接口。是 Motorola
首先在其 MC68HCXX 系列处理器上定义的。SPI 接口主要应用在 EEPROM,FLASH,实时时
钟,AD 转换器,还有数字信号处理器和数字信号解码器之间。SPI,是一种高速的,全双工,
同步的通信总线
,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为 PCB 的布局
上节省空间,提供方便,正是出于这种简单易用的特性,现在越来越多的芯片集成了这种通信
协议,STM32 也有 SPI 接口。
SPI 接口一般使用 4 条线通信:
MISO 主设备数据输入,从设备数据输出。
MOSI 主设备数据输出,从设备数据输入。
SCLK 时钟信号,由主设备产生。
CS 从设备片选信号,由主设备控制。

SPI 主要特点:

3线全双工同步传输;
8或16位传输帧格式选择;
主或从操作,支持多主模式;
主模式和从模式下均可以由软件或硬件进行NSS管理:主/从操作模式的动态改变;
可编程的时钟极性和相位;
可编程的数据顺序,MSB在前或LSB在前;
可触发中断的专用发送和接收标志;
SPI总线忙状态标志;
支持可靠通信的硬件CRC;
可触发中断的主模式故障、过载以及CRC错误标志;
支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求。

SPI 总线四种工作方式 SPI 模块为了和外设进行数据交换,根据外设工作要求,其输出串
行同步时钟极性和相位可以进行配置,时钟极性(CPOL)对传输协议没有重大的影响。如果
CPOL=0,串行同步时钟的空闲状态为低电平;如果 CPOL=1,串行同步时钟的空闲状态为高电
平。时钟相位(CPHA)能够配置用于选择两种不同的传输协议之一进行数据传输。如果 CPHA=0,
在串行同步时钟的第一个跳变沿(上升或下降)数据被采样;如果 CPHA=1,在串行同步时钟
的第二个跳变沿(上升或下降)数据被采样
。SPI 主模块和与之通信的外设备时钟相位和极性
应该一致。
在这里插入图片描述
对于SPI的四种通讯模式,总结起来,就是:

CPOL=0,CPHA=0:此时空闲态时,SCLK处于低电平,数据采样是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在上升沿;
CPOL=0,CPHA=1:此时空闲态时,SCLK处于低电平,数据发送是在第1个边沿,也就是SCLK由低电平到高电平的跳变,所以数据采样是在下降沿;
CPOL=1,CPHA=0:此时空闲态时,SCLK处于高电平,数据采集是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在下降沿;
CPOL=1,CPHA=1:此时空闲态时,SCLK处于高电平,数据发送是在第1个边沿,也就是SCLK由高电平到低电平的跳变,所以数据采集是在上升沿。

SPI通讯协议

SPI引脚说明
SPI的通信原理很简单,它以主从方式工作,这种模式通常有一个主设备和一个或多个从设备,需要至少4根线,事实上3根也可以(单向传输时)。这四根线分别是MISO、MOSI、SCLK、CS,具体的描述见下表:

在这里插入图片描述
CS:控制芯片是否被选中的,也就是说只有片选信号为预先规定的使能信号时(一般默认为低电位),对此芯片的操作才有效,这就允许在同一总线上连接多个SPI设备成为可能

也就是说:当有多个从设备的时候,因为每个从设备上都有一个片选引脚接入到主设备机中,当我们的主设备和某个从设备通信时将需要将从设备对应的片选引脚电平拉低。
在这里插入图片描述
MISO/MOSI/SCLK:通讯是通过数据交换完成的,这里先要知道SPI是串行通讯协议,也就是说数据是一位一位的传输的。这就是SCLK时钟线存在的原因,由SCLK提供时钟脉冲,MISO,MOSI则基于此脉冲完成数据传输。数据输出通过MOSI线,数据在时钟上升沿或下降沿时采样,同时也会有返回数据用于接受。完成一位数据传输,输入也使用同样原理。这样,在至少8次时钟信号的改变(上沿和下沿为一次),就可以完成8位数据的传输。

要注意的是:

SCLK信号线只由主设备控制,从设备不能控制信号线。同样,在一个基于SPI的设备中,至少有一个主控设备;
在点对点的通信中,SPI接口不需要进行寻址操作,且为全双工通信,显得简单高效。在多个从设备的系统中,每个从设备需要独立的使能信号,硬件上比I2C系统要稍微复杂一些

SPI工作机制

SPI可分为主、从两种模式,并且支持全双工模式,所以这也就导致STM32的SPI接口比较复杂。比如:配置SPI为主模式、配置SPI为从模式、配置SPI为单工通信、配置SPI为双工通信等等。这里的内容就非常庞大,涉及到的寄存器的位也比较多,所以就不介绍太多,想要了解更多可以去查看STM32F1xx官方资料的第23章节。

SPI接口框图
在这里插入图片描述
SPI引脚
STM32的SPI接口通过4个引脚与外部器件相连,与标准的SPI协议是一致的:

MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据;
MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据;
SCK:串口时钟,作为主设备的输入,从设备的输入;
NSS:从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为“片选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。
从选择(NSS)脚管理

有2种NSS模式:

软件NSS模式:可以通过设置SPI_CR1寄存器的SSM位来使能这种模式。在这种模式下NSS引脚可以用作它用,而内部NSS信号电平可以通过写SPI_CR1的SSI位来驱动;
硬件NSS模式,分两种情况:
NSS输出被使能:当STM32F10xxx工作为主SPI,并且NSS输出已经通过SPI_CR2寄存器的SSOE位使能,这时NSS引脚被拉低,所有NSS引脚与这个主SPI的NSS引脚相连并配置为硬件NSS的SPI设备,将自动变成从SPI设备。 当一个SPI设备需要发送广播数据,它必须拉低NSS信号,以通知所有其它的设备它是主设备;如果它不能拉低NSS,这意味着总线上有另外一个主设备在通信,这时将产生一个硬件失败错误;
NSS输出被关闭:允许操作于多主环境。

SPI数据帧格式

根据SPI_CR1寄存器中的LSBFIRST位,输出数据位时可以左对齐(MSB对齐标准)也可以右对齐(LSB对齐标准)。
根据SPI_CR1寄存器的DFF位,每个数据帧可以是8位或是16位。所选择的数据帧格式对发送和/或接收都有效。

SPI状态标志

应用程序通过3个状态标志可以完全监控SPI总线的状态:

发送缓冲器空闲标志(TXE)
此标志为1时表明发送缓冲器为空,可以写下一个待发送的数据进入缓冲器中。当写入SPI_DR时,TXE标志被清除。

接收缓冲器非空(RXNE)
此标志为1时表明在接收缓冲器中包含有效的接收数据。读SPI数据寄存器可以清除此标志。

忙(Busy)标志
BSY标志由硬件设置与清除(写入此位无效果),此标志表明SPI通信层的状态。

当它被设置为1时,表明SPI正忙于通信,但有一个例外:在主模式的双向接收模式下(MSTR=1、BDM=1并且BDOE=0),在接收期间BSY标志保持为低。

在软件要关闭SPI模块并进入停机模式(或关闭设备时钟)之前,可以使用BSY标志检测传输是否结束,这样可以避免破坏最后一次传输,因此需要严格按照下述过程执行。

SPI中断

在这里插入图片描述

SPI引脚位置

在这里插入图片描述
外设的GPIO配置
在这里插入图片描述

STM32的主模式配置步骤

1) ) 配置相关引脚的能 复用功能,使能 SPI1 时钟。
我们要用 SPI1,第一步就要是能 SPI1 的时钟,SPI1 的时钟通过 APB2ENR 的第 12 位来设
置。其次要设置 SPI1 的相关引脚为复用输出,这样才会连接到 SPI1 上否则这些 IO 口还是默认
的状态,也就是标准输入输出口。这里我们使用的是 PA5、6、7 这 3 个(SCK.、MISO、MOSI,
CS 使用软件管理方式),所以设置这三个为复用 IO。
2) )设置 SPI1 工作模式。
这一步全部是通过 SPI1_CR1 来设置,我们设置 SPI1 为主机模式,设置数据格式为 8 位,
然后通过 CPOL 和 CPHA 位来设置 SCK 时钟极性及采样方式。并设置 SPI1 的时钟频率(最大
18Mhz),以及数据的格式(MSB 在前还是 LSB 在前)。
3) )使能 SPI1 。
这一步通过 SPI1_CR1 的 bit6 来设置,以启动 SPI1,在启动之后,我们就可以开始 SPI 通
讯了。

ICM20948简介:

icm20948由两个裸片(die)构成,QFN封装(3x3x1mm 24PIN)。一个die集成3轴陀螺仪,3轴加速计和一个DMP,另一个die集成旭化成的AK09913的3轴磁力计。它支持以下功能:
1.512字节的FIFO(FIFO的大小根据DMP功能集而定)
2.运行时校准功能
3.增强的FSYNC功能,可改善类似EIS(视频防抖)应用的时序

陀螺仪可编程量程范围:±250dps ±500dps ±1000dps ±2000dps
加速计可编程量程范围:±2g ±4g ±8g ±16g
这两个传感器的灵敏度初始化(工厂校准)降低了产线的校准要求。

其他关键功能,片上16位ADC,可编程数字滤波器,内嵌的温度传感器以及可编程中断。设备功能接口有I2C和SPI,VDD操作电压范围1.71V到3.6V以及一个独立的数字IO供电,VDDIO从1.71V到1.95V。
与设备上的寄存器进行通信是通过I2C(高达100KHZ-标准或400KHZ-快速),或者高达7MHZ的SPI。

应用场景:
1.智能手机和平板
2.可穿戴传感器
3.IoT场景
4.无人机

SPI相关配置库函数

SPI配置

void spi1_init()
{
    
    
	SPI_InitTypeDef SPI_InitStruct;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);  //
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
	GPIO_mode_af(GPIOA,GPIO_Pin_7);//MOSI
	GPIO_mode_af(GPIOA,GPIO_Pin_6);//MISO
	GPIO_mode_af(GPIOA,GPIO_Pin_5);//SCK
	
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource5,GPIO_AF_SPI1); //PA5复用为 SPI1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_SPI1); //PA6复用为 SPI1
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource7,GPIO_AF_SPI1); //PA7复用为 SPI1
	
	//CS   
	//gpio_mode_out(GPIOB,GPIO_Pin_12);	CS_AK = 1;
	GPIO_mode_out(GPIOC,GPIO_Pin_4);	
    CS_H() ;
	//gpio_mode_out(GPIOA,GPIO_Pin_11);	CS_SPL = 1;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);

	SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;//84/4 21Mhz
	SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge;
	SPI_InitStruct.SPI_CPOL = SPI_CPOL_High;
	SPI_InitStruct.SPI_CRCPolynomial = 7;
	SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStruct.SPI_Mode = SPI_Mode_Master;
	SPI_InitStruct.SPI_NSS = SPI_NSS_Soft;
	SPI_Init(SPI1,&SPI_InitStruct);
	SPI_Cmd(SPI1, ENABLE);
}

void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)
{
    
    
	SPI_BaudRatePrescaler&=0X07;			//限制范围
	SPI1->CR1&=0XFFC7; 
	SPI1->CR1|=SPI_BaudRatePrescaler;	//设置SPI1速度  
	SPI1->CR1|=1<<6; 		//SPI设备使能	  
} 

uint8_t spi1_read_write_byte(uint8_t txc)
{
    
    
	u16 retry = 0;
	while((SPI1->SR&SPI_SR_TXE)==0)
	{
    
    
		if(++retry > 100 )
			return 0;//延迟一段时间后返回
	}
	SPI1->DR = txc;
	retry = 0;
	while((SPI1->SR&SPI_SR_RXNE)==0)
	{
    
    
			if(++retry > 100)
			return 0;//延迟一段时间后返回
	}
	return SPI1->DR;	
}


uint8_t spi1_write_reg(uint8_t reg_addr,uint8_t reg_val)
{
    
    
	spi1_read_write_byte(reg_addr&0x7f);
	spi1_read_write_byte(reg_val);
	return 0;
}


uint8_t spi1_read_reg(uint8_t reg_addr)
{
    
    
	spi1_read_write_byte(reg_addr|0x80);
	return spi1_read_write_byte(0xff);
}

uint8_t spi1_read_reg_buffer(uint8_t reg_addr,void *buffer,uint16_t len)
{
    
    
	uint8_t *p = buffer;
	uint16_t i;
	spi1_read_write_byte(reg_addr|0x80);
	for(i=0;i<len;i++)
	{
    
    
		*p++= spi1_read_write_byte(0xff);
	}
	return 0;
}

void GPIO_mode_af(GPIO_TypeDef* GPIOx,uint16_t pin)
{
    
    
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;//推挽输出
	GPIO_InitStruct.GPIO_Pin = pin;
	GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;//上拉
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_Init(GPIOx,&GPIO_InitStruct);
}

void GPIO_mode_out(GPIO_TypeDef* GPIOx,uint16_t pin)
{
    
    
	GPIO_InitTypeDef GPIO_InitStruct;
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStruct.GPIO_Pin = pin;
	GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOx,&GPIO_InitStruct);
}

void GPIO_Set(GPIO_TypeDef* GPIOx,u32 BITx,u32 MODE,u32 OTYPE,u32 OSPEED,u32 PUPD)
{
    
      
	u32 pinpos=0,pos=0,curpin=0;
	for(pinpos=0;pinpos<16;pinpos++)
	{
    
    
		pos=1<<pinpos;	//一个个位检查 
		curpin=BITx&pos;//检查引脚是否要设置
		if(curpin==pos)	//需要设置
		{
    
    
			GPIOx->MODER&=~(3<<(pinpos*2));	//先清除原来的设置
			GPIOx->MODER|=MODE<<(pinpos*2);	//设置新的模式 
			if((MODE==0X01)||(MODE==0X02))	//如果是输出模式/复用功能模式
			{
    
      
				GPIOx->OSPEEDR&=~(3<<(pinpos*2));	//清除原来的设置
				GPIOx->OSPEEDR|=(OSPEED<<(pinpos*2));//设置新的速度值  
				GPIOx->OTYPER&=~(1<<pinpos) ;		//清除原来的设置
				GPIOx->OTYPER|=OTYPE<<pinpos;		//设置新的输出模式
			}  
			GPIOx->PUPDR&=~(3<<(pinpos*2));	//先清除原来的设置
			GPIOx->PUPDR|=PUPD<<(pinpos*2);	//设置新的上下拉
		}
	}
} 

void GPIO_AF_Set(GPIO_TypeDef* GPIOx,u8 BITx,u8 AFx)
{
    
      
	GPIOx->AFR[BITx>>3]&=~(0X0F<<((BITx&0X07)*4));
	GPIOx->AFR[BITx>>3]|=(u32)AFx<<((BITx&0X07)*4);
}   

ICM20948读取函数

//--------------------------------------------
//数据读取
//--------------------------------------------
void  ICM94_WriteReg(uint8_t writeAddr, uint8_t writeData)
{
    
    
	//return myiic_write_reg(ICM20602_ADDRESS,reg,val);
	CS_L();
	spi1_write_reg(writeAddr,writeData);
	CS_H();
}
//bank change
void ICM94_SwitchUserBank(uint8_t bank)
{
    
    
  ICM94_WriteReg(ICM20948_REG_BANK_SEL,(bank& 0xCF) << 4);
}

uint8_t ICM94_ReadReg(uint8_t readAddr)
{
    
    
	uint8_t res;
	//return myiic_read_reg(ICM20602_ADDRESS,reg);
	CS_L();
	res = spi1_read_reg(readAddr);
	CS_H();
	return res;
}

void  ICM94_ReadRegs( uint8_t readAddr, uint8_t *readData, uint8_t lens )
{
    
    
	//return myiic_read_buffer(ICM20602_ADDRESS,reg,len,buffer);
	CS_L();
	spi1_read_reg_buffer(readAddr,readData,lens);
	CS_H();

}
/**
  * @brief  ICM94_AUX_WriteRegs
  */
void ICM94_AUX_WriteReg( uint8_t slaveAddr, uint8_t writeAddr, uint8_t writeData )
{
    
    
  uint8_t  status;
  uint32_t timeout = AUX_READ_TIMEOUT;

  ICM94_SwitchUserBank(3);
  ICM94_WriteReg(ICM20948_I2C_SLV4_ADDR, slaveAddr >> 1);
  delay_ms(2);
  ICM94_WriteReg(ICM20948_I2C_SLV4_REG, writeAddr);
  delay_ms(2);
  ICM94_WriteReg(ICM20948_I2C_SLV4_DO, writeData);
  delay_ms(2);
  ICM94_WriteReg(ICM20948_I2C_SLV4_CTRL, ICM20948_I2C_SLVx_EN);
  delay_ms(2);
  ICM94_SwitchUserBank(0);

  do 
  {
    
    
    status = ICM94_ReadReg(ICM20948_I2C_MST_STATUS);
    delay_ms(1);
  } while (((status & ICM20948_I2C_SLV4_DONE) == 0) && (timeout--));
}
void ICM94_AUX_WriteRegs( uint8_t slaveAddr, uint8_t writeAddr, uint8_t *writeData, uint8_t lens )
{
    
    
  uint8_t  status;
  uint32_t timeout = AUX_READ_TIMEOUT;

  ICM94_SwitchUserBank(3);
  ICM94_WriteReg(ICM20948_I2C_SLV4_ADDR, slaveAddr >> 1);
  delay_ms(2);
  for (uint8_t i = 0; i < lens; i++) 
	{
    
    
    ICM94_SwitchUserBank(3);
    ICM94_WriteReg(ICM20948_I2C_SLV4_REG, writeAddr + i);
    delay_ms(2);
    ICM94_WriteReg(ICM20948_I2C_SLV4_DO, writeData[i]);
    delay_ms(2);
    ICM94_WriteReg(ICM20948_I2C_SLV4_CTRL, ICM20948_I2C_SLVx_EN);
    delay_ms(2);
    ICM94_SwitchUserBank(0);
    do {
    
    
      status = ICM94_ReadReg(ICM20948_I2C_MST_STATUS);
      delay_ms(2);
    } while (((status & ICM20948_I2C_SLV4_DONE) == 0) && (timeout--));
  }
}

/**
  * @brief  ICM94_AUX_ReadReg
  */
uint8_t ICM94_AUX_ReadReg( uint8_t slaveAddr, uint8_t readAddr )
{
    
    
  uint8_t status;
  uint8_t readData;
  uint32_t timeout = AUX_READ_TIMEOUT;

  ICM94_SwitchUserBank(3);
  ICM94_WriteReg(ICM20948_I2C_SLV4_ADDR, (slaveAddr >> 1) | 0x80);
  delay_ms(2);
  ICM94_WriteReg(ICM20948_I2C_SLV4_REG, readAddr);
  delay_ms(2);
  ICM94_WriteReg(ICM20948_I2C_SLV4_CTRL, ICM20948_I2C_SLVx_EN);
  delay_ms(2);
  ICM94_SwitchUserBank(0);

  do {
    
    
    status = ICM94_ReadReg(ICM20948_I2C_MST_STATUS);
    delay_ms(2);
  } while (((status & ICM20948_I2C_SLV4_DONE) == 0) && (timeout--));

  ICM94_SwitchUserBank(3);
  readData = ICM94_ReadReg(ICM20948_I2C_SLV4_DI);
  ICM94_SwitchUserBank(0);

  return readData;
}

/**
  * @brief  ICM94_AUX_ReadRegs
  */

void ICM94_AUX_ReadRegs( uint8_t slaveAddr, uint8_t readAddr, uint8_t *readData, uint8_t lens )
{
    
    
  uint8_t status;
  uint32_t timeout = AUX_READ_TIMEOUT;

  ICM94_SwitchUserBank(3);
  ICM94_WriteReg(ICM20948_I2C_SLV4_ADDR, (slaveAddr >> 1) | 0x80);
  delay_ms(1);
  for (uint8_t i = 0; i< lens; i++) {
    
    
    ICM94_SwitchUserBank(3);
    ICM94_WriteReg(ICM20948_I2C_SLV4_REG, readAddr + i);
    delay_ms(1);
    ICM94_WriteReg(ICM20948_I2C_SLV4_CTRL, ICM20948_I2C_SLVx_EN);
    delay_ms(1);
    ICM94_SwitchUserBank(0);
    do {
    
    
      status = ICM94_ReadReg(ICM20948_I2C_MST_STATUS);
      delay_ms(1);
    } 
		while (((status & ICM20948_I2C_SLV4_DONE) == 0) && (timeout--));

    ICM94_SwitchUserBank(3);
    readData[i] = ICM94_ReadReg(ICM20948_I2C_SLV4_DI);
    delay_ms(1);
    ICM94_SwitchUserBank(0);
  }
}

/**
  * @brief  ICM94_AUX_SLVx_Config
  */
void ICM94_AUX_SLVx_Config( uint8_t slv, uint8_t slaveAddr, uint8_t readAddr, uint8_t lens )
{
    
    
  uint8_t offset = slv << 2;

  ICM94_SwitchUserBank(3);
  ICM94_WriteReg(ICM20948_I2C_SLV0_ADDR + offset, (slaveAddr >> 1) | 0x80);
  delay_ms(1);
  ICM94_WriteReg(ICM20948_I2C_SLV0_REG + offset, readAddr);
  delay_ms(1);
  ICM94_WriteReg(ICM20948_I2C_SLV0_CTRL + offset, ICM20948_I2C_SLVx_EN | (lens & 0x0F));
  delay_ms(1);
  ICM94_SwitchUserBank(0);
}


//--------------------------------------------
//icm20948???
//--------------------------------------------
uint8_t ICM20948_init()
{
    
    
	uint8_t ICM20948_InitData[ICM20948_INIT_REG_LENS][2] = {
    
    
    {
    
    0x20, ICM20948_USER_CTRL},             /* [0]  USR0, Release AUX I2C               */
    {
    
    0x80, ICM20948_PWR_MGMT_1},            /* [1]  USR0, Reset Device                  */
    {
    
    0x01, ICM20948_PWR_MGMT_1},            /* [2]  USR0, Clock Source 自动选择最佳时钟源 */
    {
    
    0x30, ICM20948_USER_CTRL},             /* [3]  USR0, Set I2C_MST_EN, I2C_IF_DIS    */
    {
    
    0x10, ICM20948_INT_PIN_CFG},           /* [4]  USR0, Set INT_ANYRD_2CLEAR          */
    {
    
    0x01, ICM20948_INT_ENABLE},            /* [5]  USR0, Set RAW_RDY_EN Enable I2C master interrupt to propagate to interrupt pin 1.                */
    {
    
    0x00, ICM20948_PWR_MGMT_2},            /* [6]  USR0, Enable all Accel & Gyro            */

    {
    
    0x00, ICM20948_GYRO_SMPLRT_DIV},       /* [7]  USR2, Sample Rate Divider 1.1khz          */
    {
    
    0x00, ICM20948_GYRO_CONFIG_1},         /* [8]  USR2, default : +-250dps            */
    {
    
    0x00, ICM20948_ACCEL_CONFIG},          /* [9]  USR2, default : +-2G                */
    {
    
    0x00, ICM20948_ACCEL_CONFIG_2},        /* [10] USR2, default : AccLPS_460Hz low-power mode时使用 */
    {
    
    0x00, ICM20948_TEMP_CONFIG},           /* [11] USR2, DLPF                          */

    {
    
    0x07, ICM20948_I2C_MST_CTRL},          /* [12] USR3, Set INT_ANYRD_2CLEAR          */
    {
    
    0x80, ICM20948_I2C_MST_DELAY_CTRL},    /* [13] USR3, Delays Shadowing              */
  };
	
	
	ICM20948_InitData[8][0]  |= MPU_GYRO_FILTER | (MPU_GYRO_RANGLE) | MPU_GYRO_FCHOICE;       
	//  GYRO_DLPFCFG = 0,3DB BW 196.6hz  +-1000 dps; GYRO_FCHOICE = 1; 
	//0 196.6; 1 151.8; 2 119.5; 3 51.2; 4 23.9; 5 11.6; 6 5.7; 7 361.4 rate 1125/(1+GYRO_SMPLRT_DIV)Hz
  ICM20948_InitData[9][0]  |= MPU_ACCEL_FILTER | (MPU_ACCEL_RANGLE) | MPU_ACCEL_FCHOICE;       
	//  ACCEL_DLPFCFG = 0,3DB BW 246.0; +-8g; ACCEL_FCHOICE = 1;
	//0 246; 1 246; 2 111.4; 3 50.4; 4 23.9; 5 11.5; 6 5.7; 7 473 rate 1125/(1+ACCEL_SMPLRT_DIV)Hz
  ICM20948_InitData[11][0] |= (1 << 0);                             
	// TEMP_DLPCFG = 1

  ICM94_SwitchUserBank(0);
  ICM94_WriteReg(ICM20948_USER_CTRL, ICM94_ReadReg(ICM20948_USER_CTRL) & ~ICM20948_InitData[0][0]); // release aux i2c
  delay_ms(10);
  ICM94_WriteReg(ICM20948_InitData[1][1], ICM20948_InitData[1][0]);     // reset device
  delay_ms(10);
  ICM94_WriteReg(ICM20948_InitData[2][1], ICM20948_InitData[2][0]);     // set clock source
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[3][1], ICM20948_InitData[3][0]);     // set I2C_MST_EN, I2C_IF_DIS
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[4][1], ICM20948_InitData[4][0]);     // set INT_ANYRD_2CLEAR
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[5][1], ICM20948_InitData[5][0]);     // set RAW_RDY_EN
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[6][1], ICM20948_InitData[6][0]);     // enable accel & gyro
  delay_ms(1);
	
  ID = ICM94_ReadReg(ICM20948_WHO_AM_I);
	MagID=ICM94_AUX_ReadReg(AK09916_I2C_ADDR, AK09916_WIA);
	
  ICM94_SwitchUserBank(2);
  ICM94_WriteReg(ICM20948_InitData[7][1], ICM20948_InitData[7][0]);     // set gyro sample rate divider  1Khz
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[8][1], ICM20948_InitData[8][0]);     // set gyro full-scale range, filter
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[9][1], ICM20948_InitData[9][0]);     // set accel full-scale range, filter
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[10][1], ICM20948_InitData[10][0]);   // set samples average
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[11][1], ICM20948_InitData[11][0]);   // set INT_ANYRD_2CLEAR
  delay_ms(1);

  ICM94_SwitchUserBank(3);
  ICM94_WriteReg(ICM20948_InitData[12][1], ICM20948_InitData[12][0]);   // set temp filter
  delay_ms(1);
  ICM94_WriteReg(ICM20948_InitData[13][1], ICM20948_InitData[13][0]);   // delays shadowing
  delay_ms(1);
	
	ICM94_AUX_WriteReg(AK09916_I2C_ADDR, AK09916_CNTL3, 0x01);
  delay_ms(10);
  ICM94_AUX_WriteReg(AK09916_I2C_ADDR, AK09916_CNTL2,0x00);
  delay_ms(1);
  ICM94_AUX_WriteReg(AK09916_I2C_ADDR, AK09916_CNTL2,0x08);
  delay_ms(1);
	ICM94_AUX_SLVx_Config(0, AK09916_I2C_ADDR, AK09916_ST1, 9);
	delay_ms(10);
	ICM94_SwitchUserBank(0);//bank 0
//  SPI1_SetSpeed(SPI_BaudRatePrescaler_4);		//
	return 0;
}

ICM20948原始数据读取

/*全部原始数据获取*/
void IMU_GetRawData( IMU_RAW_DATA *raw_data ) 
{
    
    
	uint8_t buff[23] = {
    
    0};

  ICM94_ReadRegs(ICM20948_ACCEL_XOUT_H, buff, 23);  /* Acc, Gyr, Mag, Temp */
//  raw[0] = (int16_t)(buff[12] << 8) | buff[13];   /* ICTemp */
  raw_data->Gyro_X =  (int16_t)((buff[6]   << 8) | buff[7]);    /* Gyr.X */
  raw_data->Gyro_Y =  (int16_t)((buff[8]   << 8) | buff[9]);    /* Gyr.Y */
  raw_data->Gyro_Z =  (int16_t)((buff[10]  << 8) | buff[11]);   /* Gyr.Z */
	
  raw_data->Accel_X = (int16_t)((buff[0]   << 8) | buff[1]);    /* Acc.X */
  raw_data->Accel_Y = (int16_t)((buff[2]   << 8) | buff[3]);    /* Acc.Y */
  raw_data->Accel_Z = (int16_t)((buff[4]   << 8) | buff[5]);    /* Acc.Z */

//  raw[7] =  (int16_t)(((buff[16]  << 8) | buff[15])+17 ); /* Mag.X */
//  raw[8] =  (int16_t)(((buff[18]  << 8) | buff[17])-132 ); /* Mag.Y */
//  raw[9] =  (int16_t)(((buff[20]  << 8) | buff[19])+66 ); /* Mag.Z */
//	
	raw_data->Mag_X =  (int16_t)(((buff[16]  << 8) | buff[15])); /* Mag.X */
  raw_data->Mag_Y =  (int16_t)(((buff[18]  << 8) | buff[17])); /* Mag.Y */
  raw_data->Mag_Z =  (int16_t)(((buff[20]  << 8) | buff[19])); /* Mag.Z */


  LostCounterFeed(GetLostCounter(LOST_COUNTER_INDEX_IMU));   //feed(clear) the IMU ERROR Count
}

通过以上的配置之后就可以通过STM32的SPI读取ICM20948的原始数据了。

猜你喜欢

转载自blog.csdn.net/weixin_43491077/article/details/110220304