在咱们地磁传感器里的开发板:
开发板上的地磁传感器型号是QMC5883L,它也是使用I2C与ESP32通信,I2C地址为0X0D。这个项目,我们使用地磁传感器QMC5883L计算方位角,最终,把开发板放平到桌子上,旋转开发板一周,输出0~359°的数值到串口终端
在这个项目里,我们首先写的是编写QMC5883L驱动程序,我们还是使用sample project作为模板,我们复制esp-idf-v5.1.3\examples\get-started\sample_project这个工程到我们的实验文件夹,然后把这个文件夹的名称修改为azimuth,azimuth是方位角的意思哈,在VSCode软件,然后打开azimuth文件夹
第一步还像我们往常一样,我们先打开azimuth工程目录下的CMakeList.txt文件,修改工程的名称为azimuth,然后保存关闭此文件
project(azimuth)
在这个项目中我们需要用到I2C通信,现在我们把温湿度例程里面的myi2c.h和myi2c.c文件复制到Attitude工程中的main目录下,这个是在电脑上完成复制和粘贴
我们点开main目录下的CMakeLists.txt文件,可以看到myi2c.c文件已经添加到编译路径,然后我们在main目录下新建2个文件,分别是qmc5883l.c和qmc5883l.h文件
idf_component_register(SRCS "myi2c.c" "main.c"
INCLUDE_DIRS ".")
然后我们再点开main目录下的CMakeLists.txt文件,确认一下qmc5883l.c文件有没有被添加到路径
idf_component_register(SRCS "qmc5883l.c" "myi2c.c" "main.c"
INCLUDE_DIRS ".")
点击打开qmc5883l.h文件,在最上面添加#pragma once
#pragma once
点击打开qmc5883l.c文件,在最上面添加包含qmc5883l.h文件
#include "qmc5883l.h"
点击打开main.c文件,添加头文件
#include "myi2c.h"
#include "qmc5883l.h"
在app_main函数中,先调用I2C初始化函数
void app_main(void)
{
ESP_ERROR_CHECK(i2c_master_init());
ESP_LOGI(TAG, "I2C initialized successfully");
}
函数里使用到了ESP_LOGI,需要包含esp_log.h头文件
#include "esp_log.h"
还需要给ESP_LOGI里面的TAG定义一下
static const char *TAG = "MAIN";
接下来,开始写qmc5883l的驱动函数,我们先写两个读取qmc5883l寄存器的函数和写入qmc5883l寄存器的函数。写入函数用于配置传感器的参数,读取函数用于读取传感器的寄存器数据,例如ID号,状态等。把这两个函数放到qmc5883l.c文件中
esp_err_t qmc5883L_register_read(uint8_t reg_addr, uint8_t *data, size_t len)
{
return i2c_master_write_read_device(I2C_MASTER_NUM, QMC5883L_SENSOR_ADDR, ®_addr, 1, data, len, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
} //该函数返回一个 esp_err_t 类型的错误码,表示操作是否成功,reg_addr: 要读取的寄存器地址,data: 用于存储读取数据的缓冲区指针,len: 要读取的数据长度(字节数)
//i2c_master_write_read_device 函数实现读取操作;I2C_MASTER_NUM: I2C总线编号
//QMC5883L_SENSOR_ADDR: QMC5883L传感器的I2C地址;®_addr: 要读取的寄存器地址
//1: 寄存器地址的长度(1字节);data: 用于存储读取数据的缓冲区指针
//len: 要读取的数据长度(字节数);I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS: I2C操作的超时时间
esp_err_t qmc5883L_register_write_byte(uint8_t reg_addr, uint8_t data)//该函数返回一个 esp_err_t 类型的错误码,表示操作是否成功;reg_addr: 要写入的寄存器地址;data: 要写入寄存器的数据
{
uint8_t write_buf[2] = {reg_addr, data};
return i2c_master_write_to_device(I2C_MASTER_NUM, QMC5883L_SENSOR_ADDR, write_buf, sizeof(write_buf), I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
}
//i2c_master_write_to_device 函数实现写入操作;I2C_MASTER_NUM: I2C总线编号
//QMC5883L_SENSOR_ADDR: QMC5883L传感器的I2C地址;write_buf: 包含寄存器地址和数据的缓冲区
//write_buf[0] = reg_addr: 寄存器地址;write_buf[1] = data: 要写入的数据
//sizeof(write_buf): 缓冲区的长度(2字节);I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS: I2C操作的超时时间
然后我们在qmc5883l.c文件中添加这两个函数需要的头文件
#include "driver/i2c.h"
#include "myi2c.h"
函数里面用到了QMC5883L_SENSOR_ADDR,我们在qmc5883l.h文件中定义一下
#define QMC5883L_SENSOR_ADDR 0x0D
接下来,我们需要写一个qmc5883l初始化函数,用于读取ID号,配置加速度、陀螺仪范围等参数。这个函数涉及到了qmc5883l的寄存器,所以我们先用枚举类型定义寄存器,放到qmc5883l.h文件中
enum qmc5883l_reg
{
QMC5883L_XOUT_L,
QMC5883L_XOUT_H,
QMC5883L_YOUT_L,
QMC5883L_YOUT_H,
QMC5883L_ZOUT_L,
QMC5883L_ZOUT_H,
QMC5883L_STATUS,
QMC5883L_TOUT_L,
QMC5883L_TOUT_H,
QMC5883L_CTRL1,
QMC5883L_CTRL2,
QMC5883L_FBR,
QMC5883L_CHIPID = 13
};
结合QMC5883L的数据手册中的寄存器定义表格,写出这个枚举定义。枚举类型的第一个值默认是0,和寄存器XOUT_L的地址一样,所以不用标出,然后依次递增,遇到地址不连续的寄存器地址时,单独标出,最后的结果如上代码所示
接下来写qmc5883l初始化函数到qmc5883l.c文件
void qmc5883l_init(void)
{
uint8_t id = 0; //用于存储从传感器读取的ID值
qmc5883L_register_read(QMC5883L_CHIPID, &id ,1); //读取QMC5883L传感器的ID寄存器,并将结果存储在 id 变量中
while (id != 0xff) // 确定ID号是否正确
{
vTaskDelay(1000 / portTICK_PERIOD_MS); //延迟1000毫秒(1秒),然后再次读取ID
qmc5883L_register_read(QMC5883L_CHIPID, &id ,1); //再次读取ID寄存器
}
ESP_LOGI(TAG, "QMC5883L OK!"); //如果ID值正确(等于0xff),打印一条信息,表示传感器初始化成功
qmc5883L_register_write_byte(QMC5883L_CTRL2, 0x80); // 复位芯片
vTaskDelay(10 / portTICK_PERIOD_MS); //延迟10毫秒,等待复位完成
qmc5883L_register_write_byte(QMC5883L_CTRL1, 0x05); //Continuous模式 50Hz
qmc5883L_register_write_byte(QMC5883L_CTRL2, 0x00); //向控制寄存器2写入0x00,清除复位标志
qmc5883L_register_write_byte(QMC5883L_FBR, 0x01); //向滤波器带宽寄存器写入0x01,配置滤波器带宽
}
初始化函数里面,首先读取qmc5883l的ID号,如果不正确,就继续读,如果正确,往下执行。确定qmc5883l没有问题,先复位芯片,然后进行配置。CTRL1,配置成了连续采集模式,输出速率50Hz。CTRL2,可以用来配置是否复位以及数据读取方式。FBR寄存器,数据手册推荐写入0x01
函数里面用到了ESP_LOGI,用来输出信息,这里的TAG,需要定义。我们把这个TAG定义,放到qmc5883l.c文件中的包含头文件的下面
static const char *TAG = "QMC5883L";
函数里面使用了freeRTOS的延时函数,所以需要包含freeRTOS头文件,放到qmc5883l.c文件中
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
函数里面也用到了ESP_LOGI,所以还需要添加log头文件
#include "esp_log.h"
现在我们把这个函数的声明写到qmc5883l.h文件
extern void qmc5883l_init(void);
接下来我们在main.c文件中的app_main函数中调用这个初始化函数
void app_main(void)
{
ESP_ERROR_CHECK(i2c_master_init());
ESP_LOGI(TAG, "I2C initialized successfully");
qmc5883l_init();
}