欢迎光临
我们一直在努力

基于STM32L4XX的LCD液晶显示屏(HS13L01WZ01、驱动芯片:SH1106)驱动C程序设计

一、主要技术参数:

项目规格/参数
1. 显示模式 被动矩阵 (Passive Matrix)
2. 显示颜色 单色(白色)
3. 驱动占空比 1/64 Duty
4. 像素数量 128 × 64
5. PCB尺寸 35.4 × 33.5 × 2.6 (mm)
6. 有效显示区域 29.42 × 14.7 (mm)
7. 像素间距 0.23 × 0.23 (mm)
8. 像素尺寸 0.21 × 0.21 (mm)

二、外形尺寸:

三、引脚定义:

引脚编号符号描述
1 GND 逻辑电路地线
这是接地引脚,必须连接到地。
2 VCC OLED电源输入
这是电压供应引脚,必须连接到电源。
3 SCL 串行时钟输入 SCL
4 SDA 串行数据输入 SDA
5 DC 数据/命令控制
该引脚用于控制输入数据为显示数据或命令。当引脚为高电平时,SDA 输入被视为显示数据;当引脚为低电平时,SDA 输入将被传送到命令寄存器。
6 CS1 OLED芯片选择引脚;低电平使能,高电平禁用。
7 FS0 字库芯片数据输出引脚。
8 CS2 字库芯片选择引脚;低电平使能,高电平禁用。

四、指令集:

D/C#HexD7D6D5D4D3D2D1D0命令说明
0 81 1 0 0 0 0 0 0 1 设置对比度控制 双字节命令,从256级对比度中选择一级。对比度随数值增加而增加。(复位值 = 7Fh)
0 A[7:0] A7 A6 A5 A4 A3 A2 A1 A0
0 DB 1 1 0 1 1 0 1 1 设置VCOMH取消选择电平 A[6:4]: 十六进制码
A[7:0] 0 A6 A5 A4 0 0 0 0 000b 00h ~ 0.65 × V<sub>CC</sub>
010b 20h ~ 0.77 × V<sub>CC</sub>(复位值)
011b 30h ~ 0.83 × V<sub>CC</sub>
0 A6/A7 1 0 1 0 0 1 1 X0 设置正常/反相显示 A6h, X[0]=0b: 正常显示(复位值)
0 在RAM中:显示面板 OFF
1 在RAM中:显示面板 ON
A7h, X[0]=1b: 反相显示
0 在RAM中:显示面板 ON
1 在RAM中:显示面板 OFF
0 AE/AF 1 0 1 0 1 1 1 X0 设置显示 ON/OFF AEh, X[0]=0b: 显示 OFF(睡眠模式)(复位值)
AFh, X[0]=1b: 显示 ON(正常模式)
0 A0/A1 1 0 1 0 0 0 0 X0 设置段重映射 A0h, X[0]=0b: 列地址0映射到SEG0(复位值)
A1h, X[0]=1b: 列地址127映射到SEG0
0 C0/C8 1 1 0 0 0 0 0 X3 设置COM输出扫描方向 C0h, X[3]=0b: 正常模式(复位值)
从COM0扫描至COM[N-1]
C8h, X[3]=1b: 重映射模式
从COM[N-1]扫描至COM0
其中N为复用比

五、头文件:

#ifndef __SH1106_H
#define __SH1106_H

#include "stm32f1xx_hal.h"
#include "stdint.h"
#include "stdlib.h"
#include "string.h"
#include "fonts.h"

/* SH1106 OLED驱动芯片相关定义 */
#define SH1106_WIDTH          128
#define SH1106_HEIGHT         64
#define SH1106_BUFFER_SIZE    (SH1106_WIDTH * SH1106_HEIGHT / 8)

/* SH1106命令定义 */
#define SH1106_SET_CONTRAST       0x81
#define SH1106_SET_NORM_INV       0xA6
#define SH1106_SET_DISP           0xAE
#define SH1106_SET_SCAN_DIR       0xC0
#define SH1106_SET_DISP_OFFSET    0xD3
#define SH1106_SET_COM_PIN_CFG    0xDA
#define SH1106_SET_VCOM_DESEL     0xDB
#define SH1106_SET_DISP_CLK_DIV   0xD5
#define SH1106_SET_PRECHARGE      0xD9
#define SH1106_SET_MUX_RATIO      0xA8
#define SH1106_SET_CHARGE_PUMP    0x8D
#define SH1106_SET_DISP_START_LINE 0x40
#define SH1106_SET_SEG_REMAP      0xA0
#define SH1106_SET_MEM_ADDR_MODE  0x20
#define SH1106_SET_COL_ADDR       0x21
#define SH1106_SET_PAGE_ADDR      0x22

