STM32F103 控制12864液晶显示屏(串行模式)实例

硬件规格

1. 显示屏信息

  • 型号:12864液晶(128×64点阵)
  • 控制器:ST7567/ST7565/UC1701等(兼容)
  • 接口:串行SPI/3线/4线模式
  • 背光:LED背光,可PWM调光

2. 引脚定义

12864模块引脚 -> STM32F103引脚
  1. VSS  ─── GND
  2. VDD  ─── 3.3V
  3. VO   ─── 10k电位器中点(对比度调节)
  4. RS   ─── PA4 (数据/命令选择)
  5. R/W  ─── 接地(串行模式固定为写)
  6. E    ─── PA5 (SPI时钟SCK)
  7. DB0  ─── 悬空
  8. DB1  ─── 悬空
  9. DB2  ─── 悬空
 10. DB3  ─── 悬空
 11. DB4  ─── 悬空
 12. DB5  ─── 悬空
 13. DB6  ─── 悬空
 14. DB7  ─── PA7 (SPI数据MOSI)
 15. PSB  ─── GND(选择串行模式)
 16. NC   ─── 悬空
 17. RST  ─── PA6 (复位)
 18. NC   ─── 悬空
 19. LED+ ─── 3.3V(背光正极)
 20. LED- ─── GND(背光负极)

C语言代码

1. 主程序文件(main.c)

/************************************************************
 * STM32F103 串行控制12864液晶显示屏
 * 硬件:STM32F103C8T6 (Blue Pill)
 * 接口:模拟SPI(4线串行模式)
 * 编译:Keil MDK-ARM V5
 ************************************************************/

#include "stm32f10x.h"
#include "delay.h"
#include "lcd12864.h"
#include "chinese_font.h"
#include <stdio.h>
#include <string.h>

// 全局变量
uint8_t display_buffer[8][128];  // 显示缓冲区(8页×128列)

int main(void) {
    // 系统初始化
    SystemInit();
    Delay_Init();
    LCD_Init();
    
    // 清屏
    LCD_Clear();
    
    // 显示欢迎信息
    LCD_ShowString(0, 0, "STM32F103 12864 Demo");
    LCD_ShowString(1, 0, "----------------------");
    LCD_ShowString(2, 0, "LCD: 128x64");
    LCD_ShowString(3, 0, "Mode: Serial SPI");
    LCD_ShowString(4, 0, "MCU: STM32F103C8T6");
    LCD_ShowString(5, 0, "Freq: 72MHz");
    LCD_ShowString(6, 0, "Author: AI Assistant");
    LCD_ShowString(7, 0, "Ver: 1.0");
    
    Delay_ms(2000);
    LCD_Clear();
    
    // 显示中文字符
    LCD_ShowChinese(0, 0, "欢迎使用");   // 位置:第0行,第0列
    LCD_ShowChinese(0, 32, "液晶显示屏"); // 位置:第0行,第32列
    LCD_ShowChinese(2, 0, "嵌入式开发");  // 位置:第2行,第0列
    LCD_ShowChinese(2, 32, "系统演示");   // 位置:第2行,第32列
    
    // 显示图形
    LCD_DrawRectangle(0, 0, 127, 63);  // 画边框
    
    // 画对角线
    for(uint8_t i = 0; i < 64; i++) {
        LCD_DrawPoint(i*2, i);
    }
    
    Delay_ms(2000);
    
    // 动态显示
    uint8_t counter = 0;
    float temperature = 25.6;
    float humidity = 60.3;
    
    while(1) {
        char buffer[20];
        
        // 清空第4-7行
        LCD_ClearLine(4);
        LCD_ClearLine(5);
        LCD_ClearLine(6);
        LCD_ClearLine(7);
        
        // 显示计数器
        sprintf(buffer, "Counter: %04d", counter);
        LCD_ShowString(4, 0, buffer);
        
        // 显示温度湿度
        sprintf(buffer, "Temp: %.1fC", temperature);
        LCD_ShowString(5, 0, buffer);
        
        sprintf(buffer, "Humidity: %.1f%%", humidity);
        LCD_ShowString(6, 0, buffer);
        
        // 显示进度条
        uint8_t progress = counter % 100;
        LCD_DrawProgressBar(7, 0, 127, progress, 100);
        
        // 模拟数据变化
        counter++;
        temperature += 0.1;
        humidity += 0.2;
        
        if(temperature > 30.0) temperature = 20.0;
        if(humidity > 80.0) humidity = 40.0;
        
        Delay_ms(200);
    }
}

