短笛君 发表于 2026-7-26 02:01:28

【瑞萨RA-eco-RA6M4评测】 使用IIC驱动OLED屏幕

# 【瑞萨RA-eco-RA6M4评测】 使用IIC驱动OLED屏幕

最近收到了RA6M4这款点点taptap安卓的评估板 但是因为误操作导致点点taptap安卓解锁只能翻出来之前的PROMOTION BOARD进行测评

点点taptap安卓是R7FA6M4AF3CFB,Cortex-M33内核,200MHz主频,256KB SRAM,带FPU。和·Trustzone 本期测评是使用IIC总线驱动oled屏幕

---

## 硬件接线

接线很简单,四根线搞定:

RA6M4这边用了SCI0(P100做SCL,P101做SDA),电源直接从板子3.3V取RA6M4的SCI IIC用的是NMOS开漏输出,内部没有上拉,不接电阻总线上没有信号,一半OLED都会有上拉电阻

ADC输入接到了P004(AN004),用一个可调电位器分压做信号源。调试口走标准的JTAG四线,P108/P109/P110/P300,接J-Link。

---

## 为什么用SCI IIC而不是RIIC

RA6M4的IIC驱动有两条路:SCI的简易IIC模式,或者专用的RIIC外设。

最开始我也纠结了一下。RIIC是全功能的I2C控制器,支持Fast-mode 400kHz,该有的都有。但RA6M4只有两个RIIC通道,数量少;SCI通道有十个,对OLED这种只需要主机写操作的外设来说,用SCI IIC完全够用,还能把RIIC留给后面接传感器。

FSP里配SCI IIC也很省事,通道选0,速率选Standard,回调函数填个名字,中断优先级给12,别的不用管。FSP的bitrate计算器会自动算出BRR/MDDR/SNR这些寄存器值——PCLKB是50MHz,算出来实际Bitrate约100kHz。

---

## OLED驱动

驱动直接移植先前STM32的驱动,RA6M4整体内存很大 可以开一个大的buffer来操作图像这样可以将刷新了做的很高很流畅,整体思路很简单:​**在内存里维护一个512字节的帧缓冲**​,所有画点画字画图标的操作都在内存里完成,最后调用`oled_update()`将数据发送到GDDRAM里。
全局volatile标志位配合回调函数做帧画面同步:

```c
static volatile bool g_i2c_scan_done;

void sci_i2c_master_callback(i2c_master_callback_args_t *p_args)
{
    g_i2c_scan_done = true;
}

// 发命令:控制字节0x00 + 命令字
void oled_write_cmd(uint8_t cmd)
{
    uint8_t buf = {0x00, cmd};
    g_i2c_scan_done = false;
    g_i2c0.p_api->write(g_i2c0.p_ctrl, buf, 2, false);
    while (!g_i2c_scan_done);
}

// 发数据:控制字节0x40 + 数据体
void oled_write_data(const uint8_t *data, uint32_t len)
{
    uint8_t header = 0x40;
    for (uint32_t sent = 0; sent < len; ) {
      uint8_tchunk;
      uint32_t n = (len - sent > 63) ? 63 : (len - sent);
      chunk = header;
      memcpy(&chunk, &data, n);
      g_i2c_scan_done = false;
      g_i2c0.p_api->write(g_i2c0.p_ctrl, chunk, n + 1, false);
      while (!g_i2c_scan_done);
      sent += n;
    }
}
```


初始化序列照着SSD1306的datasheet写就行:

```c
void oled_init(void)
{
    static const uint8_t init_seq[] = {
      0xAE,       // Display OFF
      0xD5, 0x80, // OSC频率
      0xA8, 0x1F, // MUX比 32-1
      0xD3, 0x00, // 显示偏移
      0x40,       // 起始行
      0x8D, 0x14, // 开Charge Pump
      0x20, 0x00, // 水平寻址
      0xA1,       // 段重映射 左右镜像
      0xC8,       // COM扫描方向 上下翻转
      0xDA, 0x02, // COM引脚硬件配置
      0x81, 0x7F, // 对比度
      0xD9, 0xF1, // 预充电周期
      0xDB, 0x40, // VCOMH电平
      0xA4,       // 正常显示
      0xA6,       // 非反白
      0xAF      // Display ON
    };
    for (uint32_t i = 0; i < sizeof(init_seq); i++)
      oled_write_cmd(init_seq);
    oled_write_cmd(0x2E); // 关滚动
}
```

