#申请原创# @21小跑堂
项目背景 在工业领域,带有显示的人机交互界面有不少的解决方案,像组态屏、串口屏、LCD液晶显示屏等等;组态屏、串口屏本身具备了UI的基础操作,完善的上位机制作软件,只需要外部主控MCU加以配合就可以完成人机交互的功能,其成本也比后面的LCD液晶显示屏要高出不少;LCD液晶显示屏本身只是一个显示模组,其操作和显示内容完全由主控MCU来控制,更具有控制上的灵活性,但在显示内容及控制逻辑上面需要设计者更多的投入。 因此在使用LCD液晶显示屏来作为人机交互显示界面时,一套轻量级的成熟且开源的SimpleGUI成为了本文的首选。(本文中提及的LCD液晶显示屏为单色点阵式LCD液晶显示屏,并非彩色TFT制式)。
MM32L3xx系列介绍 MM32L3xx系统MCU使用的是高性能的ARM Cortex-M3作为内核的32位微控制器,最高工作频率可达96MHz,高达128KB的内部FLASH程序存储空间和20KB的SRAM,丰富的增强型I/O端口和外设;支持2.0V到5.5V的宽供电电压,工作温度范围支持常规型的-40℃~85℃和扩展型的-40℃~105℃。
MM32F3270系列介绍 MM32F3270系统MCU使用的是高性能的ARM Cortex-M3作为内核的32位微控制器,最高工作频率可达120MHz,高达512KB的内部FLASH程序存储空间和128KB的SRAM,丰富的增强型I/O端口和外设;支持2.0V到5.5V的宽供电电压,工作温度范围支持常规型的-40℃~85℃和扩展型的-40℃~105℃。
SimpleGUI介绍 SimpleGUI是一个开源项目,它是一个针对单色显示屏设计和开发的GUI接口,支持目前市面上12864、240128等LCD单色液晶显示屏。SimpleGUI目标就是轻量化,在尽可能减小资源消耗的前提下,提供了点、线、基本几何图形、单色位图、文字等的绘制功能,以及列表、进度条、滚动条、提示框、曲线图等组件的显示功能;另外为了方便脱离硬件平台进行部分GUI开发,SimpleGUI还提供了单色显示屏模拟环境,VirtualSDK,配合SimpleGUI的低耦合性移植接口定义,几乎可以无缝的移植到预期的硬件平台上。
此外SimpleGUI作者为了码农们能够进一步了解和快速上手,为此还录制了部分视频讲解教程,供参考: | | | https://www.bilibili.com/video/av86593220/ | | https://www.bilibili.com/video/av86890300/ | | https://www.bilibili.com/video/av87098997/ | | https://www.bilibili.com/video/av87432375/ | | https://www.bilibili.com/video/av87530421/ | | https://www.bilibili.com/video/av87713369/ | | https://www.bilibili.com/video/BV1qz4y12771/ |
SimpleGUI码云链接为:https://gitee.com/Polarix/simplegui
原理图设计 MCU我们选用MM32L373PF或者是MM32F3273G6P,这两颗MCU都支持宽电压输入;LCD我们选用的是绘晶科技的HJ240128A液晶屏,工作电压为5V;所以在原理图设计的时候,我们使用一个DC电源输入接口,经过LDO(AMS1117-5.0V)将系统工作电压稳定输出在5V,同时带有电压指示灯;另外通过MAX232芯片将MCU的UART转换成RS-232,建立与PC的通讯链路,方便程序调试和打印输出日志信息,另外就是可以结合BOOT引脚,通过ISP的方式给MCU下载程序。
MM32L373PF和MM32F3273G6P芯片引脚上是PIN TO PIN完全兼容的,MCU可以工作在内部的系统时钟,就可以将外部的时钟晶振省略,这是对时钟要求不高的情况下可以这么设计;系统提供了5个按键,一个是MCU复位按键,另外4个按键功能分别定义为向上、向下、确认、返回;预留了BOOT切换的拨动开关和SWD的程序下载接口;LCD部分使用了PA端口的底8位作为LCD的数据口,其它引脚作为LCD的控制引脚;预留了LCD的背光调节电阻,将LCD的背光电压通过可调电阻,调整到合适的电压,这样可以有效的解决LCD显示鬼影的问题。
另外LCD支持通用的8位并行的8080接口时序控制,MM32L373PF不支持8080接口,但MM32F3273G6P支持8080接口,所以有兴趣的小伙伴,可以修改一下原理图,使用MM32F3273G6P的8080接口来控制LCD显示屏。
PCB板设计 PCB板的尺寸和定位孔是按照LCD的尺寸来绘制的,所以PCB会相对大一些,布局和摆放元器件也宽松了很多。
回板焊接与调试 我们直接来看一下焊接好的PCBA和组装好后的整机吧。整体的原理相对简单,只要焊接OK,并且将LCD与PCB的尺寸和定位孔对应上,问题就不大了,接下来我们就来调试硬件、移植GUI等软件层面的操作了。
移植SimpleGUI 在SGUI_Config.h文件中,SimpleGUI默认是开启VirtualSDK模式的,所以在移植到我们开发板上的时候,需要将如下这两个宏定义都注释掉: //#define _SIMPLE_GUI_IN_VIRTUAL_SDK_ //#define _SIMPLE_GUI_ENABLE_DYNAMIC_MEMORY_
接下来就是初始化SimpleGUI配置参数和实现所需要的LCD接口函数了;在配置SimpleGUI参数时,需要确认LCD的水平像素点和垂直像素点,当前我们使用的240*128显示分辨率的单色LCD液晶显示屏;根据LCD的像素我们需要定义一个显示缓存,缓存的大小为240/*128/8字节;另外还需要实现3个接口函数,分别是LCD在任意坐标画点的功能函数、LCD清除显示屏的功能函数、和LCD刷新显示的功能函数;将这些都配置完成后,就可以调用SimpleGUI的其它功能函数实现简单的图形界面显示了: LCD代码实现部分: uint8_t GraphicBuffer[128][30];
/*******************************************************************************
* [url=home.php?mod=space&uid=247401]@brief[/url] 更新显示缓存
* @param
* @retval
* [url=home.php?mod=space&uid=93590]@Attention[/url]
*******************************************************************************/
void HJ240128A_DisplayGraphic(void)
{
/* 设置显示地址 */
HJ240128A_WriteCommandWithDoubleParam(0x24, 0x00, 0x02);
/* 进入自动写方式 */
HJ240128A_WriteCommandWithoutParam(0xB0);
for(uint16_t i = 0; i < 128; i++)
{
for(uint16_t j = 0; j < 30; j++)
{
HJ240128A_STA3_CheckBusy();
HJ240128A_WriteData(GraphicBuffer[i][j]);
}
}
/* 退出自动写方式 */
HJ240128A_WriteCommandWithoutParam(0xB2);
}
/*******************************************************************************
* @brief 清屏(缓存)
* @param
* @retval
* @attention
*******************************************************************************/
void LCD_ClearScreen(void)
{
memset(GraphicBuffer, 0, sizeof(GraphicBuffer));
}
/*******************************************************************************
* @brief 画点(缓存)
* @param
* @retval
* @attention
*******************************************************************************/
void LCD_DrawPoint(uint8_t X, uint8_t Y, uint8_t Flag)
{
uint8_t Buffer[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
if((X < 240) && (Y < 128))
{
if(Flag)
{
GraphicBuffer[Y][X / 8] |= Buffer[X % 8];
}
else
{
GraphicBuffer[Y][X / 8] &= ~Buffer[X % 8];
}
}
}
/*******************************************************************************
* @brief 读点(缓存)
* @param
* @retval
* @attention
*******************************************************************************/
uint8_t LCD_ReadPoint(uint8_t X, uint8_t Y)
{
uint8_t Buffer[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
return (GraphicBuffer[Y][X / 8] & Buffer[X % 8]);
}
/*******************************************************************************
* @brief 显示ASCII(缓存)
* @param
* @retval
* @attention
*******************************************************************************/
void LCD_DrawASCII(uint8_t X, uint8_t Y, char ch, uint8_t Height)
{
uint8_t Data = 0;
for(uint8_t i = 0; i < Height; i++)
{
if(Height == 12)
{
Data = ASCII_1206[ch - 0x20][i];
}
else
{
Data = ASCII_1608[ch - 0x20][i];
}
for(uint8_t j = 0; j < Height/2; j++)
{
if((Data >> j) & 0x01)
{
LCD_DrawPoint(X + j, Y + i, 1);
}
else
{
LCD_DrawPoint(X + j, Y + i, 0);
}
}
}
}
/*******************************************************************************
* @brief 更新显示(缓存)
* @param
* @retval
* @attention
*******************************************************************************/
void LCD_Refresh(void)
{
HJ240128A_DisplayGraphic();
}
SimpleGUI移植接口代码实现部分: /* Private variables --------------------------------------------------------*/
SGUI_SCR_DEV SimpleGUI_Device;
SGUI_BYTE SimpleGUI_Buffer[240*128/8];
SGUI_SIZE SimpleGUI_Lenght = 240 * 16;
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void SGUI_SDK_SetPixel(SGUI_INT iX, SGUI_INT iY, SGUI_UINT iColor)
{
if(iColor == SGUI_COLOR_FRGCLR)
{
LCD_DrawPoint(iX, iY, 1);
}
else
{
LCD_DrawPoint(iX, iY, 0);
}
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void SGUI_SDK_ClearDisplay(void)
{
LCD_ClearScreen();
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void SGUI_SDK_RefreshDisplay(void)
{
LCD_Refresh();
}
/*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void SimpleGUI_Init(void)
{
SGUI_SystemIF_MemorySet(&SimpleGUI_Device, 0x00, sizeof(SGUI_SCR_DEV));
SimpleGUI_Device.stSize.iWidth = 240;
SimpleGUI_Device.stSize.iHeight = 128;
SimpleGUI_Device.stBuffer.pBuffer = SimpleGUI_Buffer;
SimpleGUI_Device.stBuffer.sSize = SimpleGUI_Lenght;
SimpleGUI_Device.fnSetPixel = SGUI_SDK_SetPixel;
SimpleGUI_Device.fnClear = SGUI_SDK_ClearDisplay;
SimpleGUI_Device.fnSyncBuffer = SGUI_SDK_RefreshDisplay;
SimpleGUI_Device.fnClear();
SimpleGUI_Device.fnSyncBuffer();
}
SimpleGUI简单图形界面示例代码及效果: /*******************************************************************************
* @brief
* @param
* @retval
* @attention
*******************************************************************************/
void SimpleGUI_Demo(void)
{
SGUI_RECT stDisplayArea;
SGUI_POINT stInnerPos;
SimpleGUI_Device.fnClear();
stInnerPos.iX = 0;
stInnerPos.iY = 0;
stDisplayArea.iX = 130;
stDisplayArea.iY = 0;
stDisplayArea.iWidth = SGUI_DEFAULT_FONT_8.iHalfWidth * 12;
stDisplayArea.iHeight = SGUI_DEFAULT_FONT_8.iHeight;
SGUI_Text_DrawText(&SimpleGUI_Device, "Hello World!", &SGUI_DEFAULT_FONT_8, &stDisplayArea, &stInnerPos, SGUI_DRAW_NORMAL);
stDisplayArea.iX = 130;
stDisplayArea.iY = 16;
stDisplayArea.iWidth = SGUI_DEFAULT_FONT_12.iHalfWidth * 12;
stDisplayArea.iHeight = SGUI_DEFAULT_FONT_12.iHeight;
SGUI_Text_DrawText(&SimpleGUI_Device, "Hello World!", &SGUI_DEFAULT_FONT_12, &stDisplayArea, &stInnerPos, SGUI_DRAW_REVERSE);
stDisplayArea.iX = 130;
stDisplayArea.iY = 32;
stDisplayArea.iWidth = SGUI_DEFAULT_FONT_16.iHalfWidth * 12;
stDisplayArea.iHeight = SGUI_DEFAULT_FONT_16.iHeight;
SGUI_Text_DrawText(&SimpleGUI_Device, "Hello World!", &SGUI_DEFAULT_FONT_16, &stDisplayArea, &stInnerPos, SGUI_DRAW_NORMAL);
SGUI_Basic_DrawLine(&SimpleGUI_Device, 0, 10, 96, 10, SGUI_COLOR_FRGCLR);
SGUI_Basic_DrawLine(&SimpleGUI_Device, 0, 10, 15, 25, SGUI_COLOR_FRGCLR);
SGUI_Basic_DrawRectangle(&SimpleGUI_Device, 20, 35, 20, 20, SGUI_COLOR_FRGCLR, SGUI_COLOR_BKGCLR);
SGUI_Basic_DrawRoundedRectangle(&SimpleGUI_Device, 15, 80, 40, 30, 5, SGUI_COLOR_FRGCLR, SGUI_COLOR_BKGCLR);
SGUI_Basic_DrawCircle(&SimpleGUI_Device, 100, 32, 10, SGUI_COLOR_FRGCLR, SGUI_COLOR_FRGCLR);
SGUI_Basic_DrawCircle(&SimpleGUI_Device, 100, 80, 10, SGUI_COLOR_FRGCLR, SGUI_COLOR_TRANS);
SimpleGUI_Device.fnSyncBuffer();
}
移植SimpleGUI Demo演示例程 SimpleGUI主要由GUI和HMI两部分组成的,GUI部分主要功能为屏幕显示的控制,例如基础几何图形的绘制、文字的绘制、以及基础基础几何图形和文字的各种组件的绘制,而HMI部分则是负责屏幕画面的组织和与用户交互的处理,主要目的是将画面显示与业务/功能处理分割开来方便项目的维护。在官方提供的SimpleGUI源码包里DemoProc、GUI、HMI、VirtualSDK这两个文件夹;其中DemoProc提供了SimpleGUI支持的组件的示例程序和演示,结合HMI引擎,达到一个GUI小项目系统的功能。
可能刚一开始接触到DemoProc.C以及HMI_Engine.c会有点蒙;具体的可以参考官方提供的《02-移植演示程序.md》去熟悉,也可以参考本文中最后给的附件代码来理解;附件中给出的代码是基于MM32F3270系列MCU移植的SimpleGUI Demo演示程序,当然也是基于上述的开发板硬件平台上的适配程序。
运行效果显示
附件:
|