STM32 上使用 SPI 总线驱动 OLED 的程序

STM32 上使用 SPI 总线驱动 OLED(SSD1306,128×64)的程序


一、硬件连接(SPI 方式)

1、典型 SPI OLED 引脚

OLED 说明 STM32
GND GND
VCC 3.3V / 5V 3.3V
D0 SPI CLK SPI_SCK
D1 SPI MOSI SPI_MOSI
RES 复位 GPIO
DC 数据/命令 GPIO
CS 片选 GPIO

SPI 模式

  • CPOL = 0
  • CPHA = 0
  • MSB First
  • 8 bit

二、CubeMX 配置要点

SPI 配置

  • Mode:Full-Duplex Master
  • Hardware NSS:Disable
  • Data Size:8 bits
  • Prescaler:4 ~ 16(建议 8)
  • Clock Polarity:Low
  • Clock Phase:1 Edge

GPIO

引脚 模式
OLED_DC Output Push-Pull
OLED_RST Output Push-Pull
OLED_CS Output Push-Pull

三、OLED 驱动代码(HAL 库)

1、头文件 oled.h

c 复制代码
#ifndef __OLED_H
#define __OLED_H

#include "stm32f1xx_hal.h"

#define OLED_WIDTH   128
#define OLED_HEIGHT  64

#define OLED_DC_PORT  GPIOA
#define OLED_DC_PIN  GPIO_PIN_6

#define OLED_RST_PORT GPIOA
#define OLED_RST_PIN GPIO_PIN_7

#define OLED_CS_PORT  GPIOA
#define OLED_CS_PIN  GPIO_PIN_8

#define OLED_DC_LOW()  HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_RESET)
#define OLED_DC_HIGH() HAL_GPIO_WritePin(OLED_DC_PORT, OLED_DC_PIN, GPIO_PIN_SET)

#define OLED_RST_LOW()  HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_RESET)
#define OLED_RST_HIGH() HAL_GPIO_WritePin(OLED_RST_PORT, OLED_RST_PIN, GPIO_PIN_SET)

#define OLED_CS_LOW()  HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_RESET)
#define OLED_CS_HIGH() HAL_GPIO_WritePin(OLED_CS_PORT, OLED_CS_PIN, GPIO_PIN_SET)

void OLED_Init(void);
void OLED_Clear(void);
void OLED_ShowChar(uint8_t x, uint8_t y, char ch);
void OLED_ShowString(uint8_t x, uint8_t y, char *str);
void OLED_Refresh(void);

#endif

2、驱动源文件 oled.c

c 复制代码
#include "oled.h"
#include "spi.h"
#include "font.h"

static uint8_t OLED_Buffer[OLED_WIDTH * OLED_HEIGHT / 8];

/* 写命令 */
static void OLED_WriteCmd(uint8_t cmd)
{
    OLED_DC_LOW();
    OLED_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &cmd, 1, 10);
    OLED_CS_HIGH();
}

/* 写数据 */
static void OLED_WriteData(uint8_t dat)
{
    OLED_DC_HIGH();
    OLED_CS_LOW();
    HAL_SPI_Transmit(&hspi1, &dat, 1, 10);
    OLED_CS_HIGH();
}

/* 初始化 */
void OLED_Init(void)
{
    OLED_RST_LOW();
    HAL_Delay(100);
    OLED_RST_HIGH();
    HAL_Delay(100);

    OLED_WriteCmd(0xAE); // 关显示
    OLED_WriteCmd(0x20); // 地址模式
    OLED_WriteCmd(0x10); // 页地址模式
    OLED_WriteCmd(0xB0); // 页起始
    OLED_WriteCmd(0xC8); // COM 扫描方向
    OLED_WriteCmd(0x00); // 列低地址
    OLED_WriteCmd(0x10); // 列高地址
    OLED_WriteCmd(0x40); // 起始行
    OLED_WriteCmd(0x81); // 对比度
    OLED_WriteCmd(0xFF);
    OLED_WriteCmd(0xA1); // 段重映射
    OLED_WriteCmd(0xA6); // 正常显示
    OLED_WriteCmd(0xA8); // 多路复用
    OLED_WriteCmd(0x3F);
    OLED_WriteCmd(0xD3); // 显示偏移
    OLED_WriteCmd(0x00);
    OLED_WriteCmd(0xD5); // 时钟分频
    OLED_WriteCmd(0x80);
    OLED_WriteCmd(0xD9); // 预充电
    OLED_WriteCmd(0xF1);
    OLED_WriteCmd(0xDA); // COM 引脚
    OLED_WriteCmd(0x12);
    OLED_WriteCmd(0xDB); // VCOMH
    OLED_WriteCmd(0x40);
    OLED_WriteCmd(0xAF); // 开显示
}

