ICM20948 DMP代码详解(9)

接前一篇文章:ICM20948 DMP代码详解(8)

 

上一回解析完了EMP-App中的入口函数main()中重点关注的第2段代码,本回继续往下解析。为了便于理解和回顾,再次贴出main函数代码,如下:

int main (void)
{
	int rc = 0;
 
	/* Hardware initialization */
	sysclk_init();
	board_init();
	sysclk_enable_peripheral_clock(ID_TC0);
 
	/* Configure Device - Host Interface */
	configure_console();
 
#ifdef INV_MSG_ENABLE
	/* Setup message logging */
	INV_MSG_SETUP(INV_MSG_ENABLE, msg_printer);
#endif
 
	INV_MSG(INV_MSG_LEVEL_INFO, "##########################");
	INV_MSG(INV_MSG_LEVEL_INFO, "     ICM20948 example     ");
	INV_MSG(INV_MSG_LEVEL_INFO, "     Ver: %s", EMD_RELEASE_VERSION_STRING);
	INV_MSG(INV_MSG_LEVEL_INFO, "##########################");
 
	/* Initialize External Sensor Interrupt */
	ext_int_initialize(&ext_interrupt_handler);
	interface_initialize();
 
	/* Configure sysTick Timer */
	SysTick_Config(sysclk_get_cpu_hz() / MILLISECONDS_PER_SECOND);
 
	/*
	* Initialize icm20948 serif structure
	*/
	struct inv_icm20948_serif icm20948_serif;
	icm20948_serif.context   = 0; /* no need */
	icm20948_serif.read_reg  = idd_io_hal_read_reg;
	icm20948_serif.write_reg = idd_io_hal_write_reg;
	icm20948_serif.max_read  = 1024*16; /* maximum number of bytes allowed per serial read */
	icm20948_serif.max_write = 1024*16; /* maximum number of bytes allowed per serial write */
 
	icm20948_serif.is_spi = interface_is_SPI();
 
	/*
	* Reset icm20948 driver states
	*/
	inv_icm20948_reset_states(&icm_device, &icm20948_serif);
 
	inv_icm20948_register_aux_compass(&icm_device, INV_ICM20948_COMPASS_ID_AK09916, AK0991x_DEFAULT_I2C_ADDR);
 
	/*
	* Setup the icm20948 device
	*/
	rc = icm20948_sensor_setup();
 
	/*
	* Now that Icm20948 device was initialized, we can proceed with DMP image loading
	* This step is mandatory as DMP image are not store in non volatile memory
	*/
	rc += load_dmp3();
	check_rc(rc, "Error sensor_setup/DMP loading.");
 
	/*
	* Initialize Dynamic protocol stuff
	*/
	DynProTransportUart_init(&transport, iddwrapper_transport_event_cb, 0);
	DynProtocol_init(&protocol, iddwrapper_protocol_event_cb, 0);
 
	InvScheduler_init(&scheduler);
	InvScheduler_initTask(&scheduler, &commandHandlerTask, "commandHandlerTask", CommandHandlerTaskMain, 0, INVSCHEDULER_TASK_PRIO_MIN, 1);
	InvScheduler_initTask(&scheduler, &blinkerLedTask, "blinkerLedTask", BlinkerLedTaskMain, 0, INVSCHEDULER_TASK_PRIO_MIN+1, 1000000/SCHEDULER_PERIOD);
	InvScheduler_startTask(&blinkerLedTask, 0);
	InvScheduler_startTask(&commandHandlerTask, 0);
 
	hw_timer_start(20);		// Start the timestamp timer at 20 Hz.
	while (1)
    {
		InvScheduler_dispatchTasks(&scheduler);
 
		if (irq_from_device == 1) {
			inv_icm20948_poll_sensor(&icm_device, (void *)0, build_sensor_event_data);
 
			__disable_irq();
			irq_from_device = 0;
			__enable_irq();
		}
	}
 
	return 0;
}

重点关注的第3段代码如下:

	/*
	* Setup the icm20948 device
	*/
	rc = icm20948_sensor_setup();

注意,从icm20948_sensor_setup函数开始,带返回值了,之前的函数都不带返回值。

icm20948_sensor_setup函数在EMD-App\src\ICM20948\sensor.c中,代码如下:

