正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-24.5,6 SPI驱动实验-ICM20608 ADC采样值

news2024/11/26 20:27:54

前言:

本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM(MX6U)裸机篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。

引用:

正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com

《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》

正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档

SPI学习参考资料:

简述SPI通信协议-01_cpha选择为第一个边沿-CSDN博客

SPI中的CPHA,CPOL详解-CSDN博客

一文搞懂SPI通信协议_spi协议-CSDN博客

摩托罗拉 《SPI Block Guide V03.06》 手册

链接:https://pan.baidu.com/s/1_mvR5AD0-OBI2bYyx2i4Sw?pwd=f4bo 
提取码:f4bo

正文:

本文是 “正点原子[第二期]Linux之ARM(MX6U)裸机篇--第24讲 SPI驱动。本节将参考正点原子的视频教程第24讲和配套的正点原子开发指南文档进行学习。

0. 概述

通I2C一样,SPI是很常用的通信接口,也可以通过SPI来连接众多的传感器。相比I2C接口,SPI接口的通信速度很快,I2C最多400KHz,但是SPI可以到达几十MHz。I.MX6U 也有4个SPI接口,可以通过这4个SPI接口来连接一些SPI外设。I.MX6U-ALHPA使用SPI3接口连接了一个6周传感器 ICM-20608,本章我们就来学习如何使用I.MX6U的SPI接口来驱动ICM-20608,读取ICM-20608的六轴数据。

1. ICM20608 6轴传感器量程

icm20608 6轴传感器,支持陀螺仪x,y,z三轴的角速度测量和加速度计x,y,z三轴的加速度计测量。icm20608 陀螺仪的量程范围可选配置为±250,±500,±1000和±2000 °/s,角速度计的量程范围可选配置为±2g,±4g,±8g和±16g。icm20608 x,y,z 轴的输出是一个 16位的 ADC 采样值,16位值的有符号数(补码)表示范围为 -32768~32767,一共可以表示65536个数值,那么采样值和量程范围的关系是什么哪?

参考链接:

IMX6ULL裸机篇之SPI实验-ICM20608传感器_icm20608数据手册-CSDN博客

  • 陀螺仪量程

如果陀螺仪所设置的分辨率范围为 ±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值为 16384,则这时的加速度计的加速度是多少?
16384 / 16384  = 1g

2. ICM20608传感器ADC采样值计算

ADC值是16位,表示值范围为0~65535,一共65536个;量程范围为 ±2000,一共是4000,计算出来多少个ADC值代表一个角速度:

16.4 = 65536/(4000°/s)

例如,icm20608 加速度的量程范围选择为±16g,icm20608 加速度计z轴读取出来的值为2041,因为icm20608 ADC的值是16位其中存放的是一个有符号短整型数(unsigned short)的补码形式,计算处量程范围为±16g,icm20608 加速度计z轴ADC的值为2041的实际加速读值为:

acc_z = (ADC值 * 量程)/ADC位数无符号整型范围

           =(2041 * 32)/65536

           = 0.99g 

其中ADC的值是从icmp20608寄存器里读取出来相应轴的ADC采样值,是有符号短整型数,有正负符号。

3. 源码编写

源码编写读取icm20608传感器6个轴的采样值,源码如下:

bsp_icm2060.h

#ifndef __BSP_20608_H__
#define __BSP_20608_H__

#include "imx6u.h"


#define ICM20608G_ID						0xAF
#define ICM20608N_ID						0xAE