/* 清屏 */
void OLED_Clear(void)
{
    memset(OLED_Buffer, 0x00, sizeof(OLED_Buffer));
}

/* 刷新显存 */
void OLED_Refresh(void)
{
    for (uint8_t i = 0; i < 8; i++) {
        OLED_WriteCmd(0xB0 + i);
        OLED_WriteCmd(0x00);
        OLED_WriteCmd(0x10);

        for (uint8_t j = 0; j < 128; j++) {
            OLED_WriteData(OLED_Buffer[i * 128 + j]);
        }
    }
}

/* 画点 */
void OLED_DrawPixel(uint8_t x, uint8_t y, uint8_t color)
{
    if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return;

    if (color)
        OLED_Buffer[x + (y / 8) * OLED_WIDTH] |= (1 << (y % 8));
    else
        OLED_Buffer[x + (y / 8) * OLED_WIDTH] &= ~(1 << (y % 8));
}

/* 显示字符 */
void OLED_ShowChar(uint8_t x, uint8_t y, char ch)
{
    uint8_t c = ch - ' ';
    for (uint8_t i = 0; i < 6; i++) {
        uint8_t line = font6x8[c][i];
        for (uint8_t j = 0; j < 8; j++) {
            OLED_DrawPixel(x + i, y + j, (line >> j) & 0x01);
        }
    }
}

/* 显示字符串 */
void OLED_ShowString(uint8_t x, uint8_t y, char *str)
{
    while (*str) {
        OLED_ShowChar(x, y, *str++);
        x += 6;
    }
}

3、ASCII 字库 font.h

c 复制代码
#ifndef __FONT_H
#define __FONT_H

#include <stdint.h>

extern const uint8_t font6x8[][6];

#endif
c 复制代码
#include "font.h"

