STM32CubeMX第七篇之MCU屏

Posted 海洋想想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32CubeMX第七篇之MCU屏相关的知识,希望对你有一定的参考价值。

前言

本文主要讲解自己实现MCU控制程序。

本程序具有以下功能:

  1. 能够输出汉字
  2. 能够输出ASCII字符串
  3. 能够输出整数

本文使用的HAL库的版本为:STM32Cube_FW_F4_V1.25.0
本文使用的STM32CubeMX版本为:6.1.1

该工程的下载地址为:

关于MCU屏的相关知识,可以参考博客<硬件介绍之NT35510(80系列并口使用)>。

关于SRAM及其HAL库的介绍,可以参考博客<STM32F429第二十四篇之SRAM原理>和<STM32F429第二十五篇之MCU屏实验详解>。

主程序

/**
  ******************************************************************************
  * @file    main.c
  * @author  zhy
  * @version 1.0
  * @date    2021-05-13
  * @brief   通过SRAM控制LCD
  *             1.输出汉字
  *             2.输出英文字符串
  *             3.输出数字
  ******************************************************************************
  */

#include "stm32f4xx_hal.h"
#include "lcd.h"
#include "gpio.h"
#include "led.h"
#include "fmc.h"
#include "sys.h"
#include "delay.h"

int main()
{
    HAL_Init();
    SystemClock_Config();
    LedInit();
    GpioInit();
    SramInit();
    LcdInit();

    const uint8_t encode[] = {0, 1, 2, 255, 253};
    LcdShowChinese(encode);
    LcdShowNumber(123);
    LcdShowString("\\nHello, zhy!");
    
    while (1)
    {
        delay_ms(500);
        LED0 = !LED0;
        LED1 = !LED0;
    }
}

在主程序中,已经以下几个步骤:

  1. 外设配置
  2. 输出OLED屏内容
  3. 点亮LED灯,表现程序运行。

在外设初始化中,要注意到,LCD使用了SRAM和GPIO两个底层应用。GPIO用于点亮LCD的背光灯,而SRAM用于与LCD之间通信。所以,若要使用LCD,这两个模块的初始化必不可少!

在输出OLED屏内容的时候,可以知道:本文提供了方便的程序用于显示汉字,数字,以及字符串。

配置

模式A配置

首先,可以找到LCD的时序图如下所示:

在这里插入图片描述

通过观察上面的波形图,可以从SRAM所有的模式中选择模式A为比较符合,如下图所示:

在这里插入图片描述

在这里插入图片描述

所以,首先,应该确定读写使用模式A完成。模式A的寄存器配置如下:

在这里插入图片描述
在这里插入图片描述
对应程序如下:

    SRAM_HandleTypeDef hsram1 = {0};
    hsram1.Instance = FMC_NORSRAM_DEVICE;                           //设置SRAM的寄存器地址
    hsram1.Extended = FMC_NORSRAM_EXTENDED_DEVICE;                  //设置SRAM拓展寄存器地址
    hsram1.Init.NSBank = FMC_NORSRAM_BANK1;                         //设置存储区域的地址
    hsram1.Init.DataAddressMux = FMC_DATA_ADDRESS_MUX_DISABLE;      //设置数据与地址
    hsram1.Init.MemoryType = FMC_MEMORY_TYPE_SRAM;                  //存储类型
    hsram1.Init.MemoryDataWidth = FMC_NORSRAM_MEM_BUS_WIDTH_16;     //总线长度
    hsram1.Init.BurstAccessMode = FMC_BURST_ACCESS_MODE_DISABLE;    //不支持突发模式
    hsram1.Init.WaitSignalPolarity = FMC_WAIT_SIGNAL_POLARITY_HIGH; //等待信号极性:高
    hsram1.Init.WrapMode = FMC_WRAP_MODE_DISABLE;                   //禁止回卷模式
    hsram1.Init.WaitSignalActive = FMC_WAIT_TIMING_BEFORE_WS;       //等待状态一个数据周期NWAIT有效
    hsram1.Init.WriteOperation = FMC_WRITE_OPERATION_ENABLE;        //写操作使能
    hsram1.Init.WaitSignal = FMC_WAIT_SIGNAL_DISABLE;               //禁用等待信号
    hsram1.Init.ExtendedMode = FMC_EXTENDED_MODE_ENABLE;            //打开拓展模式
    hsram1.Init.AsynchronousWait = FMC_ASYNCHRONOUS_WAIT_DISABLE;   //同步等待
    hsram1.Init.WriteBurst = FMC_WRITE_BURST_DISABLE;               //写突发禁止
    hsram1.Init.ContinuousClock = FMC_CONTINUOUS_CLOCK_SYNC_ONLY;   //连续时钟同步
    hsram1.Init.PageSize = FMC_PAGE_SIZE_NONE;                      //页大小无

时序

在模式A中,读/写时序能够改变的时间选项共有三项:

  • BUSTURN:总线反转时间,范围为[0-15]
  • DATASET:数据建立时间,范围为[0-255]
  • ADDSET:地址建立时间,范围为[0-15]

通过观察,可以知道以上三个时间分别对应LCD时序中的前六项:

  1. 读/写 周期时间
  2. 读/写 低电平时间
  3. 读/写 高电平时间

