当前位置:首页 > 显示光电 > 显示光电
[导读]裸机跑SSD1306最典型的写法是在主循环里逐字节I2C/SPI发送,1024字节全屏刷一次CPU全程被外设阻塞住,主业务全卡着。要降占用,核心不是“刷得快”,是把“数据搬运”交给DMA,CPU只改显存、只在传输完成中断里换缓冲。


裸机跑SSD1306最典型的写法是在主循环里逐字节I2C/SPI发送,1024字节全屏刷一次CPU全程被外设阻塞住,主业务全卡着。要降占用,核心不是“刷得快”,是把“数据搬运”交给DMA,CPU只改显存、只在传输完成中断里换缓冲。

一、先留一份本地显存

SSD1306内部有GDDRAM,但MCU侧必须维护一份镜像缓冲,所有画点/字符/图形先改这份缓冲,再统一下发。128×64屏显存=8页×128=1024字节。

uint8_t oled_gram[8][128];      // 当前显示内容

uint8_t oled_dma_buf[1024+1];   // 0x40控制字节 + 数据

页寻址模式是默认模式,按页设B0~B7+列地址;全屏刷建议切水平寻址(0x20 0x00),一次连续写1024字节最省命令开销。

二、SPI DMA搬运整帧

SPI屏最适合DMA:CLK一拉,DMA从内存搬字节到SPI DR,CPU中途完全脱手。I2C屏也能用DMA,但要处理控制字节拆分、I2C事件中断、和别的I2C传感器互斥,新手建议先SPI。

刷整帧流程:

填控制字节0x40到dma_buf[0]

把oled_gram按页拼进dma_buf[1..1024]

发“写GRAM”命令0x2C(I2C)或直接拉DC=1(SPI)

启动DMA

传输完成中断里置ready

void oled_start_dma(void){

   oled_dma_buf[0] = 0x40;

   int p=1;

   for(int pg=0;pg<8;pg++)

       memcpy(&oled_dma_buf[p], oled_gram[pg], 128), p+=128;

   HAL_SPI_Transmit_DMA(&hspi1, oled_dma_buf, 1024+1);

}


void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef*hspi){

   if(hspi->Instance==SPI1) oled_busy=0;   // 标记可准备下一帧

}

CPU占用从“搬1024字节期间全阻塞”变成“只调一次启动函数”,传输期去做按键扫描、传感器解算都行。

三、双缓冲避免花屏

单缓冲最大坑:DMA还没发完,主循环又开始改oled_gram,屏幕会花。正解开两块显存,一块给DMA发,一块给GUI写:

uint8_t gram_front[8][128];   // DMA正在发

uint8_t gram_back[8][128];    // CPU正在画

volatile uint8_t *tx_ptr;


void oled_swap(void){

   if(oled_busy) return;            // DMA没完不换

   uint8_t (*tmp)[128]=gram_front;

   gram_front=gram_back; gram_back=tmp;

   tx_ptr=(uint8_t*)gram_front;

   oled_start_dma();

}

画图函数永远写gram_back,主循环周期到点调oled_swap()。这样“画下一帧”和“发上一帧”并行,不撕屏。

四、局部刷新比全屏更省

不是每帧都要1024字节。状态栏、菜单、传感器数值往往只动某几页:

void oled_refresh_page(uint8_t page){

   oled_set_pos(0,page);

   oled_dc_high();

   HAL_SPI_Transmit_DMA(&hspi1, oled_gram[page], 128);

}

只刷第2页(标题)、第6~7页(底部状态),一帧数据量从1KB掉到两三百字节,DMA启动次数和总线占用都降下来。

五、裸机落坑清单

DMA没完就改缓冲:花屏根源,换缓冲前必须看完成标志

局部数组当DMA缓冲:函数返回内存没了,DMA还在读→硬错

I2C屏和传感器共线:HAL_I2C_Transmit_DMA和传感器读抢总线冲突,OLED独占总线更稳

Cache没清:M7带Cache时DMA读前Invalidate,写后Clean

DC脚时序错:SPI发数据前DC必须提前拉高,DMA启动后别再动DC

阻塞等DMA:裸机别while等状态,真要等就退化成轮询,失去意义

SPI波特率过高:OLED模块一般撑8~10M,F1/F4别直接上36M

没开完成中断:只启动不处理TxCplt,双缓冲切换永远不触发

六、实测收益

F103+SPL写法下,阻塞SPI刷1024字节约2~3ms CPU全占;改DMA后这2~3ms主循环完全空闲,只付出一次启动+一次中断,CPU占用按主循环业务占比算能降一大截,标称“直降80%”在工程里是合理的。

裸机OLED用DMA的本质就三句话:本地显存分离绘图与发送、DMA搬字节CPU脱手、双缓冲在TxCplt里换指针。全屏刷保底,局部刷省带宽,中断里别干重活——这样哪怕主循环还跑按键、ADC、CAN解析,OLED也不卡顿。



本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除( 邮箱:macysun@21ic.com )。
关闭