const uint8_t font6x8[][6] = {
{0x00,0x00,0x00,0x00,0x00,0x00}, //  
{0x00,0x00,0x5F,0x00,0x00,0x00}, // !
{0x00,0x07,0x00,0x07,0x00,0x00}, // "
{0x14,0x7F,0x14,0x7F,0x14,0x00}, // #
{0x24,0x2A,0x7F,0x2A,0x12,0x00}, // $
{0x23,0x13,0x08,0x64,0x62,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}, // )
{0x08,0x2A,0x1C,0x2A,0x08,0x00}, // *
{0x08,0x08,0x3E,0x08,0x08,0x00}, // +
{0x00,0x50,0x30,0x00,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,0x00}, // 0
{0x00,0x42,0x7F,0x40,0x00,0x00}, // 1
{0x42,0x61,0x51,0x49,0x46,0x00}, // 2
{0x21,0x41,0x45,0x4B,0x31,0x00}, // 3
{0x18,0x14,0x12,0x7F,0x10,0x00}, // 4
{0x27,0x45,0x45,0x45,0x39,0x00}, // 5
{0x3C,0x4A,0x49,0x49,0x30,0x00}, // 6
{0x01,0x71,0x09,0x05,0x03,0x00}, // 7
{0x36,0x49,0x49,0x49,0x36,0x00}, // 8
{0x06,0x49,0x49,0x29,0x1E,0x00}, // 9
{0x00,0x36,0x36,0x00,0x00,0x00}, // :
{0x00,0x56,0x36,0x00,0x00,0x00}, // ;
{0x00,0x08,0x14,0x22,0x41,0x00}, // <
{0x14,0x14,0x14,0x14,0x14,0x00}, // =
{0x41,0x22,0x14,0x08,0x00,0x00}, // >
{0x02,0x01,0x51,0x09,0x06,0x00}, // ?
{0x32,0x49,0x79,0x41,0x3E,0x00}, // @
{0x7E,0x11,0x11,0x11,0x7E,0x00}, // A
{0x7F,0x49,0x49,0x49,0x36,0x00}, // B
{0x3E,0x41,0x41,0x41,0x22,0x00}, // C
{0x7F,0x41,0x41,0x22,0x1C,0x00}, // D
{0x7F,0x49,0x49,0x49,0x41,0x00}, // E
{0x7F,0x09,0x09,0x09,0x01,0x00}, // F
{0x3E,0x41,0x49,0x49,0x7A,0x00}, // G
{0x7F,0x08,0x08,0x08,0x7F,0x00}, // H
{0x00,0x41,0x7F,0x41,0x00,0x00}, // I
{0x20,0x40,0x41,0x3F,0x01,0x00}, // J
{0x7F,0x08,0x14,0x22,0x41,0x00}, // K
{0x7F,0x40,0x40,0x40,0x40,0x00}, // L
{0x7F,0x02,0x0C,0x02,0x7F,0x00}, // M
{0x7F,0x04,0x08,0x10,0x7F,0x00}, // N
{0x3E,0x41,0x41,0x41,0x3E,0x00}, // O
{0x7F,0x09,0x09,0x09,0x06,0x00}, // P
{0x3E,0x41,0x51,0x21,0x5E,0x00}, // Q
{0x7F,0x09,0x19,0x29,0x46,0x00}, // R
{0x46,0x49,0x49,0x49,0x31,0x00}, // S
{0x01,0x01,0x7F,0x01,0x01,0x00}, // T
{0x3F,0x40,0x40,0x40,0x3F,0x00}, // U
{0x1F,0x20,0x40,0x20,0x1F,0x00}, // V
{0x3F,0x40,0x38,0x40,0x3F,0x00}, // W
{0x63,0x14,0x08,0x14,0x63,0x00}, // X
{0x07,0x08,0x70,0x08,0x07,0x00}, // Y
{0x61,0x51,0x49,0x45,0x43,0x00}, // Z
};

4、主函数调用

c 复制代码
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_SPI1_Init();

    OLED_Init();
    OLED_Clear();

    OLED_ShowString(0, 0, "STM32 OLED");
    OLED_ShowString(0, 16, "SPI TEST OK");
    OLED_Refresh();

    while (1);
}

参考代码 STM32单片机;OLED程序,使用SPI总线 www.youwenfan.com/contentcsv/69789.html

四、常见问题

问题 原因
屏幕不亮 RES 未拉高
花屏 SPI 时钟太快
缺字 字库未包含
刷新慢 未用 DMA
相关推荐
foundbug99910 小时前
STM32 + uIP协议栈实现TCP/UDP功能指南
stm32·tcp/ip·udp
XINVRY-FPGA10 小时前
XC7Z010-2CLG400I Xilinx Zynq-7000 FPGA
arm开发·嵌入式硬件·算法·fpga开发·硬件工程·dsp开发·fpga
WYH28710 小时前
一、驱动基础
linux·嵌入式硬件
XINVRY-FPGA11 小时前
XCZU11EG-2FFVC1156I Xilinx Zynq UltraScale+ MPSoC EG FPGA
图像处理·嵌入式硬件·计算机视觉·fpga开发·硬件工程·dsp开发·fpga
山木嵌入式1 天前
【STM32实战】轻量级任务调度器实现
stm32·单片机·rtos·任务调度器·裸机开发
guygg881 天前
基于霍尔传感器的BLDC控制源码
单片机·嵌入式硬件
ytttr8731 天前
DSP 28335 CAN总线通信程序
开发语言·stm32·单片机
一枝小雨1 天前
RISC-V架构sp寄存器 & RISC-V架构下FreeRTOS任务上下文保存与恢复
单片机·架构·嵌入式·risc-v·rtos·内核原理
BW.SU1 天前
PackagingTool 嵌入式资源打包合并工具
单片机·二进制·嵌入式开发·资源合并软件·图片打包