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.c的SystemInit()后添加:
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);
注意事项
- 对比度调节:通过10k电位器调节VO引脚电压
- 供电电压:确保3.3V稳定,可并联100μF电容
- 时序延迟:根据LCD速度调整延时
- 字库存储:完整字库较大,可考虑外置Flash存储
- 背光电流:LED背光需串联限流电阻(通常100Ω)
优化建议
- 使用硬件SPI:修改为硬件SPI接口提高速度
- 双缓冲机制:减少屏幕闪烁
- 字库压缩:使用压缩算法减少存储空间
- DMA传输:使用DMA传输显示数据
- GUI框架:实现简单的GUI控件(按钮、菜单等)

浙公网安备 33010602011771号