STM32 7针0.96寸OLED显示屏(硬件SPI+DMA)无需内核响应 超高刷新率! 您所在的位置:网站首页 屏幕的含义 STM32 7针0.96寸OLED显示屏(硬件SPI+DMA)无需内核响应 超高刷新率!

STM32 7针0.96寸OLED显示屏(硬件SPI+DMA)无需内核响应 超高刷新率!

2024-07-11 12:37| 来源: 网络整理| 查看: 265

芯片:STM32f103c8t6 f103型号大同小异,其他芯片请根据情况修改接口和配置

使用的是7针的0.96寸OLED屏幕,黑白两色显示 以下是实物图 7针0.96寸OLED屏幕

一.原理:

首先你要了解STM32上的AFIO(复用功能),DMA,SPI 和OLED上的SSD1306驱动的原理和命令,还有C语言的指针,如果其中任何一项不熟悉的话,请先学习一遍 相关资料: DMA原理 SPI的基本原理(库函数版) 学习笔记 端口复用&重映射 一文彻底了解SSD1306驱动0.96寸OLED 通过学习后相信你会更好明白这篇文章以及对STM32的了解

1.STM32部分:

DMA每次会将8bit的数据从SRAM中传输到SPI的缓存区中 SPI启用DMA功能后会自动响应DMA传输完成的请求,将8bit数据写入发送缓存区,此款屏幕总共有128 * 64个像素,所以要传输128*64/8=1024次 当发送寄存器为空时,发送缓存区会将将数据写入到发送寄存器 当8bit数据写满时,发送寄存器通过MOSI口输出数据

在这里插入图片描述

2.OLED上的SSD1306驱动部分:

OLED屏幕有三种刷新方式分别为页地址模式, 水平地址模式和垂直地址模式,。 水平地址模式和垂直地址模式可以在一页(一列)写完后自动换页(列) 所以在水平地址寻址或者垂直地址寻址模式下,只要源源不断的发送数据即可

页地址模式 在这里插入图片描述

水平地址模式 在这里插入图片描述 垂直地址模式 在这里插入图片描述 参考资料 https://blog.csdn.net/gengyuchao/article/details/86608037

3.所以过程是:

SRAM->DMA传输->SPI响应DMA请求->写入缓存区->写入寄存器->自动写入发送寄存器->发送数据->OLED接收数据并写入显存->显示 这个过程是不需要STM32响应中断的,全程走DMA 缺点是需要一次性写入整个OLED_SRAM 但是DMA和硬件SPI拥有很快的传输速度,所以这个缺点也被克服了 经过测试,帧率有大约30帧左右

二.相关接口:

1.GND 电源地 2.VCC 电源正(3~5.5V) 3.D0(SCL) SCK管脚 4.D1(SDA) MOSI管脚 5.RES(RST) 用来复位(低电平复位) 6.DC(D/C) 数据和命令控制管脚 1表示数据 0表示命令 7.CS(NSS) 片选管脚 在这里插入图片描述

三.IO口和SPI初始化: 1.IO口设置

通过查阅STM32C8T6的原理图(其他型号芯片可以看自己的原理图),可以发现有两个SPI 我用的是SPI1 在这里插入图片描述在这里插入图片描述 因为在SPI启用了主机信号由软件控制,并且4线SPI的0.96寸OLED模块没有发送数据的针脚

所以将PA4作为RST功能使用,将PA6作为DC功能使用

#ifndef __oled_spi_dma_H #define __oled_spi_dma_H #include "sys.h" #define S6X8 0 #define S8X16 1 #define OLED_SCL_CLR() GPIO_ResetBits(GPIOA,GPIO_Pin_5) //时钟 #define OLED_SCL_SET() GPIO_SetBits(GPIOA,GPIO_Pin_5) #define OLED_SDA_LOW() GPIO_ResetBits(GPIOA,GPIO_Pin_7) //MOSI主设备输出 #define OLED_SDA_HIGH() GPIO_SetBits(GPIOA,GPIO_Pin_7) #define OLED_RST_OFF() GPIO_ResetBits(GPIOA,GPIO_Pin_4) //接低电平复位 #define OLED_RST_ON() GPIO_SetBits(GPIOA,GPIO_Pin_4) #define OLED_DC_CMD() GPIO_ResetBits(GPIOA,GPIO_Pin_6) //模式 #define OLED_DC_DAT() GPIO_SetBits(GPIOA,GPIO_Pin_6) void GPIO_Configuration(void); //GPIO和SPI初始化 void OLED_Write(u8 ye,u8 lie,u8* ascii,u8 size); //写入ASCII文字 void OLED_SendCmd(u8 TxData); //发送命令 void OLED_Init(void); //OLED初始化 void DMA_OLED_Init(void); //DMA初始化 #endif 2.通过SPI通信原理图:

在这里插入图片描述

将SPI配置成

1.单线只发送或者双线全双工 2.主机模式 3.一次发送8位数据 4.空闲时间为低电平时 5.第一个上升沿采样 6.主机片选信号(CS)由软件控制 7.预分频 256 8.数据传输从 MSB 高位开始 低位为LSB 9.CRC 值计算的多项式设置为大于1即可 10.SPI1->CR2=1 DMA_InitTypeDef DMA_InitStructure; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能 DMA 时钟 DMA_DeInit(DMA1_Channel3); DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&SPI1->DR; //DMA 外设 ADC 基地址 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)OLED_SRAM; //DMA 内存基地址 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //从储存器读取发送到外设 DMA_InitStructure.DMA_BufferSize = 1024; //DMA 通道的 DMA 缓存的大小 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址不变 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址递增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //8 位 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 8 位 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环传输模式 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA 通道 x 拥有中优先级 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //非内存到内存传输 DMA_Init(DMA1_Channel3, &DMA_InitStructure); //根据指定的参数初始化 //DMA_Cmd(DMA1_Channel3, DISABLE); //不使能DMA1 CH3所指示的通道 DMA_Cmd(DMA1_Channel3, ENABLE); //使能DMA1 CH3所指示的通道 } 五.命令发送函数和OLED初始化:

DC脚低电平时写入命令,高电平时写入数据

SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE)函数获取缓存区是否为空 SPI_I2S_SendData(SPI1, TxData)函数发送发送寄存器中的数据

(这两个函数是官方标准库中自带的 可以在stm32f10x_spi.c和stm32f10x_spi.h找到使用方法)

void OLED_SendCmd(u8 TxData) //发送命令 { u8 retry=0; OLED_DC_CMD(); //命令模式 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的 SPI标志位设置与否:发送缓存空标志位 { retry++; if(retry>200) return ; } delay_ms(100); SPI_I2S_SendData(SPI1, TxData); //通过外设 SPIx 发送一个数据 retry=0; OLED_DC_DAT(); //数据模式 }

关于OLED初始化 以下是需要注意的

OLED_RST_OFF(); delay_ms(100); //延时很重要!否则电平变化太快ssd1306无法检测到变化 OLED_RST_ON(); 重置OLEDOLED_SendCmd(0xd5);//设置时钟分频因子,震荡频率 OLED_SendCmd(0x80);//[3:0],分频因子;[7:4],震荡频率 不分频,震荡频率即有效采样八次后写入数据 OLED_SendCmd(0xc9);//0xc9上下反置 0xc8正常 OLED_SendCmd(0xa1);//0xa0左右反置 0xa1正常 因为OLED_SRAM[X][Y]中X,Y越小越先被输入到OLED中 如果设置为正常模式,则会显示反过来的图像 void OLED_Init(void) //初始化函数 { GPIO_Configuration();//端口初始化 delay_ms(1000); OLED_RST_OFF(); delay_ms(100); OLED_RST_ON(); OLED_SendCmd(0xae);//关闭显示 OLED_SendCmd(0xd5);//设置时钟分频因子,震荡频率 OLED_SendCmd(0x80);//[3:0],分频因子;[7:4],震荡频率 OLED_SendCmd(0x81);//设置对比度 OLED_SendCmd(0x7f);//128 OLED_SendCmd(0x8d);//设置电荷泵开关 OLED_SendCmd(0x14);//开 OLED_SendCmd(0x20);//设置模式 OLED_SendCmd(0x00);//设置为水平地址模式 OLED_SendCmd(0x21);//设置列地址的起始和结束的位置 OLED_SendCmd(0x00);//0 OLED_SendCmd(0x7f);//127 OLED_SendCmd(0x22);//设置页地址的起始和结束的位置 OLED_SendCmd(0x00);//0 OLED_SendCmd(0x07);//7 OLED_SendCmd(0xc9);//0xc9上下反置 0xc8正常 OLED_SendCmd(0xa1);//0xa0左右反置 0xa1正常 OLED_SendCmd(0xa4);//全局显示开启;0xa4正常,0xa5无视命令点亮全屏 OLED_SendCmd(0xa6);//设置显示方式;bit0:1,反相显示;0,正常显示 OLED_SendCmd(0xaf);//开启显示 OLED_SendCmd(0x56); DMA_OLED_Init();//DMA初始化 } 六.写入文字

通过之前的配置,STM32已经能够写入OLED的显存啦! 因为此款OLED没有自带字库,所以在STM32中写入字库