2. LCD12864驱动程序(lcd12864.h)

#ifndef __LCD12864_H
#define __LCD12864_H

#include "stm32f10x.h"

// 引脚定义
#define LCD_RS_PIN    GPIO_Pin_4   // PA4: 数据/命令选择
#define LCD_RS_PORT   GPIOA
#define LCD_RS_CLK    RCC_APB2Periph_GPIOA

#define LCD_SCK_PIN   GPIO_Pin_5   // PA5: 时钟
#define LCD_SCK_PORT  GPIOA
#define LCD_SCK_CLK   RCC_APB2Periph_GPIOA

#define LCD_SDA_PIN   GPIO_Pin_7   // PA7: 数据
#define LCD_SDA_PORT  GPIOA
#define LCD_SDA_CLK   RCC_APB2Periph_GPIOA

#define LCD_RST_PIN   GPIO_Pin_6   // PA6: 复位
#define LCD_RST_PORT  GPIOA
#define LCD_RST_CLK   RCC_APB2Periph_GPIOA

// 控制命令
#define LCD_CMD       0
#define LCD_DATA      1

// LCD参数
#define LCD_WIDTH     128
#define LCD_HEIGHT    64
#define LCD_PAGES     8          // 64/8 = 8页
#define LCD_COLUMNS   128

// 函数声明
void LCD_GPIO_Init(void);
void LCD_Init(void);
void LCD_Clear(void);
void LCD_SetCursor(uint8_t page, uint8_t column);
void LCD_WriteByte(uint8_t data, uint8_t cmd);
void LCD_ShowChar(uint8_t page, uint8_t column, char ch);
void LCD_ShowString(uint8_t page, uint8_t column, char *str);
void LCD_ShowChinese(uint8_t page, uint8_t column, char *chinese);
void LCD_DrawPoint(uint8_t x, uint8_t y);
void LCD_ClearPoint(uint8_t x, uint8_t y);
uint8_t LCD_ReadPoint(uint8_t x, uint8_t y);
void LCD_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
void LCD_DrawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
void LCD_DrawCircle(uint8_t x0, uint8_t y0, uint8_t r);
void LCD_ShowBMP(uint8_t page, uint8_t column, uint8_t width, uint8_t height, const uint8_t *bmp);
void LCD_Refresh(void);
void LCD_ClearLine(uint8_t page);
void LCD_SetBacklight(uint8_t brightness);
void LCD_DrawProgressBar(uint8_t page, uint8_t x1, uint8_t x2, uint16_t value, uint16_t max_value);

#endif

3. LCD12864驱动程序(lcd12864.c)

#include "lcd12864.h"
#include "delay.h"
#include "ascii_font.h"
#include "chinese_font.h"
#include <string.h>

// 显示缓冲区(8页×128列)
static uint8_t display_buffer[8][128];

/************************************************************
 * 函数:LCD_GPIO_Init
 * 功能:初始化GPIO引脚
 ************************************************************/
void LCD_GPIO_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能时钟
    RCC_APB2PeriphClockCmd(LCD_RS_CLK | LCD_SCK_CLK | LCD_SDA_CLK | LCD_RST_CLK, ENABLE);
    
    // 配置RS引脚
    GPIO_InitStructure.GPIO_Pin = LCD_RS_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_RS_PORT, &GPIO_InitStructure);
    
    // 配置SCK引脚
    GPIO_InitStructure.GPIO_Pin = LCD_SCK_PIN;
    GPIO_Init(LCD_SCK_PORT, &GPIO_InitStructure);
    
    // 配置SDA引脚
    GPIO_InitStructure.GPIO_Pin = LCD_SDA_PIN;
    GPIO_Init(LCD_SDA_PORT, &GPIO_InitStructure);
    
    // 配置RST引脚
    GPIO_InitStructure.GPIO_Pin = LCD_RST_PIN;
    GPIO_Init(LCD_RST_PORT, &GPIO_InitStructure);
    
    // 初始状态
    GPIO_SetBits(LCD_RS_PORT, LCD_RS_PIN);
    GPIO_SetBits(LCD_SCK_PORT, LCD_SCK_PIN);
    GPIO_SetBits(LCD_SDA_PORT, LCD_SDA_PIN);
    GPIO_SetBits(LCD_RST_PORT, LCD_RST_PIN);
}

