Hướng Dẫn Triển Khai Màn Hình OLED 0.96 Sử Dụng Giao Thức IIC Phần Cứng Trên Nền Tảng STM32

Dưới đây là quy trình tích hợp màn hình hiển thị OLED kích thước 0.96 inch lên board vi điều khiển STM32G0B1RET6 thông qua giao tiếp I2C phần cứng (Hardware I2C), hỗ trợ thư viện STM32 HAL. Môi trường phát triển bao gồm STM32CubeMX cho cấu hình hệ thống và Keil/VS Code làm nơi biên dịch mã nguồn.

Cài Đặt Cấu Hình Hệ Thống

Bước đầu tiên trong quá trình phát triển là thiết lập môi trường nhúng cơ bản:

  • Kiểm tra xung nhịp hệ thống: Cấu hình bộ điều chỉnh RCC để đảm bảo tần số hoạt động của vi xử lý đạt yêu cầu cho thời gian trễ chính xác.
  • Thiết lập chân GPIO: Xác định các chân SCL và SDA tương ứng với ngoại trừ I2C được chọn (ví dụ I2C1 trên port B).
  • Tải dự án: Xuất mã nguồn từ công cụ cấu hình sang thư mục dự án IDE (Keil MDK hoặc GCC dựa trên Visual Studio).

Sau khi hoàn thành cấu hình phần cứng, tiến hành tạo các module phần mềm để quản lý lớp truyền thông dữ liệu xuống màn hình.

Triển Khai Tầng Treo Và Điều Khiển

Để giảm độ phức tạp, mã nguồn được chia thành nhiều lớp độc lập. Lớp dưới cùng đảm nhiệm việc giao tiếp I2C, trong khi lớp trên xử lý logic hiển thị điểm ảnh, chữ và hình ảnh.

Mô-đun Giao Diện Cơ Bản

File header này định nghĩa các hằng số địa chỉ, lệnh mặc định và khai báo hàm cần thiết cho việc truy cập màn hình.


#ifndef OLED_INTERFACE_H
#define OLED_INTERFACE_H

#include "main.h"
#include "stm32g0xx_hal.h"

// Địa chỉ thiết bị trên bus I2C
#define OLED_DEV_ADDR       0x78 

// Các lệnh điều khiển hệ thống
#define CMD_SCROLL_LEFT     0x27
#define CMD_SCROLL_RIGHT    0x26
#define CMD_DISPLAY_ON      0xA7
#define CMD_DISPLAY_OFF     0xA6

// Đặc điểm màn hình 0.96 inch (128x64)
#define DISP_HEIGHT         64
#define DISP_WIDTH          128
#define DISP_PAGES          8

// Khai báo chức năng điều khiển
void Send_Command(uint8_t cmd);
void Send_Data(uint8_t data);
void Init_OLED(void);
void Clear_Display(void);
void Set_Pixel(int x, int y, uint8_t state);
void Refresh_Buffer(void);
#endif // OLED_INTERFACE_H

Trong file thực thi `.c`, chúng ta sử dụng API của HAL để gửi byte đơn lẻ qua bộ nhớ nhớ của thiết bị.


#include "OLED_Interface.h"
#include "i2c_driver.h"
#include "delay_lib.h"

uint8_t Display_Memory[DISP_PAGES][DISP_WIDTH];

/**
 * @brief  Gửi một byte dữ liệu tới register của OLED
 */
static void I2C_TransferByte(uint8_t reg_addr, uint8_t val) {
    extern I2C_HandleTypeDef hi2c_instance;
    HAL_I2C_Mem_Write(&hi2c_instance, OLED_DEV_ADDR, 
                      reg_addr, I2C_MEMADD_SIZE_8BIT, &val, 1, HAL_MAX_DELAY);
}

void Send_Command(uint8_t cmd) {
    // Byte lệnh thường bắt đầu ở addr 0x00
    I2C_TransferByte(0x00, cmd);
}

void Send_Data(uint8_t data) {
    // Byte dữ liệu thường bắt đầu ở addr 0x40
    I2C_TransferByte(0x40, data);
}

/**
 * @brief  Khởi tạo chuỗi lệnh cấu hình màn hình
 */
