slmxiaodai 发表于 2026-8-4 17:08:11

【瑞萨RA-eco-RA6M4评测】 + 05 I2C综合应用及视频演示

本帖最后由 slmxiaodai 于 2026-8-4 17:33 编辑

接着前面4篇
【瑞萨RA-eco-RA6M4评测】+ 01 开发板初体验及开发环境搭建 :https://www.eefocus.com/forum/thread-236019-1-1.html
【瑞萨RA-eco-RA6M4评测】+ 02 不同场景下的功耗测试:https://www.eefocus.com/forum/thread-236029-1-1.html
【瑞萨RA-eco-RA6M4评测】+ 03 驱动ADXL345三轴加速度计:https://www.eefocus.com/forum/thread-236049-1-1.html
【瑞萨RA-eco-RA6M4评测】+ 04 驱动AHT20温湿度模块测试:https://www.eefocus.com/forum/thread-236050-1-1.html

10 I2C综合应用(整合03和04案例)
将03 驱动ADXL345三轴加速度计工程与04 驱动AHT20温湿度模块工程结合起来使其同时驱动两个模块。
大致流程如下:
首先是创建I2C公共驱动文件,两个小模块都使用了I2C协议,因此他们的引脚配置和I2C配置有共用部分,因此为了编程清晰需要创建公共文件,其中就包含了I2C回调函数以及阻塞等待函数,创建i2c_common.c,其内容如下。
---------------------------------------------------------------------------------------------------------------------------------------------------------
#include "i2c_common.h"
volatile bool i2c_transfer_complete = false;
volatile fsp_err_t i2c_transfer_status = FSP_SUCCESS;
void sci_i2c_master_callback(i2c_master_callback_args_t *p_args)
{
    if (NULL != p_args)
    {
      i2c_transfer_status = (p_args->event == I2C_MASTER_EVENT_ABORTED) ? FSP_ERR_ABORTED : FSP_SUCCESS;
      i2c_transfer_complete = true;
    }
}
fsp_err_t wait_for_completion(uint32_t timeout_ms)
{
    i2c_transfer_complete = false;
    uint32_t elapsed = 0;
    while (!i2c_transfer_complete && elapsed < timeout_ms)
    {
      R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_MILLISECONDS);
      elapsed++;
    }
    if (elapsed >= timeout_ms) return FSP_ERR_TIMEOUT;
    return i2c_transfer_status;
}
---------------------------------------------------------------------------------------------------------------------------------------------------------