#define ICM20608_REG_SMLPRT_DIV				0x19
#define ICM20608_REG_CONFIG					0x1A
#define ICM20608_REG_GYRO_CONFIG			0x1B
#define ICM20608_REG_ACCEL_CONFIG			0x1C
#define ICM20608_REG_ACCEL_CONFIG2			0x1D
#define ICM20608_REG_LP_MODE_CFG			0x1E
#define ICM20608_REG_ACCEL_WOM_THR			0x1F
#define ICM20608_REG_FIFO_EN				0x23
#define ICM20608_REG_ACCEL_XOUT_H			0x3B
#define ICM20608_REG_ACCEL_XOUT_L			0x3C
#define ICM20608_REG_ACCEL_YOUT_H			0x3D
#define ICM20608_REG_ACCEL_YOUT_L			0x3E
#define ICM20608_REG_ACCEL_ZOUT_H			0x3F
#define ICM20608_REG_ACCEL_ZOUT_L			0x40
#define ICM20608_REG_TEMP_OUT_H				0x41
#define ICM20608_REG_TEMP_OUT_L				0x42
#define ICM20608_REG_GYRO_XOUT_H			0x43
#define ICM20608_REG_GYRO_XOUT_L			0x44
#define ICM20608_REG_GYRO_YOUT_H			0x45
#define ICM20608_REG_GYRO_YOUT_L			0x46
#define ICM20608_REG_GYRO_ZOUT_H			0x47
#define ICM20608_REG_GYRO_ZOUT_L			0x48
#define ICM20608_REG_PWR_MGMT_1				0x6B
#define ICM20608_REG_PWR_MGMT_2				0x6C
#define ICM20608_REG_WHO_AM_I				0x75
#define ICM20608_REG_XA_OFFSET_H			0x77
#define ICM20608_REG_XA_OFFSET_L			0x78
#define ICM20608_REG_YA_OFFSET_H			0x7A
#define ICM20608_REG_YA_OFFSET_L			0x7B
#define ICM20608_REG_ZA_OFFSET_H			0x7D
#define ICM20608_REG_ZA_OFFSET_L			0x7E


copy from 正点原子示例程序 
/* 陀螺仪和加速度自测(出产时设置,用于与用户的自检输出值比较) */
#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

copy from 正点原子示例程序 



struct icm20608_device {
	/* ADC 采样值,原始数据 */
	signed short accel_x_adc;
	signed short accel_y_adc;
	signed short accel_z_adc;
	signed short temperature_adc;
	signed short gyro_x_adc;
	signed short gyro_y_adc;
	signed short gyro_z_adc;

	/* 计算得到的实际值 */
	signed short accel_x_act;
	signed short accel_y_act;
	signed short accel_z_act;
	signed short temperature_act;
	signed short gyro_x_act;
	signed short gyro_y_act;
	signed short gyro_z_act;
};

extern struct icm20608_device icm20608_dev;

#define  ICM20608_CSN(n) 	do{n ? gpio_pinwrite(GPIO1, 20, 1) : gpio_pinwrite(GPIO1, 20, 0);} while(0)

void icm20608_init(void);
unsigned char icm20608_read_reg(ECSPI_Type *base, unsigned char reg);
void icm20608_write_reg(ECSPI_Type *base, unsigned char reg, unsigned char value);
void icm20608_read_len(ECSPI_Type *base, unsigned char reg, unsigned char len, unsigned char *buf);
void icm20608_read_dev_accel_gyro(struct icm20608_device *idev);
int icm20608_gyroscale_get(void);
int icm20608_accelscale_get(void);

#endif

bsp_icm2060.c

#include "bsp_icm20608.h"
#include "bsp_gpio.h"
#include "bsp_spi.h"
#include "bsp_delay.h"
#include "stdio.h"

