【免费开源】stm32串行驱动LCD12864显示正弦函数 波形可视化神器完整项目分享

14 阅读11分钟

【免费开源】stm32串行驱动LCD12864显示正弦函数 波形可视化神器完整项目分享

一、项目概述

本项目是一个基于STM32微控制器的LCD12864液晶显示屏驱动程序,通过串行通信方式控制LCD12864显示屏,实现正弦函数波形的实时显示。这个项目将数学函数可视化,让抽象的数学概念变得直观可见,是学习STM32嵌入式开发和图形显示技术的绝佳实践项目。

LCD12864是一款128×64点阵的图形型液晶显示模块,具有体积小、功耗低、显示内容丰富等特点,广泛应用于各种嵌入式系统中。本项目采用串行接口方式驱动LCD12864,相比并行接口方式,可以节省更多的IO引脚资源,非常适合引脚资源有限的STM32微控制器使用。

正弦函数是数学中最基础的周期函数之一,在信号处理、通信系统、音频处理等领域有着广泛的应用。通过在LCD12864上实时显示正弦波形,不仅能够直观地展示正弦函数的周期性特征,还能帮助理解数字信号处理的基本原理。

本项目的核心功能包括:LCD12864的初始化配置、串行通信接口的实现、正弦函数数据的计算与存储、波形绘制算法的实现等。通过这个项目,读者可以深入学习STM32的GPIO、SPI通信、定时器等外设的使用方法,掌握图形显示的基本原理和编程技巧。

源码分享

直接放到之前写的文章里了,免费开源,下载学习即可。

https://blog.csdn.net/weixin_52908342/article/details/158101046

二、系统设计流程图

flowchart TD
    A[系统启动] --> B[STM32初始化]
    B --> C[GPIO时钟配置]
    C --> D[SPI接口配置]
    D --> E[LCD12864初始化]
    E --> F[清屏操作]
    F --> G[正弦函数数据计算]
    G --> H[波形绘制]
    H --> I{是否继续显示}
    I -->|是| G
    I -->|否| J[结束]
    
    style A fill:#e1f5ff
    style B fill:#fff4e1
    style C fill:#fff4e1
    style D fill:#fff4e1
    style E fill:#e1ffe1
    style F fill:#e1ffe1
    style G fill:#ffe1f5
    style H fill:#ffe1f5
    style I fill:#f5ffe1
    style J fill:#ffcccc

三、硬件设计

3.1 STM32微控制器选型

本项目采用STM32F103系列微控制器作为主控芯片,该系列基于ARM Cortex-M3内核,具有丰富的外设资源和良好的性价比。STM32F103C8T6是最常用的型号,具有64KB Flash存储器、20KB SRAM、48个GPIO引脚,完全满足本项目的需求。

STM32F103的主要特性包括:

  • 72MHz系统时钟频率
  • 3个SPI接口
  • 2个I2C接口
  • 5个USART/UART接口
  • 3个12位ADC
  • 多个定时器
  • 丰富的GPIO配置选项

在这里插入图片描述

3.2 LCD12864液晶显示屏

LCD12864是一款128×64点阵的图形型液晶显示模块,内置ST7920控制器,支持串行和并行两种通信方式。主要技术参数如下:

  • 分辨率:128×64点阵
  • 显示颜色:黄绿底黑字或蓝底白字
  • 工作电压:5V
  • 工作电流:约2-3mA
  • 视角:6点钟方向
  • 控制器:ST7920
  • 内置字库:8192个中文汉字(16×16点阵)
  • 内置ROM:128个字符(8×16点阵)

3.3 硬件连接方案

采用串行接口方式连接STM32和LCD12864,需要以下连接:

  • LCD12864的VSS引脚连接GND
  • LCD12864的VDD引脚连接5V电源
  • LCD12864的V0引脚连接电位器用于对比度调节
  • LCD12864的RS(CS)引脚连接STM32的PA0
  • LCD12864的RW(SID)引脚连接STM32的PA1
  • LCD12864的E(SCLK)引脚连接STM32的PA2
  • LCD12864的PSB引脚连接GND(选择串行模式)
  • LCD12864的RST引脚连接STM32的PA3
  • LCD12864的BLA引脚连接5V(背光正极)
  • LCD12864的BLK引脚连接GND(背光负极)

