玩转RT-Thread系列教程(6)--移植STemwin


玩转RT-Thread系列教程(6)–移植STemwin

一、STemwin介绍

在实际应用中我们时常需要制作 UI 界面来实现人机交互,简单的 UI 我们可以自己直接写代码,但是对于那些复杂的交互方式和界面自己写代码的话难度就会很大。为了方便开发我们可以使用第三方的 GUI 库来做 UI 界面设计,在 STM32 上最火的 GUI 库莫过于 UCGUI,而UCGUI 的高级版本就是 emWin,STemWin 是 SEGGER 授权给 ST 的 emWin 版本,ST 的芯片可以免费使用 STemWin,而且 STemWin 针对 ST 的芯片做了优化。

二、添加软件包

1.进入env配置界面

​ menuconfig

2.选择我们要选择的软件包

​ 添加屏幕驱动(这里我使用的是正点原子4.3寸电容触摸屏–触摸芯片型号为GT9147)

3.使能内存分配算法

4.添加IIC驱动

​ 经过对比引脚,我们开发板的触摸iic引脚分布在PB1、PF11上。

5.添加STemwin软件包

6.使用命令从仓库拉取软件包

7.保存配置使用命令生成MDK工程

​ 打开工程可以看见已经生成了对应的工程

三、驱动配置

1.查看电路原理图

​ SRAM和LCD对应的FSMC_NE3,FSMC_NE4

2.使用Cubemx配置驱动

3.CRC配置

4.添加SRAM驱动

​ 打开STemwin官方实例的drv_lcd.c文件,正好正点原子屏幕是使用fsmc驱动的,所以我们只需简单配置即可。

//初始化外部SRAM
void FSMC_SRAM_Init(void)
{
    RCC->AHBENR |= 1 << 8;       //使能FSMC时钟
    RCC->APB2ENR |= 1 << 5;      //使能PORTD时钟
    RCC->APB2ENR |= 1 << 6;      //使能PORTE时钟
    RCC->APB2ENR |= 1 << 7;      //使能PORTF时钟
    RCC->APB2ENR |= 1 << 8;      //使能PORTG时钟

    //PORTD复用推挽输出
    GPIOD->CRH &= 0X00000000;
    GPIOD->CRH |= 0XBBBBBBBB;
    GPIOD->CRL &= 0XFF00FF00;
    GPIOD->CRL |= 0X00BB00BB;
    //PORTE复用推挽输出
    GPIOE->CRH &= 0X00000000;
    GPIOE->CRH |= 0XBBBBBBBB;
    GPIOE->CRL &= 0X0FFFFF00;
    GPIOE->CRL |= 0XB00000BB;
    //PORTF复用推挽输出
    GPIOF->CRH &= 0X0000FFFF;
    GPIOF->CRH |= 0XBBBB0000;
    GPIOF->CRL &= 0XFF000000;
    GPIOF->CRL |= 0X00BBBBBB;

    //PORTG复用推挽输出 PG10->NE3
    GPIOG->CRH &= 0XFFFFF0FF;
    GPIOG->CRH |= 0X00000B00;
    GPIOG->CRL &= 0XFF000000;
    GPIOG->CRL |= 0X00BBBBBB;

    //寄存器清零
    //bank1有NE1~4,每一个有一个BCR+TCR,所以总共八个寄存器。
    //这里我们使用NE3 ,也就对应BTCR[4],[5]。
    FSMC_Bank1->BTCR[4] = 0X00000000;
    FSMC_Bank1->BTCR[5] = 0X00000000;
    FSMC_Bank1E->BWTR[4] = 0X00000000;
    //操作BCR寄存器  使用异步模式,模式A(读写共用一个时序寄存器)
    //BTCR[偶数]:BCR寄存器;BTCR[奇数]:BTR寄存器
    FSMC_Bank1->BTCR[4] |= 1 << 12;    //存储器写使能
    FSMC_Bank1->BTCR[4] |= 1 << 4; //存储器数据宽度为16bit
    //操作BTR寄存器
    FSMC_Bank1->BTCR[5] |= 3 << 8; //数据保持时间(DATAST)为3个HCLK 4/72M=55ns(对EM的SRAM芯片)
    FSMC_Bank1->BTCR[5] |= 0 << 4; //地址保持时间(ADDHLD)未用到
    FSMC_Bank1->BTCR[5] |= 0 << 0; //地址建立时间(ADDSET)为2个HCLK 1/36M=27ns
    //闪存写时序寄存器
    FSMC_Bank1E->BWTR[4] = 0x0FFFFFFF;    //默认值
    //使能BANK1区域3
    FSMC_Bank1->BTCR[4] |= 1 << 0;
}