/* 初始化ICM20608 */
void icm20608_init(void)
{
	gpio_pin_config_t config;

	/* 1.SPI 引脚的初始化 */
	IOMUXC_SetPinMux(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0);				/* 复用为ECSPI3_SCLK */
	IOMUXC_SetPinMux(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0);				/* 复用为ECSPI3_MOSI */
	IOMUXC_SetPinMux(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0);				/* 复用为ECSPI3_MISO */


	IOMUXC_SetPinConfig(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0x10B1);		/* IO特性 */
	IOMUXC_SetPinConfig(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0x10B1);		/* IO特性 */
	IOMUXC_SetPinConfig(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0x10B1);		/* IO特性 */


	IOMUXC_SetPinMux(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0);				/* 复用为GPIO1_20 */
	IOMUXC_SetPinConfig(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0x10B0);		/* IO特性 */
	config.direction = kGPIO_DigitalOutput;
	config.outputLogic = 0;
	gpio_init(GPIO1, 20, &config);


	/* 2.SPI控制器的初始化 */
	spi_init(ECSPI3);


	/* 3.icm20608的初始化 */
	/* 睡眠模式,手册中说icm20608复位之后默认是sleep模式 */
	icm20608_write_reg(ECSPI3, ICM20608_REG_PWR_MGMT_1, 0x80);				/* 复位 */
	delay_ms(50);
	icm20608_write_reg(ECSPI3, ICM20608_REG_PWR_MGMT_1, 0x01);				/* 关闭睡眠模式,时钟选择 */
	delay_ms(50);

	unsigned char value;
	value = icm20608_read_reg(ECSPI3, ICM20608_REG_WHO_AM_I);
	printf("ICM20608G ICM20608_REG_WHO_AM_I=0x%02x\r\n", value);

	icm20608_write_reg(ECSPI3, ICM20608_REG_SMLPRT_DIV, 0x0);				/* 设置采样率输出速率 */
	icm20608_write_reg(ECSPI3, ICM20608_REG_GYRO_CONFIG, 0x18);				/* 陀螺仪量程FS(Full Scale), ±2000dps*/
	icm20608_write_reg(ECSPI3, ICM20608_REG_ACCEL_CONFIG, 0x18);			/* 加速度计量程FS(Full Scale), ±16g */
	icm20608_write_reg(ECSPI3, ICM20608_REG_CONFIG, 0x04);					/* 陀螺仪, BW=20Hz, 低通滤波器DLPF(Digital Low Pass Filter) */
	icm20608_write_reg(ECSPI3, ICM20608_REG_ACCEL_CONFIG2, 0x04);			/* 加速度, BW=20Hz, 低通滤波器DLPF(Digital Low Pass Filter) */
	icm20608_write_reg(ECSPI3, ICM20608_REG_PWR_MGMT_2, 0x00);				/* 启用所有陀螺仪,加速度计 X,Y,Z轴 */
	icm20608_write_reg(ECSPI3, ICM20608_REG_LP_MODE_CFG, 0x00);				/* 关闭低功耗模式,陀螺仪,加速度计的低功耗模式 */
	icm20608_write_reg(ECSPI3, ICM20608_REG_FIFO_EN, 0x00);					/* 关闭FIFO,陀螺仪,加速度计,温度计,FIFO使能 */

}

unsigned char icm20608_read_reg(ECSPI_Type *base, unsigned char reg)
{
	unsigned char rxdata = 0;

	reg |= 0x80;									/* icmp20608 读,最高位为 1 */	
	
	ICM20608_CSN(0);								/* 拉低SPI CS片选信号,选中从设备 */
	spi_readwrite_onebyte(base, reg);				/* icm20608读,寄存器地址 */	
	rxdata = spi_readwrite_onebyte(base, 0xFF);		/* icm20608读,dummy数据*/
	ICM20608_CSN(1);								/* 拉高SPI CS片选信号,去选中从设备 */

	return rxdata;
}

void icm20608_write_reg(ECSPI_Type *base, unsigned char reg, unsigned char value)
{
	reg &= ~0x80;							/* icmp20608 写,最高位清零 */	

	ICM20608_CSN(0);						/* 拉低SPI CS片选信号,选中从设备 */
	spi_readwrite_onebyte(base, reg);		/* icm20608写,寄存器地址 */	
	spi_readwrite_onebyte(base, value);		/* icm20608写,数据*/
	ICM20608_CSN(1);						/* 拉高SPI CS片选信号,去选中从设备 */
}

void icm20608_read_len(ECSPI_Type *base, unsigned char reg, unsigned char len, unsigned char *buf)
{
	int i  = 0;

	// for(i=0; i< len; i++)
	// {
	// 	buf[i] = icm20608_read_reg(base, reg+i);
	// }

	reg |= 0x80;									/* icmp20608 读,最高位为 1 */	
	
	ICM20608_CSN(0);								/* 拉低SPI CS片选信号,选中从设备 */
	spi_readwrite_onebyte(base, reg);				/* icm20608读,寄存器地址 */	
	/* 连续读取多个数据 */
	for(i=0; i<len; i++){
		buf[i] = spi_readwrite_onebyte(base, 0xFF);	/* icm20608读,dummy数据*/
	}
	ICM20608_CSN(1);								/* 拉高SPI CS片选信号,去选中从设备 */

}


