【瑞萨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偏差和输入噪声。
附件是项目工程代码
页:
[1]