发新帖本帖赏金 100.00元(功能说明)我要提问
12下一页
返回列表
打印
[MM32生态]

基于MM32的SimpleGUI移植与演示(工业HMI解决方案)

[复制链接]
6477|30
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创#     @21小跑堂

项目背景
在工业领域,带有显示的人机交互界面有不少的解决方案,像组态屏、串口屏、LCD液晶显示屏等等;组态屏、串口屏本身具备了UI的基础操作,完善的上位机制作软件,只需要外部主控MCU加以配合就可以完成人机交互的功能,其成本也比后面的LCD液晶显示屏要高出不少;LCD液晶显示屏本身只是一个显示模组,其操作和显示内容完全由主控MCU来控制,更具有控制上的灵活性,但在显示内容及控制逻辑上面需要设计者更多的投入。
因此在使用LCD液晶显示屏来作为人机交互显示界面时,一套轻量级的成熟且开源的SimpleGUI成为了本文的首选。(本文中提及的LCD液晶显示屏为单色点阵式LCD液晶显示屏,并非彩色TFT制式)。

MM32L3xx系列介绍
MM32L3xx系统MCU使用的是高性能的ARM Cortex-M3作为内核的32位微控制器,最高工作频率可达96MHz,高达128KB的内部FLASH程序存储空间和20KBSRAM,丰富的增强型I/O端口和外设;支持2.0V5.5V的宽供电电压,工作温度范围支持常规型的-40~85℃和扩展型的-40~105℃。

MM32F3270系列介绍
MM32F3270系统MCU使用的是高性能的ARM Cortex-M3作为内核的32位微控制器,最高工作频率可达120MHz,高达512KB的内部FLASH程序存储空间和128KBSRAM,丰富的增强型I/O端口和外设;支持2.0V5.5V的宽供电电压,工作温度范围支持常规型的-40~85℃和扩展型的-40~105℃。

SimpleGUI介绍
SimpleGUI是一个开源项目,它是一个针对单色显示屏设计和开发的GUI接口,支持目前市面上12864240128LCD单色液晶显示屏。SimpleGUI目标就是轻量化,在尽可能减小资源消耗的前提下,提供了点、线、基本几何图形、单色位图、文字等的绘制功能,以及列表、进度条、滚动条、提示框、曲线图等组件的显示功能;另外为了方便脱离硬件平台进行部分GUI开发,SimpleGUI还提供了单色显示屏模拟环境,VirtualSDK,配合SimpleGUI的低耦合性移植接口定义,几乎可以无缝的移植到预期的硬件平台上。

此外SimpleGUI作者为了码农们能够进一步了解和快速上手,为此还录制了部分视频讲解教程,供参考:
内容
地址
01.SimpleGUI概述
https://www.bilibili.com/video/av86593220/
02.基础绘图
https://www.bilibili.com/video/av86890300/
03.文本文字
https://www.bilibili.com/video/av87098997/
04.拓展组件概述
https://www.bilibili.com/video/av87432375/
05.交互引擎HMI
https://www.bilibili.com/video/av87530421/
06.VirtualSDK概述
https://www.bilibili.com/video/av87713369/
07.基于VirtualSDKGUI开发
https://www.bilibili.com/video/BV1qz4y12771/

SimpleGUI码云链接为:https://gitee.com/Polarix/simplegui

原理图设计
MCU我们选用MM32L373PF或者是MM32F3273G6P,这两颗MCU都支持宽电压输入;LCD我们选用的是绘晶科技的HJ240128A液晶屏,工作电压为5V;所以在原理图设计的时候,我们使用一个DC电源输入接口,经过LDOAMS1117-5.0V)将系统工作电压稳定输出在5V,同时带有电压指示灯;另外通过MAX232芯片将MCUUART转换成RS-232,建立与PC的通讯链路,方便程序调试和打印输出日志信息,另外就是可以结合BOOT引脚,通过ISP的方式给MCU下载程序。

MM32L373PFMM32F3273G6P芯片引脚上是PIN TO PIN完全兼容的,MCU可以工作在内部的系统时钟,就可以将外部的时钟晶振省略,这是对时钟要求不高的情况下可以这么设计;系统提供了5个按键,一个是MCU复位按键,另外4个按键功能分别定义为向上、向下、确认、返回;预留了BOOT切换的拨动开关和SWD的程序下载接口;LCD部分使用了PA端口的底8位作为LCD的数据口,其它引脚作为LCD的控制引脚;预留了LCD的背光调节电阻,将LCD的背光电压通过可调电阻,调整到合适的电压,这样可以有效的解决LCD显示鬼影的问题。

另外LCD支持通用的8位并行的8080接口时序控制,MM32L373PF不支持8080接口,但MM32F3273G6P支持8080接口,所以有兴趣的小伙伴,可以修改一下原理图,使用MM32F3273G6P8080接口来控制LCD显示屏。

