接前一篇文章:ICM20948 DMP代码详解(22)
上一回解析完了inv_icm20948_wakeup_mems函数,本回回到inv_icm20948_initialize_lower_driver函数中,继续往下解析。为了便于理解和回顾,再次贴出inv_icm20948_initialize_lower_driver函数源码,在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948DataBaseDriver.c中,如下:
/** Should be called once on power up. Loads DMP3, initializes internal variables needed
* for other lower driver functions.
*/
int inv_icm20948_initialize_lower_driver(struct inv_icm20948 * s, enum SMARTSENSOR_SERIAL_INTERFACE type, const uint8_t *dmp3_image, uint32_t dmp3_image_size)
{int result = 0;static unsigned char data;// set static variables->sAllowLpEn = 1;s->s_compass_available = 0;// ICM20948 do not support the proximity sensor for the moment.// s_proximity_available variable is nerver changess->s_proximity_available = 0;// Set varialbes to default valuesmemset(&s->base_state, 0, sizeof(s->base_state));s->base_state.pwr_mgmt_1 = BIT_CLK_PLL;s->base_state.pwr_mgmt_2 = BIT_PWR_ACCEL_STBY | BIT_PWR_GYRO_STBY | BIT_PWR_PRESSURE_STBY;s->base_state.serial_interface = type;result |= inv_icm20948_read_mems_reg(s, REG_USER_CTRL, 1, &s->base_state.user_ctrl);result |= inv_icm20948_wakeup_mems(s);result |= inv_icm20948_read_mems_reg(s, REG_WHO_AM_I, 1, &data);/* secondary cycle mode should be set all the time */data = BIT_I2C_MST_CYCLE|BIT_ACCEL_CYCLE|BIT_GYRO_CYCLE;// Set default mode to low power moderesult |= inv_icm20948_set_lowpower_or_highperformance(s, 0);// Disable Ivory DMP.if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) s->base_state.user_ctrl = BIT_I2C_IF_DIS;elses->base_state.user_ctrl = 0;result |= inv_icm20948_write_single_mems_reg(s, REG_USER_CTRL, s->base_state.user_ctrl);//Setup Ivory DMP.result |= inv_icm20948_load_firmware(s, dmp3_image, dmp3_image_size);if(result)return result;elses->base_state.firmware_loaded = 1;result |= inv_icm20948_set_dmp_address(s);// Turn off all sensors on DMP by default.//result |= dmp_set_data_output_control1(0); // FIXME in DMP, these should be off by default.result |= dmp_icm20948_reset_control_registers(s);// set FIFO watermark to 80% of actual FIFO sizeresult |= dmp_icm20948_set_FIFO_watermark(s, 800);// Enable Interrupts.data = 0x2;result |= inv_icm20948_write_mems_reg(s, REG_INT_ENABLE, 1, &data); // Enable DMP Interruptdata = 0x1;result |= inv_icm20948_write_mems_reg(s, REG_INT_ENABLE_2, 1, &data); // Enable FIFO Overflow Interrupt// TRACKING : To have accelerometers datas and the interrupt without gyro enables.data = 0XE4;result |= inv_icm20948_write_mems_reg(s, REG_SINGLE_FIFO_PRIORITY_SEL, 1, &data);// Disable HW temp fixinv_icm20948_read_mems_reg(s, REG_HW_FIX_DISABLE,1,&data);data |= 0x08;inv_icm20948_write_mems_reg(s, REG_HW_FIX_DISABLE,1,&data);// Setup MEMs properties.s->base_state.accel_averaging = 1; //Change this value if higher sensor sample avergaing is required.s->base_state.gyro_averaging = 1; //Change this value if higher sensor sample avergaing is required.inv_icm20948_set_gyro_divider(s, FIFO_DIVIDER); //Initial sampling rate 1125Hz/19+1 = 56Hz.inv_icm20948_set_accel_divider(s, FIFO_DIVIDER); //Initial sampling rate 1125Hz/19+1 = 56Hz.// Init the sample rate to 56 Hz for BAC,STEPC and B2Sdmp_icm20948_set_bac_rate(s, DMP_ALGO_FREQ_56);dmp_icm20948_set_b2s_rate(s, DMP_ALGO_FREQ_56);// FIFO Setup.result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_CFG, BIT_SINGLE_FIFO_CFG); // FIFO Config. fixme do once? burst write?result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_RST, 0x1f); // Reset all FIFOs.result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_RST, 0x1e); // Keep all but Gyro FIFO in reset.result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_EN, 0x0); // Slave FIFO turned off.result |= inv_icm20948_write_single_mems_reg(s, REG_FIFO_EN_2, 0x0); // Hardware FIFO turned off.s->base_state.lp_en_support = 1;if(s->base_state.lp_en_support == 1)inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1);result |= inv_icm20948_sleep_mems(s); return result;
}
当前来到以下一行:
result |= inv_icm20948_read_mems_reg(s, REG_WHO_AM_I, 1, &data);
再次读一下WHO_AM_I寄存器。这一句代码似乎没有什么作用。
接下来是:
/* secondary cycle mode should be set all the time */data = BIT_I2C_MST_CYCLE|BIT_ACCEL_CYCLE|BIT_GYRO_CYCLE;
BIT_I2S_MST_CYCLE、BIT_ACCEL_CYCLE、BIT_GYRO_CYCLE三个宏都是在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Defs.h中定义,如下:
#define REG_LP_CONFIG (BANK_0 | 0x05)
#define BIT_I2C_MST_CYCLE 0x40
#define BIT_ACCEL_CYCLE 0x20
#define BIT_GYRO_CYCLE 0x10
对应芯片手册中的内容如下:
这段代码的意思是设置LP_CONFIG寄存器中的I2C_MST_CYCLE、ACCEL_CYCLE、GYRO_CYCLE位,也即设置i2C master、加速度计、陀螺仪为占空比模式。当然,这里还没有实际设置到寄存器中,只是先进行了变量赋值。
接下来是:
// Set default mode to low power moderesult |= inv_icm20948_set_lowpower_or_highperformance(s, 0);
inv_icm20948_set_lowpower_or_highperformance函数在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Setup.c中,代码如下:
int inv_icm20948_set_lowpower_or_highperformance(struct inv_icm20948 *s, uint8_t lowpower_or_highperformance)
{s->go_back_lp_when_odr_low = 0;if(lowpower_or_highperformance)return inv_icm20948_enter_low_noise_mode(s);elsereturn inv_icm20948_enter_duty_cycle_mode(s);
}
inv_icm20948_set_lowpower_or_highperformance函数一上来先将s->go_back_lp_when_odr_low设置为0。go_back_lp_when_odr_low是struct inv_icm20948中的成员,定义如下:
typedef struct inv_icm20948 {……uint8_t go_back_lp_when_odr_low; // set to 1 when we forced a switch from LP to LN mode to be able to reach 1kHz ODR, so we will need to go back to LP mode ASAP……
} inv_icm20948_t;
接下来根据第2个参数uint8_t lowpower_or_highperformance,选择不同分支。
1)非0分支 —— inv_icm20948_enter_low_noise_mode函数
inv_icm20948_enter_low_noise_mode函数也在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948DataBaseDriver.c中,代码如下:
int inv_icm20948_enter_low_noise_mode(struct inv_icm20948 *s)
{/* secondary cycle mode should be set all the time */unsigned char data = BIT_I2C_MST_CYCLE;s->base_state.chip_lp_ln_mode = CHIP_LOW_NOISE_ICM20948;return inv_icm20948_write_mems_reg(s, REG_LP_CONFIG, 1, &data);
}
上边提到了BIT_I2C_MST_CYCLE,这里是实际将值BIT_I2C_MST_CYCLE设置到了LP_CONFIG寄存器,也即设置i2C master为占空比模式。同时将s->base_state.chip_lp_ln_mode设置为CHIP_LOW_NOISE_ICM20948。
CHIP_LOW_NOISE_ICM20948为枚举值,在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948.h中,定义如下:
typedef enum {CHIP_LOW_NOISE_ICM20948,CHIP_LOW_POWER_ICM20948,
} chip_lp_ln_mode_icm20948_t;
2)0分支 —— inv_icm20948_enter_duty_cycle_mode函数
inv_icm20948_enter_duty_cycle_mode函数也在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948DataBaseDriver.c中,代码如下:
int inv_icm20948_enter_duty_cycle_mode(struct inv_icm20948 * s)
{/* secondary cycle mode should be set all the time */unsigned char data = BIT_I2C_MST_CYCLE|BIT_ACCEL_CYCLE|BIT_GYRO_CYCLE;s->base_state.chip_lp_ln_mode = CHIP_LOW_POWER_ICM20948;return inv_icm20948_write_mems_reg(s, REG_LP_CONFIG, 1, &data);
}
这个函数其实就是上边那段代码加上实际写入寄存器。
/* secondary cycle mode should be set all the time */data = BIT_I2C_MST_CYCLE|BIT_ACCEL_CYCLE|BIT_GYRO_CYCLE;
估计是老版本中直接是在inv_icm20948_initialize_lower_driver函数中设置的,后来多了个Low Noise(LN)模式,就单独封了inv_icm20948_set_lowpower_or_highperformance函数,但是忘记把上边这一段代码删掉了。从这里以及整体工程代码的混乱格式就能感觉出来,要么这个DMP库代码赶时间,对于代码规范做得不够细致;要么这个项目是Invensense找的外包做的,实现了功能就得,对于其它不大要求。总之,以Linux内核代码规范标准来评判,整个工程代码在格式上“一门都没有”。
书归正题,此处由于传给inv_icm20948_set_lowpower_or_highperformance函数的实参是0,因此走的是0分支。
// Set default mode to low power moderesult |= inv_icm20948_set_lowpower_or_highperformance(s, 0);
至此,inv_icm20948_set_lowpower_or_highperformance函数就解析完了。下一回继续解析inv_icm20948_initialize_lower_driver函数中的其余部分。