20260125 - Linu驱动学习笔记:SPI-OLED测试

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);![image-20260116135634897](C:\Users\Admin\AppData\Roaming\Typora\typora-user-images\image-20260116135634897.png)
}

实验结果:

屏幕显示数据的速率肉眼可见地缓慢,性能太差。原因分析:

**设置位置 (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() 函数优化步骤:

  1. 修改 dc_pin_init,提前打开文件。
  2. 修改 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) 的优化路线。

相关推荐
LCG米1 天前
STM32 ADC多通道DMA定时器触发实测:TRGO触发链、采样率精确控制与乒乓缓冲落地
stm32·单片机·嵌入式·adc
youcans_1 天前
【嵌入式软件AI编程】22. STM32 基础实验:GPIO外部中断
stm32·单片机·嵌入式硬件·ai编程·claude
未来_攻城狮1 天前
STM32F407VGT6 芯片详解:高性能 Cortex-M4 MCU 入门指南
stm32·单片机·嵌入式硬件
xcLeigh1 天前
FreeRTOS实战:用STM32+SEGGER打造工业级实时嵌入式系统
人工智能·stm32·单片机·嵌入式硬件·ai
硅基造物主1 天前
STM32 从 0 到 1 做智能家居(十二):USART2 分时复用 —— ESP-12F + MQTT
网络·stm32·mqtt·智能家居·esp-12f
芯岭技术郦1 天前
XL4460 FSK 无线透传模组
stm32·单片机·嵌入式硬件
硅基造物主1 天前
STM32 从 0 到 1 做智能家居(十):串口与 printf 重定向
stm32·单片机·智能家居·usart·中断
新晨单片机设计1 天前
S033A-基于STM32单片机拼图游戏机【Proteus仿真+Keil程序+原理图】
stm32·单片机·嵌入式硬件·拼图游戏机
comyccccc2 天前
STM32F407VET6核心板的部分电路绘制学习笔记
笔记·stm32·学习
简单电磁炮2 天前
关于can的调试
c语言·stm32·单片机