/* 通信模式选择 */
#define SH1106_I2C_ADDRESS    0x78  // I2C地址
// #define SH1106_SPI_MODE        // 如果使用SPI,取消注释

/* 颜色定义 */
#define BLACK           0
#define WHITE           1
#define INVERSE         2

/* 字体结构体 */
typedef struct {
    const uint8_t *data;
    uint16_t width;
    uint16_t height;
    uint8_t first_char;
    uint8_t last_char;
    const uint16_t *offset;
    const uint8_t *width_table;
} FontDef;

/* SH1106句柄结构体 */
typedef struct {
    uint16_t CurrentX;
    uint16_t CurrentY;
    uint8_t Inverted;
    uint8_t Initialized;
    uint8_t Buffer[SH1106_BUFFER_SIZE];
    I2C_HandleTypeDef *hi2c;      // I2C句柄
    SPI_HandleTypeDef *hspi;      // SPI句柄
    GPIO_TypeDef *CS_Port;        // 片选引脚
    uint16_t CS_Pin;
    GPIO_TypeDef *DC_Port;        // 数据/命令引脚
    uint16_t DC_Pin;
    GPIO_TypeDef *RES_Port;       // 复位引脚
    uint16_t RES_Pin;
} SH1106_t;

/* 函数声明 */
void SH1106_Init(SH1106_t *dev, I2C_HandleTypeDef *hi2c);
void SH1106_DrawPixel(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t color);
void SH1106_UpdateScreen(SH1106_t *dev);
void SH1106_Clear(SH1106_t *dev);
void SH1106_Fill(SH1106_t *dev, uint8_t color);
void SH1106_DrawLine(SH1106_t *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color);
void SH1106_DrawRectangle(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color);
void SH1106_DrawFilledRectangle(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color);
void SH1106_DrawCircle(SH1106_t *dev, uint8_t x0, uint8_t y0, uint8_t r, uint8_t color);
void SH1106_DrawFilledCircle(SH1106_t *dev, uint8_t x0, uint8_t y0, uint8_t r, uint8_t color);
void SH1106_DrawTriangle(SH1106_t *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, uint8_t color);
void SH1106_DrawFilledTriangle(SH1106_t *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, uint8_t color);
void SH1106_DrawBitmap(SH1106_t *dev, uint8_t x, uint8_t y, const uint8_t *bitmap, uint8_t w, uint8_t h, uint8_t color);
void SH1106_GotoXY(SH1106_t *dev, uint8_t x, uint8_t y);
char SH1106_Putc(SH1106_t *dev, char ch, FontDef *font, uint8_t color);
char SH1106_Puts(SH1106_t *dev, char *str, FontDef *font, uint8_t color);
void SH1106_InvertColors(SH1106_t *dev);
void SH1106_NormalColors(SH1106_t *dev);

#endif /* __SH1106_H */

六、源文件:

#include "sh1106.h"

/* 私有函数声明 */
static void SH1106_WriteCommand(SH1106_t *dev, uint8_t cmd);
static void SH1106_WriteData(SH1106_t *dev, uint8_t *data, uint16_t size);
static void SH1106_Reset(SH1106_t *dev);
static void SH1106_SetDisplayOn(SH1106_t *dev, uint8_t on);
static void SH1106_SetContrast(SH1106_t *dev, uint8_t contrast);

/* I2C通信函数 */
static void SH1106_I2C_Write(SH1106_t *dev, uint8_t reg, uint8_t *data, uint16_t size) {
    uint8_t buffer[size + 1];
    buffer[0] = reg;
    memcpy(&buffer[1], data, size);
    HAL_I2C_Master_Transmit(dev->hi2c, SH1106_I2C_ADDRESS, buffer, size + 1, HAL_MAX_DELAY);
}

/* 写命令函数 */
static void SH1106_WriteCommand(SH1106_t *dev, uint8_t cmd) {
    SH1106_I2C_Write(dev, 0x00, &cmd, 1);
}

/* 写数据函数 */
static void SH1106_WriteData(SH1106_t *dev, uint8_t *data, uint16_t size) {
    SH1106_I2C_Write(dev, 0x40, data, size);
}

