接前一篇文章:ICM20948 DMP代码详解(19)
本回继续对inv_icm20948_read_mems_reg函数的其余内容进行解析。为了便于理解和回顾,再次贴出inv_icm20948_read_mems_reg函数源码,在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Transport.c中,如下:
/**
* @brief Read data from a register on MEMs.
* @param[in] Register address
* @param[in] Length of data
* @param[in] Data to be written
* @return 0 if successful.
*/
int inv_icm20948_read_mems_reg(struct inv_icm20948 * s, uint16_t reg, unsigned int length, unsigned char *data)
{
int result = 0;
unsigned int bytesRead = 0;
unsigned char regOnly = (unsigned char)(reg & 0x7F);
unsigned char i, dat[INV_MAX_SERIAL_READ];
unsigned char power_state = inv_icm20948_get_chip_power_state(s);
if((power_state & CHIP_AWAKE) == 0) // Wake up chip since it is asleep
result = inv_icm20948_set_chip_power_state(s, CHIP_AWAKE, 1);
if(check_reg_access_lp_disable(s, reg)) // Check if register needs LP_EN to be disabled
result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 0); //Disable LP_EN
result |= inv_set_bank(s, reg >> 7);
while (bytesRead<length)
{
int thisLen = min(INV_MAX_SERIAL_READ, length-bytesRead);
if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) {
result |= inv_icm20948_read_reg(s, regOnly+bytesRead, &dat[bytesRead], thisLen);
} else {
result |= inv_icm20948_read_reg(s, regOnly+bytesRead, &data[bytesRead],thisLen);
}
if (result)
return result;
bytesRead += thisLen;
}
if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) {
for (i=0; i< length; i++) {
*data= dat[i];
data++;
}
}
if(check_reg_access_lp_disable(s, reg)) // Check if register needs LP_EN to be enabled
result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1); //Enable LP_EN
return result;
}
接下来进入while循环,开始数据的接收。代码片段如下:
while (bytesRead<length)
{
int thisLen = min(INV_MAX_SERIAL_READ, length-bytesRead);
if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) {
result |= inv_icm20948_read_reg(s, regOnly+bytesRead, &dat[bytesRead], thisLen);
} else {
result |= inv_icm20948_read_reg(s, regOnly+bytesRead, &data[bytesRead],thisLen);
}
if (result)
return result;
bytesRead += thisLen;
}
先讲一下regOnly,它在inv_icm20948_read_mems_reg函数的开头处赋值:
unsigned char regOnly = (unsigned char)(reg & 0x7F);
代码中是将寄存器和其所属的bank联合起来进行表示的,bit0~6是寄存器,bit7~8代表其所属的bank。这个regOnly表示的就是不带bank、只带寄存器的值。也就是取下图中红色框中的部分:
回到上边while循环。一开始bytesRead的值为0,length的值为1,因此会进入循环。
thisLen为INV_MAX_SERIAL_READ与length-bytesRead(1-0=1)的较小值。INV_MAX_SERIAL_READ宏在EMD-Core\sources\Invn\Devices\Drivers\ICM20948\Icm20948Transport.h中定义,如下:
/** @brief Max size that can be read across I2C or SPI data lines */
#define INV_MAX_SERIAL_READ 16
/** @brief Max size that can be written across I2C or SPI data lines */
#define INV_MAX_SERIAL_WRITE 16
那么thisLen的值这里就是1。
接下来走I2C的分支:
else
{
result |= inv_icm20948_read_reg(s, regOnly+bytesRead, &data[bytesRead], thisLen);
}
inv_ivm20948_read_reg函数前边已经讲过多次,在此不赘述。直接看一下传给它的实参:
regOnly+bytesRead就是regOnly;&data[bytesRead]第一次进入循环的时候是&data[0];thisLen的值是1。这句代码的实际意义就是读取USER_CTRL寄存器,读取1个字节,保存到&data[0]。也就是获取USER_CTRL寄存器的内容。
由于只读取1个字节的内容,因此读一次之后就跳出while循环了。
接下来来到以下代码片段:
if(s->base_state.serial_interface == SERIAL_INTERFACE_SPI) {
for (i=0; i< length; i++) {
*data= dat[i];
data++;
}
}
对于SPI接口来说,还要从dat[]到data[]“倒”一下。笔者不太理解为何要这样做,不过当前是用I2C接口,也不用“深抠”这一块了。
接下来就到最后一段了:
if(check_reg_access_lp_disable(s, reg)) // Check if register needs LP_EN to be enabled
result |= inv_icm20948_set_chip_power_state(s, CHIP_LP_ENABLE, 1); //Enable LP_EN
check_reg_access_lp_disable函数上一回讲过,对于USER_CTRL寄存器,总是返回1,会执行inv_icm20948_set_chip_power_state函数。这一句代码的意思是恢复置位LP_EN,也即恢复低功耗使能状态。
至此,inv_icm20948_read_mems_reg函数就都解析完了。其功能概括如下:
唤醒芯片;退出低功耗模式;设置(选择)bank;读取指定寄存器数据;恢复低功耗模式。
下一回将回到inv_icm20948_initialize_lower_driver函数中,继续往下解析接下来的内容。