四、软件设计

4.1 系统架构设计

软件系统采用模块化设计,主要包含以下模块:

  1. 系统初始化模块:负责STM32系统时钟、GPIO、SPI等外设的初始化配置
  2. LCD12864驱动模块:实现LCD12864的底层驱动函数,包括写命令、写数据、清屏等操作
  3. 图形绘制模块:实现基本的图形绘制功能,如画点、画线、画矩形等
  4. 波形显示模块:实现正弦函数波形的计算和显示
  5. 主控模块:协调各模块的工作,实现系统的整体功能

4.2 LCD12864驱动程序

LCD12864的串行通信协议采用三线制(CS、SID、SCLK),数据传输时序严格遵循ST7920控制器的规范。以下是LCD12864初始化的核心代码:

#include "stm32f10x.h"

#define LCD_CS_PIN    GPIO_Pin_0
#define LCD_SID_PIN   GPIO_Pin_1
#define LCD_SCLK_PIN  GPIO_Pin_2
#define LCD_RST_PIN   GPIO_Pin_3
#define LCD_PORT      GPIOA

void LCD_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = LCD_CS_PIN | LCD_SID_PIN | LCD_SCLK_PIN | LCD_RST_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_PORT, &GPIO_InitStructure);
    
    GPIO_SetBits(LCD_PORT, LCD_CS_PIN);
    GPIO_SetBits(LCD_PORT, LCD_SID_PIN);
    GPIO_SetBits(LCD_PORT, LCD_SCLK_PIN);
    GPIO_SetBits(LCD_PORT, LCD_RST_PIN);
}

void LCD_WriteByte(uint8_t data, uint8_t rs)
{
    uint8_t i;
    uint8_t temp;
    
    GPIO_ResetBits(LCD_PORT, LCD_CS_PIN);
    
    for(i = 0; i < 5; i++) {
        GPIO_ResetBits(LCD_PORT, LCD_SCLK_PIN);
        GPIO_SetBits(LCD_PORT, LCD_SID_PIN);
        GPIO_SetBits(LCD_PORT, LCD_SCLK_PIN);
    }
    
    GPIO_ResetBits(LCD_PORT, LCD_SCLK_PIN);
    if(rs) GPIO_SetBits(LCD_PORT, LCD_SID_PIN);
    else GPIO_ResetBits(LCD_PORT, LCD_SID_PIN);
    GPIO_SetBits(LCD_PORT, LCD_SCLK_PIN);
    
    GPIO_ResetBits(LCD_PORT, LCD_SCLK_PIN);
    GPIO_SetBits(LCD_PORT, LCD_SID_PIN);
    GPIO_SetBits(LCD_PORT, LCD_SCLK_PIN);
    
    for(i = 0; i < 4; i++) {
        temp = data;
        temp = temp << i;
        temp = temp & 0x80;
        
        GPIO_ResetBits(LCD_PORT, LCD_SCLK_PIN);
        if(temp) GPIO_SetBits(LCD_PORT, LCD_SID_PIN);
        else GPIO_ResetBits(LCD_PORT, LCD_SID_PIN);
        GPIO_SetBits(LCD_PORT, LCD_SCLK_PIN);
    }
    
    for(i = 4; i < 8; i++) {
        temp = data;
        temp = temp << i;
        temp = temp & 0x80;
        
        GPIO_ResetBits(LCD_PORT, LCD_SCLK_PIN);
        if(temp) GPIO_SetBits(LCD_PORT, LCD_SID_PIN);
        else GPIO_ResetBits(LCD_PORT, LCD_SID_PIN);
        GPIO_SetBits(LCD_PORT, LCD_SCLK_PIN);
    }
    
    GPIO_SetBits(LCD_PORT, LCD_CS_PIN);
}