/************************************************************
 * 函数:LCD_WriteByte
 * 功能:向LCD写入一个字节(串行模式)
 * 参数:data - 要写入的数据
 *       cmd  - 命令(0)或数据(1)
 ************************************************************/
void LCD_WriteByte(uint8_t data, uint8_t cmd) {
    uint8_t i;
    
    // 设置RS:命令/数据选择
    if(cmd == LCD_CMD) {
        GPIO_ResetBits(LCD_RS_PORT, LCD_RS_PIN);  // 命令
    } else {
        GPIO_SetBits(LCD_RS_PORT, LCD_RS_PIN);    // 数据
    }
    
    // 发送8位数据
    for(i = 0; i < 8; i++) {
        // 设置数据位
        if(data & 0x80) {
            GPIO_SetBits(LCD_SDA_PORT, LCD_SDA_PIN);
        } else {
            GPIO_ResetBits(LCD_SDA_PORT, LCD_SDA_PIN);
        }
        
        // 产生时钟上升沿
        GPIO_ResetBits(LCD_SCK_PORT, LCD_SCK_PIN);
        Delay_us(2);
        GPIO_SetBits(LCD_SCK_PORT, LCD_SCK_PIN);
        Delay_us(2);
        
        data <<= 1;  // 左移一位
    }
    
    // 最后将RS置高
    GPIO_SetBits(LCD_RS_PORT, LCD_RS_PIN);
}

/************************************************************
 * 函数:LCD_Init
 * 功能:初始化LCD显示屏
 ************************************************************/
void LCD_Init(void) {
    // 初始化GPIO
    LCD_GPIO_Init();
    
    // 硬件复位
    GPIO_ResetBits(LCD_RST_PORT, LCD_RST_PIN);
    Delay_ms(20);
    GPIO_SetBits(LCD_RST_PORT, LCD_RST_PIN);
    Delay_ms(20);
    
    // 发送初始化命令序列
    // 注意:不同控制器初始化命令可能不同
    LCD_WriteByte(0xE2, LCD_CMD);  // 系统复位
    Delay_ms(10);
    
    LCD_WriteByte(0xA3, LCD_CMD);  // 偏压设置 1/7 bias
    LCD_WriteByte(0xA0, LCD_CMD);  // ADC选择 normal
    LCD_WriteByte(0xC8, LCD_CMD);  // COM扫描方向 reverse
    LCD_WriteByte(0x40, LCD_CMD);  // 起始行设为0
    LCD_WriteByte(0x25, LCD_CMD);  // 电阻比率设置
    LCD_WriteByte(0x81, LCD_CMD);  // 设置对比度
    LCD_WriteByte(0x1F, LCD_CMD);  // 对比度值 0-63
    LCD_WriteByte(0xA4, LCD_CMD);  // 全点显示关
    LCD_WriteByte(0xA6, LCD_CMD);  // 正显示
    LCD_WriteByte(0x2F, LCD_CMD);  // 电源控制
    LCD_WriteByte(0xAF, LCD_CMD);  // 开显示
    
    Delay_ms(100);
    
    // 清空显示缓冲区
    LCD_Clear();
    
    // 刷新到显示屏
    LCD_Refresh();
}

/************************************************************
 * 函数:LCD_SetCursor
 * 功能:设置显示位置
 * 参数:page   - 页地址 (0-7)
 *       column - 列地址 (0-127)
 ************************************************************/
void LCD_SetCursor(uint8_t page, uint8_t column) {
    if(page > 7) page = 7;
    if(column > 127) column = 127;
    
    // 设置页地址
    LCD_WriteByte(0xB0 + page, LCD_CMD);
    
    // 设置列地址高4位
    LCD_WriteByte(0x10 + (column >> 4), LCD_CMD);
    
    // 设置列地址低4位
    LCD_WriteByte(0x00 + (column & 0x0F), LCD_CMD);
}

/************************************************************
 * 函数:LCD_Clear
 * 功能:清屏
 ************************************************************/