随后,分别把温湿度模块以及加速度模块的驱动中关于公共的部分删除即可。
关于加速度模块的驱动代码如下。
---------------------------------------------------------------------------------------------------------------------------------------------------------
#include "adxl345.h"
#include "hal_data.h"
#include "i2c_common.h"
// 定义 I2C 实例(需与您在 FSP 配置中生成的全局变量名称一致,例如 g_i2c0_ctrl 和 g_i2c0_cfg)
//extern i2c_master_instance_t g_i2c0; 已经在hal_data.h中完成定义
// 写入单字节寄存器(原 IIC_WriteByte 的替代)
static fsp_err_t adxl345_write_byte(uint8_t reg, uint8_t data)
{
    fsp_err_t err;
    uint8_t write_buf;
    write_buf = reg;
    write_buf = data;
    // 设置从机地址(7位模式)
    err = R_SCI_I2C_SlaveAddressSet(&g_i2c0_ctrl, ADXL345_ADDR, I2C_MASTER_ADDR_MODE_7BIT);
    if (FSP_SUCCESS != err) return err;
    // 执行写操作(不产生 STOP,以便**连续操作? 这里我们直接停止)
    err = R_SCI_I2C_Write(&g_i2c0_ctrl, write_buf, 2, false);
    if (FSP_SUCCESS != err) return err;
    return wait_for_completion(100); // 等待最多100ms
}
// 读取多个字节(原 IIC_ReadLen 的替代)
static fsp_err_t adxl345_read_bytes(uint8_t reg, uint8_t *p_data, uint8_t len)
{
    fsp_err_t err;
    // 步骤1: 先写入要读取的寄存器地址(写操作,无 STOP,产生 RESTART)
    err = R_SCI_I2C_SlaveAddressSet(&g_i2c0_ctrl, ADXL345_ADDR, I2C_MASTER_ADDR_MODE_7BIT);
    if (FSP_SUCCESS != err) return err;
    err = R_SCI_I2C_Write(&g_i2c0_ctrl, ®, 1, true); // restart = true,发送 RESTART
    if (FSP_SUCCESS != err) return err;
    err = wait_for_completion(100);
    if (FSP_SUCCESS != err) return err;
    // 步骤2: 执行读操作(从当前寄存器地址开始读取 len 个字节)
    err = R_SCI_I2C_SlaveAddressSet(&g_i2c0_ctrl, ADXL345_ADDR, I2C_MASTER_ADDR_MODE_7BIT);
    if (FSP_SUCCESS != err) return err;
    err = R_SCI_I2C_Read(&g_i2c0_ctrl, p_data, len, false); // false = 发送 STOP
    if (FSP_SUCCESS != err) return err;

    return wait_for_completion(100);
}
//-------------------------------------------------------------
// 对外接口函数
//-------------------------------------------------------------
void ADLX345_Init(void)
{
// 注意:I2C 的硬件初始化已经在 hal_entry 中通过 R_SCI_I2C_Open(&g_i2c0_ctrl, &g_i2c0_cfg) 完成
    // 这里仅配置 ADXL345 内部寄存器
    // 设置数据格式:±16g,全分辨率,左对齐(与原来保持一致)
    adxl345_write_byte(DATA_FORMAT, 0x0B);
    // 设置省电模式:链接使能,测量模式
    adxl345_write_byte(POWER_CTL, 0x28);
    // 禁用中断
    adxl345_write_byte(INT_ENABLE, 0x00);
    // 偏移寄存器归零
    adxl345_write_byte(OFSX, 0x00);
    adxl345_write_byte(OFSY, 0x00);
    adxl345_write_byte(OFSZ, 0x00);
}
void ADLX345_GetData(uint8_t *data, uint8_t num)
{
    // 从 DATA_X0 寄存器开始连续读取 num 个字节 (通常为6)
    adxl345_read_bytes(DATA_X0, data, num);
}
---------------------------------------------------------------------------------------------------------------------------------------------------------
关于温湿度模块的核心代码已经在04中展示过了,因此在此处展示它们的上层代码,如下。
---------------------------------------------------------------------------------------------------------------------------------------------------------
// 初始化(上电或软复位后调用)
void AHT21_WriteInit(void)
{
    aht20_write_command(AHT20_CMD_INIT, AHT20_CMD_INIT_DATA0, AHT20_CMD_INIT_DATA1);
}
// 触发测量
void AHT21_WriteMeasure(void)
{
    aht20_write_command(AHT20_CMD_MEASURE, AHT20_CMD_MEASURE_DATA0, AHT20_CMD_MEASURE_DATA1);
}
// 软复位
void AHT21_WriteReset(void)
{aht20_write_command_only(AHT20_CMD_RESET);}
// 幂函数(原代码中用于转换,保留)
static uint32_t AHT21_Pow(uint32_t num, uint32_t power)
{   uint32_t result = 1;
    while (power--) result *= num;
    return result;}
// 读取温湿度(阻塞)
float* AHT21_ReadTemperatureHumidity(void)
{   static float TH = {0.0f, 0.0f};
    uint8_t array = {0};
    uint32_t temp = 0;
    uint32_t result = {0};
    // 发送测量命令
    AHT21_WriteMeasure();
    R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);// 至少等待80ms,这里500ms
    // 读取6字节:状态+湿度(2)+温度(2)+CRC(忽略)
    if (FSP_SUCCESS != aht20_read_bytes(array, 6))
    {   TH = 0; TH = 0;
      return TH;}
    // 检查状态位 bit 是否为0(测量完成)
    if ((array & 0x80) == 0x00)
    {   // 湿度:20位数据 (array<<12 | array<<4 | array>>4)
      temp = 0;
      temp = (temp | array) << 8;
      temp = (temp | array) << 8;
      temp = (temp | array);
      temp = temp >> 4;
      result = temp;
      // 温度:20位数据 (array<<16 | array<<8 | array) 取低20位
      temp = 0;
      temp = (temp | array) << 8;
      temp = (temp | array) << 8;
      temp = (temp | array);
      temp = temp & 0x000FFFFF;
      result = temp;
      // 转换
      TH = (float)result / AHT21_Pow(2, 20);   // 湿度 (0~1)
      TH = (float)result / AHT21_Pow(2, 20) * 200.0f - 50.0f; // 温度 ℃
      return TH;}
    else
    {TH = 0; TH = 0;
      return TH;}}
// AHT21初始化
void AHT21_Init(void)
{   uint8_t state = 0;
    // 检查状态 bit (0x08) 表示初始化是否完成
    state = AHT21_ReadState();
    if ((state & 0x08) != 0x08)
    {AHT21_WriteInit();
      R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);}}
---------------------------------------------------------------------------------------------------------------------------------------------------------
额外要注意的地方是I2C的从地址设置,这次涉及到了两个模块,虽然保持原来的单个模块地址也可以正常运行,但保险起见建议设置为0x00,如下图所示。


运行效果展示视频。
https://www.bilibili.com/video/BV1q9uc6tEi9/?vd_source=51b58975146283ff8159c6de2fe76126







页: [1]
查看完整版本: 【瑞萨RA-eco-RA6M4评测】 + 05 I2C综合应用及视频演示