const u8 F6X8[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 , // sp 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00 , // ! 0x00, 0x00, 0x07, 0x00, 0x07, 0x00 , // " 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14 , // # 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12 , // $ 0x00, 0x62, 0x64, 0x08, 0x13, 0x23 , // % 0x00, 0x36, 0x49, 0x55, 0x22, 0x50 , // & 0x00, 0x00, 0x05, 0x03, 0x00, 0x00 , // ' 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00 , // ( 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00 , // ) 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14 , // * 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08 , // + 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00 , // , 0x00, 0x08, 0x08, 0x08, 0x08, 0x08 , // - 0x00, 0x00, 0x60, 0x60, 0x00, 0x00 , // . 0x00, 0x20, 0x10, 0x08, 0x04, 0x02 , // / 0x00, 0x3E, 0x51, 0x49, 0x45, 0x3E , // 0 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00 , // 1 0x00, 0x42, 0x61, 0x51, 0x49, 0x46 , // 2 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31 , // 3 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10 , // 4 0x00, 0x27, 0x45, 0x45, 0x45, 0x39 , // 5 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30 , // 6 0x00, 0x01, 0x71, 0x09, 0x05, 0x03 , // 7 0x00, 0x36, 0x49, 0x49, 0x49, 0x36 , // 8 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E , // 9 0x00, 0x00, 0x36, 0x36, 0x00, 0x00 , // : 0x00, 0x00, 0x56, 0x36, 0x00, 0x00 , // ; 0x00, 0x08, 0x14, 0x22, 0x41, 0x00 , // 0x00, 0x02, 0x01, 0x51, 0x09, 0x06 , // ? 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E , // @ 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C , // A 0x00, 0x7F, 0x49, 0x49, 0x49, 0x36 , // B 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22 , // C 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C , // D 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41 , // E 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01 , // F 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A , // G 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F , // H 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00 , // I 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01 , // J 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41 , // K 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40 , // L 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F , // M 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F , // N 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E , // O 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06 , // P 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E , // Q 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46 , // R 0x00, 0x46, 0x49, 0x49, 0x49, 0x31 , // S 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01 , // T 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F , // U 0x00, 0x1F, 0x20, 0x40, 0x20, 0x1F , // V 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F , // W 0x00, 0x63, 0x14, 0x08, 0x14, 0x63 , // X 0x00, 0x07, 0x08, 0x70, 0x08, 0x07 , // Y 0x00, 0x61, 0x51, 0x49, 0x45, 0x43 , // Z 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00 , // [ 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55 , // 55 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00 , // ] 0x00, 0x04, 0x02, 0x01, 0x02, 0x04 , // ^ 0x00, 0x40, 0x40, 0x40, 0x40, 0x40 , // _ 0x00, 0x00, 0x01, 0x02, 0x04, 0x00 , // ' 0x00, 0x20, 0x54, 0x54, 0x54, 0x78 , // a 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38 , // b 0x00, 0x38, 0x44, 0x44, 0x44, 0x20 , // c 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F , // d 0x00, 0x38, 0x54, 0x54, 0x54, 0x18 , // e 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02 , // f 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C , // g 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78 , // h 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00 , // i 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00 , // j 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00 , // k 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00 , // l 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78 , // m 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78 , // n 0x00, 0x38, 0x44, 0x44, 0x44, 0x38 , // o 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18 , // p 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC , // q 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08 , // r 0x00, 0x48, 0x54, 0x54, 0x54, 0x20 , // s 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20 , // t 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C , // u 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C , // v 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C , // w 0x00, 0x44, 0x28, 0x10, 0x28, 0x44 , // x 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C , // y 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44 , // z 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 // horiz lines };

下面是我自己写的OLED_Write的函数,调用即可 OLED_Write(页,列,文字,大小) 暂时只支持S6X8,S8X16大小暂不可用

void OLED_Write(u8 ye,u8 lie,u8* ascii,u8 size) { u8 i=0,j=0,c=*ascii; if (size==S6X8) { for (i=0;i for (j=0;j OLED_SRAM[ye+j][lie+i]=F8X16[(c-32)*8+1+i]; } } } 7.main主函数和测试

在[0,1]的位置显示A 在[1,1]的位置显示B 在[3,50]的位置显示C

#include "oled_spi_dma.h" #include "delay.h" int main() { uint8_t OLED_SRAM[8][128]; //图像储存在SRAM里 delay_init(); //delay函数不初始化要出大问题!!!!!!!!! OLED_Init(); OLED_Write(0,1,"A",0); OLED_Write(2,1,"B",0); OLED_Write(3,50,"C",0); }

在这里插入图片描述 成功!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有