void LCD_Clear(void) {
    uint8_t i, j;
    
    // 清空显示缓冲区
    for(i = 0; i < LCD_PAGES; i++) {
        for(j = 0; j < LCD_COLUMNS; j++) {
            display_buffer[i][j] = 0x00;
        }
    }
    
    // 刷新到屏幕
    LCD_Refresh();
}

/************************************************************
 * 函数:LCD_ClearLine
 * 功能:清除指定行
 * 参数:page - 要清除的行 (0-7)
 ************************************************************/
void LCD_ClearLine(uint8_t page) {
    if(page >= LCD_PAGES) return;
    
    for(uint8_t j = 0; j < LCD_COLUMNS; j++) {
        display_buffer[page][j] = 0x00;
    }
    
    // 刷新该行
    LCD_SetCursor(page, 0);
    for(uint8_t j = 0; j < LCD_COLUMNS; j++) {
        LCD_WriteByte(0x00, LCD_DATA);
    }
}

/************************************************************
 * 函数:LCD_Refresh
 * 功能:将缓冲区内容刷新到显示屏
 ************************************************************/
void LCD_Refresh(void) {
    uint8_t i, j;
    
    for(i = 0; i < LCD_PAGES; i++) {
        // 设置起始位置
        LCD_SetCursor(i, 0);
        
        // 发送该页的128个字节
        for(j = 0; j < LCD_COLUMNS; j++) {
            LCD_WriteByte(display_buffer[i][j], LCD_DATA);
        }
    }
}

/************************************************************
 * 函数:LCD_ShowChar
 * 功能:显示一个ASCII字符
 * 参数:page   - 页地址 (0-7)
 *       column - 列地址 (0-127)
 *       ch     - 要显示的字符
 ************************************************************/
void LCD_ShowChar(uint8_t page, uint8_t column, char ch) {
    uint8_t i;
    uint8_t *font_ptr;
    
    if(page >= LCD_PAGES || column >= LCD_COLUMNS) {
        return;
    }
    
    // 获取字模数据指针
    font_ptr = (uint8_t *)&ASCII_Font[(ch - 32) * 16];
    
    // 将字模写入缓冲区
    for(i = 0; i < 8; i++) {
        if(column < LCD_COLUMNS) {
            display_buffer[page][column] = font_ptr[i];
        }
        column++;
    }
    
    // 如果是宽字符,还需要显示下一行的8个像素
    for(i = 0; i < 8; i++) {
        if(column < LCD_COLUMNS) {
            display_buffer[page+1][column-8] = font_ptr[8+i];
        }
    }
}

/************************************************************
 * 函数:LCD_ShowString
 * 功能:显示字符串
 * 参数:page   - 起始页 (0-7)
 *       column - 起始列 (0-127)
 *       str    - 要显示的字符串
 ************************************************************/
void LCD_ShowString(uint8_t page, uint8_t column, char *str) {
    while(*str) {
        if(column > 122) {  // 换行处理
            column = 0;
            page += 2;  // 每个字符占2行高度
            if(page >= LCD_PAGES) break;
        }
        
        LCD_ShowChar(page, column, *str);
        column += 8;  // 每个字符占8列宽度
        str++;
    }
    
    // 刷新显示
    LCD_Refresh();
}

/************************************************************
 * 函数:LCD_ShowChinese
 * 功能:显示中文字符(16×16点阵)
 * 参数:page     - 起始页 (0-7)
 *       column   - 起始列 (0-127)
 *       chinese  - 要显示的中文字符串
 ************************************************************/
void LCD_ShowChinese(uint8_t page, uint8_t column, char *chinese) {
    uint8_t i, j, k = 0;
    uint8_t font_index;
    
    while(chinese[k] != '\0' && chinese[k+1] != '\0') {
        // 计算字模索引
        font_index = ((chinese[k] - 0xA1) * 94 + (chinese[k+1] - 0xA1)) * 32;
        
        // 显示上半部分(8行)
        for(i = 0; i < 16; i++) {
            if(column + i < LCD_COLUMNS) {
                display_buffer[page][column + i] = Chinese_Font[font_index + i];
            }
        }
        
        // 显示下半部分(8行)
        for(i = 0; i < 16; i++) {
            if(column + i < LCD_COLUMNS) {
                display_buffer[page + 1][column + i] = Chinese_Font[font_index + 16 + i];
            }
        }
        
        column += 16;  // 每个汉字占16列
        k += 2;        // 跳过2个字节
        
        if(column > 112) {  // 换行处理
            column = 0;
            page += 2;  // 每个汉字占2行高度
            if(page >= LCD_PAGES - 1) break;
        }
    }
    
    // 刷新显示
    LCD_Refresh();
}