void icm20608_read_dev_accel_gyro(struct icm20608_device *idev)
{
	unsigned char buf[14];
	int gyroscale;
	int accescale;

	icm20608_read_len(ECSPI3, ICM20_ACCEL_XOUT_H, 14, buf);

	idev->accel_x_adc = (signed short)((buf[0] << 8) | buf[1]);
	idev->accel_y_adc = (signed short)((buf[2] << 8) | buf[3]);
	idev->accel_z_adc = (signed short)((buf[4] << 8) | buf[5]);

	idev->temperature_adc =  buf[6] << 8;
	idev->temperature_adc |= buf[7];

	idev->gyro_x_adc =  (signed short)((buf[8] << 8) | buf[9]);
	idev->gyro_y_adc =  (signed short)((buf[10] << 8) | buf[11]);
	idev->gyro_z_adc =  (signed short)((buf[12] << 8) | buf[13]);

	gyroscale = icm20608_gyroscale_get();
	accescale = icm20608_accelscale_get();

	/* 乘上100 用来保存小数,用的时候再除以100 */
	idev->accel_x_act = (((signed int)idev->accel_x_adc * accescale)*100)/65536;
	idev->accel_y_act = (((signed int)idev->accel_y_adc * accescale)*100)/65536;
	idev->accel_z_act = (((signed int)idev->accel_z_adc * accescale)*100)/65536;
	idev->gyro_x_act = (((signed int)idev->gyro_x_adc * gyroscale)*100)/65536;
	idev->gyro_y_act = (((signed int)idev->gyro_y_adc * gyroscale)*100)/65536;
	idev->gyro_z_act = (((signed int)idev->gyro_z_adc * gyroscale)*100)/65536;
}


/*
 * @description 	: 获取陀螺仪分辨率(量程)。
 * @param  			: 无
 * @return 			: 无
 */
int icm20608_gyroscale_get(void)
{
	unsigned char rxdata;
	int gyroScale = 0;

	rxdata = icm20608_read_reg(ECSPI3, ICM20608_REG_GYRO_CONFIG);

	switch(((rxdata >> 3) & 0x3))
	{
		case 0:
			gyroScale = 500;			//gyro_scale ±250dps
			break;
		case 1:
			gyroScale = 1000;			//gyro_scale ±500dps
			break;
		case 2:
			gyroScale = 2000;			//gyro_scale ±1000dps
			break;
		case 3:
			gyroScale = 4000;			//gyro_scale ±2000dps
			break;
	}

	printf("%s() rxdata=%02x gyroScale=%d\r\n", __FUNCTION__, rxdata, gyroScale);
	return gyroScale;
}



/*
 * @description 	: 获取加速度计分辨率(量程)。
 * @param  			: 无
 * @return 			: 无
 */
int icm20608_accelscale_get(void)
{
	unsigned char rxdata;
	int accelScale;

	rxdata = icm20608_read_reg(ECSPI3, ICM20608_REG_ACCEL_CONFIG);

	switch(((rxdata >> 3) & 0x3))
	{
		case 0:
			accelScale = 4;			//accel_scale ±2g
			break;
		case 1:
			accelScale = 8;			//accel_scale ±4g
			break;
		case 2:
			accelScale = 16;		//accel_scale ±8g
			break;
		case 3:
			accelScale = 32;		//accel_scale ±16g
			break;
	}
	
	printf("%s() rxdata=%02x accelScale=%d\r\n", __FUNCTION__, rxdata, accelScale);
	return accelScale;
}

main.c 文件,打印计算并打印出来从icm20608传感器读取出来的6个轴的采样值,并根据icm20608的量程计算出实际的x,y,z轴的加速度,和x,y,z轴的角速度。

main.c

#include "cc.h"
#include "bsp_clk.h"
#include "bsp_led.h"
#include "bsp_delay.h"
#include "bsp_beep.h"
#include "bsp_gpio.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_exti.h"
#include "bsp_epittimer.h"
#include "bsp_keyfilter.h"
#include "bsp_delay.h"
#include "bsp_uart.h"
#include "stdio.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"
#include "bsp_rtc.h"
#include "bsp_ap3216c.h"
#include "bsp_i2c.h"
#include "bsp_spi.h"
#include "bsp_icm20608.h"