​ 添加到LCD初始化下面

进入gt9147.c中,添加触摸屏初始化:

//复位引脚
#define GT9147_RST_PIN 91
//中断引脚--PF10 INT
#define GT9147_IRQ_PIN 90

int rt_hw_gt9147_port(void)
{
    struct rt_touch_config config;
    rt_uint8_t rst;

    rst = GT9147_RST_PIN;
    config.dev_name = "i2c1";
    config.irq_pin.pin = GT9147_IRQ_PIN;
    config.irq_pin.mode = PIN_MODE_INPUT_PULLDOWN;
    config.user_data = &rst;

    rt_hw_gt9147_init("gt", &config);

    return 0;
}
INIT_ENV_EXPORT(rt_hw_gt9147_port);

这样,我们的LCD和SRAM就配置好了。

5.编译、下载,打开串口助手

输入list_device,可以看见lcd和gt触摸屏设备已经注册好了。

输入free,查看串口返回:

串口返回1048528字节,1048528/1024=1,023.9k=1M,间接证明了我们开发板的SRAM的内存是1M。

四、STemwin配置

1.关于STemwin官方文件说明

2.系统配置

​ 在GUIConf.c中使用动态从SRAM中申请方式为STemwin分配512k的内存。

3.触摸屏移植

​ 配置GUI_X_Touch_Analog.c 文件:

GUI_X_Touch_Analog.c 文件中有四个函数 :GUI_TOUCH_X_ActivateX()GUI_TOUCH_X_ActivateY()**, **GUI_TOUCH_X_MeasureX()**和 **GUI_TOUCH_X_MeasureY()**。其中前两个我们没有使用到 ,STemWin 真正调用GUI_TOUCH_X_MeasureXGUI_TOUCH_X_MeasureY**这两个函数来获取触摸屏按下时的X轴和Y轴AD值。

void GUI_TOUCH_X_ActivateX(void)
{
}

void GUI_TOUCH_X_ActivateY(void)
{
}

int  GUI_TOUCH_X_MeasureX(void)
{
    int32_t xvalue;

    for (rt_uint8_t i = 0; i < info.point_num; i++)
    {
        if (read_data[i].event == RT_TOUCH_EVENT_DOWN || read_data[i].event == RT_TOUCH_EVENT_MOVE)
        {
            xvalue = read_data[i].x_coordinate;
        }
        else
        {
            xvalue = 0xffff;
        }
        return xvalue;
    }
}

int  GUI_TOUCH_X_MeasureY(void)
{
    int32_t yvalue;

    for (rt_uint8_t i = 0; i < info.point_num; i++)
    {
        if (read_data[i].event == RT_TOUCH_EVENT_DOWN || read_data[i].event == RT_TOUCH_EVENT_MOVE)
        {
            yvalue = read_data[i].y_coordinate;
        }
        else
        {
            yvalue = 0xffff;
        }
        return yvalue;
    }
}

static void gt9147_entry(void *parameter)
{
    rt_device_control(dev, RT_TOUCH_CTRL_GET_INFO, &info);

    read_data = (struct rt_touch_data *)rt_malloc(sizeof(struct rt_touch_data) * info.point_num);

    while (1)
    {
        //获取信号量
        rt_sem_take(gt9147_sem, RT_WAITING_FOREVER);
        //触摸屏是否按下
        rt_device_read(dev, 0, read_data, info.point_num);
        //开中断
        rt_device_control(dev, RT_TOUCH_CTRL_ENABLE_INT, RT_NULL);
    }
}

static rt_err_t rx_callback(rt_device_t dev, rt_size_t size)
{
    //释放信号量
    rt_sem_release(gt9147_sem);
    //关中断
    rt_device_control(dev, RT_TOUCH_CTRL_DISABLE_INT, RT_NULL);
    return 0;
}

