【超详细】磁力计数据读取--以IST8310为例

这里以iSentek公司产出的IST8310型号的磁力计为例,尺寸为 3.0 * 3.0 * 1.0mm,支持快速 I2C 通信,可达 400kHz,14 位磁场数据,测量范围可达1600uT(x,y-axis)和 2500uT(z-axis), 最高 200Hz 输出频率

同时这里的磁力计是安装在大疆公司出产的RoboMaster开发板C型,单片机芯片是STM32F407IGH6,其外围电路已经设计好,只需要读取磁力计数据即可。

本篇不会介绍SPI、I2C等嵌入式通信协议,需要有一定嵌入式开发基础的同学来看

文末附代码

零、数据手册分析

第一章讲了IST8310的特性,I2C通信,最高支持400kHz通信速率,14位或者16位自适应数据输出等特性

image-20221203143646334

第二章讲了内部结构

image-20221203143810184

第三章讲了电气特性

image-20221203143859175

第四章讲了如何联系他们

image-20221203144121246

然后数据手册就没了。。。在大疆的开发手册中找到了寄存器的手册,如下:

image-20221203165413897

image-20221203165444657

image-20221203165507558

image-20221203165822701

image-20221203165838781

image-20221203165849045

一、CubeMX配置

点开I2C配置选项, 下图是配好的:

image-20221203165955297

观察IST8310的数据手册,发现其支持最大400kHz的I2C通信速率,也就是快速I2C模式,所以第一行I2C Speed Mode我们选Fast Mode

image-20221203170120553

同时不要忘记了在C板中I2C3的两个IO口分别是PA8和PC9(一般来说都是这两个)

image-20221203170657996

之后观察大疆和IST8310的数据手册,发现控制IST8310重启的是PG6的GPIO口,低电平为重启,所以我们将其设置为高电平上拉输出模式

image-20221203170402663

image-20221203170428263

image-20221203170517446

image-20221203170602068

因为我们读取IST8310的程序运行在1kHz的freertos线程中,无需使用中断方式,所以我们不配置中断口

二、数据读取

总代码附在文末,这里放一些核心函数

IST8310初始化:

void IST8310_INIT(ist8310_data_t* ist8310_data) {
    memset(ist8310_data, 0, sizeof(ist8310_data_t));

    ist8310_data->meg_error = IST8310_NO_ERROR;

    // 把磁力计重启
    HAL_GPIO_WritePin(IST8310_GPIOx, IST8310_GPIOp, GPIO_PIN_RESET);
    HAL_Delay(50);
    HAL_GPIO_WritePin(IST8310_GPIOx, IST8310_GPIOp, GPIO_PIN_SET);
    HAL_Delay(50);

    // 基础配置
    // 不使能中断,直接读取
    WriteSingleDataFromIST8310(IST8310_CNTL2_ADDR, IST8310_STAT2_NONE_ALL);
    // 平均采样四次
    WriteSingleDataFromIST8310(IST8310_AVGCNTL_ADDR, IST8310_AVGCNTL_FOURTH);
    // 200Hz的输出频率
    WriteSingleDataFromIST8310(IST8310_CNTL1_ADDR, IST8310_CNTL1_CONTINUE);

    ist8310_data->meg_error |= VerifyMegId(&ist8310_data->chip_id);
}

读取单个数据:

uint8_t ReadSingleDataFromIST8310(uint8_t addr) {
    uint8_t data;
    HAL_I2C_Mem_Read(&IST8310_I2C, (IST8310_I2C_ADDR << 1), addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 10);
    return data;
}

读取多个数据:

void ReadMultiDataFromIST8310(uint8_t addr, uint8_t* data, uint8_t len) {
    HAL_I2C_Mem_Read(&IST8310_I2C, (IST8310_I2C_ADDR << 1), addr, I2C_MEMADD_SIZE_8BIT, data, len, 10);
}

写入单个数据:

void WriteSingleDataFromIST8310(uint8_t addr, uint8_t data) {
    HAL_I2C_Mem_Write(&IST8310_I2C, (IST8310_I2C_ADDR << 1), addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 10);
}

写入多个数据:

void WriteMultiDataFromIST8310(uint8_t addr, uint8_t* data, uint8_t len) {
    HAL_I2C_Mem_Write(&IST8310_I2C, (IST8310_I2C_ADDR << 1), addr, I2C_MEMADD_SIZE_8BIT, data, len, 10);
}

这里会发现一个比较有意思的事情,就是这里的地址都左移了一位,是因为根据I2C协议,[7:0]的一个字节的数据,前七位是地址,后一位是代表读或者写的位,这样子就需要把地址左移

读取磁力计数据:

void ReadIST8310Data(ist8310_raw_data_t* meg_data) {
    uint8_t buf[6];
    int16_t temp_ist8310_data = 0;
    ReadMultiDataFromIST8310(IST8310_DATA_XL_ADDR, buf, 6);
    temp_ist8310_data = (int16_t)((buf[1] << 8) | buf[0]);
    meg_data->x = MAG_SEN * temp_ist8310_data;
    temp_ist8310_data = (int16_t)((buf[3] << 8) | buf[2]);
    meg_data->y = MAG_SEN * temp_ist8310_data;
    temp_ist8310_data = (int16_t)((buf[5] << 8) | buf[4]);
    meg_data->z = MAG_SEN * temp_ist8310_data;
}

