STM32CubeMX第七篇之MCU屏
Posted 海洋想想
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32CubeMX第七篇之MCU屏相关的知识,希望对你有一定的参考价值。
前言
本文主要讲解自己实现MCU控制程序。
本程序具有以下功能:
- 能够输出汉字
- 能够输出ASCII字符串
- 能够输出整数
本文使用的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;
}
}
在主程序中,已经以下几个步骤:
- 外设配置
- 输出OLED屏内容
- 点亮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时序中的前六项:
- 读/写 周期时间
- 读/写 低电平时间
- 读/写 高电平时间
所以,对应初始化程序如下:
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中写入一个字符的程序,大致分成四个步骤:
- 判断是否越界
- 设置字符所在区域
- 写入字符
- 光标右移
其实除去多余的处理程序,写入一个字符只需要两个步骤即可:
- 设置字符所在的区域
- 写入字符
如下图所示:
设置字符所在区域的函数如下所示:
/**
* @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屏的主要内容,如果未能解决你的问题,请参考以下文章