STM32F103系列_OLED屏幕(SSD1306、SSD1315驱动)SPI驱动【DMA】(高刷)
- 一、SSD1306和SSD1315
- 二、电路原理图(SPI接法)
- 三、STM32_SPI
- 四、STM32_DMA
- 五、代码
-
- OLED.c
- OLED.h
- OLED_Library.h
- Delay.h
- 六、调用方法
-
- 例:main.c
- 七、库函数的优缺点
-
- 优点
- 缺点
一、SSD1306和SSD1315
分辨率都是128*64,电压都在3.3V最好,两者可以相互替代,但价格上SSD1315会比SSD1306便宜,毕竟用的人少。
二、电路原理图(SPI接法)
为提高屏幕刷新速度(帧率),SPI接法远优于IIC接法。 电路图如下: 其中:
- 电源为3.3V最佳显示效果。
- 电阻电容包装建议大于或等于。
这里OLED的四条SPI直接对接信号线对应的接口上。
- 这四条SPI建议信号线越短越好,尽量避免绕线过多,即从OLED屏幕引脚到STM32芯片的距离尽量要短,以减小周围信号对SPI干扰信号线,避免屏幕显示异常。
- SPI的四条信号线建议间距为7~10mil,并排走。
- SPI建议四条信号线与其他信号之间的距离≥20mil。
三、STM32_SPI
作者@Swiler的文章《STM32之SPI详细分析很好,可以参考。
四、STM32_DMA
作者@Z小旋的文章《【STM32】 DMA原理,步骤超详细解释,一篇文章理解DMA》说得好,可以参考一下。
五、代码
OLED.c
#include "stm32f10x.h" #include "Delay.h" #include "OLED.h" #include "OLED_Library.h" // OLED屏幕ISP接口初始化 void OLED_IO_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; ///重用输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA5(SCL),PA7(SDA) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度50MHz GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA4(RST),PA6(DC) GPIO_SetBits(GPIOA, GPIO_Pin_5 | GPIO_Pin_7); // PA5 and PA7上拉 SPI_InitStructure.SPI_Direction = SPI_Direction_1Line_Tx; //设置SPI单线只发送 SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主SPI SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; // SPI 发送接收8位帧结构 SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; //串行同步时钟的空闲状态为低电平 SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //第1个跳变沿数据被采样 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; // NSS主机片选信号(CS)由软件控制 SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16; //预分频 16 // SPI 速度设置函数(调整传输速度快慢 只有4个分频可选) // SPI_BaudRatePrescaler_2 2 分频 (SPI 36M@sys 72M) // SPI_BaudRatePrescaler_8 8 分频 (SPI 9M@sys 72M) // SPI_BaudRatePrescaler_16 16 分频 (SPI 4.5M@sys 72M) // SPI_BaudRatePrescaler_256 256 分频 (SPI 281.25K@sys 72M) SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //数据传输从 MSB 高位开始 低位为LSB SPI_InitStructure.SPI_CRCPolynomial = 7; // CRC 值计算的多项式 SPI_Init(SPI1, &SPI_InitStructure); //根据指定的参数初始化外设 SPIx 寄存器 SPI1->CR2 = 1 << 1; //允许DMA往缓冲区内发送 SPI_Cmd(SPI1, ENABLE); //使能 SPI 外设 }; uint8_t OLED_SRAM[8][128]; //图像储存在SRAM里 void OLED_DMA_Init(void) { 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所指示的通道 } void OLED_SendCmd(u8 TxData) //发送命令 { OLED_DC_CMD(); //命令模式 while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //检查指定的 SPI标志位设置与否:发送缓存空标志位 { for (u8 retry = 0; retry < 200; retry++) ; return; } Delay_ms(100); SPI_I2S_SendData(SPI1, TxData); //通过外设 SPIx 发送一个数据 OLED_DC_DAT(); //数据模式 } // OLED初始化函数 void OLED_Init(void) { OLED_IO_Init(); //端口初始化 Delay_s(1); //延时1秒稳定端口状态 OLED_RST_OFF(); // OLED复位 Delay_ms(10); //复位延时 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(0xc8); // 0xc9上下反置 0xc8正常 OLED_SendCmd(0xa1); // 0xa0左右反置 0xa1正常 OLED_SendCmd(0xa4); //全局显示开启;0xa4正常,0xa5无视命令点亮全屏 OLED_SendCmd(0xa6); //设置显示方式;bit0:1,反相显示;0,正常显示 OLED_SendCmd(0xaf); //开启显示 OLED_SendCmd(0x56); OLED_DMA_Init(); // DMA初始化 } //指定位置显示单字符,X+Y+单字符 void OLED_Write(u8 x, u8 y, u8 *ascii) { u8 i = 0, c = *ascii; for (i = 0; i < 6; i++) OLED_SRAM[y][x + i] = YIN_F6X8[(c - 32) * 6 + 1 + i]; } //清屏--全灭 void OLED_Clear(void) { for (u8 y = 0; y < 7; y++) for (u8 x = 0; x < 126; x += 6) OLED_ZFC(x, y, " "); } char OLED_zfc[] = { 0}; //字符转化为字符串储存于此数组 //显示多个字符,x+y+字符串 void OLED_ZFC(u8 x, u8 y, u8 *chr) { u8 j = 0; while (chr[j] != '\0') { u8 c = chr[j]; for (u8 i = 0; i < 6; i++) OLED_SRAM[y][x + i] = YIN_F6X8[(c - 32) * 6 + 1 + i]; x += 6; if (x > 120)//自动换行 { x = 0; y++; } j++; } }
OLED.h
#ifndef __OLED_H #define __OLED_H #include "stm32f10x.h" #pragma diag_suppress 167, 940 //消除格式警告 extern char OLED_zfc[]; //字符转化为字符串储存于此数组 #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 OLED_IO_Init(void); // GPIO和SPI初始化 void OLED_Write(u8 lie, u8 ye, u8 *ascii); //写入ASCII文字 void OLED_SendCmd(u8 TxData); //发送命令 void OLED_Clear(void); void OLED_ZFC(u8 x, u8 y, u8 *chr); void OLED_Init(void); // OLED初始化 void OLED_DMA_Init(void); // DMA初始化 #endif
OLED_Library.h
#ifndef __OLED_LIBRARY_H #define __OLED_LIBRARY_H const u8 YIN_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, 0x14, 0x14, 0x14, 0x14, 0x14, // = 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // > 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,