void Init_OLED(void) {
    // tắt hiển thị tạm thời
    Send_Command(0xAE); 
    // đặt chế độ địa chỉ bộ nhớ theo trang
    Send_Command(0x20); 
    Send_Command(0x10); 
    
    // định vị COM scan direction
    Send_Command(0xC8); 
    
    // cài đặt vị trí xuất hàng cột
    Send_Command(0x00); 
    Send_Command(0x10); 
    
    // đặt dòng khởi đầu
    Send_Command(0x40); 
    
    // điều chỉnh độ tương phản
    Send_Command(0x81); 
    Send_Command(0xFF); 
    
    // phân đoạn mapping ngược
    Send_Command(0xA1); 
    
    // hiển thị bình thường
    Send_Command(0xA6); 
    
    // đặt tỷ lệ đa dạng hóa COM
    Send_Command(0xA8); 
    Send_Command(0x3F); 
    
    // hiển thị RAM nội dung
    Send_Command(0xA4); 
    
    // set hiển thị offset
    Send_Command(0xD3); 
    Send_Command(0x00); 
    
    // clock divide ratio
    Send_Command(0xD5); 
    Send_Command(0xF0); 
    
    // pre-charge period
    Send_Command(0xD9); 
    Send_Command(0x22); 
    
    // com pin configuration
    Send_Command(0xDA); 
    Send_Command(0x12); 
    
    // vcomh setting
    Send_Command(0xDB); 
    Send_Command(0x20); 
    
    // bật charge pump
    Send_Command(0x8D); 
    Send_Command(0x14); 
    
    // mở panel
    Send_Command(0xAF); 
    
    Clear_Display();
}

/**
 * @brief  Xóa toàn bộ nội dung trên màn hình
 */
void Clear_Display(void) {
    for (int p = 0; p < DISP_PAGES; p++) {
        Send_Command(0xB0 + p);
        Send_Command(0x00);
        Send_Command(0x10);
        for (int c = 0; c < DISP_WIDTH; c++) {
            Send_Data(0x00);
        }
    }
}

/**
 * @brief  Cập nhật bộ đệm Ram ra màn hình vật lý
 */
void Refresh_Buffer(void) {
    for (uint8_t p = 0; p < DISP_PAGES; p++) {
        Send_Command(0xB0 + p);
        Send_Command(0x00);
        Send_Command(0x10);
        for (uint8_t c = 0; c < DISP_WIDTH; c++) {
            Send_Data(Display_Memory[p][c]);
        }
    }
}

/**
 * @brief  Đổi trạng thái điểm ảnh tại tọa độ (x,y)
 */
void Set_Pixel(int x, int y, uint8_t set_state) {
    if (x >= 0 && x < DISP_WIDTH && y >= 0 && y < DISP_HEIGHT) {
        uint8_t page_idx = y / 8;
        uint8_t bit_pos = y % 8;
        uint8_t mask = (1 << bit_pos);
        
        if (set_state) {
            Display_Memory[page_idx][x] |= mask;
        } else {
            Display_Memory[page_idx][x] &= ~mask;
        }
        Refresh_Buffer();
    }
}

/**
 * @brief  Hiệu chỉnh độ sáng màn hình
 */
void Adjust_Brightness(uint8_t level) {
    Send_Command(0x81);
    Send_Command(level);
}

Mô-đun Hiển Thị Cao Cấp

Lớp này xây dựng trên nền tảng I2C bên dưới để cung cấp các hàm vẽ ký tự, văn bản tiếng Trung và hình ảnh bitmap.


#include "OLED_Functions.h"
#include "OLED_Interface.h"

// Hàm vẽ chuỗi ký tự ASCII
void Draw_String(uint8_t x, uint8_t y, const char* str, uint8_t size) {
    // Logic lặp qua từng ký tự và vẽ điểm ảnh dựa trên ma trận font đã đăng ký
    // Kích thước 1: 6x8 pixel | Kích thước 2: 8x16 pixel
    
    for (const char* p = str; *p != '\0'; p++) {
        int char_idx = (*p) - 32;
        if (char_idx < 0) continue; // bỏ qua ký tự không nằm trong bảng
        
        // Xử lý giới hạn vùng hiển thị và auto-line wrap
        // ... (Logic vẽ chi tiết dựa trên ma trận F6X8 hoặc F8X16)
        
        x += (size == 1 ? 6 : 8);
        y += (size == 2 ? 16 : 8);
    }
}

// Hàm vẽ ký tự Unicode/CN16x16
void Draw_CN_Char(uint8_t x, uint8_t y, const uint8_t* chinese_code) {
    // Duyệt qua mảng từ điển chữ cái Hán, tìm kiếm mã chỉ số phù hợp
    // Sau đó gọi Set_Pixel dựa trên ma trận encoder
    // ... (Chi tiết thuật toán tìm kiếm và giải mã)
}