这里乘了一个系数MAG_SEN,它的值是0.3,是将读取到的数据转化为单位为uT的磁场值

下面就是源码,把IST8310_INIT()函数放在程序开始的地方,然后剩下的读取函数放在不断执行的线程里,就可以得到磁力计数据了

ist8310.c

/**
 * @Author         : Minghang Li
 * @Date           : 2022-12-03 14:29
 * @LastEditTime   : 2022-12-03 16:52
 * @Note           :
 * @Copyright(c)   : Minghang Li Copyright
 */
#include "ist8310.h"

#include <string.h>

#include "i2c.h"
#include "ist8310reg.h"

void IST8310_INIT(ist8310_data_t* ist8310_data) {
    memset(ist8310_data, 0, sizeof(ist8310_data_t));

    ist8310_data->meg_error = IST8310_NO_ERROR;

    // 把磁力计重启
    HAL_GPIO_WritePin(IST8310_GPIOx, IST8310_GPIOp, GPIO_PIN_RESET);
    HAL_Delay(50);
    HAL_GPIO_WritePin(IST8310_GPIOx, IST8310_GPIOp, GPIO_PIN_SET);
    HAL_Delay(50);

    // 基础配置
    // 不使能中断,直接读取
    WriteSingleDataFromIST8310(IST8310_CNTL2_ADDR, IST8310_STAT2_NONE_ALL);
    // 平均采样四次
    WriteSingleDataFromIST8310(IST8310_AVGCNTL_ADDR, IST8310_AVGCNTL_FOURTH);
    // 200Hz的输出频率
    WriteSingleDataFromIST8310(IST8310_CNTL1_ADDR, IST8310_CNTL1_CONTINUE);

    ist8310_data->meg_error |= VerifyMegId(&ist8310_data->chip_id);
}

uint8_t ReadSingleDataFromIST8310(uint8_t addr) {
    uint8_t data;
    HAL_I2C_Mem_Read(&IST8310_I2C, (IST8310_I2C_ADDR << 1), addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 10);
    return data;
}

void WriteSingleDataFromIST8310(uint8_t addr, uint8_t data) {
    HAL_I2C_Mem_Write(&IST8310_I2C, (IST8310_I2C_ADDR << 1), addr, I2C_MEMADD_SIZE_8BIT, &data, 1, 10);
}

void ReadMultiDataFromIST8310(uint8_t addr, uint8_t* data, uint8_t len) {
    HAL_I2C_Mem_Read(&IST8310_I2C, (IST8310_I2C_ADDR << 1), addr, I2C_MEMADD_SIZE_8BIT, data, len, 10);
}

void WriteMultiDataFromIST8310(uint8_t addr, uint8_t* data, uint8_t len) {
    HAL_I2C_Mem_Write(&IST8310_I2C, (IST8310_I2C_ADDR << 1), addr, I2C_MEMADD_SIZE_8BIT, data, len, 10);
}

void ReadIST8310Data(ist8310_raw_data_t* meg_data) {
    uint8_t buf[6];
    int16_t temp_ist8310_data = 0;
    ReadMultiDataFromIST8310(IST8310_DATA_XL_ADDR, buf, 6);
    temp_ist8310_data = (int16_t)((buf[1] << 8) | buf[0]);
    meg_data->x = MAG_SEN * temp_ist8310_data;
    temp_ist8310_data = (int16_t)((buf[3] << 8) | buf[2]);
    meg_data->y = MAG_SEN * temp_ist8310_data;
    temp_ist8310_data = (int16_t)((buf[5] << 8) | buf[4]);
    meg_data->z = MAG_SEN * temp_ist8310_data;
}

ist8310_error_e VerifyMegId(uint8_t* id) {
    *id = ReadSingleDataFromIST8310(IST8310_CHIP_ID_ADDR);
    if (*id != IST8310_CHIP_ID_VAL) {
        return MEG_ID_ERROR;
    } else {
        return IST8310_NO_ERROR;
    }
}

ist8310.h

/**
 * @Author         : Minghang Li
 * @Date           : 2022-12-03 14:29
 * @LastEditTime   : 2022-12-03 16:49
 * @Note           :
 * @Copyright(c)   : Minghang Li Copyright
 */
#pragma once

#include <stdint.h>

typedef struct ist8310_raw_data_t {
    float x;
    float y;
    float z;
} ist8310_raw_data_t;

typedef enum ist8310_error_e {
    IST8310_NO_ERROR = 0x00,
    MEG_ID_ERROR = 0x01,
} ist8310_error_e;

typedef struct ist8310_data_t {
    uint8_t chip_id;
    ist8310_raw_data_t meg_data;
    ist8310_error_e meg_error;
} ist8310_data_t;

/*-----整形向uT转换-----*/
#define MAG_SEN 0.3f