char *banner = 	"========================================================\r\n"
				"正点原子I.MX6ULL ALPHA/Mini开发板Linux驱动之ARM裸机开发\r\n" \
				"--Date: 	%s\r\n" \
				"--Author: 	ChenHaoxu, Dimon.chen, 11813202388@qq.com\r\n" \
				"========================================================\r\n";


/*
 * @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);
}

int main(void)
{

	static uint8_t led_state = OFF;
	// static uint8_t beep_state = OFF;

	imx6ul_hardfpu_enable();	/* 使能I.MX6U的硬件浮点 			*/
	int_init();			/* 中断初始化 */
	imx6u_clkinit();	/* 时钟主频初始化,PLL1, PLL2, PLL3 */
	clk_init();			/* 使能所有外设时钟 */
	led_init();			/* led gpio 初始化 */
	beep_init();		/* beep gpio 初始化 */
	//key_init();			/* key gpio 初始化 */
	exti_init();		/* gpio外设中断初始化 */

	//epittimer_init(0, 0, 33000000);	/* EPIT分频frac=0 1分频,EPIT1时钟源66MHz,EPIT1->LR加载值计数器=33MHz,定时周期为500ms */
	//epittimer_init(1, 0, 66000000/20);	/* EPIT分频frac=0 1分频,EPIT1时钟源66MHz,EPIT1->LR加载值计数器=33MHz,定时周期为1000/20=50ms */

	keyfilter_init();
	delay_init();
	uart_init();		/* UART初始化 */
	//lcd_init();			/* LCD 初始化 */
	


	led_switch(LED_0, ON);


	beep_switch(ON);
	delay(200);
	beep_switch(OFF);
	delay(200);
	beep_switch(ON);
	delay(200);
	beep_switch(OFF);

	printf(banner, __DATE__);
	printf("Hello World\r\n");

	lcd_init();			/* LCD 初始化 */
	rtc_init();		
	

	i2c_ap3216c_init();
	icm20608_init();

	// unsigned short ir, ps, als;
	struct icm20608_device icm20608_dev;

	//启用I.MX6U浮点数计算


    while(1){
		led_state = !led_state;
		led_switch(LED_0, led_state);
		delay_ms(1000);

		// ap3216c_readdata(&ir, &ps, &als);
		// printf("ir=%d\r\n", ir);
		// printf("ps=%d\r\n", ps);
		// printf("als=%d\r\n", als);


		icm20608_read_dev_accel_gyro(&icm20608_dev);
		printf("accel %04x x:%hd\r\n", icm20608_dev.accel_x_adc, icm20608_dev.accel_x_adc);
		printf("accel %04x y:%hd\r\n", icm20608_dev.accel_y_adc, icm20608_dev.accel_y_adc);
		printf("accel %04x z:%hd\r\n", icm20608_dev.accel_z_adc, icm20608_dev.accel_z_adc);

		printf("gyro  %04x x:%hd\r\n", icm20608_dev.gyro_x_adc, icm20608_dev.gyro_x_adc);
		printf("gyro  %04x y:%hd\r\n", icm20608_dev.gyro_y_adc, icm20608_dev.gyro_y_adc);
		printf("gyro  %04x z:%hd\r\n", icm20608_dev.gyro_z_adc, icm20608_dev.gyro_z_adc);



		printf("ACT accel %04x x:%hd\r\n", icm20608_dev.accel_x_act, icm20608_dev.accel_x_act);
		printf("ACT accel %04x y:%hd\r\n", icm20608_dev.accel_y_act, icm20608_dev.accel_y_act);
		printf("ACT accel %04x z:%hd\r\n", icm20608_dev.accel_z_act, icm20608_dev.accel_z_act);

		printf("ACT gyro  %04x x:%hd\r\n", icm20608_dev.gyro_x_act, icm20608_dev.gyro_x_act);
		printf("ACT gyro  %04x y:%hd\r\n", icm20608_dev.gyro_y_act, icm20608_dev.gyro_y_act);
		printf("ACT gyro  %04x z:%hd\r\n", icm20608_dev.gyro_z_act, icm20608_dev.gyro_z_act);
    }

    return 0;
}