// Hàm hiển thị ảnh bitmap
void Show_Image(uint8_t x0, uint8_t y0, uint16_t width, uint16_t height, const uint8_t img_data[]) {
    // Duyệt tuần tự dữ liệu ảnh theo bit và viết vào bộ đệm ScreenBuffer
    const uint8_t* ptr = img_data;
    for (uint16_t row = y0; row < y0 + height; row += 8) {
        for (uint16_t col = x0; col < x0 + width; col++) {
             for (uint8_t bit = 0; bit < 8; bit++) {
                if ((*ptr >> bit) & 0x01) {
                    Set_Pixel(col, row + bit, 1);
                }
             }
             ptr++;
        }
    }
}

Ma Trận Font Chữ

Dữ liệu ma trận hiển thị (Font Table) chứa các giá trị hex đại diện cho hình dáng ký tự. Dưới đây là cấu trúc định nghĩa mẫu cho bộ ký tự nhỏ 6x8.


typedef struct {
    const uint8_t data[6];
} FontChar6x8;

const FontChar6x8 Font_Ascii[] = {
    // Ký hiệu khoảng trắng (Space)
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
    // Dấu chấm than (!)
    {0x00, 0x00, 0x00, 0x2F, 0x00, 0x00},
    // Dấu ngoặc kép (")
    {0x00, 0x00, 0x07, 0x00, 0x07, 0x00},
    // ... (Tiếp tục liệt kê các ký tự còn lại A-Z, 0-9...)
};

Tương tự, đối với ký tự tiếng Trung 16x16, ta sử dụng cấu trúc liên kết chứa cả mã chỉ dẫn và dữ liệu điểm ảnh 32-byte mỗi ký tự.


typedef struct {
    uint8_t index_id[2];
    uint8_t glyph_data[32];
} CharDB_GB2312;

// Ví dụ mã dữ liệu chữ "嵌入"
const CharDB_GB2312 CN_Font_Table[] = {
    {"嵌", {0x80, 0x80, 0xEE, 0x88, /* ... */}},
    {"入", {0x00, 0x00, 0x00, 0x00, /* ... */}},
    // Danh sách đầy đủ các ký tự GB2312 cần thiết cho ứng dụng
};

Ví Dụ Tích Hợp Vào Ứng Dụng Chính

Cuối cùng, đưa các module đã tạo vào luồng chạy chính của chương trình `main.c` để thực thi các tác vụ hiển thị.

Xử Lý Vẽ Chữ Tiếng Anh


int main(void) {
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_I2C1_Init();

    Init_OLED();
    Draw_String(0, 0, "System Ready", 1);
    Draw_String(0, 10, "Voltage OK", 2);
    
    while(1) {
        // Vòng lặp kiểm soát chính
        Task_Loop();
    }
}

Xử Lý Vẽ Văn Bản Tiếng Trung


    // ...
    Init_OLED();
    Draw_CN_Char(0, 16, "嵌入式");
    Draw_CN_Char(0, 32, "系统开发");
    // Lưu ý: Cần đảm bảo mã nguồn project được lưu bằng encoding GB2312/GBK 
    // để tránh lỗi ký tự khi biên dịch trên compiler Windows.

Hiệu Ứng Chuyển Động Ngang

Màn hình hỗ trợ cuộn nội dung trực tiếp từ driver controller.


void Activate_Scroll_Right(void) {
    Send_Command(0x2E); // Dừng cuộn
    Send_Command(0x26); // Chọn hướng sang phải
    Send_Command(0x00); // Virtual Bytes
    Send_Command(0x02); // Start Page
    Send_Command(0x05); // Frame interval
    Send_Command(0x04); // End Page
    Send_Command(0x00); 
    Send_Command(0xFF); 
    Send_Command(0x2F); // Bắt đầu cuộn
}

Hiển Thị Hình Ảnh Anim

Sử dụng mảng dữ liệu ảnh bitmap đã lấy từ tool convert để hiển thị chuyển động tuần tự.


// Biến toàn cục chứa danh sách khung hình
extern const unsigned char bmp_sequence[]; 

while (1) {
    uint16_t current_frame_index = Get_Current_Frame();
    Show_Image(0, 0, 128, 64, (bmp_sequence + current_frame_index));
    Delay_MS(200);
}

Thẻ: STM32 I2C OLED embedded-systems stm32-hal

Đăng vào ngày 30 tháng 8 lúc 01:33