PCB板设计
PCB板的尺寸和定位孔是按照LCD的尺寸来绘制的,所以PCB会相对大一些,布局和摆放元器件也宽松了很多。

回板焊接与调试
我们直接来看一下焊接好的PCBA和组装好后的整机吧。整体的原理相对简单,只要焊接OK,并且将LCDPCB的尺寸和定位孔对应上,问题就不大了,接下来我们就来调试硬件、移植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主要由GUIHMI两部分组成的,GUI部分主要功能为屏幕显示的控制,例如基础几何图形的绘制、文字的绘制、以及基础基础几何图形和文字的各种组件的绘制,而HMI部分则是负责屏幕画面的组织和与用户交互的处理,主要目的是将画面显示与业务/功能处理分割开来方便项目的维护。在官方提供的SimpleGUI源码包里DemoProcGUIHMIVirtualSDK这两个文件夹;其中DemoProc提供了SimpleGUI支持的组件的示例程序和演示,结合HMI引擎,达到一个GUI小项目系统的功能。

可能刚一开始接触到DemoProc.C以及HMI_Engine.c会有点蒙;具体的可以参考官方提供的《02-移植演示程序.md》去熟悉,也可以参考本文中最后给的附件代码来理解;附件中给出的代码是基于MM32F3270系列MCU移植的SimpleGUI Demo演示程序,当然也是基于上述的开发板硬件平台上的适配程序。

运行效果显示

附件:
HJ240128A: 240128A模块说明书.pdf (935.82 KB)
原理图: Schematic_MM32L373PF_LCD240128_2022-02-16.pdf (148.99 KB)
SimpleGUI移植例程: SimpleGUI.zip (9.78 MB)
SimpleGUI DemoProc演示例程: SimpleGUI_DemoProc.part1.rar (5 MB) SimpleGUI_DemoProc.part2.rar (4.65 MB)
SimpleGUI官方程序: simplegui-Develope.zip (9.25 MB)


使用特权

评论回复

打赏榜单

21小跑堂 打赏了 100.00 元 2022-02-17
理由:恭喜通过原创奖审核!请多多加油哦!

沙发
yangxiaor520| | 2022-2-16 20:37 | 只看该作者
牛X,学习了。

使用特权

评论回复
板凳
guijial511| | 2022-2-17 08:40 | 只看该作者
这个不错

使用特权

评论回复
地板
WoodData| | 2022-2-17 09:22 | 只看该作者
效果不错啊

使用特权

评论回复
5
xld0932|  楼主 | 2022-2-17 12:22 | 只看该作者

使用特权

评论回复
6
htmlme| | 2022-2-20 15:04 | 只看该作者
SimpleGUI怎么样   

使用特权

评论回复
7
minzisc| | 2022-2-20 15:19 | 只看该作者
工业HMI?      

使用特权

评论回复
8
primojones| | 2022-2-21 11:43 | 只看该作者
GUI移植真是难呢   

使用特权

评论回复
9
macpherson| | 2022-2-21 11:52 | 只看该作者
SimpleGUI移植示例

使用特权

评论回复
10
jtracy3| | 2022-2-21 12:02 | 只看该作者
运行的效果怎么样  

使用特权

评论回复
11
belindagraham| | 2022-2-21 12:12 | 只看该作者
这个工业上用的吗  

使用特权

评论回复
评论
xld0932 2022-2-21 12:37 回复TA
可以用在工业上 
12
coslight| | 2022-2-23 19:48 | 只看该作者
不错的使用,这个值得留意

使用特权

评论回复
13
LED2013| | 2022-2-28 22:27 | 只看该作者
这个学习起来还是够力

使用特权

评论回复
14
wiba| | 2022-3-1 14:09 | 只看该作者
移植过程顺利吗

使用特权

评论回复
15
kxsi| | 2022-3-1 14:16 | 只看该作者
应用领域应该很广泛

使用特权

评论回复
16
coshi| | 2022-3-1 14:30 | 只看该作者
感觉还是比较流畅的

使用特权

评论回复
17
drer| | 2022-3-1 14:38 | 只看该作者
这个是什么接口的屏幕啊

使用特权

评论回复
18
qcliu| | 2022-3-1 14:58 | 只看该作者
原理图很见功底啊

使用特权

评论回复
19
foxsbig| | 2022-3-4 17:23 | 只看该作者
赞~~~非常好的说

使用特权

评论回复
20
zjsx8192| | 2022-8-15 10:16 | 只看该作者
lcd部分不错

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:上海灵动微电子股份有限公司资深现场应用工程师
简介:诚信·承诺·创新·合作

70

主题

3000

帖子

31

粉丝