分享一个12864液晶屏的单片机驱动

 https://www.toutiao.com/a6997216195019833892/

 手上有个12864液晶屏,IC是ST7920的,内置中英文点阵字库。用于显示一些简短的字符串还是蛮合适的。周末花了点时间调了下这款点阵屏,用的是并口方式,使用了STM32F103的PB0-PB7作为液晶的DB0-DB7接口。实现了内建字库显示字符串的功能和用于图形显示的画点功能。对于图形显示来说,由于液晶屏画点需要进行读-改-写操作,导致刷屏很慢。所以加了个MCU内置图形缓存的功能(其实就是一个数组),对刷屏时间要求高的,可以先在MCU内置图形缓存中通过画点生成图形,然后一次性将全屏数据写入ST7920,这样刷屏速度有了非常明显的提高。
  整个驱动比较简单,移植的话,就是修改下单片机的接口IO,修改下并口数据的写入读取函数就行了。
  由于没花太多时间调试,驱动没经过严格测试,可能存在问题,但目前我所用到的功能还没出现过问题,对于正在调该款屏幕的人来说,程序的参考意义还是有的。

下面是显示效果图:

图形显示效果

内建字库字符串显示效果

代码如下:

#include "st7920.h"

/**
    |  RS  |  RW  |  description                                          |
    |——————|——————|———————————————————————————————————————————————————————|
    |  L   |  L   |  MPU write instruction to instruction register(IR)  |
    |——————|——————|———————————————————————————————————————————————————————|
    |  L   |  H   |  MPU read busy flag(BF) and address counter(AC)   |
    |——————|——————|———————————————————————————————————————————————————————|
    |  H   |  L   |  MPU write data to data register(DR)                |
    |——————|——————|———————————————————————————————————————————————————————|
    |  H   |  H   |  MPU read data from data register(DR)               |
*/

/**
  * @brief 液晶屏控制IO初始化
  */
void ST7920_IoInit(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    /** 背光控制IO */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
    /** RS信号 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    /** RW信号 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    /** EN信号 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    /** PSB信号 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
    /** RST信号 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    /** DB0-DB7信号 */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
}

/**
  * @brief 在ST7920 向DB0-DB7输出数据
  */
static inline void ST7920_SetBusData(uint8_t data)
{
    GPIOB->ODR = (((uint16_t)GPIOB->ODR) & 0xFF00) | ((uint16_t)data);
}

/**
  * @brief 读取ST7920 DB0-DB7上的数据
  */
static inline uint8_t ST7920_GetBusData(void)
{
    return ((uint16_t)GPIOB->IDR) & 0x00FF;
}

/**
  * @brief 等待ST7920的上条指令或数据处理完毕
  */
void ST7920_WaitBusy(void)
{
    LCD_RS = 0;
    LCD_RW = 1;
    LCD_E = 1;
    ST7920_SetBusData(0xFF);
    while(ST7920_GetBusData() & 0x80);
    LCD_E = 0;
}

/**
  * @brief 获取当前Address counter(AC)
  */
uint8_t ST7920_GetAddrCounter(void)
{
    uint8_t addr;

    LCD_RS = 0;
    LCD_RW = 1;
    LCD_E = 1;
    ST7920_SetBusData(0xFF);
    while(ST7920_GetBusData() & 0x80);
    addr = ST7920_GetBusData();
    LCD_E = 0;

    return addr;
}

/**
  * @brief 写指令码至ST7920指令寄存器(IR)
  */
void ST7920_WriteCmd(uint8_t cmd)
{
    ST7920_WaitBusy();
    LCD_RS = 0;
    LCD_RW = 0;
    LCD_E = 1;
    ST7920_SetBusData(cmd);
    LCD_E = 0;
}

/**
  * @brief 写数据至ST7920数据寄存器(DR)
  */
void ST7920_WriteData(uint8_t data)
{
    ST7920_WaitBusy();
    LCD_RS = 1;
    LCD_RW = 0;
    LCD_E = 1;
    ST7920_SetBusData(data);
    LCD_E = 0;
}

/**
  * @brief 从ST7920数据寄存器(DR)读取数据
  */
