发帖
0 0 0

FreeRTOS学习-02-i2c

干簧管
论坛元老

22

主题

1265

回帖

1万

积分

论坛元老

积分
10627
Ai-M61系列 100 0 2026-5-3 17:21:24

使用i2c采集MPU6500的原始加速度和陀螺仪数据

复制之前的 01-pwm 工程目录,重命名为02-i2c,需要修改下述FW bin名称否则烧录有问题

AiPi-Open-Kits\FreeRTOS_study\01-pwm\flash_prog_cfg.ini

image.png

同时创建 driver和include目录

image.png

CMakelist这里需要按照这样修改,不然会导致编译报错或者链接时报错

image.png

i2c_driver.c

#include "i2c_driver.h"
#include "bflb_gpio.h"
#include "bflb_i2c.h"
#include <stdio.h>

static struct bflb_device_s *i2c0;       //i2c0外设句柄

bool i2c_init(void) 
{
    // 1. 配置I2C引脚 (SCL, SDA)
    // 2. 初始化I2C控制器,设置时钟频率 (通常400kHz)
    // bflb_i2c_init(...);

    struct bflb_device_s* gpio;

    gpio = bflb_device_get_by_name("gpio");
    /* I2C0_SCL */
    bflb_gpio_init(gpio, GPIO_PIN_0, GPIO_FUNC_I2C0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);
    /* I2C0_SDA */
    bflb_gpio_init(gpio, GPIO_PIN_1, GPIO_FUNC_I2C0 | GPIO_ALTERNATE | GPIO_PULLUP | GPIO_SMT_EN | GPIO_DRV_1);
    i2c0 = bflb_device_get_by_name("i2c0");
    bflb_i2c_init(i2c0, 400000);

    printf("[I2C] Init successful.\n");
    return true;
}

bool i2c_write_reg(uint8_t devaddr,uint8_t reg,uint8_t reglen,uint8_t *datas,uint16_t datalen)
{
    struct bflb_i2c_msg_s msgs[2];
    msgs[0].addr = devaddr;
    msgs[0].flags = I2C_M_NOSTOP;
    msgs[0].buffer = ®
    msgs[0].length = reglen;

    msgs[1].addr = devaddr;
    msgs[1].flags = 0;
    msgs[1].buffer = datas;
    msgs[1].length = datalen;
    if(reglen > 0)
        return bflb_i2c_transfer(i2c0, msgs, 2) == 0?0:1;
    else
        return bflb_i2c_transfer(i2c0, &msgs[1], 1) == 0?0:1;
}

bool i2c_read_reg(uint8_t devaddr,uint8_t reg,uint8_t reglen,uint8_t *datas,uint16_t datalen)
{
    struct bflb_i2c_msg_s msgs[2];
    msgs[0].addr = devaddr;
    msgs[0].flags = I2C_M_NOSTOP;
    msgs[0].buffer = ®
    msgs[0].length = reglen;

    msgs[1].addr = devaddr;
    msgs[1].flags = I2C_M_READ;
    msgs[1].buffer = datas;
    msgs[1].length = datalen;
    if(reglen > 0)
        return bflb_i2c_transfer(i2c0, msgs, 2) == 0?0:1;
    else
        return bflb_i2c_transfer(i2c0, &msgs[1], 1) == 0?0:1;
}

i2c_driver.h

#ifndef I2C_DRIVER_H
#define I2C_DRIVER_H

#include <stdint.h>
#include <stdbool.h>

/**
 * @brief I2C初始化
 * @return true 成功, false 失败
 */
bool i2c_init(void);

/**
 * @brief I2C写入寄存器
 * @param dev_addr 设备地址 (7-bit)
 * @param reg_addr 寄存器地址
 * @param data 数据指针
 * @param len 数据长度
 * @return true 成功, false 失败
 */
bool i2c_write_reg(uint8_t devaddr,uint8_t reg,uint8_t reglen,uint8_t *datas,uint16_t datalen);

/**
 * @brief I2C读取寄存器
 * @param dev_addr 设备地址 (7-bit)
 * @param reg_addr 寄存器地址
 * @param data 数据缓冲区
 * @param len 数据长度
 * @return true 成功, false 失败
 */
bool i2c_read_reg(uint8_t devaddr,uint8_t reg,uint8_t reglen,uint8_t *datas,uint16_t datalen);

#endif /* I2C_DRIVER_H */

mpu6500.c


#include "mpu6500.h"
#include "i2c_driver.h"
#include <stdio.h>

static bool mpu6500_write_byte(uint8_t reg, uint8_t value) {
    printf("[MPU6500] mpu6500_write_byte %x\n",reg);
    return i2c_write_reg(MPU6500_I2C_ADDR, reg, 1,&value, 1);
}

static bool mpu6500_read_bytes(uint8_t reg, uint8_t *buf, uint16_t len) {
    //printf("[MPU6500] mpu6500_read_bytes %x\n",reg);
    return i2c_read_reg(MPU6500_I2C_ADDR, reg, 1,buf, len);
}