/*-----I2C接口定义-----*/
#define IST8310_I2C_ADDR 0x0E
#define IST8310_I2C hi2c3

/*-----GPIO口定义-----*/
#define IST8310_GPIOx GPIOG
#define IST8310_GPIOp GPIO_PIN_6

void IST8310_INIT(ist8310_data_t* ist8310_data);

// 基础读取函数
uint8_t ReadSingleDataFromIST8310(uint8_t addr);
void WriteSingleDataFromIST8310(uint8_t addr, uint8_t data);
void ReadMultiDataFromIST8310(uint8_t addr, uint8_t* data, uint8_t len);
void WriteMultiDataFromIST8310(uint8_t addr, uint8_t* data, uint8_t len);

// 功能函数
void ReadIST8310Data(ist8310_raw_data_t* meg_data);

// 校验函数
ist8310_error_e VerifyMegId(uint8_t* id);

ist8310reg.h

/**
 * @Author         : Minghang Li
 * @Date           : 2022-12-03 15:27
 * @LastEditTime   : 2022-12-03 16:53
 * @Note           :
 * @Copyright(c)   : Minghang Li Copyright
 */
#pragma once

/*-----IST8310寄存器地址-----*/
#define IST8310_CHIP_ID_ADDR 0x00
#define IST8310_CHIP_ID_VAL 0x10

#define IST8310_STAT1_ADDR 0x02

#define IST8310_DATA_XL_ADDR 0x03
#define IST8310_DATA_XH_ADDR 0x04
#define IST8310_DATA_YL_ADDR 0x05
#define IST8310_DATA_YH_ADDR 0x06
#define IST8310_DATA_ZL_ADDR 0x07
#define IST8310_DATA_ZH_ADDR 0x08

#define IST8310_STAT2_ADDR 0x09

#define IST8310_CNTL1_ADDR 0x0A
#define IST8310_CNTL1_SLEEP 0x00
#define IST8310_CNTL1_SINGLE 0x01
#define IST8310_CNTL1_CONTINUE 0x0B

#define IST8310_CNTL2_ADDR 0x0B
#define IST8310_STAT2_NONE_ALL 0x00

#define IST8310_SELF_CHECK_ADDR 0x0C

#define IST8310_TEMPL_ADDR 0x1C
#define IST8310_TEMPH_ADDR 0x1D

#define IST8310_AVGCNTL_ADDR 0x41
#define IST8310_AVGCNTL_TWICE 0x09
#define IST8310_AVGCNTL_FOURTH 0x12

效果如下:
image-20221203171539401

以下是IST8310磁力计的示例程序,该程序可以与Arduino板配合使用: ```cpp #include <Wire.h> #define IST8310_ADDRESS 0x0E #define IST8310_DEVICE_ID_A 0x10 #define IST8310_DEVICE_ID_B 0x11 #define IST8310_DEVICE_ID_C 0x12 #define IST8310_REG_ID 0x00 #define IST8310_REG_DATA_OUT_X_L 0x03 #define IST8310_REG_DATA_OUT_X_H 0x04 #define IST8310_REG_DATA_OUT_Y_L 0x05 #define IST8310_REG_DATA_OUT_Y_H 0x06 #define IST8310_REG_DATA_OUT_Z_L 0x07 #define IST8310_REG_DATA_OUT_Z_H 0x08 #define IST8310_REG_CNTL1 0x0A #define IST8310_REG_CNTL2 0x0B #define IST8310_REG_AVGCNTL 0x41 // Initialize IST8310 void IST8310_init() { Wire.beginTransmission(IST8310_ADDRESS); Wire.write(IST8310_REG_CNTL1); Wire.write(0x01); Wire.endTransmission(); delay(200); } // Read IST8310 data void IST8310_read(int16_t* x, int16_t* y, int16_t* z) { uint8_t data[6]; // Read data from IST8310 Wire.beginTransmission(IST8310_ADDRESS); Wire.write(IST8310_REG_DATA_OUT_X_L); Wire.endTransmission(false); Wire.requestFrom(IST8310_ADDRESS, 6, true); for (int i = 0; i < 6; i++) { data[i] = Wire.read(); } // Convert data *x = ((int16_t)data[1] << 8) | data[0]; *y = ((int16_t)data[3] << 8) | data[2]; *z = ((int16_t)data[5] << 8) | data[4]; } // Print IST8310 data void IST8310_print() { int16_t x, y, z; IST8310_read(&x, &y, &z); Serial.print("x: "); Serial.print(x); Serial.print(", y: "); Serial.print(y); Serial.print(", z: "); Serial.println(z); } void setup() { Serial.begin(9600); Wire.begin(); IST8310_init(); } void loop() { IST8310_print(); delay(100); } ``` 需要注意的是,该程序需要使用Wire库实现I2C通信。该程序中IST8310_init()函数用于初始化IST8310传感器,IST8310_read()函数用于读取IST8310传感器的数据IST8310_print()函数用于打印IST8310传感器的数据。在setup()函数中初始化串口和Wire库,然后调用IST8310_init()函数进行初始化。在loop()函数中循环调用IST8310_print()函数进行打印。
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值