帧缓冲的结构是这样:128列 × 4个page(每个page对应8行像素),总共512字节。SSD1306是page寻址的,一个字节管一列的8个像素,bit0在最上面

```c
void oled_set_pixel(uint8_t x, uint8_t y, bool on)
{
    if (x >= 128 || y >= 32) return;
    if (on) oled_buf |=(1 << (y & 7));
    else    oled_buf &= ~(1 << (y & 7));
}

void oled_update(void)
{
    oled_write_cmd(0x21); oled_write_cmd(0); oled_write_cmd(127);
    oled_write_cmd(0x22); oled_write_cmd(0); oled_write_cmd(3);
    oled_write_data(oled_buf, 512);
}
```

刷新顺序是:先设列范围(0x21指令,0\~127),再设页范围(0x22指令,0\~3),最后512字节数据一次发送出去

---

## 字库和图标

驱动里塞了两套ASCII字库。Font12是6x11的点阵,行距12px,数字符号还行,字母辨识度一般般。Font14是7x13的点阵,行距14px,在小屏上效果好不少,\$和@这些符号也能看清。

128x32的屏实在太小了,Font14一行只能放大约16个字符,整屏就两行文字。第三行只能用Font12硬挤。字库的结构是每列两个字节(高字节管上半截,低字节低几位管下半截),从SPACE到\~一共95个字符。

另外画了16个8x8的小图标和5个16x16的图标,都是2D位图直接手写的字模。8x8的电池和WiFi在这种分辨率下基本就是糊的,16x16的蓝牙和时钟倒是能看清。画图标的函数其实就是调`oled_draw_bitmap`,逐列把位图数据写到帧缓冲里:

```c
void oled_draw_bitmap(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *data)
{
    for (uint8_t col = 0; col < w; col++) {
      for (uint8_t pg = 0; pg < (h + 7) / 8; pg++) {
            uint8_t b = data;
            for (uint8_t bit = 0; bit < 8; bit++) {
                uint8_t py = pg * 8 + bit;
                if (py >= h) break;
                if (b & (1 << bit)) oled_set_pixel(x + col, y + py, true);
            }
      }
    }
}
```

---

## 测一下I2C波形和帧率

拿示波器挂了SCL和SDA看了下。初始化序列25条命令耗时约4.3ms,波形没毛病,SCL频率约100kHz,占空比接近50%。SDA上升沿大概530ns(4.7k上拉的效果),远低于Standard-mode的1us上限,可以接受。

整帧刷新比较慢,512字节数据分成了9个chunk发,光I2C传输就花了约44ms,加上前面6条地址设置命令1ms,一帧总共45ms左右。算下来整屏刷新帧率大概22fps。!(//m.qjumbo.com/forum/data/attachment/forum/202607/26/015824k2nczun4cpdcrr2h.png)


---
<iframe src="//player.bilibili.com/player.html?bvid=BV1QhKT6dEWz&page=1" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>
---

## ADC采集和显示

板子上有ADC采集通道做demo。通道4(P004),12位精度,VREF用板子3.3V,软件触发单次扫描。

实测了几个电压点,拿万用表对比了一下:

* 输入0.50V,ADC读数627(505mV),差5mV
* 输入1.00V,ADC读数1238(997mV),差3mV
* 输入1.65V,ADC读数2050(1651mV),差1mV
* 输入2.50V,ADC读数3101(2498mV),差2mV
* 输入3.30V,ADC读数4095(3298mV),差2mV

整体误差在几毫伏以内,作为电压表用足够了。12位精度理论分辨率约0.8mV/LSB,实测误差主要是VREF偏差和输入噪声。



短笛君 发表于 2026-7-26 02:03:27

附件是项目工程代码
页: [1]
查看完整版本: 【瑞萨RA-eco-RA6M4评测】 使用IIC驱动OLED屏幕

Baidu
map