bool mpu6500_init(void) {
    uint8_t who_am_i = 0;
    uint8_t i=0;
    printf("[MPU6500] mpu6500_init\n");

    /*for (i=0;i<256;i++)   //当不知道设备地址时可以做扫描动作
    {
        i2c_read_reg(i, MPU6500_WHO_AM_I, 1,&who_am_i, 1);
        if (who_am_i !=0)
        {
            printf("[MPU6500] I2C addr is %x,ID is %x\n",i,who_am_i);
            break;
        }

    }*/

    if (mpu6500_read_bytes(MPU6500_WHO_AM_I, &who_am_i, 1)) {
        printf("[MPU6500] I2C Read Failed\n");
        return false;
    }
    printf("[MPU6500] ID: 0x%02X\n", who_am_i);
    if (who_am_i != 0x70 && who_am_i != 0x71) { // 6500通常是0x70或0x71
        printf("[MPU6500] Wrong ID: 0x%02X\n", who_am_i);
        return false;
    }
    // 2. 唤醒传感器 (清除睡眠位)
    mpu6500_write_byte(MPU6500_PWR_MGMT_1, 0x00);
  
    // 3. 配置陀螺仪量程 (+/- 250 dps)
    mpu6500_write_byte(MPU6500_GYRO_CONFIG, 0x00);
  
    // 4. 配置加速度计量程 (+/- 2g)
    mpu6500_write_byte(MPU6500_ACCEL_CONFIG, 0x00);

    printf("[MPU6500] Init Success\n");
    return true;
}

bool mpu6500_read_raw(mpu6500_data_t *data) {

    uint8_t buf[14];
    // 读取加速度(6字节) + 温度(2字节) + 陀螺仪(6字节)
    if (mpu6500_read_bytes(MPU6500_ACCEL_XOUT_H, buf, 14)) {
        return false;
    }

    data->ax = (int16_t)(buf[0] << 8 | buf[1]);
    data->ay = (int16_t)(buf[2] << 8 | buf[3]);
    data->az = (int16_t)(buf[4] << 8 | buf[5]);

    int16_t temp_raw = (int16_t)((buf[6] << 8) | buf[7]);
    data->temp = ((float)temp_raw / 340.0f) + 36.53f; // 温度公式

    data->gx = (int16_t)(buf[8] << 8 | buf[9]);
    data->gy = (int16_t)(buf[10] << 8 | buf[11]);
    data->gz = (int16_t)(buf[12] << 8 | buf[13]);
    return true;
}

mpu6500.h

#ifndef MPU6500_H
#define MPU6500_H

#include <stdint.h>
#include <stdbool.h>

// MPU6500 默认I2C地址
#define MPU6500_I2C_ADDR        0x68
#define MPU6500_WHO_AM_I        0x75
#define MPU6500_PWR_MGMT_1      0x6B
#define MPU6500_GYRO_CONFIG     0x1B
#define MPU6500_ACCEL_CONFIG    0x1C
#define MPU6500_ACCEL_XOUT_H    0x3B

// 数据结构
typedef struct {
    int16_t ax, ay, az;
    int16_t gx, gy, gz;
    float   temp;
} mpu6500_data_t;

/**
 * @brief 初始化MPU6500
 * @return true 成功, false 失败
 */
bool mpu6500_init(void);

/**
 * @brief 读取原始数据
 * @param data 输出数据结构体指针
 * @return true 成功, false 失败
 */
bool mpu6500_read_raw(mpu6500_data_t *data);

#endif /* MPU6500_H */

main.c

#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include "mpu6500.h"
#include "i2c_driver.h"

// 定义传感器数据任务句柄
TaskHandle_t xMpuTaskHandle = NULL;

/**
 * @brief MPU6500数据采集任务
 * @param pvParameters 任务参数
 */
void vMpuDataTask(void *pvParameters) {
    mpu6500_data_t sensor_data;
    TickType_t xLastWakeTime;
    const TickType_t xFrequency = pdMS_TO_TICKS(10); // 10ms周期, 100Hz采样率

    // 初始化硬件
    i2c_init();
    if (!mpu6500_init()) {
        printf("[ERROR] MPU6500 Init Failed! Task Suspended.\n");
        vTaskSuspend(NULL);
    }

    // 初始化延时基准时间
    xLastWakeTime = xTaskGetTickCount();

    printf("[INFO] MPU6500 Data Task Started.\n");

    for (;;) {
        // 严格等间隔调度,避免主循环抖动影响控制算法
        vTaskDelayUntil(&xLastWakeTime, xFrequency);
        if (mpu6500_read_raw(&sensor_data)) {
            // 数据处理:打印或送入滤波算法/姿态解算
            printf("Ax: %d, Ay: %d, Az: %d | Gx: %d, Gy: %d, Gz: %d | Temp: %.2f\n",
                   sensor_data.ax, sensor_data.ay, sensor_data.az,
                   sensor_data.gx, sensor_data.gy, sensor_data.gz,
                   sensor_data.temp);
        } else {
            printf("[WARN] Read Error\n");
        }
    }
}

/**
 * @brief 系统入口函数 (BL616 SDK通常入口为 main)
 */
int main(void) {
    board_init();
    // 创建MPU6500数据采集任务
    // 栈大小根据实际需求调整,优先级设为较高以保证实时性
    xTaskCreate(vMpuDataTask, "MpuData", 2048, NULL, 5, &xMpuTaskHandle);

    // 启动调度器
    vTaskStartScheduler();

    // 如果调度器启动失败,会运行到这里
    for (;;) {
        printf("[ERROR] FreeRTOS Scheduler Failed\n");
    }
  
    return 0;
}

串口助手采集的数据

image.png

──── 0人觉得很赞 ────

使用道具 举报

您需要登录后才可以回帖 立即登录
高级模式
返回
统计信息
  • 会员数: 30957 个
  • 话题数: 44900 篇