uint8_t ST7920_ReadData(void)
{
    uint8_t data;

    ST7920_WaitBusy();
    LCD_RS = 1;
    LCD_RW = 1;
    LCD_E = 1;
    ST7920_SetBusData(0xFF);
    data = ST7920_GetBusData();
    LCD_E = 0;

    return data;
}

/**
  * @brief DDRAM清零(DDRAM用于显示内建字库字体)
  */
void ST7920_DdramClear(void)
{
    ST7920_WriteCmd(ST7920_CMD_FUNCTION_BASIC_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE); //设置为基本指令
    ST7920_WriteCmd(ST7920_CMD_CLEAR); //清屏
}

/**
  * @brief GDRAM清零(GDRAM用于显示图形)
  */
void ST7920_GdramClear(void)
{
    uint8_t i,j;

    ST7920_WriteCmd(ST7920_CMD_FUNCTION_EXTENDED_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE | ST7920_CMD_FUNCTION_GRAPHIC_OFF); //关闭图形显示
    /** 清零GDRAM */
    for(i=0; i<64; i++) {
        ST7920_WriteCmd(0x80 + i);
        ST7920_WriteCmd(0x80);
        for (j=0; j<=0x0F; j++) {
            ST7920_WriteData(0x00);
            ST7920_WriteData(0x00);
        }
    }
    ST7920_WriteCmd(ST7920_CMD_FUNCTION_EXTENDED_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE | ST7920_CMD_FUNCTION_GRAPHIC_ON); //开启图形显示

}

/**
  * @brief 通过ST7920内建字库显示字符串(GB码)
  * @attention 使用DDRAM显示字符串时,如果字符串有ASCII字符与中文字符混合的情况,
  *            ASCII字符必须成对出现。这是由于DDRAM的每个显示地址内含2字节数据,
  *            如果字符串中出现奇数个ASCII字符,将导致随后的中文字符数据与DDRAM
  *            地址不对齐,从而ASCII字符后的中文字符将乱码。
  *            12864屏幕将ST7920的第一行和第二行的后半部分作为屏幕的下半屏,因此
  *            要在液晶的第三行显示内建字库,line应该填1,offset应该填8。
  * @param[in] line: DDRAM中的行[1-4]
  * @param[in] offset: 行中字符位置偏移(按中文字符占位算)
  * @param[in] str: 要显示的字符串(GB编码)
  * @retval None.
  */
void ST7920_ShowBuildInStr(uint8_t line, uint8_t offset, const char *str)
{
    uint8_t i;
    uint8_t addr;

    ASSERT((line >= 1) && (line <= 4));
    ASSERT(offset <= 15);

    ST7920_WriteCmd(ST7920_CMD_FUNCTION_BASIC_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE); //恢复为基本指令
    addr = 0x80 + (line - 1) * 0x10 + offset;
    ST7920_WriteCmd(addr);
    for (i = 0; str[i] != 0; i++) {
        ST7920_WriteData(str[i]);
    }
}

/**
  * @brief 按屏幕显示效果上的行意义,通过ST7920内建字库显示字符串
  * @note 由于12864屏幕的下半屏并非对应于ST7920 DDRAM中的第三行第四行。
  *       而是对应于DDRAM第一行第二行的后半段。因此增加此函数,用于按照
  *       屏幕显示效果上的行意义进行内建字库显示。
  * @param[in] line: 实体屏幕逻辑意义上的行[1-4]
  * @param[in] offset: 行中字符位置偏移(按中文字符占位算)
  * @param[in] str: 要显示的字符串(GB编码)
  * @retval None.
  */
void ST7920_ScreenShowBuildInStr(uint8_t line, uint8_t offset, const char *str)
{
    char tmp_str[17];

    snprintf(tmp_str, sizeof(tmp_str), "%s", str);
    switch (line)
    {
        case 1: ST7920_ShowBuildInStr(1, offset, tmp_str); break;
        case 2: ST7920_ShowBuildInStr(2, offset, tmp_str); break;
        case 3: ST7920_ShowBuildInStr(1, offset + 8, tmp_str); break;
        case 4: ST7920_ShowBuildInStr(2, offset + 8, tmp_str); break;
        default: break;
    }
}