int icm20948_sensor_setup(void)
{
	int rc;
	uint8_t i, whoami = 0xff;

	/*
	* Just get the whoami
	*/
	rc = inv_icm20948_get_whoami(&icm_device, &whoami);
	if (interface_is_SPI() == 0)	{		// If we're using I2C
		if (whoami == 0xff) {				// if whoami fails try the other I2C Address
			switch_I2C_to_revA();
			rc = inv_icm20948_get_whoami(&icm_device, &whoami);
		}
	}
	INV_MSG(INV_MSG_LEVEL_INFO, "ICM20948 WHOAMI value=0x%02x", whoami);

	/*
	* Check if WHOAMI value corresponds to any value from EXPECTED_WHOAMI array
	*/
	for(i = 0; i < sizeof(EXPECTED_WHOAMI)/sizeof(EXPECTED_WHOAMI[0]); ++i) {
		if(whoami == EXPECTED_WHOAMI[i]) {
			break;
		}
	}

	if(i == sizeof(EXPECTED_WHOAMI)/sizeof(EXPECTED_WHOAMI[0])) {
		INV_MSG(INV_MSG_LEVEL_ERROR, "Bad WHOAMI value. Got 0x%02x.", whoami);
		return rc;
	}

	/* Setup accel and gyro mounting matrix and associated angle for current board */
	inv_icm20948_init_matrix(&icm_device);

	/* set default power mode */
	INV_MSG(INV_MSG_LEVEL_VERBOSE, "Putting Icm20948 in sleep mode...");
	rc = inv_icm20948_initialize(&icm_device, dmp3_image, sizeof(dmp3_image));
	if (rc != 0) {
		INV_MSG(INV_MSG_LEVEL_ERROR, "Initialization failed. Error loading DMP3...");
		return rc;
	}

	/*
	* Configure and initialize the ICM20948 for normal use
	*/
	INV_MSG(INV_MSG_LEVEL_INFO, "Booting up icm20948...");

	/* Initialize auxiliary sensors */
	inv_icm20948_register_aux_compass( &icm_device, INV_ICM20948_COMPASS_ID_AK09916, AK0991x_DEFAULT_I2C_ADDR);
	rc = inv_icm20948_initialize_auxiliary(&icm_device);
	if (rc == -1) {
		INV_MSG(INV_MSG_LEVEL_ERROR, "Compass not detected...");
	}

	icm20948_apply_mounting_matrix();

	icm20948_set_fsr();

	/* re-initialize base state structure */
	inv_icm20948_init_structure(&icm_device);

	/* we should be good to go ! */
	INV_MSG(INV_MSG_LEVEL_VERBOSE, "We're good to go !");

	return 0;
}

icm20948_sensor_setup函数包含了很多段以及子函数,还是一段一段、一个函数一个函数来看。

先来看第1段:

	/*
	* Just get the whoami
	*/
	rc = inv_icm20948_get_whoami(&icm_device, &whoami);
	if (interface_is_SPI() == 0)	{		// If we're using I2C
		if (whoami == 0xff) {				// if whoami fails try the other I2C Address
			switch_I2C_to_revA();
			rc = inv_icm20948_get_whoami(&icm_device, &whoami);
		}
	}
	INV_MSG(INV_MSG_LEVEL_INFO, "ICM20948 WHOAMI value=0x%02x", whoami);

	/*
	* Check if WHOAMI value corresponds to any value from EXPECTED_WHOAMI array
	*/
	for(i = 0; i < sizeof(EXPECTED_WHOAMI)/sizeof(EXPECTED_WHOAMI[0]); ++i) {
		if(whoami == EXPECTED_WHOAMI[i]) {
			break;
		}
	}

	if(i == sizeof(EXPECTED_WHOAMI)/sizeof(EXPECTED_WHOAMI[0])) {
		INV_MSG(INV_MSG_LEVEL_ERROR, "Bad WHOAMI value. Got 0x%02x.", whoami);
		return rc;
	}

inv_icm20948_get_whoami函数在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Setup.c中,代码如下:

/* Identification related functions */
int inv_icm20948_get_whoami(struct inv_icm20948 * s, uint8_t * whoami)
{
	return inv_icm20948_read_reg_one(s, REG_WHO_AM_I, whoami);
}

函数本身很好理解,就是调用inv_icm20948_read_reg_one函数,读取REG_WHO_AM_I寄存器的值,并返回给第2个参数uint8_t whoami。

REG_WHO_AM_I宏在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Defs.h中定义,如下:

/*register and associated bit definition*/
/* bank 0 register map */
#define REG_WHO_AM_I            (BANK_0 | 0x00)

对应的就是ICM20948芯片手册中的WHO_AM_I寄存器,如下所示:

d20a95f74f9848e6a7f20652a143350d.png

