【瑞萨RA-eco-RA6M4评测】实现I2C点亮oled屏显示
本次学习I2C外设应用,以点亮I2C接口OLED12864屏为例子。开发板关于I2C接口引脚如下:!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/145538qkjn3717jwahkqun.png)
这里使用SCI3外设的I2C接口,引脚为P408和P409。
接着打开RASC之前工程,配置I2C初始化。先配置引脚:
!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/145717sdy800ca8khsn0qd.png)
接着添加I2C外设驱动SDK,配置I2C接口参数。
!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/145831xxv953apxa3gsasv.png)
勾选I2C组件库。
!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/150002msksexexzxetihtt.png)
最后生成KEIL工程。
!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/150129qwi3d4d3wuj42uwu.png)
在keil工程内添加I2C驱动代码:
```
/*====================================================================*
*多路 I2C 总线上下文结构体
*====================================================================*/
typedef struct ra6m4_i2c_para_t
{
sci_i2c_instance_ctrl_t *_i2c; /**< I2C 控制器实例指针 */
volatile uint8_t i2c_channel; /**< SCI 通道号 (与 FSP callback p_args->channel 对应) */
volatile uint8_t i2c_transfer_done;/**< 传输完成标志 (回调中置 1) */
volatile i2c_master_event_t i2c_event; /**< 当前传输事件 */
int timeout_us; /**< 超时倒计时 (us) */
} ra6m4_i2c_para_t;
/*====================================================================*
*全局变量
*====================================================================*/
/** 总线上下文数组 — 每条总线一个条目 */
ra6m4_i2c_para_t user_i2c_para;
/*====================================================================*
*中断回调函数 (所有总线共用)
*====================================================================*/
void sci_i2c_master_callback(i2c_master_callback_args_t *p_args)
{
/*-----------------------------------------------------------------*
* 派发策略 1 (优先): 通过 p_context 直接定位 — O(1)
* drv_i2c_init() 中 CallbackSet 将 &user_i2c_para 写入 p_context
*-----------------------------------------------------------------*/
ra6m4_i2c_para_t *para = (ra6m4_i2c_para_t *)p_args->p_context;
if (NULL != para)
{
para->i2c_event = p_args->event;
para->i2c_transfer_done= 1;
return;
}
// /*-----------------------------------------------------------------*
// * 派发策略 2 (回退): 通过 channel 匹配 — O(N)
// * 当 p_context 为 NULL (未调用 CallbackSet) 时使用
// *-----------------------------------------------------------------*/
// for (uint8_t i = 0; i < I2C_BUS_NUM; i++)
// {
// if (user_i2c_para.i2c_channel == p_args->channel)
// {
// user_i2c_para.i2c_event = p_args->event;
// user_i2c_para.i2c_transfer_done= 1;
// break;
// }
// }
}
/*====================================================================*
*初始化
*====================================================================*/
void drv_i2c_init(void)
{
/*--- Bus 0: SCI0 ---*/
user_i2c_para._i2c = &g_i2c0_ctrl;
user_i2c_para.i2c_channel = 2; /* SCI0 → FSP channel 0 */
user_i2c_para.i2c_transfer_done = 0;
user_i2c_para.i2c_event = I2C_MASTER_EVENT_ABORTED;
user_i2c_para.timeout_us = I2C_TIMEOUT_US;
R_SCI_I2C_Open(&g_i2c0_ctrl, &g_i2c0_cfg);
/* 设置回调上下文指针: 回调中可直接通过 p_args->p_context 定位总线, O(1) */
R_SCI_I2C_CallbackSet(&g_i2c0_ctrl, sci_i2c_master_callback,
&user_i2c_para, NULL);
/*--- Bus 1: SCI1 (需在 FSP 配置器中添加) ---*/
user_i2c_para._i2c = &g_i2c1_ctrl;
user_i2c_para.i2c_channel = 3; /* SCI1 → FSP channel 1 */
user_i2c_para.i2c_transfer_done = 0;
user_i2c_para.i2c_event = I2C_MASTER_EVENT_ABORTED;
user_i2c_para.timeout_us = I2C_TIMEOUT_US;
R_SCI_I2C_Open(&g_i2c1_ctrl, &g_i2c1_cfg);
R_SCI_I2C_CallbackSet(&g_i2c1_ctrl, sci_i2c_master_callback,
&user_i2c_para, NULL);
}
/*====================================================================*
*内部辅助函数
*====================================================================*/
/**
* @brief等待指定总线的 I2C 传输完成
*
* 轮询 i2c_transfer_done 标志:
* - false 且超时未到 → 继续等待
* - true→ 回调已触发, 检查事件是否匹配
* - 超时 → 返回失败 (从机无响应)
*
* @param bus 总线索引
* @param expected_event 期望事件 (TX_COMPLETE / RX_COMPLETE)
* @retval0成功
* @retval1失败 (超时或收到异常事件)
*/
static uint8_t I2C_WaitDone(uint8_t bus, i2c_master_event_t expected_event)
{
ra6m4_i2c_para_t *para = &user_i2c_para;
while (!para->i2c_transfer_done && (para->timeout_us > 0))
{
R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MICROSECONDS);
para->timeout_us--;
}
/* 清除标志, 为下次传输做准备 */
para->i2c_transfer_done = 0;
if (para->timeout_us <= 0)
return 1; /* 超时: 从机无响应 */
if (para->i2c_event != expected_event)
return 1; /* 收到异常事件 (如 NACK → ABORTED) */
return 0; /* 成功 */
}
/**
* @brief将寄存器地址填入缓冲区
*
* 自动判断 8 位 / 16 位地址:
* - addr <= 0xFF → 1 字节
* - addr >0xFF → 2 字节 (大端序: 高字节在前)
*
* @param buff目标缓冲区 (至少 2 字节)
* @param addr寄存器地址
* @retval 地址字节数 (1 或 2)
*/
static uint8_t I2C_FillAddr(uint8_t *buff, uint32_t addr)
{
if (addr > 0xFF)
{
buff = (uint8_t)((addr >> 8) & 0xFF);
buff = (uint8_t)(addr & 0xFF);
return 2;
}
else
{
buff = (uint8_t)(addr & 0xFF);
return 1;
}
}
/**
* @brief启动一次写传输并等待完成 (指定总线)
*/
static uint8_t I2C_DoWrite(uint8_t bus, uint8_t *data, uint32_t len, bool restart)
{
ra6m4_i2c_para_t *para = &user_i2c_para;
/* 重置状态, 为新传输做准备 */
para->i2c_event = I2C_MASTER_EVENT_ABORTED;
para->i2c_transfer_done= 0;
para->timeout_us = I2C_TIMEOUT_US;
if (FSP_SUCCESS != R_SCI_I2C_Write(para->_i2c, data, len, restart))
return 1;
return I2C_WaitDone(bus, I2C_MASTER_EVENT_TX_COMPLETE);
}
/**
* @brief启动一次读传输并等待完成 (指定总线)
*/
static uint8_t I2C_DoRead(uint8_t bus, uint8_t *data, uint32_t len, bool restart)
{
ra6m4_i2c_para_t *para = &user_i2c_para;
/* 重置状态, 为新传输做准备 */
para->i2c_event = I2C_MASTER_EVENT_ABORTED;
para->i2c_transfer_done= 0;
para->timeout_us = I2C_TIMEOUT_US;
if (FSP_SUCCESS != R_SCI_I2C_Read(para->_i2c, data, len, restart))
return 1;
return I2C_WaitDone(bus, I2C_MASTER_EVENT_RX_COMPLETE);
}
/*====================================================================*
*公共 API 实现
*====================================================================*/
uint8_t I2C_WriteByte(uint8_t bus, uint32_t dev_addr, uint32_t addr, uint8_t Byte)
{
uint8_t buff;
uint8_t addr_len;
if (bus >= I2C_BUS_NUM)
return 1;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr,
(dev_addr > 0xff) ? I2C_MASTER_ADDR_MODE_10BIT : I2C_MASTER_ADDR_MODE_7BIT))
return 1;
addr_len = I2C_FillAddr(buff, addr);
buff = Byte;
/* START + slaveW + addr[..] + Byte + STOP */
return I2C_DoWrite(bus, buff, addr_len + 1, false);
}
uint8_t I2C_ReadByte(uint8_t bus, uint32_t dev_addr, uint32_t addr)
{
uint8_t buff;
uint8_t addr_len;
if (bus >= I2C_BUS_NUM)
return 0;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr,
(dev_addr > 0xff) ? I2C_MASTER_ADDR_MODE_10BIT : I2C_MASTER_ADDR_MODE_7BIT))
return 0;
addr_len = I2C_FillAddr(buff, addr);
/* 写寄存器地址, 发 RESTART (不发 STOP, 保持总线占用) */
if (I2C_DoWrite(bus, buff, addr_len, true))
return 0;
/* 读 1 字节数据 */
if (I2C_DoRead(bus, buff, 1, false))
return 0;
return buff;
}
uint8_t I2C_PageWrite(uint8_t bus, uint32_t dev_addr, uint32_t addr, uint8_t *str, uint32_t num)
{
uint8_t buff;
uint8_t addr_len;
if ((bus >= I2C_BUS_NUM) || (NULL == str) || (0 == num) || (num > (I2C_BUF_MAX_SIZE-4)))
return 1;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr, I2C_MASTER_ADDR_MODE_7BIT))
return 1;
/* 将寄存器地址 + 数据合并到同一缓冲区, 单次 I2C 事务发送:
* START + slaveW + addr[..] + data + STOP
*/
addr_len = I2C_FillAddr(buff, addr);
memcpy(&buff, str, num);
return I2C_DoWrite(bus, buff, (uint32_t)(addr_len + num), false);
}
uint8_t I2C_PageRead(uint8_t bus, uint32_t dev_addr, uint32_t addr, uint8_t *str, uint32_t num)
{
uint8_t buff;
uint8_t addr_len;
if ((bus >= I2C_BUS_NUM) || (NULL == str) || (0 == num))
return 1;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr,
(dev_addr > 0xff) ? I2C_MASTER_ADDR_MODE_10BIT : I2C_MASTER_ADDR_MODE_7BIT))
return 1;
addr_len = I2C_FillAddr(buff, addr);
/* 写寄存器地址, 发 RESTART */
if (I2C_DoWrite(bus, buff, addr_len, true)) return 1;
/* 读 num 字节数据 (直接读入用户缓冲区) */
return I2C_DoRead(bus, str, num, false);
}
uint8_t I2C_Write(uint8_t bus, uint32_t dev_addr, uint8_t *cmd, uint32_t len, uint8_t *str, uint32_t num)
{
uint8_t buff;
uint32_t total;
if (bus >= I2C_BUS_NUM)
return 1;
total = len + num;
if ((0 == total) || (total > I2C_BUF_MAX_SIZE))
return 1;
if ((len > 0) && (NULL == cmd))
return 1;
if ((num > 0) && (NULL == str))
return 1;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr, I2C_MASTER_ADDR_MODE_7BIT))
return 1;
/* 将 cmd + str 合并到同一缓冲区, 单次 I2C 事务发送:
* START + slaveW + cmd + str + STOP
*/
if (len > 0) memcpy(buff, cmd, len);
if (num > 0) memcpy(&buff, str, num);
return I2C_DoWrite(bus, buff, total, false);
}
uint8_t I2C_Read(uint8_t bus, uint32_t dev_addr, uint8_t *cmd, uint32_t len, uint8_t *str, uint32_t num)
{
if ((bus >= I2C_BUS_NUM) || (NULL == cmd) || (NULL == str) || (0 == len) || (0 == num))
return 1;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr,
(dev_addr > 0xff) ? I2C_MASTER_ADDR_MODE_10BIT : I2C_MASTER_ADDR_MODE_7BIT))
return 1;
/* 发送命令字节, 发 RESTART (不发 STOP) */
if (I2C_DoWrite(bus, cmd, len, true)) return 1;
/* 读 num 字节数据 */
return I2C_DoRead(bus, str, num, false);
}
uint8_t I2C_WriteOnly(uint8_t bus, uint32_t dev_addr, uint8_t *str, uint32_t num)
{
if ((bus >= I2C_BUS_NUM) || (NULL == str) || (0 == num))
return 1;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr,
(dev_addr > 0xff) ? I2C_MASTER_ADDR_MODE_10BIT : I2C_MASTER_ADDR_MODE_7BIT))
return 1;
return I2C_DoWrite(bus, str, num, false);
}
uint8_t I2C_ReadOnly(uint8_t bus, uint32_t dev_addr, uint8_t *str, uint32_t num)
{
if ((bus >= I2C_BUS_NUM) || (NULL == str) || (0 == num))
return 1;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr,
(dev_addr > 0xff) ? I2C_MASTER_ADDR_MODE_10BIT : I2C_MASTER_ADDR_MODE_7BIT))
return 1;
return I2C_DoRead(bus, str, num, false);
}
uint8_t I2C_IsDeviceReady(uint8_t bus, uint32_t dev_addr)
{
if (bus >= I2C_BUS_NUM)
return 1;
if (FSP_SUCCESS != R_SCI_I2C_SlaveAddressSet(user_i2c_para._i2c,
dev_addr,
(dev_addr > 0xff) ? I2C_MASTER_ADDR_MODE_10BIT : I2C_MASTER_ADDR_MODE_7BIT))
return 1;
/* 发送 0 字节写: START + slaveW + STOP
* 从机 ACK→ TX_COMPLETE → 返回 0 (就绪)
* 从机 NACK → ABORTED → 返回 1 (无响应)
*/
return I2C_DoWrite(bus, NULL, 0, false);
}
```
接着添加OLED12864的驱动代码,以及字库。
!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/150340s2pa88282j6l10l2.png)
在hal_entry.c中调用初始化i2c,然后测试oled显示代码:
!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/150513f7f7sdrois87ox9g.png)
最终显示效果如下:
!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/150537zxs9yczvczyvoz7d.png)
!(https://www.eefocus.com/forum/data/attachment/forum/202608/09/150551uo5uhaza6znt3jea.png)
页:
[1]