/**
  * @brief 画点函数
  * @param[in] x,y: 坐标
  * @param[in] color: 点颜色
  * @retval None.
  */
void ST7920_SetPixel(uint16_t x, uint16_t y, uint16_t color)
{
    uint8_t x_ram, y_ram;
    uint8_t data_h, data_l;
    uint8_t offset;

    ASSERT(x < 128);
    ASSERT(y < 64);
    ST7920_WriteCmd(ST7920_CMD_FUNCTION_EXTENDED_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE | ST7920_CMD_FUNCTION_GRAPHIC_ON); //切换为拓展指令
    /** 计算GDRAM坐标 */
    x_ram = 0x80 | (x/16 + 8*(y/32));
    y_ram = 0x80 | (y%32);
    /** 读取现有点数据 */
    ST7920_WriteCmd(y_ram);
    ST7920_WriteCmd(x_ram);
    ST7920_ReadData(); //Dummy Read
    data_h = ST7920_ReadData();
    data_l = ST7920_ReadData();
    /** 计算新画点数据 */
    offset = x % 16;
    if (offset < 8) {
        if (color) data_h |= 0x80 >> offset;
        else data_h &= ~(0x80 >> offset);
    } else {
        if (color) data_l |= 0x80 >> (offset - 8);
        else data_l &= ~(0x80 >> (offset - 8));
    }
    /** 写入新点数据 */
    ST7920_WriteCmd(y_ram);
    ST7920_WriteCmd(x_ram);
    ST7920_WriteData(data_h);
    ST7920_WriteData(data_l);
}

/**
  * @brief 将整页数据写入ST7920
  * @retval None.
  */
void ST7920_FlushPage(uint8_t data[64][16])
{
    uint16_t i, j;

    ST7920_WriteCmd(ST7920_CMD_FUNCTION_EXTENDED_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE | ST7920_CMD_FUNCTION_GRAPHIC_ON); //切换为拓展指令
    /** 刷新上半屏 */
    for (i = 0; i < 32; i++) {
        ST7920_WriteCmd(0x80 + i);
        ST7920_WriteCmd(0x80);
        for (j = 0; j < 8; j++) {
            ST7920_WriteData(data[i][2*j]);
            ST7920_WriteData(data[i][2*j+1]);
        }
    }
    /** 刷新下半屏 */
    for (i = 32; i < 64; i++) {
        ST7920_WriteCmd(0x80 + i - 32);
        ST7920_WriteCmd(0x80 + 8);
        for (j = 0; j < 8; j++) {
            ST7920_WriteData(data[i][2*j]);
            ST7920_WriteData(data[i][2*j+1]);
        }
    }
}

#ifdef USE_MCU_GRAM

/** @defgroup MCU内部GRAM相关函数
  * @brief 由于ST7920_SetPixel函数需要执行读-改-写操作,刷屏非常耗时。通过在MCU内部建立GRAM可大幅提高刷屏速度。
  * @attention 使用ST7920_SetMcuGramPixel画完点后,需要调用ST7920_FlushMcuGram更新ST7920内部GDRAM;
  * @{
  */

static uint8_t s_MCU_GRAM[64][16];

/**
  * @brief 在MCU内部显存画点
  * @param[in] x,y: 坐标
  * @param[in] color: 点颜色
  * @retval None.
  */
void ST7920_SetMcuGramPixel(uint16_t x, uint16_t y, uint16_t color)
{
    uint8_t index_1, index_2;
    uint8_t offset;

    /** 计算显存数组下标及位偏移 */
    index_1 = y;
    index_2 = x/8;
    offset = x % 8;

    if (color) s_MCU_GRAM[index_1][index_2] |= 0x80 >> offset;
    else s_MCU_GRAM[index_1][index_2] &= ~(0x80 >> offset);
}

/**
  * @brief 将MCU内部显存写入ST7920
  * @retval None.
  */
void ST7920_FlushMcuGram(void)
{
    ST7920_FlushPage(s_MCU_GRAM);
}

/**
  * @}
  */

#endif

/**
  * @brief ST7920初始化
  */
