瑞萨单片机论坛
直播中

qiao

未满1年用户 11经验值
擅长:嵌入式技术 控制/MCU
私信 关注
[经验]

【瑞萨RA6E2】硬件IIC驱动九轴传感器与OLED显示

VID20251127020926

一、项目概述

1.1 项目简介

本次试用基于瑞萨RA6E2开发板,通过硬件IIC协议驱动GY-85九轴传感器模块(包含ITG3205陀螺仪、ADXL345加速度计和QMC5883P磁力计),并在OLED屏幕上实时显示传感器采集的原始数据。项目直接使用现有的完整代码实现,未对传感器数据进行滤波处理,仅获取和显示基本数据,部分数据来源与AI不保证100%准确。

1.2 目标

  • 学习&验证RA6E2硬件IIC接口的稳定性和可靠性
  • 实现多传感器数据的采集和显示
  • 测试系统的实时性能和基本功能

二、硬件平台

2.1 核心开发板

  • 主控芯片 :RA6E2 (Cortex-M33内核,200MHz主频)
  • 开发环境 :e² studio + FSP (Flexible Software Package)

2.2 传感器模块

传感器 型号 通信协议 测量范围
陀螺仪 ITG3205 I2C ±2000°/s
加速度计 ADXL345 I2C ±16g
磁力计 QMC5883P I2C ±8 Gauss

2.3 显示模块

  • 型号 :SSD1315 OLED
  • 分辨率 :128×64像素
  • 通信接口 :I2C

三、软件架构

3.1 系统架构

基于现有代码实现的系统架构:
text

+───────────────────+    +───────────────────+    +───────────────────+
│    传感器数据采集    │    │    数据处理与转换   │    │    OLED显示管理  │
│   - ITG3205陀螺仪   │───▶│   - 原始数据解析  │───▶│   - 多页面显示  │
│   - ADXL345加速度计  │    │   - 单位转换       │    │   - 按键切换     │
│   - QMC5883P磁力计   │    │                   │    │   - 实时刷新     │
+───────────────────+    +───────────────────+    +───────────────────+
           │                        │                        │
           └────────────────────────┼────────────────────────┘
                                    │
                          +───────────────────+
                          │    硬件IIC驱动层   │
                          │   - 多设备管理     │
                          │   - 回调处理       │
                          +───────────────────+

3.2 代码模块说明

现有代码包含的模块:

3.2.1. 头文件包含和类型定义模块
#include "hal_data.h"

// 类型定义简化
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
const unsigned char  asc2_0806[][6] ={/* 6*8 ascii*/};
// OLED命令和数据模式定义
#define OLED_CMD  0x00
#define OLED_DATA 0x40

// I2C设备地址定义
#define OLED_ADDR 0x3C
#define ITG3205_ADDR 0xD0
#define ADXL345_ADDR 0xA6
#define QMC5883P_ADDR 0x2C

// GPIO操作宏定义
#define WRITE_GPIO_PIN(pin, level) \
    R_IOPORT_PinWrite(&g_ioport_ctrl, pin, level)

#define READ_GPIO_PIN(pin) \
    ({ \
        bsp_io_level_t level; \
        R_IOPORT_PinRead(&g_ioport_ctrl, pin, &level); \
        level; \
    })
3.2.2. I2C通信模块
// I2C通信状态变量
static volatile bool g_i2c_complete = false;    // I2C传输完成标志
static fsp_err_t g_i2c_result = FSP_SUCCESS;   // I2C操作结果

/**
 * [url=home.php?mod=space&uid=2666770]@Brief[/url] I2C主控回调函数
 * [url=home.php?mod=space&uid=3142012]@param[/url] p_args 回调参数指针
 * [url=home.php?mod=space&uid=1902110]@NOTE[/url] 处理I2C传输完成和错误事件
 */
void sci_i2c_master_callback(i2c_master_callback_args_t * p_args)
{
    if (NULL != p_args)
    {
        switch (p_args->event)
        {
            case I2C_MASTER_EVENT_TX_COMPLETE:  // 发送完成事件
            case I2C_MASTER_EVENT_RX_COMPLETE:  // 接收完成事件
                g_i2c_complete = true;
                g_i2c_result = FSP_SUCCESS;
                break;
                
            case I2C_MASTER_EVENT_ABORTED:      // 传输中止事件
                g_i2c_complete = true;
                g_i2c_result = FSP_ERR_ABORTED;
                break;
                
            default:
                break;
        }
    }
}		

/**
 * @brief OLED I2C初始化
 * @note 初始化OLED的I2C通信接口
 */
void I2C_SCI_OLED_Init(void)
{
    fsp_err_t err = FSP_SUCCESS;
    err = R_SCI_I2C_Open(&g_i2c_sci_oled_ctrl, &g_i2c_sci_oled_cfg);
    if (FSP_SUCCESS != err)
    {
        while(1);  // 初始化失败,进入死循环
    }
}

/**
 * @brief 加速度计I2C初始化
 * @note 初始化ADXL345的I2C通信接口
 */
void I2C_SCI_ACCEL_Init(void)
{
    fsp_err_t err = FSP_SUCCESS;
    err = R_SCI_I2C_Open(&g_i2c_sci_accel_ctrl, &g_i2c_sci_accel_cfg);
    if (FSP_SUCCESS != err)
    {
        while(1);
    }
}

/**
 * @brief 陀螺仪I2C初始化
 * @note 初始化ITG3205的I2C通信接口
 */