/* 复位函数 */
static void SH1106_Reset(SH1106_t *dev) {
    if (dev->RES_Port) {
        HAL_GPIO_WritePin(dev->RES_Port, dev->RES_Pin, GPIO_PIN_RESET);
        HAL_Delay(10);
        HAL_GPIO_WritePin(dev->RES_Port, dev->RES_Pin, GPIO_PIN_SET);
        HAL_Delay(10);
    }
}

/* 初始化函数 */
void SH1106_Init(SH1106_t *dev, I2C_HandleTypeDef *hi2c) {
    dev->hi2c = hi2c;
    dev->CurrentX = 0;
    dev->CurrentY = 0;
    dev->Inverted = 0;
    dev->Initialized = 0;
    
    // 硬件复位
    SH1106_Reset(dev);
    
    // 初始化序列
    SH1106_WriteCommand(dev, SH1106_SET_DISP | 0x00);  // 显示关闭
    SH1106_WriteCommand(dev, SH1106_SET_MUX_RATIO);
    SH1106_WriteCommand(dev, 0x3F);  // 多路复用比例
    
    SH1106_WriteCommand(dev, SH1106_SET_DISP_OFFSET);
    SH1106_WriteCommand(dev, 0x00);  // 显示偏移
    
    SH1106_WriteCommand(dev, SH1106_SET_DISP_START_LINE | 0x00);
    
    SH1106_WriteCommand(dev, SH1106_SET_SEG_REMAP | 0x01);  // 段重映射
    SH1106_WriteCommand(dev, SH1106_SET_SCAN_DIR | 0x08);   // 扫描方向
    
    SH1106_WriteCommand(dev, SH1106_SET_COM_PIN_CFG);
    SH1106_WriteCommand(dev, 0x12);  // COM引脚配置
    
    SH1106_SetContrast(dev, 0x7F);   // 对比度设置
    
    SH1106_WriteCommand(dev, SH1106_SET_NORM_INV);  // 正常显示
    
    SH1106_WriteCommand(dev, SH1106_SET_DISP_CLK_DIV);
    SH1106_WriteCommand(dev, 0x80);  // 显示时钟分频
    
    SH1106_WriteCommand(dev, SH1106_SET_PRECHARGE);
    SH1106_WriteCommand(dev, 0x22);  // 预充电周期
    
    SH1106_WriteCommand(dev, SH1106_SET_VCOM_DESEL);
    SH1106_WriteCommand(dev, 0x30);  // VCOMH取消选择级别
    
    SH1106_WriteCommand(dev, SH1106_SET_CHARGE_PUMP);
    SH1106_WriteCommand(dev, 0x14);  // 电荷泵设置
    
    SH1106_WriteCommand(dev, SH1106_SET_DISP | 0x01);  // 显示开启
    
    HAL_Delay(100);
    
    // 清屏
    SH1106_Clear(dev);
    SH1106_UpdateScreen(dev);
    
    dev->Initialized = 1;
}

/* 设置对比度 */
static void SH1106_SetContrast(SH1106_t *dev, uint8_t contrast) {
    SH1106_WriteCommand(dev, SH1106_SET_CONTRAST);
    SH1106_WriteCommand(dev, contrast);
}

/* 画点函数 */
void SH1106_DrawPixel(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t color) {
    if (x >= SH1106_WIDTH || y >= SH1106_HEIGHT) {
        return;
    }
    
    if (dev->Inverted) {
        color = !color;
    }
    
    switch(color) {
        case WHITE:
            dev->Buffer[x + (y / 8) * SH1106_WIDTH] |= (1 << (y % 8));
            break;
        case BLACK:
            dev->Buffer[x + (y / 8) * SH1106_WIDTH] &= ~(1 << (y % 8));
            break;
        case INVERSE:
            dev->Buffer[x + (y / 8) * SH1106_WIDTH] ^= (1 << (y % 8));
            break;
    }
}

/* 更新屏幕显示 */
void SH1106_UpdateScreen(SH1106_t *dev) {
    uint8_t page;
    
    for (page = 0; page < 8; page++) {
        SH1106_WriteCommand(dev, 0xB0 + page);  // 设置页地址
        SH1106_WriteCommand(dev, 0x02);         // 设置列地址低4位
        SH1106_WriteCommand(dev, 0x10);         // 设置列地址高4位
        
        SH1106_WriteData(dev, &dev->Buffer[SH1106_WIDTH * page], SH1106_WIDTH);
    }
}

/* 清屏函数 */
void SH1106_Clear(SH1106_t *dev) {
    memset(dev->Buffer, 0x00, SH1106_BUFFER_SIZE);
}