这里,要对inv_icm20948_read_reg_one函数做详细分析和介绍,因为后边读各个寄存器也会用到该函数。inv_icm20948_read_reg_one函数在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Transport.h中,代码如下:

static inline int inv_icm20948_read_reg_one(struct inv_icm20948 * s, uint8_t reg, uint8_t * reg_value)
{
	return inv_icm20948_read_reg(s, reg, reg_value, 1);
}

inv_icm20948_read_reg_one函数实际调用的是inv_icm20948_read_reg函数,只是最后一个参数传入的值是1,代表读取1个寄存器的内容。可以看到,inv_icm20948_read_reg_one函数实际上是inv_icm20948_read_reg函数的一个特例。

inv_icm20948_read_reg函数在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Transport.c中,代码如下 :

int inv_icm20948_read_reg(struct inv_icm20948 * s, uint8_t reg,	uint8_t * buf, uint32_t len)
{
	return inv_icm20948_serif_read_reg(&s->serif, reg, buf, len);
}

这里重点说一下调用inv_icm20948_serif_read_reg函数时的第个参数&s->serif。前文书(https://phmatthaus.blog.youkuaiyun.com/article/details/141951849)已讲过,s->serif是在inv_icm20948_reset_states函数中赋值的,值取自于main函数中创建并初始化的struct inv_icm20948_serif icm20948_serif。

/** @brief Reset and initialize driver states
 *  @param[in] s             handle to driver states structure
 */
static inline void inv_icm20948_reset_states(struct inv_icm20948 * s,
		const struct inv_icm20948_serif * serif)
{
	assert(icm20948_instance == 0);
 
	memset(s, 0, sizeof(*s));
	s->serif = *serif;
	icm20948_instance = s;
}

当然,此处前边还有&符,代表实际上传入的是变量的地址。

inv_icm20948_serif_read_reg函数在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Serif.h中,代码如下:

static inline int inv_icm20948_serif_read_reg(struct inv_icm20948_serif * s,
		uint8_t reg, uint8_t * buf, uint32_t len)
{
	assert(s);

	if(len > s->max_read)
		return INV_ERROR_SIZE;

	if(s->read_reg(s->context, reg, buf, len) != 0)
		return INV_ERROR_TRANSPORT;

	return 0;
}

inv_icm20948_serif_read_reg函数中会用到两个在main函数中赋值的变量:

53a5790647f348ca8fa5e14cb51caaea.png

max_read用于对inv_icm20948_serif_read_reg函数的参数uint32_t len进行检查,确保一次最多只能读取16K字节。

read_reg指向了实际的读取函数idd_io_hal_read_reg。该函数在EMD-App\src\ICM20948\system.c中,代码如下:

int idd_io_hal_read_reg(void * context, uint8_t reg, uint8_t * rbuffer, uint32_t rlen){
	(void)context;
#if SERIF_TYPE_SPI
	return spi_master_transfer_rx(NULL, reg, rbuffer, rlen);
#else /* SERIF_TYPE_I2C */
	return i2c_master_read_register(I2C_Address, reg, rlen, rbuffer);
#endif	
}

这才调用了真正的i2c寄存器读取函数。

之所以弄得分了这么多层,应该是为了便于移植。本例中,调用的最终函数为i2c_master_read_register。这个函数是适配TDK SAMG55 Dev Kit的。如果是自己的板子(比如笔者是乐鑫ESP32系列模组),那么在此替换掉这个i2c_master_read_register函数,改为适合自己板子的i2c读取函数就好(当然,上下文和函数参数也可能会有一定微调)。

7b3490b5360b4f16a6a4b00c793785d3.png

i2c_master_read_register函数是平台相关的了,这里就不再深入,只贴出代码(EMD-App\src\ICM20948\system.c中),了解一下就好。

static unsigned long i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){
	twi_packet_t packet_read;

	if(Address == 0){
		Address = I2C_Address;	// Slave Address is 0x69 for on-board sensors, 0x68 for external sensors
	}

	packet_read.chip = Address;
	packet_read.addr[0] = RegisterAddr;
	packet_read.addr_length = 1;
	packet_read.buffer = RegisterValue;
	packet_read.length = RegisterLen;

	if(twi_master_read((Twi*)BOARD_BASE_TWI, &packet_read) == TWI_SUCCESS){
		return TWI_SUCCESS;
	}
	return TWI_BUSY;
}

至此,icm20948_sensor_setup函数第1段代码中的inv_icm20948_get_whoami函数就解析完了。下一回继续讲解第1段代码余下的部分。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蓝天居士

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值