void I2C_SCI_GYRO_Init(void)
{
    fsp_err_t err = FSP_SUCCESS;
    err = R_SCI_I2C_Open(&g_i2c_sci_gyro_ctrl, &g_i2c_sci_gyro_cfg);
    if (FSP_SUCCESS != err)
    {
        while(1);
    }
}

/**
 * @brief 磁力计I2C初始化
 * @note 初始化QMC5883P的I2C通信接口
 */
void I2C_SCI_MAG_Init(void)
{
    fsp_err_t err = FSP_SUCCESS;
    err = R_SCI_I2C_Open(&g_i2c_sci_mag_ctrl, &g_i2c_sci_mag_cfg);
    if (FSP_SUCCESS != err)
    {
        while(1);
    }
}

/**
 * @brief I2C写数据函数
 * @param dev_addr 设备地址
 * @param mode 命令/数据模式
 * @param dat 要写入的数据
 * @note 向指定I2C设备写入数据
 */
void IIC_Write(u8 dev_addr, u8 mode, u8 dat)
{
    fsp_err_t err = FSP_SUCCESS;
    uint8_t write_buffer[2] = {mode, dat};  // 数据缓冲区
    
    g_i2c_complete = false;
    
    // 根据设备地址选择对应的I2C控制器
    if(dev_addr == OLED_ADDR)
    {
        I2C_SCI_OLED_Init();
        err = R_SCI_I2C_Write(&g_i2c_sci_oled_ctrl, write_buffer, 2, false);
    }
    else if(dev_addr == ITG3205_ADDR)
    {
        I2C_SCI_GYRO_Init();
        err = R_SCI_I2C_Write(&g_i2c_sci_gyro_ctrl, write_buffer, 2, false);
    }
    else if(dev_addr == ADXL345_ADDR)
    {
        I2C_SCI_ACCEL_Init();
        err = R_SCI_I2C_Write(&g_i2c_sci_accel_ctrl, write_buffer, 2, false);
    }
    else if(dev_addr == QMC5883P_ADDR)
    {
        I2C_SCI_MAG_Init();
        err = R_SCI_I2C_Write(&g_i2c_sci_mag_ctrl, write_buffer, 2, false);
    }

    if (FSP_SUCCESS != err)
    {
        while(1);  // 写入失败,进入死循环
    }
    
    // 等待传输完成
    while (!g_i2c_complete)
    {
        __NOP();  // 空操作,等待中断
    }
}

/**
 * @brief I2C读数据函数
 * @param dev_addr 设备地址
 * @param reg_addr 寄存器地址
 * [url=home.php?mod=space&uid=1141835]@Return[/url] 读取到的数据
 * @note 从指定I2C设备的寄存器读取数据
 */
u8 IIC_Read(u8 dev_addr, u8 reg_addr)
{  
    fsp_err_t err = FSP_SUCCESS;
    u8 reg_data = 0;
    
    uint8_t write_buffer[1] = {reg_addr};  // 寄存器地址缓冲区
    
    g_i2c_complete = false;
    
    // 先写入要读取的寄存器地址
    if(dev_addr == OLED_ADDR)
    {
        I2C_SCI_OLED_Init();
        err = R_SCI_I2C_Write(&g_i2c_sci_oled_ctrl, write_buffer, 1, false);
    }
    else if(dev_addr == ITG3205_ADDR)
    {
        I2C_SCI_GYRO_Init();
        err = R_SCI_I2C_Write(&g_i2c_sci_gyro_ctrl, write_buffer, 1, false);
    }
    else if(dev_addr == ADXL345_ADDR)
    {
        I2C_SCI_ACCEL_Init();
        err = R_SCI_I2C_Write(&g_i2c_sci_accel_ctrl, write_buffer, 1, false);
    }
    else if(dev_addr == QMC5883P_ADDR)
    {
        I2C_SCI_MAG_Init();
        err = R_SCI_I2C_Write(&g_i2c_sci_mag_ctrl, write_buffer, 1, false);
    }

    if (FSP_SUCCESS != err)
    {
        while(1);
    }
    
    // 等待写入完成
    while (!g_i2c_complete)
    {
        __NOP();
    }
    
    uint8_t read_buffer[1] = {0};  // 读取数据缓冲区
    
    g_i2c_complete = false;
    
    // 读取数据
    if(dev_addr == OLED_ADDR)
    {
        err = R_SCI_I2C_Read(&g_i2c_sci_oled_ctrl, read_buffer, 1, false);
    }
    else if(dev_addr == ITG3205_ADDR)
    {
        err = R_SCI_I2C_Read(&g_i2c_sci_gyro_ctrl, read_buffer, 1, false);
    }
    else if(dev_addr == ADXL345_ADDR)
    {
        I2C_SCI_ACCEL_Init();
        err = R_SCI_I2C_Read(&g_i2c_sci_accel_ctrl, read_buffer, 1, false);
    }
    else if(dev_addr == QMC5883P_ADDR)
    {
        I2C_SCI_MAG_Init();
        err = R_SCI_I2C_Read(&g_i2c_sci_mag_ctrl, read_buffer, 1, false);
    }
    
    if (FSP_SUCCESS != err)
    {
        while(1);
    }
    
    // 等待读取完成
    while (!g_i2c_complete)
    {
        __NOP();
    }
    
    reg_data = read_buffer[0];
    return reg_data;
}
3.2.3. OLED显示驱动模块
/**
 * @brief 设置OLED显示位置
 * @param x 横坐标 (0-127)
 * @param y 纵坐标 (0-7,每页8行)
 * @note 设置OLED的GRAM写入位置
 */