所以,对应初始化程序如下:

    FMC_NORSRAM_TimingTypeDef readTiming = {0};
    readTiming.AccessMode = FMC_ACCESS_MODE_A; //读取模式为:A
    readTiming.AddressSetupTime = 15;          //地址建立时间(15/180M=83ns)
    readTiming.DataSetupTime = 9;              //数据建立时间(9/180M=50ns)
    readTiming.BusTurnAroundDuration = 8;      //总线反转时间((15+9+8)/180M=177ns)

    FMC_NORSRAM_TimingTypeDef writeTiming = {0};
    writeTiming.AccessMode = FMC_ACCESS_MODE_A; //写入模式为:A
    writeTiming.AddressSetupTime = 3;           //地址建立时间:(3/180M=16.6ns)
    writeTiming.DataSetupTime = 3;              //数据建立时间:(3/180M=16.6ns)
    writeTiming.BusTurnAroundDuration = 1;      //总线反转时间:(7/180M=38.8ns)

应用

下面介绍,如何通过指令来写入一个字符:

完整程序如下:

/** 
 * @brief 在给定位置显示给定颜色的汉字
 * @note 无
 * @param {uint8_t} pc 汉字编码数组
 * @param {CharacterType} type 字符类型:英文或者中文
 * @retval 无
 */
void ShowWord(const uint8_t *pc, CharacterType type)
{
    int length = 0;
    uint16_t *col = &character.st.y;
    uint16_t *row = &character.st.x;
    uint8_t sizeX = character.size;
    uint8_t sizeY = type == EN ? character.size >> 1 : character.size;

    /* 1.判断字符是否越界 */
    if (character.st.y + sizeY > LCD_WIDTH)
    {
        character.st.x += sizeX;
        character.st.y = 0;
    }
    if (character.st.x + sizeX > LCD_HEIGHT)
    {
        return;
    }

    /* 2.设置字符区域 */
    SetRegion(*col, *col + sizeY - 1, *row, *row + sizeX - 1);
    length = sizeX * sizeY >> 3;

    /* 3.写入字符 */
    LCD->command = 0x2c00;
    for (int i = 0; i < length; i++)
    {
        uint8_t temp = pc[i];
        for (int j = 0; j < 8; j++)
        {
            LCD->data = temp & 0x01 ? character.foreColor : character.backColor;
            temp >>= 1;
        }
    }

    /* 4.光标右移一个位置 */
    if (type == EN)
    {
        LcdAddSpace(1);
    }
    else
    {
        LcdAddSpace(2);
    }
}

这是在LCD中写入一个字符的程序,大致分成四个步骤:

  1. 判断是否越界
  2. 设置字符所在区域
  3. 写入字符
  4. 光标右移

其实除去多余的处理程序,写入一个字符只需要两个步骤即可:

  1. 设置字符所在的区域
  2. 写入字符

如下图所示:

在这里插入图片描述

设置字符所在区域的函数如下所示:

/** 
 * @brief 设置写入区域范围
 * @note 从0开始计数
 * @param {uint16_t} colS 开始的列数(包含)
 * @param {uint16_t} colE 结束的列数(包含)
 * @param {uint16_t} rowS 开始的行数(包含)
 * @param {uint16_t} rowE 结束的行数(包含)
 * @retval 无
 */
void SetRegion(uint16_t colS, uint16_t colE, uint16_t rowS, uint16_t rowE)
{
    /* 1.设置列范围 */
    __LcdWriteReg(0x2a00, colS >> 8);
    __LcdWriteReg(0x2a01, colS & 0xFF);
    __LcdWriteReg(0x2a02, colE >> 8);
    __LcdWriteReg(0x2a03, colE & 0xFF);

    /* 2.设置行范围 */
    __LcdWriteReg(0x2b00, rowS >> 8);
    __LcdWriteReg(0x2b01, rowS & 0xFF);
    __LcdWriteReg(0x2b02, rowE >> 8);
    __LcdWriteReg(0x2b03, rowE & 0xFF);
}

即使用指令0x2A和0X2B实现。

写入一个字符的具体实现部分如下:

    LCD->command = 0x2c00;
    for (int i = 0; i < length; i++)
    {
        uint8_t temp = pc[i];
        for (int j = 0; j < 8; j++)
        {
            LCD->data = temp & 0x01 ? character.foreColor : character.backColor;
            temp >>= 1;
        }
    }

首先,写入指令0x2c00。然后,通过判断字符编码,判断写入前景色还是背景色。

字符编码可以使用软件PCtoLCD2002 实现,本文不详细介绍其用法。其软件界面截图如下:

在这里插入图片描述

以上是关于STM32CubeMX第七篇之MCU屏的主要内容,如果未能解决你的问题,请参考以下文章

STM32F429第二十五篇之MCU屏实验详解

STM32F429第二十五篇之MCU屏实验详解

LVGL学习笔记 | 02 - 移植LVGL 8.2到STM32F407开发板(MCU屏)

STM32CubeMX第八篇之DMA

STM32CubeMX第八篇之DMA

STM32F429第二十七篇之DMA实验详解