/* 填充屏幕 */
void SH1106_Fill(SH1106_t *dev, uint8_t color) {
    uint8_t fill_value = (color == WHITE) ? 0xFF : 0x00;
    memset(dev->Buffer, fill_value, SH1106_BUFFER_SIZE);
}

/* 画线函数 */
void SH1106_DrawLine(SH1106_t *dev, uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t color) {
    int16_t dx = abs(x2 – x1);
    int16_t dy = abs(y2 – y1);
    int16_t sx = (x1 < x2) ? 1 : -1;
    int16_t sy = (y1 < y2) ? 1 : -1;
    int16_t err = dx – dy;
    int16_t err2;
    
    while (1) {
        SH1106_DrawPixel(dev, x1, y1, color);
        if (x1 == x2 && y1 == y2) break;
        err2 = 2 * err;
        if (err2 > -dy) {
            err -= dy;
            x1 += sx;
        }
        if (err2 < dx) {
            err += dx;
            y1 += sy;
        }
    }
}

/* 画矩形 */
void SH1106_DrawRectangle(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) {
    SH1106_DrawLine(dev, x, y, x + w, y, color);
    SH1106_DrawLine(dev, x + w, y, x + w, y + h, color);
    SH1106_DrawLine(dev, x + w, y + h, x, y + h, color);
    SH1106_DrawLine(dev, x, y + h, x, y, color);
}

/* 画填充矩形 */
void SH1106_DrawFilledRectangle(SH1106_t *dev, uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) {
    for (uint8_t i = 0; i <= h; i++) {
        SH1106_DrawLine(dev, x, y + i, x + w, y + i, color);
    }
}