void OLED_Set_Pos(u8 x, u8 y) 
{ 
    IIC_Write(OLED_ADDR, OLED_CMD, 0xb0 + y);           // 设置页地址
    IIC_Write(OLED_ADDR, OLED_CMD, ((x & 0xf0) >> 4) | 0x10);  // 设置列地址高4位
    IIC_Write(OLED_ADDR, OLED_CMD, (x & 0x0f));        // 设置列地址低4位
}       

/**
 * @brief 计算幂函数
 * @param m 底数
 * @param n 指数
 * @return m的n次方
 * @note 用于数字显示时的位权计算
 */
u32 oled_pow(u8 m, u8 n)
{
    u32 result = 1;    
    while(n--) result *= m;    
    return result;
}                  

/**
 * @brief 显示单个字符
 * @param x 起始横坐标
 * @param y 起始纵坐标
 * @param chr 要显示的字符
 * @param sizey 字符高度(8或16)
 * @note 在指定位置显示指定大小的ASCII字符
 */
void OLED_ShowChar(u8 x, u8 y, u8 chr, u8 sizey)
{          
    u8 c = 0, sizex = sizey / 2;  // 字符宽度为高度的一半
    u16 i = 0, size1;
    
    // 计算字符数据大小
    if(sizey == 8) 
        size1 = 6;      // 6*8字符
    else 
        size1 = (sizey / 8 + ((sizey % 8) ? 1 : 0)) * (sizey / 2);  // 16*8字符
    
    c = chr - ' ';  // 计算字符在字库中的索引
    
    OLED_Set_Pos(x, y);
    
    // 逐字节写入字符数据
    for(i = 0; i < size1; i++)
    {
        // 16像素字符需要换行
        if(i % sizex == 0 && sizey != 8) 
            OLED_Set_Pos(x, y++);
        
        // 根据字符大小选择字库
        if(sizey == 8) 
            IIC_Write(OLED_ADDR, OLED_DATA, asc2_0806[c][i]);
        else if(sizey == 16) 
            IIC_Write(OLED_ADDR, OLED_DATA, asc2_1608[c][i]);
        else 
            return;
    }
}

/**
 * @brief 显示数字
 * @param x 起始横坐标
 * @param y 起始纵坐标
 * @param num 要显示的数字
 * @param len 数字长度
 * @param sizey 字符高度
 * @note 在指定位置显示指定长度的数字
 */
void OLED_ShowNum(u8 x, u8 y, u32 num, u8 len, u8 sizey)
{             
    u8 t, temp, m = 0;
    u8 enshow = 0;  // 前导零抑制标志
    
    if(sizey == 8) 
        m = 2;  // 8像素字符间距调整
    
    // 逐位显示数字
    for(t = 0; t < len; t++)
    {
        temp = (num / oled_pow(10, len - t - 1)) % 10;  // 提取当前位数字
        
        // 前导零抑制处理
        if(enshow == 0 && t < (len - 1))
        {
            if(temp == 0)
            {
                OLED_ShowChar(x + (sizey / 2 + m) * t, y, ' ', sizey);
                continue;
            }
            else 
                enshow = 1;
        }
        
        OLED_ShowChar(x + (sizey / 2 + m) * t, y, temp + '0', sizey);
    }
}

/**
 * @brief 显示字符串
 * @param x 起始横坐标
 * @param y 起始纵坐标
 * @param chr 字符串指针
 * @param sizey 字符高度
 * @note 在指定位置显示字符串
 */
void OLED_ShowString(u8 x, u8 y, u8 *chr, u8 sizey)
{
    u8 j = 0;
    while (chr[j] != '\0')
    {       
        OLED_ShowChar(x, y, chr[j++], sizey);
        // 根据字符大小调整下一个字符位置
        if(sizey == 8) 
            x += 6;      // 8像素字符宽度+间距
        else 
            x += sizey / 2;  // 16像素字符宽度
    }
}

/**
 * @brief 清屏函数
 * @note 清除OLED所有显示内容
 */
void OLED_Clear(void)  
{  
    u8 i, n;            
    for(i = 0; i < 8; i++)  // 遍历所有页(8页)
    {  
        OLED_Set_Pos(0, i);
        for(n = 0; n < 128; n++) 
            IIC_Write(OLED_ADDR, OLED_DATA, 0);  // 写入0清除像素
    }
}

/**
 * @brief OLED初始化
 * @note 初始化OLED显示控制器,配置各种显示参数
 */