void ST7920_DisplayInit(void)
{
    LCD_BL_ON;   //开启背光
    LCD_PSB = 1; //并行接口
    LCD_RST = 0;
    delay_ms(5);
    LCD_RST = 1;
    delay_ms(50);

    /******************** 初始化配置 ********************/
    ST7920_WriteCmd(ST7920_CMD_FUNCTION_8BIT_INTERFACE); //设置并行接口为8位
    ST7920_WriteCmd(ST7920_CMD_FUNCTION_8BIT_INTERFACE); //设置并行接口为8位
    ST7920_WriteCmd(ST7920_CMD_FUNCTION_BASIC_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE); //设置为基本指令
//    ST7920_WriteCmd(ST7920_CMD_DISPLAY_DISPLAY_ON | ST7920_CMD_DISPLAY_CURSOR_ON | ST7920_CMD_DISPLAY_BLINK_ON); //整体显示,游标显示,游标闪烁
    ST7920_WriteCmd(ST7920_CMD_DISPLAY_DISPLAY_ON); //开总显示
    ST7920_WriteCmd(ST7920_CMD_ENTRY_CURSOR_MOVE_RIGHT); //光标右移
    ST7920_DdramClear();

    /******************** 开启图形显示 ********************/
    ST7920_DdramClear();
    ST7920_WriteCmd(ST7920_CMD_FUNCTION_EXTENDED_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE | ST7920_CMD_FUNCTION_GRAPHIC_OFF); //设置为扩展指令,关闭图形显示
    ST7920_WriteCmd(ST7920_CMD_SCROLL_OR_RAM_EN_VERTICAL); //使能垂直滚动
    ST7920_WriteCmd(ST7920_CMD_FUNCTION_EXTENDED_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE | ST7920_CMD_FUNCTION_GRAPHIC_ON); //开启图形显示

    ST7920_WriteCmd(ST7920_CMD_FUNCTION_BASIC_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE); //恢复为基本指令
}

头文件:

#ifndef ST7920_H__
#define ST7920_H__

#include "common.h"

#define LCD_RS          PBout(11)
#define LCD_RS_DATA     PBout(11) = 1
#define LCD_RS_CMD      PBout(11) = 0
#define LCD_RW          PBout(10)
#define LCD_RW_READ     PBout(10) = 1
#define LCD_RW_WRITE    PBout(10) = 0
#define LCD_E           PCout(5)
#define LCD_DB0         PBout(0)
#define LCD_DB1         PBout(1)
#define LCD_DB2         PBout(2)
#define LCD_DB3         PBout(3)
#define LCD_DB4         PBout(4)
#define LCD_DB5         PBout(5)
#define LCD_DB6         PBout(6)
#define LCD_DB7         PBout(7)
#define LCD_PSB         PCout(4)
#define LCD_RST         PBout(9)
#define LCD_BL_ON       PAout(7) = 1
#define LCD_BL_OFF      PAout(7) = 0

/** @defgroup ST7920命令宏(根据数据手册,有些同组命令需要和或操作配合使用)
  * @{
  */

