持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第29天,点击查看活动详情
本文介绍在 BLE 协议栈下如何读写mpu6050。
接口介绍
mpu 6050 使用 twi 接口。该接口有多种实现API。twi_master_transfer
、nrf_drv_twi_tx
。
在SDK目录components\drivers_ext\mpu6050
中有对应驱动的实现。
寄存器
寄存器说明可参考文章MPU6050寄存器介绍。 本节简单罗列出寄存器,重要的稍加说明。
陀螺仪采样率分频寄存器(0X19)
配置寄存器(0X1A)
低3字节的DLPF(低通滤波器)表示。
陀螺仪配置寄存器(0X1B)
FS_SEL(Bit3~Bit4)决定了陀螺仪的量程。一般使用2000度/秒(正负),对应的值为3。由于ADC为16比特,因此灵敏度为65536/4000=16.384度/秒(LSB)。
加速度传感器配置寄存器(0X1C)
和陀螺仪类似,AFS_SEL(Bit3~Bit4)决定了加速度的量程。一般设置为2g(正负),对应的值为0。由于ADC为16比特,因此灵敏度为65536/4=16384LSB/g。
FIFO使能寄存器(0X23)
加速度传感器数据输出寄存器(0X3B~0X40)
加速度数据共6个寄存器,分别输出X、Y、Z轴的加速度值,高字节在前,低字节在后,读取时需要注意。
温度传感器数据输出寄存器(0X41~0X42)
温度数据共2个寄存器,分别为高字节和低字节,读取时要使用附带的公式进行转换,否则数据不正确。
陀螺仪数据输出寄存器(0X43~0X48)
和加速度数据类似,陀螺仪也为6个寄存器,分别输出X、Y、Z轴的值,高字节在前,低字节在后,读取时需要注意。
电源管理寄存器1(0X6B)
电源管理寄存器2(0X6C)
数值转换
加速度计换算:
假定加速度计量程为-2~+2。数字-32767对应-2g,32767对应2g。把32767除以2,得到16384, 即为灵敏度。将读到的数值除以16384,可得到加速度的数值。如读到的数值为1000,则对应的加速度数据是1000/16384=0.061g。g为加速度的单位(1g等于9.8米每平方秒)。在水平静止时,Z轴的加速度为1g(对应数值为16384),X、Y轴为0。
加速度计换算:
假定陀螺仪角速度量程为-2000~+2000。MPU6050输出数据为16位带符号数,数据类型为int16,范围为-32767~32767。最小值-32767对应-2000度/秒,32767对应2000度/秒。用32767除以2000,就可以得到16.40, 其即为灵敏度。因此,把陀螺仪数值除以16.40,即得到对应的角速度数值。如读到的数值为1000,则对应的角速度为1000/16.40=61度每秒。在一些姿态计算中,需要将角度值换算成弧度值。360度对应弧度是2Pi,则1度换算成弧度为:2Pi/360=(2*3.1415926)/360=0.0174532,用倒数表示就是:1/57.30。
小结:陀螺仪角速度量程为-2000~+2000范围时,可以通过下面的公式转换:
gyro_x/(16.40 * 57.30)=gyro_x * 0.001064 // gyro_x 为陀螺仪 X 轴原始数据
复制代码
接口实现
nRF52832 使用 TWI0 与 pmu6050 通信。
输出数据方面,使用结构体封装,将加速度、温度、陀螺仪数据一并读取,因为这些寄存器都是连接的。
初始化
MPU6050的总线地址如下:
- AD0为低电平:0x68
- AD0为高电平:0x69
板子上AD0连接的是低电平,因此地址为0x68。
初始化twi0:
static volatile bool m_xfer_done = false;
// TWI0
static const nrf_drv_twi_t m_twi = NRF_DRV_TWI_INSTANCE(0);
//TWI事件处理函数
void twi_handler(nrf_drv_twi_evt_t const * p_event, void * p_context)
{
//判断TWI事件类型
switch (p_event->type)
{
case NRF_DRV_TWI_EVT_DONE:
m_xfer_done = true;
break;
default:
break;
}
}
//TWI初始化
void twi_master_init(void)
{
ret_code_t err_code;
const nrf_drv_twi_config_t twi_config = {
.scl = TWI_SCL_M,
.sda = TWI_SDA_M,
.frequency = NRF_DRV_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.clear_bus_init = false
};
err_code = nrf_drv_twi_init(&m_twi, &twi_config, twi_handler, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&m_twi);
}
复制代码
imu寄存器读写及初始化:
bool mpu6050_register_write(uint8_t register_address, uint8_t value)
{
ret_code_t err_code;
uint8_t tx_buf[MPU6050_ADDRESS_LEN+1];
tx_buf[0] = register_address;
tx_buf[1] = value;
m_xfer_done = false;
err_code = nrf_drv_twi_tx(&m_twi, MPU6050_ADDRESS, tx_buf, MPU6050_ADDRESS_LEN+1, false);
while (m_xfer_done == false) { }
if (NRF_SUCCESS != err_code)
{
return false;
}
return true;
}
bool mpu6050_register_read(uint8_t register_address, uint8_t * destination, uint8_t number_of_bytes)
{
ret_code_t err_code;
m_xfer_done = false;
err_code = nrf_drv_twi_tx(&m_twi, MPU6050_ADDRESS, ®ister_address, 1, true);
while (m_xfer_done == false) { }
if (NRF_SUCCESS != err_code)
{
return false;
}
m_xfer_done = false;
err_code = nrf_drv_twi_rx(&m_twi, MPU6050_ADDRESS, destination, number_of_bytes);
while (m_xfer_done == false) { }
if (NRF_SUCCESS != err_code)
{
return false;
}
return true;
}
bool mpu6050_verify_product_id(void)
{
uint8_t who_am_i;
if (mpu6050_register_read(ADDRESS_WHO_AM_I, &who_am_i, 1))
{
if (who_am_i != MPU6050_WHO_AM_I)
{
return false;
}
else
{
return true;
}
}
else
{
return false;
}
}
bool mpu6050_init(void)
{
if(mpu6050_verify_product_id() == false)
{
return false;
}
//唤醒MPU6050
(void)mpu6050_register_write(MPU_PWR_MGMT1_REG , 0x00);
(void)mpu6050_register_write(MPU_SAMPLE_RATE_REG, 0x07); //设置采样率 1KHz
(void)mpu6050_register_write(MPU_CFG_REG, 0x06); //设置低通滤波器,截止频率是1K,带宽是5K
(void)mpu6050_register_write(MPU_INT_EN_REG, 0x00); //关闭中断
(void)mpu6050_register_write(MPU_GYRO_CFG_REG, 0x18); //陀螺仪自检及测量范围,不自检,2000deg/s
(void)mpu6050_register_write(MPU_ACCEL_CFG_REG, 0x00); //加速度传感器量程 2G,不自检
return true;
}
复制代码
数据读取
结构体定义:
#pragma pack(push)
#pragma pack(1)
typedef struct imuinfo_tt {
int16_t acc_x;
int16_t acc_y;
int16_t acc_z;
int16_t temp;
int16_t gyro_x;
int16_t gyro_y;
int16_t gyro_z;
} imuinfo_t;
typedef struct imuinfod_tt {
double acc_x;
double acc_y;
double acc_z;
double temp;
double gyro_x;
double gyro_y;
double gyro_z;
} imuinfod_t;
typedef struct imuangle_tt {
double angle_x;
double angle_y;
double angle_z;
} imuangle_t;
#pragma pack(pop)
int mpu6050_readraw(imuinfo_t* info)
{
bool ret = false;
// ret = mpu6050_register_read(MPU6050_ACC_OUT, (uint8_t *)&info, sizeof(imuinfo_t));
uint8_t buf[14] = {0};
ret = mpu6050_register_read(MPU6050_ACC_OUT, buf, sizeof(buf));
if (ret == false) return -1;
info->acc_x = (buf[0] << 8) | buf[1];
info->acc_y = (buf[2] << 8) | buf[3];
info->acc_z = (buf[4] << 8) | buf[5];
info->temp = (buf[6] << 8) | buf[7];
info->gyro_x = (buf[8] << 8) | buf[9];
info->gyro_y = (buf[10] << 8) | buf[11];
info->gyro_z = (buf[12] << 8) | buf[13];
return 0;
}
int mpu6050_readrawbuff(int16_t* info)
{
bool ret = false;
uint8_t buf[14] = {0};
ret = mpu6050_register_read(MPU6050_ACC_OUT, (uint8_t*)&buf, sizeof(buf));
if (ret == false) return -1;
info[0] = (buf[0] << 8) | buf[1];
info[1] = (buf[2] << 8) | buf[3];
info[2] = (buf[4] << 8) | buf[5];
info[3] = (buf[6] << 8) | buf[7];
info[4] = (buf[8] << 8) | buf[9];
info[5] = (buf[10] << 8) | buf[11];
info[6] = (buf[12] << 8) | buf[13];
return 0;
}
复制代码
注意,mpu6050_readrawbuff接口主要为了后续校准作准备。