void OLED_Init(void)
{
    // OLED初始化命令序列
    IIC_Write(OLED_ADDR, OLED_CMD, 0xAE);  // 关闭显示
    IIC_Write(OLED_ADDR, OLED_CMD, 0x00);  // 设置低列地址
    IIC_Write(OLED_ADDR, OLED_CMD, 0x10);  // 设置高列地址
    IIC_Write(OLED_ADDR, OLED_CMD, 0x40);  // 设置起始行地址
    IIC_Write(OLED_ADDR, OLED_CMD, 0x81);  // 对比度设置
    IIC_Write(OLED_ADDR, OLED_CMD, 0xCF);  // 对比度值(0-255)
    IIC_Write(OLED_ADDR, OLED_CMD, 0xA1);  // 设置段重映射
    IIC_Write(OLED_ADDR, OLED_CMD, 0xC8);  // 设置COM扫描方向
    IIC_Write(OLED_ADDR, OLED_CMD, 0xA6);  // 设置正常显示
    IIC_Write(OLED_ADDR, OLED_CMD, 0xA8);  // 设置多路复用率
    IIC_Write(OLED_ADDR, OLED_CMD, 0x3f);  // 多路复用值
    IIC_Write(OLED_ADDR, OLED_CMD, 0xD3);  // 设置显示偏移
    IIC_Write(OLED_ADDR, OLED_CMD, 0x00);  // 显示偏移值
    IIC_Write(OLED_ADDR, OLED_CMD, 0xd5);  // 设置显示时钟分频
    IIC_Write(OLED_ADDR, OLED_CMD, 0x80);  // 分频比值
    IIC_Write(OLED_ADDR, OLED_CMD, 0xD9);  // 设置预充电周期
    IIC_Write(OLED_ADDR, OLED_CMD, 0xF1);  // 预充电周期值
    IIC_Write(OLED_ADDR, OLED_CMD, 0xDA);  // 设置COM硬件配置
    IIC_Write(OLED_ADDR, OLED_CMD, 0x12);  // COM配置值
    IIC_Write(OLED_ADDR, OLED_CMD, 0xDB);  // 设置VCOMH电平
    IIC_Write(OLED_ADDR, OLED_CMD, 0x40);  // VCOMH值
    IIC_Write(OLED_ADDR, OLED_CMD, 0x20);  // 设置内存地址模式
    IIC_Write(OLED_ADDR, OLED_CMD, 0x02);  // 页地址模式
    IIC_Write(OLED_ADDR, OLED_CMD, 0x8D);  // 电荷泵设置
    IIC_Write(OLED_ADDR, OLED_CMD, 0x14);  // 启用电荷泵
    IIC_Write(OLED_ADDR, OLED_CMD, 0xA4);  // 全部像素点开启
    IIC_Write(OLED_ADDR, OLED_CMD, 0xA6);  // 设置正常显示
    
    OLED_Clear();  // 清屏
    IIC_Write(OLED_ADDR, OLED_CMD, 0xAF);  // 开启显示
}

/**
 * @brief 开启显示
 * @note 唤醒OLED显示
 */
void OLED_Display_On(void)
{
    IIC_Write(OLED_ADDR, OLED_CMD, 0xAF);
}

/**
 * @brief 关闭显示
 * @note 进入OLED睡眠模式
 */
void OLED_Display_Off(void)
{
    IIC_Write(OLED_ADDR, OLED_CMD, 0xAE);
}

/**
 * @brief 颜色反转设置
 * @param mode 0-正常显示, 1-颜色反转
 * @note 设置OLED显示颜色模式
 */
void OLED_ColorTurn(u8 mode)
{
    if(mode == 0)
        IIC_Write(OLED_ADDR, OLED_CMD, 0xA6);  // 正常显示
    else
        IIC_Write(OLED_ADDR, OLED_CMD, 0xA7);  // 颜色反转
}

/**
 * @brief 显示方向设置
 * @param mode 0-正常方向, 1-旋转180度
 * @note 设置OLED显示方向
 */
void OLED_DisplayTurn(u8 mode)
{
    if(mode == 0)
    {
        IIC_Write(OLED_ADDR, OLED_CMD, 0xC8);  // 正常扫描方向
        IIC_Write(OLED_ADDR, OLED_CMD, 0xA1);  // 正常段重映射
    }
    else
    {
        IIC_Write(OLED_ADDR, OLED_CMD, 0xC0);  // 反转扫描方向
        IIC_Write(OLED_ADDR, OLED_CMD, 0xA0);  // 反转段重映射
    }
}

/**
 * @brief 显示BMP图片
 * @param x0 起始横坐标
 * @param y0 起始纵坐标
 * @param x1 结束横坐标
 * @param y1 结束纵坐标
 * @param BMP 图片数据数组
 * @note 在指定区域显示位图
 */
void OLED_DrawBMP(u8 x0, u8 y0, u8 x1, u8 y1, const u8 BMP[])
{
    u16 j = 0;
    u8 x, y;
    
    // 计算页数
    if(y1 % 8 == 0) 
        y = y1 / 8;
    else 
        y = y1 / 8 + 1;
    
    // 逐页逐列写入图片数据
    for(y = y0; y < y1; y++)
    {
        OLED_Set_Pos(x0, y);
        for(x = x0; x < x1; x++)
        {
            IIC_Write(OLED_ADDR, OLED_DATA, BMP[j++]);
        }
    }
}

/**
 * @brief 显示中文字符
 * @param x 起始横坐标
 * @param y 起始纵坐标
 * @param index 汉字在字库中的索引
 * @param sizey 字符高度(16)
 * @note 在指定位置显示16*16中文字符
 */
void OLED_ShowChinese(u8 x, u8 y, u8 index, u8 sizey)
{
    u8 i;
    u8 x0 = x;  // 保存起始x坐标
    
    OLED_Set_Pos(x, y);
    
    if(sizey == 16)
    {
        // 显示汉字上半部分(16字节)
        for(i = 0; i < 16; i++)
        {
            IIC_Write(OLED_ADDR, OLED_DATA, Hzk[index][i]);
            x++;
            if((x - x0) == 16)  // 换行
            {
                x = x0;
                y++;
                OLED_Set_Pos(x, y);
            }
        }
        
        OLED_Set_Pos(x, y);
        
        // 显示汉字下半部分(16字节)
        for(i = 16; i < 32; i++)
        {
            IIC_Write(OLED_ADDR, OLED_DATA, Hzk[index][i]);
            x++;
            if((x - x0) == 16)  // 换行
            {
                x = x0;
                y++;
                OLED_Set_Pos(x, y);
            }
        }
    }
}
3.2.4. 传感器驱动模块
/**
 * @brief 初始化ITG3205陀螺仪
 * @note 配置陀螺仪采样率、量程和滤波器等参数
 */