/** 基本指令(只需设置一次ST7920_CMD_FUNCTION_BASIC_INS) */
#define ST7920_CMD_CLEAR                        0b00000001  //清屏
#define ST7920_CMD_HOME                         0b00000010  //光标复位
#define ST7920_CMD_ENTRY_CURSOR_MOVE_RIGHT      0b00000110  //光标自动右移
#define ST7920_CMD_ENTRY_CURSOR_MOVE_LEFT       0b00000100  //光标自动左移
#define ST7920_CMD_ENTRY_DISPLAY_SHIFT_RIGHT    0b00000101  //显示右移
#define ST7920_CMD_ENTRY_DISPLAY_SHIFT_LEFT     0b00000111  //显示左移
#define ST7920_CMD_DISPLAY_DISPLAY_ON           0b00001100  //显示开启
#define ST7920_CMD_DISPLAY_CURSOR_ON            0b00001010  //显示光标
#define ST7920_CMD_DISPLAY_BLINK_ON             0b00001001  //光标闪烁
#define ST7920_CMD_CURSOR_MOVE_LEFT_BY_1        0b00010000  //AC=AC-1
#define ST7920_CMD_CURSOR_MOVE_RIGHT_BY_1       0b00010100  //AC=AC+1
#define ST7920_CMD_DISPLAY_MOVE_LEFT_BY_1       0b00011000  //显示左移1,光标跟随(AC=AC)
#define ST7920_CMD_DISPLAY_MOVE_RIGHT_BY_1      0b00011100  //显示右移1,光标跟随(AC=AC)
#define ST7920_CMD_FUNCTION_8BIT_INTERFACE      0b00110000  //8Bit总线
#define ST7920_CMD_FUNCTION_4BIT_INTERFACE      0b00100000  //4Bit总线
#define ST7920_CMD_FUNCTION_EXTENDED_INS        0b00100100  //拓展指令
#define ST7920_CMD_FUNCTION_BASIC_INS           0b00100000  //基本指令
#define ST7920_CMD_SET_CGRAM_ADDR               0b01000000  //设置CGRAM AC地址(需要确保拓展指令中SR位为0)
#define ST7920_CMD_SET_DDRAM_ADDR               0b10000000  //设置DDRAM AC地址

/** 拓展指令(只需设置一次ST7920_CMD_FUNCTION_EXTENDED_INS)*/
#define ST7920_CMD_STAND_BY                     0b00000001  //进入stand by模式
#define ST7920_CMD_SCROLL_OR_RAM_EN_VERTICAL    0b00000011  //使能垂直滚动
#define ST7920_CMD_SCROLL_OR_RAM_EN_IRAM        0b00000010  //使能IRAM
#define ST7920_CMD_REVERSE_LINE1                0b00000100  //选择DDRAM 第1行反转显示
#define ST7920_CMD_REVERSE_LINE2                0b00000101  //选择DDRAM 第2行反转显示
#define ST7920_CMD_REVERSE_LINE3                0b00000110  //选择DDRAM 第3行反转显示
#define ST7920_CMD_REVERSE_LINE4                0b00000111  //选择DDRAM 第4行反转显示
#define ST7920_CMD_FUNCTION_GRAPHIC_OFF         0b00000010  //关闭图形显示
#define ST7920_CMD_FUNCTION_GRAPHIC_ON          0b00000010  //开启图形显示
#define ST7920_CMD_SET_IRAM_OR_SCROLL_SCROLL    0b01000000  //DB5-DB0为垂直滚动地址
#define ST7920_CMD_SET_IRAM_OR_SCROLL_IRAM      0b01000000  //DB3-DB0为ICON RAM地址
#define ST7920_CMD_SET_GRAPHIC_RAM_ADDR         0b10000000  //设置GDRAM地址至AC

/**
  * @}
  */

#define USE_BASIC_INSTRUCTION       ST7920_WriteCmd(ST7920_CMD_FUNCTION_BASIC_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE)
#define USE_EXTENDED_INSTRUCTION    ST7920_WriteCmd(ST7920_CMD_FUNCTION_EXTENDED_INS | ST7920_CMD_FUNCTION_8BIT_INTERFACE | ST7920_CMD_FUNCTION_GRAPHIC_ON);

void ST7920_IoInit(void);
void ST7920_WaitBusy(void);
uint8_t ST7920_GetAddrCounter(void);
void ST7920_WriteCmd(uint8_t cmd);
void ST7920_WriteData(uint8_t data);
uint8_t ST7920_ReadData(void);
void ST7920_DdramClear(void);
void ST7920_GdramClear(void);
void ST7920_ShowBuildInStr(uint8_t line, uint8_t offset, const char *str);
void ST7920_ScreenShowBuildInStr(uint8_t line, uint8_t offset, const char *str);
void ST7920_SetPixel(uint16_t x, uint16_t y, uint16_t color);
void ST7920_FlushPage(uint8_t data[64][16]);
#ifdef USE_MCU_GRAM
void ST7920_SetMcuGramPixel(uint16_t x, uint16_t y, uint16_t color);
void ST7920_FlushMcuGram(void);
#endif
void ST7920_DisplayInit(void);

#endif
(0)

相关推荐