void LCD_WriteCommand(uint8_t cmd)
{
    LCD_WriteByte(0xf8, 0);
    LCD_WriteByte(cmd & 0xf0, 1);
    LCD_WriteByte((cmd << 4) & 0xf0, 1);
}

void LCD_WriteData(uint8_t data)
{
    LCD_WriteByte(0xfa, 0);
    LCD_WriteByte(data & 0xf0, 1);
    LCD_WriteByte((data << 4) & 0xf0, 1);
}

void LCD_Init(void)
{
    LCD_GPIO_Init();
    
    GPIO_ResetBits(LCD_PORT, LCD_RST_PIN);
    Delay_ms(10);
    GPIO_SetBits(LCD_PORT, LCD_RST_PIN);
    Delay_ms(10);
    
    LCD_WriteCommand(0x30);
    Delay_ms(5);
    LCD_WriteCommand(0x30);
    Delay_ms(5);
    LCD_WriteCommand(0x0c);
    Delay_ms(5);
    LCD_WriteCommand(0x01);
    Delay_ms(5);
    LCD_WriteCommand(0x06);
    Delay_ms(5);
}

void LCD_Clear(void)
{
    LCD_WriteCommand(0x30);
    LCD_WriteCommand(0x01);
    Delay_ms(10);
}

4.3 正弦函数波形绘制

正弦函数波形的绘制需要将数学上的连续函数转换为离散的点阵显示。LCD12864的分辨率为128×64,我们需要在这个有限的像素空间内绘制出平滑的正弦波形。

正弦函数的基本公式为:y = A × sin(ωx + φ) + k

其中:

  • A:振幅,决定波形的高度
  • ω:角频率,决定波形的周期
  • φ:初相位,决定波形的起始位置
  • k:直流偏置,决定波形的垂直位置

以下是正弦波形绘制的核心代码:

#include <math.h>

#define PI 3.14159265358979323846
#define LCD_WIDTH  128
#define LCD_HEIGHT 64

void LCD_DrawPoint(uint8_t x, uint8_t y)
{
    uint8_t x_addr, y_addr;
    uint8_t bit_data;
    
    if(x >= LCD_WIDTH || y >= LCD_HEIGHT) return;
    
    y_addr = y / 8;
    bit_data = 0x01 << (y % 8);
    
    LCD_WriteCommand(0x80 | y_addr);
    LCD_WriteCommand(0x80 | x);
    LCD_WriteData(bit_data);
}

void LCD_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2)
{
    int dx, dy, sx, sy, err, e2;
    
    dx = abs(x2 - x1);
    dy = abs(y2 - y1);
    
    if(x1 < x2) sx = 1;
    else sx = -1;
    
    if(y1 < y2) sy = 1;
    else sy = -1;
    
    err = dx - dy;
    
    while(1) {
        LCD_DrawPoint(x1, y1);
        
        if(x1 == x2 && y1 == y2) break;
        
        e2 = 2 * err;
        
        if(e2 > -dy) {
            err -= dy;
            x1 += sx;
        }
        
        if(e2 < dx) {
            err += dx;
            y1 += sy;
        }
    }
}

void LCD_DrawSineWave(void)
{
    uint8_t x, y;
    float angle;
    float amplitude = 25.0;
    float frequency = 2.0;
    float offset = 32.0;
    
    LCD_Clear();
    
    LCD_WriteCommand(0x34);
    LCD_WriteCommand(0x36);
    
    for(x = 0; x < LCD_WIDTH; x++) {
        angle = (float)x * frequency * 2.0 * PI / LCD_WIDTH;
        y = (uint8_t)(offset + amplitude * sin(angle));
        
        LCD_DrawPoint(x, y);
        
        if(x > 0) {
            uint8_t prev_x = x - 1;
            float prev_angle = (float)prev_x * frequency * 2.0 * PI / LCD_WIDTH;
            uint8_t prev_y = (uint8_t)(offset + amplitude * sin(prev_angle));
            LCD_DrawLine(prev_x, prev_y, x, y);
        }
    }
    
    LCD_WriteCommand(0x30);
}