/************************************************************
 * 函数:LCD_DrawPoint
 * 功能:画点
 * 参数:x, y - 坐标 (0-127, 0-63)
 ************************************************************/
void LCD_DrawPoint(uint8_t x, uint8_t y) {
    uint8_t page, bit_mask;
    
    if(x >= LCD_WIDTH || y >= LCD_HEIGHT) {
        return;
    }
    
    // 计算页地址和位掩码
    page = y / 8;
    bit_mask = 1 << (y % 8);
    
    // 设置点
    display_buffer[page][x] |= bit_mask;
}

/************************************************************
 * 函数:LCD_ClearPoint
 * 功能:清除点
 * 参数:x, y - 坐标 (0-127, 0-63)
 ************************************************************/
void LCD_ClearPoint(uint8_t x, uint8_t y) {
    uint8_t page, bit_mask;
    
    if(x >= LCD_WIDTH || y >= LCD_HEIGHT) {
        return;
    }
    
    // 计算页地址和位掩码
    page = y / 8;
    bit_mask = 1 << (y % 8);
    
    // 清除点
    display_buffer[page][x] &= ~bit_mask;
}

/************************************************************
 * 函数:LCD_DrawLine
 * 功能:画线(Bresenham算法)
 * 参数:x1, y1 - 起点坐标
 *       x2, y2 - 终点坐标
 ************************************************************/
void LCD_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
    int dx, dy, sx, sy, err, e2;
    
    dx = (x2 > x1) ? (x2 - x1) : (x1 - x2);
    dy = (y2 > y1) ? (y2 - y1) : (y1 - y2);
    sx = (x1 < x2) ? 1 : -1;
    sy = (y1 < y2) ? 1 : -1;
    err = dx - dy;
    
    while(1) {
        LCD_DrawPoint(x1, y1);
        
        if(x1 == x2 && y1 == y2) break;
        
        e2 = err * 2;
        if(e2 > -dy) {
            err -= dy;
            x1 += sx;
        }
        if(e2 < dx) {
            err += dx;
            y1 += sy;
        }
    }
    
    LCD_Refresh();
}

/************************************************************
 * 函数:LCD_DrawRectangle
 * 功能:画矩形
 * 参数:x1, y1 - 左上角坐标
 *       x2, y2 - 右下角坐标
 ************************************************************/
void LCD_DrawRectangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
    // 画四条边
    LCD_DrawLine(x1, y1, x2, y1);  // 上边
    LCD_DrawLine(x1, y2, x2, y2);  // 下边
    LCD_DrawLine(x1, y1, x1, y2);  // 左边
    LCD_DrawLine(x2, y1, x2, y2);  // 右边
}

/************************************************************
 * 函数:LCD_DrawCircle
 * 功能:画圆
 * 参数:x0, y0 - 圆心坐标
 *       r      - 半径
 ************************************************************/
void LCD_DrawCircle(uint8_t x0, uint8_t y0, uint8_t r) {
    int x = 0, y = r;
    int d = 3 - 2 * r;
    
    while(x <= y) {
        LCD_DrawPoint(x0 + x, y0 + y);
        LCD_DrawPoint(x0 - x, y0 + y);
        LCD_DrawPoint(x0 + x, y0 - y);
        LCD_DrawPoint(x0 - x, y0 - y);
        LCD_DrawPoint(x0 + y, y0 + x);
        LCD_DrawPoint(x0 - y, y0 + x);
        LCD_DrawPoint(x0 + y, y0 - x);
        LCD_DrawPoint(x0 - y, y0 - x);
        
        if(d < 0) {
            d = d + 4 * x + 6;
        } else {
            d = d + 4 * (x - y) + 10;
            y--;
        }
        x++;
    }
    
    LCD_Refresh();
}

/************************************************************
 * 函数:LCD_ShowBMP
 * 功能:显示位图
 * 参数:page    - 起始页
 *       column  - 起始列
 *       width   - 位图宽度
 *       height  - 位图高度
 *       bmp     - 位图数据指针
 ************************************************************/
