SPI-OLED测试
OLED的D/C引脚电平逻辑:
| 电平状态 | 模式名称 | 作用描述 | 例子 |
|---|---|---|---|
| 低电平 (Low / 0) | Command (命令) | 写入控制寄存器,设置屏幕的工作状态。 | 设置亮度、开启滚动、设置起始页。 |
| 高电平 (High / 1) | Data (数据) | 写入显存 (GDDRAM),直接改变屏幕显示的图像。 | 发送图片点阵、发送字符字模。 |
设备树定义:
c
&ecspi1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ecspi1>;
fsl,spi-num-chipselects = <2>;
cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;
status = "okay";
oled: oled {
compatible = "spidev";
reg = <0>;
spi-max-frequency = <10000000>;
};
};
D/C引脚接在了GPIO4_20,即116号引脚,在APP程序中控制即可,不需要写进驱动。
因此可直接使用内核通用SPI驱动spidev.c。
首先,在内核目录中打开menuconfig:
c
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make menuconfig
搜索SPIDEV:

搜索结果:

之前已经被设置为了M,因此会被编译成模块:
c
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ make modules
然后上传到开发板的/root目录下:
c
book@100ask:~/100ask_imx6ull-sdk/Linux-4.9.88$ adb push drivers/spi/spidev.ko /root
在APP程序中D/C引脚初始化及控制函数:
c
void dc_pin_init(int number)
{
char cmd[100];
sprintf(cmd, "echo %d > /sys/class/gpio/export", number);
system(cmd);
sprintf(cmd, "echo out > /sys/class/gpio/gpio%d/direction", number);
system(cmd);
}
void oled_set_dc_pin(int val)
{
char cmd[100];
sprintf(cmd, "echo %d > /sys/class/gpio/gpio%d/value", val, dc_pin_num);
system(cmd);
}
void dc_pin_exit(int number)
{
char cmd[100];
sprintf(cmd, "echo %d > /sys/class/gpio/unexport", number);
system(cmd);
}
实验结果:

屏幕显示数据的速率肉眼可见地缓慢,性能太差。原因分析:
**设置位置 (OLED_DIsp_Set_Pos)**时:
- 它内部调用了 3 次
oled_write_cmd_data。 - 每次
oled_write_cmd_data都会:- 执行
system("echo ...")(创建进程,很慢) - 执行
write(fd_spidev, &uc_data, 1)(1 字节写入)
- 执行
- 小计:发 3 字节命令,却搞了 3 次进程创建 + 3 次 1 字节写。
发送数据 (oled_write_datas)时:
- 执行
oled_set_dc_pin(1):又是一次system()(创建进程)。 - 执行
write(fd, buf, 8):8 字节写。
总计 :显示一个 8bitx16bit 字符,一共执行了:
- 8 次进程创建 (
system调用) - 6 次 1 字节写入 (设置坐标)
- 2 次 8 字节写入 (实际像素数据)
优化 OLED 性能的核心思路:减少系统调用次数 和消除高开销操作。
目前代码中, 导致性能低的最大原因是system() 函数,其次是碎片化的 write() 操作
system() 函数优化步骤:
- 修改
dc_pin_init,提前打开文件。 - 修改
oled_set_dc_pin,直接写文件描述符。
c
static int fd_dc_value; // 定义为静态全局变量
void dc_pin_init(int number) {
char cmd[100];
char path[100];
dc_pin_num = number;
sprintf(cmd, "echo %d > /sys/class/gpio/export", number);
system(cmd);
sprintf(cmd, "echo out > /sys/class/gpio/gpio%d/direction", number);
system(cmd);
// 【关键优化】提前打开 value 文件
sprintf(path, "/sys/class/gpio/gpio%d/value", number);
fd_dc_value = open(path, O_WRONLY);
}
void oled_set_dc_pin(int val) {
if (val) write(fd_dc_value, "1", 1);
else write(fd_dc_value, "0", 1);
}
其次是 oled_write_cmd_data,目前每写 1 字节就要切换一次 DC 引脚并执行一次 write。
优化逻辑:尽量将连续的命令或连续的数据打包在一起发送。
优化 Set_Pos 函数:
原函数执行了 3 次 write,可以合并为 1 次。
c
void OLED_DIsp_Set_Pos(int x, int y) {
unsigned char buf[3];
buf[0] = 0xb0 + y;
buf[1] = (x & 0x0f);
buf[2] = ((x & 0xf0) >> 4) | 0x10;
oled_set_dc_pin(0); // 只切换一次 DC
spi_write_datas(buf, 3); // 一次性发送 3 字节命令
}
优化后,瞬间显示一整帧字符,无卡顿、无闪烁。
之后再试试显存缓冲区 (Frame Buffer) 的优化路线。