/* 画圆 */
void SH1106_DrawCircle(SH1106_t *dev, uint8_t x0, uint8_t y0, uint8_t r, uint8_t color) {
    int16_t f = 1 – r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;
    
    SH1106_DrawPixel(dev, x0, y0 + r, color);
    SH1106_DrawPixel(dev, x0, y0 – r, color);
    SH1106_DrawPixel(dev, x0 + r, y0, color);
    SH1106_DrawPixel(dev, x0 – r, y0, color);
    
    while (x < y) {
        if (f >= 0) {
            y–;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;
        
        SH1106_DrawPixel(dev, x0 + x, y0 + y, color);
        SH1106_DrawPixel(dev, x0 – x, y0 + y, color);
        SH1106_DrawPixel(dev, x0 + x, y0 – y, color);
        SH1106_DrawPixel(dev, x0 – x, y0 – y, color);
        SH1106_DrawPixel(dev, x0 + y, y0 + x, color);
        SH1106_DrawPixel(dev, x0 – y, y0 + x, color);
        SH1106_DrawPixel(dev, x0 + y, y0 – x, color);
        SH1106_DrawPixel(dev, x0 – y, y0 – x, color);
    }
}

/* 画填充圆 */
void SH1106_DrawFilledCircle(SH1106_t *dev, uint8_t x0, uint8_t y0, uint8_t r, uint8_t color) {
    int16_t f = 1 – r;
    int16_t ddF_x = 1;
    int16_t ddF_y = -2 * r;
    int16_t x = 0;
    int16_t y = r;
    
    SH1106_DrawLine(dev, x0 – r, y0, x0 + r, y0, color);
    
    while (x < y) {
        if (f >= 0) {
            y–;
            ddF_y += 2;
            f += ddF_y;
        }
        x++;
        ddF_x += 2;
        f += ddF_x;
        
        SH1106_DrawLine(dev, x0 – x, y0 + y, x0 + x, y0 + y, color);
        SH1106_DrawLine(dev, x0 – x, y0 – y, x0 + x, y0 – y, color);
        SH1106_DrawLine(dev, x0 – y, y0 + x, x0 + y, y0 + x, color);
        SH1106_DrawLine(dev, x0 – y, y0 – x, x0 + y, y0 – x, color);
    }
}

/* 设置光标位置 */
void SH1106_GotoXY(SH1106_t *dev, uint8_t x, uint8_t y) {
    dev->CurrentX = x;
    dev->CurrentY = y;
}

/* 显示单个字符 */
char SH1106_Putc(SH1106_t *dev, char ch, FontDef *font, uint8_t color) {
    uint32_t i, b, j;
    
    if (ch < font->first_char || ch > font->last_char) {
        ch = '?';
    }
    
    // 获取字符在字体数据中的偏移
    uint32_t char_offset = (ch – font->first_char) * font->height * (font->width / 8 + ((font->width % 8) ? 1 : 0));
    
    for (i = 0; i < font->height; i++) {
        for (j = 0; j < font->width; j++) {
            b = font->data[char_offset + i * ((font->width + 7) / 8) + j / 8];
            b &= (1 << (7 – (j % 8)));
            if (b) {
                SH1106_DrawPixel(dev, dev->CurrentX + j, dev->CurrentY + i, color);
            } else {
                SH1106_DrawPixel(dev, dev->CurrentX + j, dev->CurrentY + i, !color);
            }
        }
    }
    
    dev->CurrentX += font->width;
    return ch;
}

/* 显示字符串 */
char SH1106_Puts(SH1106_t *dev, char *str, FontDef *font, uint8_t color) {
    while (*str) {
        if (SH1106_Putc(dev, *str, font, color) != *str) {
            return *str;
        }
        str++;
    }
    return *str;
}

/* 反色显示 */
void SH1106_InvertColors(SH1106_t *dev) {
    dev->Inverted = !dev->Inverted;
}

/* 正常显示 */
void SH1106_NormalColors(SH1106_t *dev) {
    dev->Inverted = 0;
}

七、字体头文件:

#ifndef __FONTS_H
#define __FONTS_H

#include <stdint.h>

/* 字体结构体 */
typedef struct {
    const uint8_t *data;
    uint16_t width;
    uint16_t height;
    uint8_t first_char;
    uint8_t last_char;
    const uint16_t *offset;
    const uint8_t *width_table;
} FontDef;

/* 字体定义 */
extern FontDef Font_7x10;
extern FontDef Font_11x18;
extern FontDef Font_16x26;

/* 8×16 ASCII字体数据 */
extern const uint8_t Font8x16[];

/* 6×8 ASCII字体数据 */
extern const uint8_t Font6x8[];

#endif /* __FONTS_H */

八、应用示例:

#include "main.h"
#include "sh1106.h"
#include "fonts.h"

/* 全局变量 */
SH1106_t oled;
I2C_HandleTypeDef hi2c1;

/* 系统初始化 */
void System_Init(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_I2C1_Init();
}

void OLED_Test(void) {
    // 初始化OLED
    SH1106_Init(&oled, &hi2c1);
    
    // 测试1: 清屏并显示文字
    SH1106_Clear(&oled);
    SH1106_GotoXY(&oled, 10, 10);
    SH1106_Puts(&oled, "Hello World!", &Font_7x10, WHITE);
    SH1106_UpdateScreen(&oled);
    HAL_Delay(2000);
    
    // 测试2: 画图形
    SH1106_Clear(&oled);
    SH1106_DrawRectangle(&oled, 10, 10, 100, 40, WHITE);
    SH1106_DrawFilledCircle(&oled, 64, 32, 15, WHITE);
    SH1106_UpdateScreen(&oled);
    HAL_Delay(2000);
    
    // 测试3: 显示数字和变量
    SH1106_Clear(&oled);
    SH1106_GotoXY(&oled, 20, 5);
    SH1106_Puts(&oled, "Temp:", &Font_7x10, WHITE);
    
    char temp_str[20];
    float temperature = 25.5f;
    sprintf(temp_str, "%.1f C", temperature);
    SH1106_GotoXY(&oled, 60, 5);
    SH1106_Puts(&oled, temp_str, &Font_7x10, WHITE);
    
    // 画进度条
    uint8_t progress = 75; // 75%
    SH1106_DrawRectangle(&oled, 20, 30, 84, 10, WHITE);
    SH1106_DrawFilledRectangle(&oled, 22, 32, progress * 80 / 100, 6, WHITE);
    
    SH1106_GotoXY(&oled, 20, 45);
    SH1106_Puts(&oled, "Progress:", &Font_7x10, WHITE);
    
    char prog_str[10];
    sprintf(prog_str, "%d%%", progress);
    SH1106_GotoXY(&oled, 80, 45);
    SH1106_Puts(&oled, prog_str, &Font_7x10, WHITE);
    
    SH1106_UpdateScreen(&oled);
}

/* 主函数 */
int main(void) {
    System_Init();
    
    while (1) {
        OLED_Test();
        HAL_Delay(5000);
    }
}

赞(0)
未经允许不得转载:171主机测评 » 基于STM32L4XX的LCD液晶显示屏(HS13L01WZ01、驱动芯片:SH1106)驱动C程序设计
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址