void Init_ITG3205()
{
   // 重置设备
   IIC_Write(ITG3205_ADDR, 0x3E, 0x80);  
   R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
   
   // 设置采样率分频器 - 采样率 = 1kHz / (7+1) = 125Hz
   IIC_Write(ITG3205_ADDR, 0x15, 0x07);  
   
   // 配置DLPF和全量程 - 设置为最高精度配置
   // Bit[4:3] = 11 (FS_SEL=3, ±2000°/s), Bit[2:0] = 010 (DLPF_CFG=2, 98Hz带宽)
   // 0x1E = 00011110 -> FS_SEL=3, DLPF_CFG=2
   IIC_Write(ITG3205_ADDR, 0x16, 0x1E);  
   
   // 中断配置 - 禁用中断
   IIC_Write(ITG3205_ADDR, 0x17, 0x00); 
   
   // 电源管理 - 选择PLL with Z Gyro作为时钟源(最高稳定性)
   // CLK_SEL = 3 (PLL with Z Gyro reference)
   IIC_Write(ITG3205_ADDR, 0x3E, 0x03);  
   
   R_BSP_SoftwareDelay(50, BSP_DELAY_UNITS_MILLISECONDS);  // 等待稳定
}

/**
 * @brief 初始化ADXL345加速度计
 * @note 配置加速度计量程、数据率和测量模式
 */
void Init_ADXL345()
{
   // 设置数据格式:全分辨率模式,±16g范围
   // 0x0B = 00001011: SELF_TEST=0, SPI=0, INT_INVERT=0, FULL_RES=1, Justify=0, Range=11(±16g)
   IIC_Write(ADXL345_ADDR, 0x31, 0x0B);
   
   // 设置带宽和输出数据率:100Hz(平衡精度和响应速度)
   // 0x0A = 00001010: LOW_POWER=0, Rate=1010 (100Hz)
   IIC_Write(ADXL345_ADDR, 0x2C, 0x0A);
   
   // 电源控制:进入测量模式
   // 0x08 = 00001000: Link=0, AUTO_SLEEP=0, Measure=1, Sleep=0, Wakeup=00
   IIC_Write(ADXL345_ADDR, 0x2D, 0x08);
   
   // 中断使能:使能数据就绪中断(可选)
   IIC_Write(ADXL345_ADDR, 0x2E, 0x80);
   
   // 偏移校准(根据实际测量调整)
   IIC_Write(ADXL345_ADDR, 0x1E, 0x00);  // X轴偏移
   IIC_Write(ADXL345_ADDR, 0x1F, 0x00);  // Y轴偏移  
   IIC_Write(ADXL345_ADDR, 0x20, 0x00);  // Z轴偏移(先设为0,根据实际测量校准)
   
   // 等待稳定
   R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
}

/**
 * @brief 初始化QMC5883P磁力计
 * @note 配置磁力计工作模式和输出数据率
 */
void Init_QMC5883P(void)
{
    IIC_Write(QMC5883P_ADDR, 0x0a, 0xff);  // 配置寄存器A
    IIC_Write(QMC5883P_ADDR, 0x0b, 0x01);  // 配置寄存器B
}
3.2.5. 传感器数据显示模块
/**
 * @brief 显示陀螺仪数据
 * @note 读取并显示ITG3205的三轴角速度数据
 */
void oledDisplayGyro(void)
{
    float gyro_x, gyro_y, gyro_z;
    char disp_buf[16];
    u8 BUF[6];
    int16_t raw_x, raw_y, raw_z;
    
    // 读取陀螺仪原始数据(16位有符号)
    // X轴: 0x1D(高), 0x1E(低)
    BUF[0] = IIC_Read(ITG3205_ADDR, 0x1D); // GYRO_XOUT_H
    BUF[1] = IIC_Read(ITG3205_ADDR, 0x1E); // GYRO_XOUT_L
    
    // Y轴: 0x1F(高), 0x20(低)  
    BUF[2] = IIC_Read(ITG3205_ADDR, 0x1F); // GYRO_YOUT_H
    BUF[3] = IIC_Read(ITG3205_ADDR, 0x20); // GYRO_YOUT_L
    
    // Z轴: 0x21(高), 0x22(低)
    BUF[4] = IIC_Read(ITG3205_ADDR, 0x21); // GYRO_ZOUT_H
    BUF[5] = IIC_Read(ITG3205_ADDR, 0x22); // GYRO_ZOUT_L
    
    // 组合16位数据(有符号)
    raw_x = (int16_t)((BUF[0] << 8) | BUF[1]);
    raw_y = (int16_t)((BUF[2] << 8) | BUF[3]);
    raw_z = (int16_t)((BUF[4] << 8) | BUF[5]);
    
    // 转换为度/秒 (灵敏度: 14.375 LSB/(°/s))
    gyro_x = (float)raw_x / 14.375f;
    gyro_y = (float)raw_y / 14.375f;
    gyro_z = (float)raw_z / 14.375f;
    
    // 显示标题
    OLED_ShowString(40, 1, (u8*)"Gyroscope", 8); 
    
    // 显示X轴数据
    OLED_ShowString(0, 3, (u8*)"X-axis:", 8);
    if(gyro_x >= 0)
        sprintf(disp_buf, "+%07.2f dps", gyro_x);
    else
        sprintf(disp_buf, "%08.2f dps", gyro_x);
    OLED_ShowString(50, 3, (u8*)disp_buf, 8);
    
    // 显示Y轴数据
    OLED_ShowString(0, 5, (u8*)"Y-axis:", 8);
    if(gyro_y >= 0)
        sprintf(disp_buf, "+%07.2f dps", gyro_y);
    else
        sprintf(disp_buf, "%08.2f dps", gyro_y);
    OLED_ShowString(50, 5, (u8*)disp_buf, 8);
    
    // 显示Z轴数据
    OLED_ShowString(0, 7, (u8*)"Z-axis:", 8);
    if(gyro_z >= 0)
        sprintf(disp_buf, "+%07.2f dps", gyro_z);
    else
        sprintf(disp_buf, "%08.2f dps", gyro_z);
    OLED_ShowString(50, 7, (u8*)disp_buf, 8);
    
    R_BSP_SoftwareDelay(60, BSP_DELAY_UNITS_MILLISECONDS);  // 显示延时
}