void LCD_ShowBMP(uint8_t page, uint8_t column, uint8_t width, uint8_t height, const uint8_t *bmp) {
    uint8_t i, j, k;
    uint8_t pages = height / 8;
    
    for(i = 0; i < pages; i++) {
        for(j = 0; j < width; j++) {
            if((page + i) < LCD_PAGES && (column + j) < LCD_COLUMNS) {
                display_buffer[page + i][column + j] = bmp[i * width + j];
            }
        }
    }
    
    LCD_Refresh();
}

/************************************************************
 * 函数:LCD_DrawProgressBar
 * 功能:绘制进度条
 * 参数:page      - 行位置
 *       x1, x2    - 进度条左右边界
 *       value     - 当前值
 *       max_value - 最大值
 ************************************************************/
void LCD_DrawProgressBar(uint8_t page, uint8_t x1, uint8_t x2, uint16_t value, uint16_t max_value) {
    uint8_t i;
    uint8_t width = x2 - x1 + 1;
    uint8_t progress_width = (uint32_t)value * width / max_value;
    
    // 清空进度条区域
    for(i = x1; i <= x2; i++) {
        display_buffer[page][i] = 0x00;
    }
    
    // 画外框
    display_buffer[page][x1] = 0xFF;
    display_buffer[page][x2] = 0xFF;
    
    // 画进度填充
    for(i = 0; i < progress_width; i++) {
        if(x1 + i < LCD_COLUMNS) {
            display_buffer[page][x1 + i] |= 0x81;  // 上下两条线
        }
    }
    
    // 刷新该行
    LCD_SetCursor(page, x1);
    for(i = x1; i <= x2; i++) {
        LCD_WriteByte(display_buffer[page][i], LCD_DATA);
    }
}

/************************************************************
 * 函数:LCD_SetBacklight
 * 功能:设置背光亮度(PWM调光)
 * 参数:brightness - 亮度 0-100
 ************************************************************/
void LCD_SetBacklight(uint8_t brightness) {
    // 如果使用PWM控制背光,在这里实现
    // 假设背光连接在PB1(TIM3_CH4)
    if(brightness > 100) brightness = 100;
    
    // 计算PWM占空比
    uint16_t pulse = (uint32_t)brightness * 720 / 100;
    
    // 设置PWM(需要初始化TIM3)
    // TIM_SetCompare4(TIM3, pulse);
}

4. 字库文件(ascii_font.h)

#ifndef __ASCII_FONT_H
#define __ASCII_FONT_H