/* Test function */
int gt9147_sample(const char *name, rt_uint16_t x, rt_uint16_t y)
{
    void *id;

    dev = rt_device_find(name);

    if (dev == RT_NULL)
    {
        rt_kprintf("can't find device:%s\n", name);
        return -1;
    }

    if (rt_device_open(dev, RT_DEVICE_FLAG_INT_RX) != RT_EOK)
    {
        rt_kprintf("open device failed!");
        return -1;
    }

    id = rt_malloc(sizeof(struct rt_touch_info));
    rt_device_control(dev, RT_TOUCH_CTRL_GET_ID, id);
    rt_uint8_t * read_id = (rt_uint8_t *)id;
    rt_kprintf("id = %d %d %d %d \n", read_id[0] - '0', read_id[1] - '0', read_id[2] - '0', read_id[3] - '0');

    rt_device_control(dev, RT_TOUCH_CTRL_SET_X_RANGE, &x);  /* if possible you can set your x y coordinate*/
    rt_device_control(dev, RT_TOUCH_CTRL_SET_Y_RANGE, &y);
    rt_device_control(dev, RT_TOUCH_CTRL_GET_INFO, id);
    rt_kprintf("range_x = %d \n", (*(struct rt_touch_info*)id).range_x);
    rt_kprintf("range_y = %d \n", (*(struct rt_touch_info*)id).range_y);
    rt_kprintf("point_num = %d \n", (*(struct rt_touch_info*)id).point_num);
    rt_free(id);

    gt9147_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO);

    if (gt9147_sem == RT_NULL)
    {
        rt_kprintf("create dynamic semaphore failed.\n");
        return -1;
    }

    rt_device_set_rx_indicate(dev, rx_callback);

    gt9147_thread = rt_thread_create("touch_lcd",
                                     gt9147_entry,
                                     RT_NULL,
                                     THREAD_STACK_SIZE,
                                     THREAD_PRIORITY,
                                     THREAD_TIMESLICE);

    if (gt9147_thread != RT_NULL)
        rt_thread_startup(gt9147_thread);

    return 0;
}

int touch_init(void)
{
    gt9147_sample("gt", 800, 480);
    return RT_EOK;
}
INIT_APP_EXPORT(touch_init);

​ 最后修改 LCDConf_FlexColor_Template.c 文件中的 LCD_X_Config()函数,在最后加上触摸屏的校准(必须)

4.STemwin的“点灯仪式”

既然我们已经移植并配置好了LCD以及触摸功能,那么就来测试一下我们的emwin是否工作正常吧

我们新建一个stemwin测试文件,测试代码如下:

#include <rtthread.h>
#include "GUI.h"
#include <board.h>

#include "DIALOG.h"
//////////////////////////////////////////////////////////
//TOUCH任务
//设置任务优先级
#define TOUCH_TASK_PRIO                  15
//任务堆栈大小
#define TOUCH_STK_SIZE                  216

//EMWINDEMO任务
//设置任务优先级
#define EMWINDEMO_TASK_PRIO            18
//任务堆栈大小
#define EMWINDEMO_STK_SIZE            2048
//////////////////////////////////////////////////////////

#define ID_FRAMEWIN_0 (GUI_ID_USER + 0x00)
#define ID_BUTTON_0 (GUI_ID_USER + 0x01)
#define ID_BUTTON_1 (GUI_ID_USER + 0x02)

//对话框资源表
static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] =
{
    { FRAMEWIN_CreateIndirect, "Framewin", ID_FRAMEWIN_0, 0, 0, 800, 480, FRAMEWIN_CF_MOVEABLE, 0x64, 0 },
    { BUTTON_CreateIndirect, "Button", ID_BUTTON_0, 300, 122, 150, 50, 0, 0x0, 0 },
    { BUTTON_CreateIndirect, "Button", ID_BUTTON_1, 300, 251, 150, 50, 0, 0x0, 0 },
};

//对话框回调函数
static void _cbDialog(WM_MESSAGE * pMsg)
{
    WM_HWIN hItem;
    int     NCode;
    int     Id;

    switch (pMsg->MsgId)
    {
        case WM_INIT_DIALOG:
            //初始化对话框
            hItem = pMsg->hWin;
            FRAMEWIN_SetTitleHeight(hItem, 30);
            FRAMEWIN_SetText(hItem, "RB_RT-Thread STemwin demo");
            FRAMEWIN_SetFont(hItem, GUI_FONT_24_ASCII);
            FRAMEWIN_SetTextAlign(hItem, GUI_TA_HCENTER | GUI_TA_VCENTER);
            FRAMEWIN_SetTextColor(hItem, 0x0000FFFF);

            //初始化BUTTON0
            hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_0);
            BUTTON_SetFont(hItem, GUI_FONT_24_ASCII);
            BUTTON_SetText(hItem, "LED1");

            //初始化BUTTON1
            hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_1);
            BUTTON_SetText(hItem, "BEEP");
            BUTTON_SetFont(hItem, GUI_FONT_24_ASCII);
            break;

        case WM_NOTIFY_PARENT:
            Id    = WM_GetId(pMsg->hWinSrc);
            NCode = pMsg->Data.v;

            switch(Id)
            {
                case ID_BUTTON_0: //BUTTON_0的通知代码,控制LED1
                    switch(NCode)
                    {
                        case WM_NOTIFICATION_CLICKED:
                            break;
                        case WM_NOTIFICATION_RELEASED: //按钮被按下并释放
                            rt_kprintf("led on\n");
                            break;
                    }
                    break;

                case ID_BUTTON_1: //BUTTON_1的通知代码,控制BEEP
                    switch(NCode)
                    {
                        case WM_NOTIFICATION_CLICKED:
                            break;

                        case WM_NOTIFICATION_RELEASED:
                            rt_kprintf("beep on\n");
                            //LED1=~LED1;
                            break;
                    }
                    break;
            }
            break;
        default:
            WM_DefaultProc(pMsg);
            break;
    }
}