另一个重要的函数是 imx6ul_hardfpu_enable,这个函数用于开启 I.MX6U 的 NEON 和硬件 FPU(浮点运算单元),因为本章使用到了浮点运算,而 I.MX6U 的Cortex-A7 是支持 NEON 和 FPU(VFPV4_D32)的,但是在使用 I.MX6U 的硬件 FPU 之前是先要开启的。


 

4. 启用I.MX6U 浮点数运算

开启I.MX6U的硬件浮点单元,否则执行浮点运算的时候程序会卡死(float),使用I.MX6U的硬件复点单元的有如下两个步骤:

  1. 开启I.MX6U的硬件浮点单元
  2. 编译时指定启用硬件浮点,指定编译的时候使用硬件浮点指令

VFPFloat Process
VFPFloat Process
FPEXCFloat-Point Execute Control 

通过如下函数写I.MX6U ARM Cortex-A7 协处理器的寄存器来启用硬件浮点运算:

/*
 * @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);
}

在Makefile里加入如下编译选项,指定编译器编译生成汇编源码时使用硬件汇编指令

-march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard

5. 编译烧写SD卡验证结果

编译源码烧录SD卡验证本节的 I.MX6U I2C驱动实验。预期烧录SD卡后正点原子I.MX6ULL ALPHA/Mini 开发板后,可以通过SPI总线读取到ICM20608 6轴传感器的加速度计x,y,z三轴的ADC采集数据和陀螺仪的x,y,z三轴的ADC采样数据,然后根据icm20608配置的量程范围来计算出实际的加速度计x,y,z轴传感器的实际物理值和陀螺仪的x,y,z轴传感器的实际物理值。

我本地验证的结果是可以通过spi接口读取到 icm20608传感器的 加速度计x,y,z三轴的ADC采集数据和陀螺仪的x,y,z三轴的ADC采样数据,计算并打印到串口。

5. 总结和实验遇到问题记录

本实验通过I.MX6U 的硬件spi接口读取到了 icm20608 6轴传感器的寄存器数据,验证了spi可以正常通信,读取了  icm20608 传感器的 chipid 寄存器并打印到串口。本实验也在I.MX6U ARM裸机的情况下直接通过寄存器的操作来初始化spi硬件接口,在实验过程中也熟悉了spi通信的协议,为之后学习打下了基础。

5.1 问题1:icm20608 ADC 采样值是是有符号短整型

icm20608 ADC 采样值是是有符号短整型,在结构体声明里应该将icm20608的 x/y/z 轴的ADC数据声明为 'unsigned short’ 型。这样才能计算处正确的±16g,±2000dps 等加速度和角速度物理量,正负号表示方向。

5.2 问题2:寄存器bit[4:3]应该先右移再和'0x3'进行与运算。

问题2:寄存器bit[4:3]应该先右移再和'0x3F'进行与操作,原来写错了写成了先与'0x3'进行与运算再右移三位,这样就得到了错误的值。

5.3 问题3:需要启用I.MX6U 硬件浮点运算单元,才能使用浮点运算否则程序会卡死

需要启用I.MX6U 硬件浮点运算单元,才能使用浮点运算否则程序会卡死,启用硬件浮点运算之后可以极大的提高浮点运算的速度。

5.4 问题4:使用硬件浮点单元,同时也需要在Makefile里通过编译选项高速编译器使用硬件浮点运算指令。

使用硬件浮点单元,除了开启I.MX6U协处理器的硬件浮点运算单元之后,还需要在Makefile里通过编译选项高速编译器使用硬件浮点运算指令。

-march=armv7-a -mfpu=neon-vfpv4

6. 结束

本文至此结束

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

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

相关文章

【高校科研前沿】新疆生地所陈亚宁研究员团队在GeoSus发文:在1.5°C和2°C全球升温情景下,中亚地区暴露于极端降水的人口增加

目录 文章简介 1.研究内容 2.相关图件 3.文章引用 文章简介 论文名称&#xff1a;Increased population exposures to extreme precipitation in Central Asia under 1.5 ◦C and 2 ◦C global warming scenarios&#xff08;在1.5C和2C全球变暖情景下&#xff0c;中亚地区…

数学建模 —— 灰色系统(4)

目录 什么是灰色系统&#xff1f; 一、灰色关联分析 1.1 灰色关联分析模型 1.2 灰色关联因素和关联算子集 1.2.1 灰色关联因素 1.2.2 关联算子集 1.3 灰色关联公理与灰色关联度 1.3.1 灰色关联度 1.3.2 灰色关联度计算步骤 1.4 广义关联度 1.4.1 灰色绝对关联…

[AI]大模型训练成本到底有多大?

大模型成本到底有多大&#xff0c;大到太平洋装不下。 上图是早前统计的&#xff0c;目前比较流行的大模型的厂家、模型名称、参数级别和类型。 大模型的成本主要有三块&#xff0c;分别是训练成本、推理成本、储存成本。 大模型的成本确实主要涉及训练成本、推理成本和储存成…

Python 包安装及常用命令【python 入门】

背景&#xff1a; 近期看到一个项目&#xff0c;做微信只能机器人&#xff0c;服务是使用python搭建的&#xff0c;于是拷贝下来自己打算跑一跑&#xff0c;部署一下&#xff0c;可是自己又没有python的经验&#xff0c;于是各种查资料学习&#xff0c;跟着敲一敲&#xff0c;顺…

Signac|成年小鼠大脑 单细胞ATAC分析(1)

引言 在本教程中&#xff0c;我们将探讨由10x Genomics公司提供的成年小鼠大脑细胞的单细胞ATAC-seq数据集。本教程中使用的所有相关文件均可在10x Genomics官方网站上获取。 本教程复现了之前在人类外周血单核细胞&#xff08;PBMC&#xff09;的Signac入门教程中执行的命令。…

【机器学习】使用Stable Diffusion实现潜在空间搜索

1、引言 1.1 潜在空间的概念 潜在空间&#xff08;Latent Space&#xff09;是在机器学习和深度学习中一个重要的概念&#xff0c;它指的是用于表示数据的一种低维空间。这个空间编码了数据中包含的所有有用信息的压缩表示&#xff0c;通常比原始数据空间的维数更低&#xff…

java守护线程介绍

在Java中&#xff0c;守护线程&#xff08;Daemon Thread&#xff09;是一种特殊类型的线程&#xff0c;它在后台默默地运行&#xff0c;为其他线程提供服务。当 JVM 中只剩下守护线程时&#xff0c;JVM 会退出。这意味着&#xff0c;守护线程不应该执行关键的任务&#xff0c;…

COMPUTEX 2024 国际电脑展即将举行,英伟达宣布将Copilot+引入RTX系列设备,赋能游戏本AI助理

COMPUTEX 2024 国际电脑展即将于2024年6月4日至7日在台北南港展览馆1馆及2馆盛大举行。作为业界瞩目的盛会&#xff0c;本次展会不仅吸引了全球各地的科技爱好者&#xff0c;更迎来了AMD CEO苏姿丰博士和NVIDIA首席执行官黄仁勋的精彩演讲。 在展会的开幕之际&#xff0c;图形…

【Python】教你彻底了解Python中的模块和包

​​​​ 文章目录 一、模块的概念1. 导入模块2. 导入特定对象3. 给模块或对象取别名 二、标准库模块1. 常用标准库模块2. 使用示例 三、自定义模块1. 创建模块2. 使用自定义模块 四、包的结构与使用1. 创建包2. 使用包中的模块 五、包的深入使用1. 相对导入2. 子包3. 使用子包…

策略模式+简单工厂

&#x1f347;工厂模式 &#x1f348;工厂模式向策略模式过度——工厂加一个保安 &#x1f34f;策略模式 &#x1f350;策略模式简单工厂 声明本文需要理解多态的基础上才能来学习 欢迎前来学习——继承和多态 学习记录 工厂模式 需要什么就生成什么 // 工厂模式 class Fact…

DP读书:如何使用badge?(开源项目下的标咋用)

最近在冲论坛&#xff0c;很少更一些内容了。但遇到了一个真的有趣的&#xff1a; 开源项目下&#xff0c;蓝蓝绿绿的标是怎么用的呢&#xff1f; 这是我的主页Readme&#xff0c;在看一些NXP的主仓时&#xff0c;突然发现没有这个玩&#xff0c;就自己整了个 再比如我的CSDN专…

php高级之框架源码、宏扩展原理与开发

在使用框架的时候我们经常会看到如下代码 类的方法不会显示地声明在代码里面&#xff0c;而是通过扩展的形式后续加进去&#xff0c;这么做的好处是可以降低代码的耦合度、保证源码的完整性。我自己看着框架源码实现了这个功能。 以下是结果: base代码 index.php <?php…

WPS表格插件方方格子【凑数】功能:选出和等于固定数字的数

文章目录 后来发现可以下载方方格子插件&#xff0c;使用【凑数】功能https://ffcell.lanzouj.com/iwhfc1kjhayh【凑数】快速【凑数】 导师让沾发票&#xff0c;需要选出若干个数额的发票&#xff0c;使它们的和等于一个指定数。不知道怎么办了&#xff0c;查了一下&#xff0c…

【MySQL】数据库入门基础

文章目录 一、数据库的概念1. 什么是数据库2. 主流数据库3. mysql和mysqld的区别 二、MySQL基本使用1. 安装MySQL服务器在 CentOS 上安装 MySQL 服务器在 Ubuntu 上安装 MySQL 服务器验证安装 2. 服务器管理启动服务器查看服务器连接服务器停止服务器重启服务器 3. 服务器&…

三十九、openlayers官网示例Extent Interaction解析——在地图上绘制范围并获取数据

官网demo 地址&#xff1a; Extent Interaction 在openlayers中可以使用ExtentInteraction添加交互事件&#xff0c;配合shiftKeyOnly实现按住shift键绘制边界区域。 const map new Map({layers: [new TileLayer({source: new OSM(),}),],target: "map",view: new …

【贡献度分析(帕累托图)】

文章目录 前言一、贡献度分析是什么&#xff1f;二、使用步骤1. 准备数据2. 排序数据3. 绘制帕累托图4. 分析结果5. 实际应用 三、示例代码 前言 贡献度分析也称为帕累托分析。它可以帮助我们理解数据集中各个因素对整体影响的程度&#xff0c;从而优先处理最重要的因素&#…

oracle数据库通过impdp导入数据时提示,ORA-31684:对象类型用户xxx已存在,和ORA-39151:表xxx存在的解决办法

前提条件&#xff1a;首先备份原数据库中此用户对应的schemas 比如名为cams_wf的schemas 以便出了问题后还可以恢复原数据。 解决办法一、 通过命令或者数据库管理工具删除掉此schemas下的所有表&#xff0c;然后在impdp中加入ignorey 来忽略ORA-31684&#xff1a;对象类型用…

分享一个 .Net core Console 项目使用 SqlSugar 的详细例子

前言 SqlSugar 是一款老牌的 .NET 开源 ORM 框架&#xff0c;性能高&#xff0c;功能全面&#xff0c;使用简单&#xff0c;支持 .NET FrameWork、.NET Core3.1、.NET5、.NET6、.NET7、.NET8、.NET9 等版本&#xff0c;线上论坛非常活跃&#xff0c;今天给大伙分享一个 .Net c…

SCARA机器人中旋转花键的维护和保养方法!

作为精密传动元件的一种&#xff0c;旋转花键在工作过程中承受了较大的负荷。在自动化设备上运用广泛&#xff0c;如&#xff1a;水平多关节机械手臂&#xff08;SCARA&#xff09;、产业用机器人、自动装载机、雷射加工机、搬运装置、机械加工中心的ATC装置等&#xff0c;最适…

services层和controller层

services层 我的理解&#xff0c;services层是编写逻辑代码语句最多的一个层&#xff0c;非常重要&#xff0c;在实际的项目中&#xff0c;负责调用Dao层中的mybatis&#xff0c;在我的项目中它调用的是这两个文件 举例代码如下 package com.example.sfdeliverysystem.servic…