/**
 * @brief 显示加速度计数据
 * @note 读取并显示ADXL345的三轴加速度数据
 */
void oledDisplayAccel(void)
{
    float accel_x, accel_y, accel_z;
    char disp_buf[16];
    u8 BUF[6];
    int16_t raw_x, raw_y, raw_z;
    
    // 读取加速度数据寄存器
    BUF[0] = IIC_Read(ADXL345_ADDR, 0x32); // DATAX0 - X轴低字节
    BUF[1] = IIC_Read(ADXL345_ADDR, 0x33); // DATAX1 - X轴高字节
    BUF[2] = IIC_Read(ADXL345_ADDR, 0x34); // DATAY0 - Y轴低字节
    BUF[3] = IIC_Read(ADXL345_ADDR, 0x35); // DATAY1 - Y轴高字节
    BUF[4] = IIC_Read(ADXL345_ADDR, 0x36); // DATAZ0 - Z轴低字节
    BUF[5] = IIC_Read(ADXL345_ADDR, 0x37); // DATAZ1 - Z轴高字节
    
    // 组合16位数据(有符号)
    raw_x = (int16_t)((BUF[1] << 8) | BUF[0]);
    raw_y = (int16_t)((BUF[3] << 8) | BUF[2]);
    raw_z = (int16_t)((BUF[5] << 8) | BUF[4]);
    
    // 转换为加速度值(mg)
    // 全分辨率模式,±16g范围:灵敏度 = 3.9 mg/LSB
    accel_x = (float)raw_x * 3.9f;
    accel_y = (float)raw_y * 3.9f;
    accel_z = (float)raw_z * 3.9f;
    
    // 显示标题
    OLED_ShowString(30, 1, (u8*)"Accelerometer", 8); 
    
    // 显示X轴数据
    OLED_ShowString(0, 3, (u8*)"X-axis:", 8);
    if(accel_x >= 0)
        sprintf(disp_buf, "+%07.1f mg", accel_x);
    else
        sprintf(disp_buf, "%08.1f mg", accel_x);
    OLED_ShowString(50, 3, (u8*)disp_buf, 8);
    
    // 显示Y轴数据
    OLED_ShowString(0, 5, (u8*)"Y-axis:", 8);
    if(accel_y >= 0)
        sprintf(disp_buf, "+%07.1f mg", accel_y);
    else
        sprintf(disp_buf, "%08.1f mg", accel_y);
    OLED_ShowString(50, 5, (u8*)disp_buf, 8);
    
    // 显示Z轴数据
    OLED_ShowString(0, 7, (u8*)"Z-axis:", 8);
    if(accel_z >= 0)
        sprintf(disp_buf, "+%07.1f mg", accel_z);
    else
        sprintf(disp_buf, "%08.1f mg", accel_z);
    OLED_ShowString(50, 7, (u8*)disp_buf, 8);
    
    R_BSP_SoftwareDelay(60, BSP_DELAY_UNITS_MILLISECONDS); 
}

/**
 * @brief 显示磁力计数据
 * @note 读取并显示QMC5883P的三轴磁场强度数据
 */