// 8×16 ASCII字模(共95个字符,从空格开始)
const unsigned char ASCII_Font[95][16] = {
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // 空格
    {0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00}, // !
    {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x36,0x36,0x36,0x36,0x00,0x00}, // "
    // ... 此处省略其他ASCII字符,实际需要完整的95个字符
    {0x00,0x00,0x00,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // ~
};

#endif

5. 中文字库(chinese_font.h)

#ifndef __CHINESE_FONT_H
#define __CHINESE_FONT_H

// 16×16中文字模(GB2312编码)
// 这里只示例几个汉字,实际使用时需要完整的字库
const unsigned char Chinese_Font[][32] = {
    // "欢" 0xBB B6
    {0x00,0x20,0x20,0x20,0x3F,0x24,0x24,0x24,0x24,0x3F,0x20,0x20,0x20,0x20,0x20,0x00,
     0x00,0x40,0x20,0x10,0x0F,0x09,0x09,0x09,0x09,0x49,0x89,0x7F,0x00,0x00,0x00,0x00},
    
    // "迎" 0xD3 AD
    {0x20,0x20,0x20,0x3F,0x20,0x7E,0x40,0x40,0x40,0x40,0x40,0x7F,0x40,0x40,0x40,0x00,
     0x00,0x40,0x20,0x1F,0x20,0x40,0x47,0x44,0x44,0x44,0x44,0x47,0x40,0x40,0x40,0x00},
    
    // "使" 0xCA B9
    {0x20,0x20,0x20,0x20,0x20,0x20,0xFF,0x20,0x20,0x20,0x20,0x3F,0x20,0x20,0x20,0x00,
     0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00,0x00},
    
    // "用" 0xD3 C3
    {0x00,0x00,0xFC,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0x04,0xFC,0x04,0x04,0x04,0x00,
     0x00,0x00,0xFF,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0x84,0xFF,0x84,0x84,0x84,0x00},
    
    // 可以继续添加更多汉字...
};

#endif

6. 延时函数(delay.h / delay.c)

#ifndef __DELAY_H
#define __DELAY_H

#include "stm32f10x.h"

void Delay_Init(void);
void Delay_us(uint32_t nus);
void Delay_ms(uint32_t nms);

#endif
#include "delay.h"

static uint8_t  fac_us = 0;
static uint16_t fac_ms = 0;

void Delay_Init(void) {
    SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
    fac_us = SystemCoreClock / 8000000;
    fac_ms = (uint16_t)fac_us * 1000;
}

void Delay_us(uint32_t nus) {
    uint32_t temp;
    SysTick->LOAD = nus * fac_us;
    SysTick->VAL = 0x00;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    do {
        temp = SysTick->CTRL;
    } while((temp & 0x01) && !(temp & (1 << 16)));
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0x00;
}

void Delay_ms(uint32_t nms) {
    uint32_t temp;
    SysTick->LOAD = (uint32_t)nms * fac_ms;
    SysTick->VAL = 0x00;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
    do {
        temp = SysTick->CTRL;
    } while((temp & 0x01) && !(temp & (1 << 16)));
    SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    SysTick->VAL = 0x00;
}

参考代码 STM32F103 串行控制12864显示实例(程序源码) www.youwenfan.com/contentcnt/115571.html

配置说明

1. Keil工程配置

1. 新建工程,选择STM32F103C8
2. 添加文件:
   - main.c
   - lcd12864.c
   - delay.c
3. 包含头文件路径:
   - 添加头文件所在目录
4. 配置编译器选项:
   - 优化等级:Level 0 (-O0)
   - 包含路径:.\
5. 配置调试器(ST-Link/J-Link)

2. 系统时钟配置

main.cSystemInit()后添加:

void RCC_Configuration(void) {
    RCC_DeInit();
    RCC_HSEConfig(RCC_HSE_ON);
    
    if(RCC_WaitForHSEStartUp() == SUCCESS) {
        RCC_HCLKConfig(RCC_SYSCLK_Div1);
        RCC_PCLK1Config(RCC_HCLK_Div2);
        RCC_PCLK2Config(RCC_HCLK_Div1);
        
        FLASH_SetLatency(FLASH_Latency_2);
        FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
        
        RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
        RCC_PLLCmd(ENABLE);
        
        while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
        
        RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
        
        while(RCC_GetSYSCLKSource() != 0x08);
    }
}

功能演示

1. 基本显示

// 显示ASCII字符串
LCD_ShowString(0, 0, "Hello World!");
LCD_ShowString(1, 0, "Temp: 25.6C");
LCD_ShowString(2, 0, "Humidity: 60%");

// 显示中文字符
LCD_ShowChinese(4, 0, "温度传感器");
LCD_ShowChinese(6, 0, "系统运行正常");

2. 图形绘制

// 画线
LCD_DrawLine(0, 0, 127, 63);
LCD_DrawLine(0, 63, 127, 0);

// 画矩形
LCD_DrawRectangle(10, 10, 117, 53);

// 画圆
LCD_DrawCircle(64, 32, 20);

// 画进度条
LCD_DrawProgressBar(7, 0, 127, 75, 100);

3. 自定义显示

// 自定义位图显示
const uint8_t bmp_logo[] = {
    // 32×32位图数据
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
    // ... 位图数据
};
LCD_ShowBMP(2, 48, 32, 32, bmp_logo);

注意事项

  1. 对比度调节:通过10k电位器调节VO引脚电压
  2. 供电电压:确保3.3V稳定,可并联100μF电容
  3. 时序延迟:根据LCD速度调整延时
  4. 字库存储:完整字库较大,可考虑外置Flash存储
  5. 背光电流:LED背光需串联限流电阻(通常100Ω)

优化建议

  1. 使用硬件SPI:修改为硬件SPI接口提高速度
  2. 双缓冲机制:减少屏幕闪烁
  3. 字库压缩:使用压缩算法减少存储空间
  4. DMA传输:使用DMA传输显示数据
  5. GUI框架:实现简单的GUI控件(按钮、菜单等)
posted @ 2026-04-20 16:39  风一直那个吹  阅读(35)  评论(0)    收藏  举报