//创建一个对话框
WM_HWIN CreateFramewin(void)
{
    WM_HWIN hWin;
    //非阻塞式对话框
    hWin = GUI_CreateDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), _cbDialog, WM_HBKWIN, 0, 0);
    return hWin;
}

//BUTTON按钮上显示位图
void Buttonbmp_Demo(void)
{
    WM_HWIN hWin;
    hWin = CreateFramewin();

    while(1)
    {
        GUI_Delay(10);
    }
}

static void Sys_CRC_Init(void)
{
    CRC_HandleTypeDef CrcHandle;
    CrcHandle.State = HAL_CRC_STATE_RESET;
    CrcHandle.Instance = CRC;
    HAL_CRC_Init(&CrcHandle);
}

//显示任务
static void STemwin_thread(void *param)
{
    Sys_CRC_Init();
    WM_SetCreateFlags(WM_CF_MEMDEV);     //启动所有窗口的存储设备
    GUI_Init();
    //
    GUI_CURSOR_Show();
    //更换皮肤
    BUTTON_SetDefaultSkin(BUTTON_SKIN_FLEX);
    CHECKBOX_SetDefaultSkin(CHECKBOX_SKIN_FLEX);
    DROPDOWN_SetDefaultSkin(DROPDOWN_SKIN_FLEX);
    FRAMEWIN_SetDefaultSkin(FRAMEWIN_SKIN_FLEX);
    HEADER_SetDefaultSkin(HEADER_SKIN_FLEX);
    MENU_SetDefaultSkin(MENU_SKIN_FLEX);
    MULTIPAGE_SetDefaultSkin(MULTIPAGE_SKIN_FLEX);
    PROGBAR_SetDefaultSkin(PROGBAR_SKIN_FLEX);
    RADIO_SetDefaultSkin(RADIO_SKIN_FLEX);
    SCROLLBAR_SetDefaultSkin(SCROLLBAR_SKIN_FLEX);
    SLIDER_SetDefaultSkin(SLIDER_SKIN_FLEX);
    SPINBOX_SetDefaultSkin(SPINBOX_SKIN_FLEX);

    while (1)
    {
        Buttonbmp_Demo();
    }
}

//触摸任务
static void touch_thread(void *param)
{
    while(1)
    {
        GUI_TOUCH_Exec();
        rt_thread_mdelay(5);
    }
}

static int Gui_emwin_init(void)
{
    rt_thread_t tid;
    tid = rt_thread_create("emwin",
                           STemwin_thread, RT_NULL,
                           EMWINDEMO_STK_SIZE,
                           EMWINDEMO_TASK_PRIO, 10);

    if (tid != RT_NULL)
        rt_thread_startup(tid);

    return RT_EOK;
}
INIT_APP_EXPORT(Gui_emwin_init);

static int Gui_touch_init(void)
{
    rt_thread_t btn_tid;
    btn_tid = rt_thread_create("touch",
                               touch_thread, RT_NULL,
                               TOUCH_STK_SIZE,
                               TOUCH_TASK_PRIO, 10);

    if (btn_tid != RT_NULL)![image-20210530164629809](C:\Users\zbr\AppData\Roaming\Typora\typora-user-images\image-20210530164629809.png)
        rt_thread_startup(btn_tid);

    return RT_EOK;
}
INIT_APP_EXPORT(Gui_touch_init);

5.编译、下载,验证

让我们点击一下中间的两个按钮,观察串口输出:

至此,我们在RT-Thread上面移植STemwin已经成功的得以已验证。


文章作者: Rb菌
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Rb菌 !
  目录