void oledDisplayMagnetic(void)
{
    float uT_x, uT_y, uT_z;
    char disp_buf[16];
    int16_t raw_x, raw_y, raw_z;
    u8 BUF[6];
    
    // 读取磁力计原始数据
    BUF[0] = IIC_Read(QMC5883P_ADDR, 0x01);
    BUF[1] = IIC_Read(QMC5883P_ADDR, 0x02);
    BUF[2] = IIC_Read(QMC5883P_ADDR, 0x03);
    BUF[3] = IIC_Read(QMC5883P_ADDR, 0x04);
    BUF[4] = IIC_Read(QMC5883P_ADDR, 0x05);
    BUF[5] = IIC_Read(QMC5883P_ADDR, 0x06);
    
    // 组合16位数据(有符号)
    raw_x = (int16_t)((BUF[1] << 8) | BUF[0]);
    raw_y = (int16_t)((BUF[3] << 8) | BUF[2]);
    raw_z = (int16_t)((BUF[5] << 8) | BUF[4]);
    
    // 转换为微特斯拉 (灵敏度: 0.1 uT/LSB)
    uT_x = raw_x * 0.1f; 
    uT_y = raw_y * 0.1f;  
    uT_z = raw_z * 0.1f;  
    
    // 显示标题(居中显示)
    OLED_ShowString((128 - 6 * 12) / 2, 1, (u8*)"Magnetometer", 8); 
    
    // 显示X轴数据
    OLED_ShowString(0, 3, (u8*)"X-axis:", 8);
    if(uT_x >= 0)
        sprintf(disp_buf, "+%07.1f uT", uT_x);
    else
        sprintf(disp_buf, "-%07.1f uT", -uT_x);
    OLED_ShowString(50, 3, (u8*)disp_buf, 8);
    
    // 显示Y轴数据
    OLED_ShowString(0, 5, (u8*)"Y-axis:", 8);
    if(uT_y >= 0)
        sprintf(disp_buf, "+%07.1f uT", uT_y);
    else
        sprintf(disp_buf, "-%07.1f uT", -uT_y);
    OLED_ShowString(50, 5, (u8*)disp_buf, 8);
    
    // 显示Z轴数据
    OLED_ShowString(0, 7, (u8*)"Z-axis:", 8);
    if(uT_z >= 0)
        sprintf(disp_buf, "+%07.1f uT", uT_z);
    else
        sprintf(disp_buf, "-%07.1f uT", -uT_z);
    OLED_ShowString(50, 7, (u8*)disp_buf, 8);
    
    R_BSP_SoftwareDelay(60, BSP_DELAY_UNITS_MILLISECONDS);
}
3.2.6. 用户界面模块
/**
 * @brief 显示主界面
 * @note 显示传感器数据的主界面布局,包含三轴数据的标题和占位符
 */
void oledDisplayMain()
{
    // 显示标题行
    OLED_ShowString(6, 1, (u8*)"Sensor", 8);
    OLED_ShowString(50 + 9, 1, (u8*)"X", 8);
    OLED_ShowString(50 + 9 + 4 * 6 + 3, 1, (u8*)"Y", 8);
    OLED_ShowString(50 + 9 + 8 * 6 + 3 + 3, 1, (u8*)"Z", 8);
    
    // 显示陀螺仪数据行
    OLED_ShowString(0, 3, (u8*)"Gry(dps)", 8); 
    OLED_ShowString(50, 3, (u8*)"00.0", 8);    
    OLED_ShowString(50 + 4 * 6 + 3, 3, (u8*)"00.0", 8);
    OLED_ShowString(50 + 8 * 6 + 6, 3, (u8*)"00.0", 8);

    // 显示加速度计数据行
    OLED_ShowString(3, 5, (u8*)"Acc(mg)", 8); 
    OLED_ShowString(50, 5, (u8*)"00.0", 8);    
    OLED_ShowString(50 + 4 * 6 + 3, 5, (u8*)"00.0", 8);
    OLED_ShowString(50 + 8 * 6 + 6, 5, (u8*)"00.0", 8);

    // 显示磁力计数据行
    OLED_ShowString(3, 7, (u8*)"Mag(uT)", 8); 
    OLED_ShowString(50, 7, (u8*)"00.0", 8);    
    OLED_ShowString(50 + 4 * 6 + 3, 7, (u8*)"00.0", 8);
    OLED_ShowString(50 + 8 * 6 + 6, 7, (u8*)"00.0", 8);
}
3.2.7. 系统初始化和主程序模块
/**
 * @brief 系统主初始化
 * @note 初始化所有硬件模块:GPIO、OLED、传感器等
 */
void Main_Init()
{
    // 初始化GPIO引脚(按键检测)
    R_IOPORT_PinCfg(&g_ioport_ctrl, BSP_IO_PORT_00_PIN_05, IOPORT_CFG_PORT_DIRECTION_OUTPUT);
    WRITE_GPIO_PIN(BSP_IO_PORT_00_PIN_05, BSP_IO_LEVEL_HIGH);
    
    // 延时等待系统稳定
    R_BSP_SoftwareDelay(200, BSP_DELAY_UNITS_MILLISECONDS);
    
    // 初始化各硬件模块
    OLED_Init();           // 初始化OLED显示
    Init_ITG3205();       // 初始化陀螺仪
    Init_ADXL345();       // 初始化加速度计
    Init_QMC5883P();      // 初始化磁力计
    
    // 配置OLED显示参数
    OLED_Display_On();    // 开启显示
    OLED_ColorTurn(0);    // 正常颜色模式
    OLED_DisplayTurn(0);  // 正常显示方向
}

/**
 * @brief 主程序入口
 * @note 系统主循环,处理按键切换和数据显示
 */
void hal_entry(void)
{
    Main_Init();  // 系统初始化
    
    while(1) 
    {       
        static u8 i = 0;  // 显示页面索引
        
        // 检测按键按下(低电平有效)
        if(READ_GPIO_PIN(BSP_IO_PORT_00_PIN_05) == BSP_IO_LEVEL_LOW)
        {
            OLED_Clear();  // 清屏
            i += 1;        // 切换到下一页
            // 等待按键释放(防抖)
            while(READ_GPIO_PIN(BSP_IO_PORT_00_PIN_05) == BSP_IO_LEVEL_LOW);
        }
        
        i %= 4;  // 页面循环(0-3)
        
        // 根据页面索引显示不同内容
        if(i == 0)
            oledDisplayMain();       // 显示主界面
        else if(i == 1)
            oledDisplayGyro();       // 显示陀螺仪数据
        else if(i == 2)
            oledDisplayAccel();      // 显示加速度计数据
        else if(i == 3)
            oledDisplayMagnetic();   // 显示磁力计数据
        
        // 主循环延时
        R_BSP_SoftwareDelay(100, BSP_DELAY_UNITS_MILLISECONDS);
    }   
}