void LCD_DisplayText(uint8_t x, uint8_t y, char *str)
{
    LCD_WriteCommand(0x80 | y);
    LCD_WriteCommand(0x80 | x);
    
    while(*str != '\0') {
        LCD_WriteData(*str);
        str++;
    }
}

4.4 主程序设计

主程序负责系统的初始化和功能协调,通过定时器实现波形的动态刷新,或者通过按键实现不同参数波形的切换。

int main(void)
{
    SystemInit();
    
    LCD_Init();
    LCD_Clear();
    
    LCD_DisplayText(0, 0, "Sine Wave Demo");
    Delay_ms(1000);
    
    LCD_DrawSineWave();
    
    LCD_DisplayText(0, 2, "y=A*sin(wx+phi)+k");
    LCD_DisplayText(0, 4, "A=25, w=2, phi=0");
    LCD_DisplayText(0, 6, "k=32");
    
    while(1) {
        Delay_ms(1000);
    }
}

void Delay_ms(uint32_t ms)
{
    uint32_t i, j;
    for(i = 0; i < ms; i++)
        for(j = 0; j < 9000; j++);
}

五、关键技术点分析

5.1 串行通信时序

LCD12864的串行通信采用SPI时序,但与标准SPI有所不同。ST7920控制器定义了特殊的通信协议,每个字节传输需要先发送5个同步字节,然后发送RS位和数据位。这种设计虽然增加了通信开销,但提高了通信的可靠性。

5.2 图形绘制算法

在点阵显示屏上绘制平滑曲线需要采用适当的算法。本项目使用Bresenham直线算法来连接离散的正弦函数点,这种方法计算效率高,适合在资源有限的嵌入式系统中使用。

5.3 浮点数运算优化

STM32F103具有硬件浮点运算单元,但为了提高运算效率,在实际应用中可以考虑使用定点数运算代替浮点数运算,或者使用查表法预先计算正弦函数值。

5.4 显示缓冲区管理

为了提高显示效率,可以建立显示缓冲区,先在内存中完成图形绘制,然后一次性更新到LCD显示屏。这种方法可以减少LCD的访问次数,提高显示速度。

六、项目扩展与应用

6.1 功能扩展

  1. 多波形显示:扩展支持余弦函数、方波、三角波等多种波形
  2. 参数调节:通过按键或电位器实时调节波形的振幅、频率、相位等参数
  3. 动态显示:实现波形的滚动显示,模拟示波器功能
  4. 数据存储:将波形数据存储到SD卡,实现数据记录功能

6.2 应用场景

  1. 教学演示:用于数学和物理教学,直观展示周期函数的特征
  2. 信号发生器:配合DAC芯片,实现可调参数的信号发生功能
  3. 仪器显示:作为各种测量仪器的显示界面
  4. 工业控制:用于工业自动化设备的参数显示和监控

七、总结

本项目通过STM32驱动LCD12864显示正弦函数波形,展示了嵌入式系统图形显示的基本原理和实现方法。项目涵盖了硬件设计、软件编程、算法实现等多个方面的知识,是学习STM32嵌入式开发的综合性实践项目。

通过本项目的学习,读者可以掌握STM32的GPIO配置、串行通信、定时器使用等基本技能,理解图形显示的原理和方法,为后续更复杂的项目开发打下坚实的基础。项目的代码结构清晰,功能模块化,便于学习和扩展。

项目采用开源方式发布,欢迎广大爱好者学习交流,共同进步。希望这个项目能够帮助更多人了解嵌入式开发的魅力,激发对电子技术的兴趣和热情。

八、参考资料

  1. ST7920 LCD控制器数据手册
  2. STM32F103参考手册
  3. LCD12864液晶显示屏技术规格书
  4. 嵌入式系统设计与应用
  5. 数字信号处理基础 在这里插入图片描述