// 安全构建相关代码(TrustZone支持)
#if BSP_TZ_SECURE_BUILD
FSP_CPP_HEADER
BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ();

BSP_CMSE_NONSECURE_ENTRY void template_nonsecure_callable ()
{
    // 非安全可调用函数模板
}
FSP_CPP_FOOTER
#endif

四、核心功能实现

4.1 硬件IIC多设备管理

现有代码实现了四个I2C设备的独立管理:

  • OLED显示模块 (地址: 0x3C)
  • ITG3205陀螺仪 (地址: 0x68)
  • ADXL345加速度计 (地址: 0x53)
  • QMC5883P磁力计 (地址: 0x2C)

每个设备都有独立的初始化函数和控制结构体,通过统一的IIC读写接口进行通信。

4.2 传感器数据采集

代码直接读取传感器原始数据并进行基本转换:

陀螺仪数据

  • 原始数据:16位有符号整数
  • 转换公式:度/秒 = 原始数据 / 14.375
  • 测量范围:±2000°/s

加速度计数据

  • 原始数据:16位有符号整数
  • 转换公式:mg = 原始数据 × 3.9
  • 测量范围:±16g

磁力计数据

  • 原始数据:16位有符号整数
  • 转换公式:μT = 原始数据 × 0.1
  • 测量范围:±8 Gauss

4.3 多页面显示系统

通过GPIO按键实现四个显示页面的切换:

  • 页面0 :主界面 - 显示所有传感器的三轴数据概要
  • 页面1 :陀螺仪详情 - 显示角速度的详细数值
  • 页面2 :加速度计详情 - 显示加速度的详细数值
  • 页面3 :磁力计详情 - 显示磁场强度的详细数值

五、测试结果

5.1 功能测试

测试项目 预期结果 实际结果 状态
I2C总线初始化 成功识别所有设备 4个设备均正常识别
陀螺仪数据采集 输出三轴角速度 X/Y/Z轴数据正常输出
加速度计数据采集 输出三轴加速度 静态1g,动态响应正常
磁力计数据采集 输出三轴磁场强度 受地磁场影响明显
OLED多页面显示 按键切换显示内容 四个页面正常切换
实时数据刷新 持续稳定显示 无卡顿、无数据丢失

5.2 性能表现

基于现有代码的实际运行表现:

  • 数据采样率 :约16Hz(基于100ms主循环延时)
  • 显示刷新率 :与采样率同步
  • I2C通信稳定性 :无通信错误,数据传输可靠
  • 系统响应性 :按键响应及时,页面切换流畅

5.3 数据精度观察

由于采用简易数据读取(无滤波和校准),观察到以下现象:

  1. 陀螺仪 :静态时有小幅波动(±0.5°/s以内)
  2. 加速度计 :Z轴接近1000mg(1g重力),X/Y轴少量偏移
  3. 磁力计 :受环境磁场影响明显,数值随方向变化

六、代码特点分析

6.1 硬件IIC实现优势

现有代码充分利用了RA6E2的硬件IIC特性:

  • 使用SCI I2C硬件控制器
  • 采用中断回调机制处理传输完成
  • 支持多设备地址管理
  • 包含完整的错误处理

6.2 模块化设计

代码具有良好的模块化结构:

  • 各传感器独立驱动
  • 显示功能与数据采集分离
  • 统一的IIC通信接口
  • 清晰的功能层次

七、RA6E2平台优势体现

7.1 处理性能

  • Cortex-M33内核轻松处理多传感器数据采集和显示
  • 200MHz主频为实时应用提供充足计算资源
  • 硬件IIC减轻CPU负担,提高系统效率

7.2 外设支持

  • 多个SCI接口支持多I2C设备
  • 丰富的GPIO用于按键控制和未来扩展
  • 灵活的时钟系统满足不同外设需求

7.3 开发便利性

  • FSP框架提供完善的I2C驱动支持
  • 配置工具简化外设初始化
  • 丰富的示例代码加速开发进程

八、总结

8.1 项目成果

通过现有代码的成功运行,验证了以下能力:

  1. 硬件IIC可靠性 :RA6E2的硬件IIC接口能够稳定驱动多个传感器设备
  2. 实时数据处理 :系统能够实时采集、处理和显示九轴传感器数据
  3. 多任务管理 :通过状态机实现多页面显示和用户交互
  4. 系统稳定性 :长时间运行无异常,数据采集连续可靠

8.2 RA6E2适用性评估

基于本次试用体验,RA6E2特别适合以下应用场景:

  • 物联网传感节点 - 多传感器数据采集和本地处理
  • 工业监控设备 - 实时数据显示和状态监控
  • 消费电子 - 运动感知和用户交互应用
  • 教育实验平台 - 传感器原理学习和算法验证

8.3 试用结论

RA6E2开发板在九轴传感器应用场景中表现优秀,硬件IIC接口稳定可靠,处理性能充足,开发环境友好。现有代码实现了基本功能需求,系统运行稳定,为更复杂的传感器应用奠定了良好基础。

更多回帖

发帖
×
20
完善资料,
赚取积分