rt-thread+littlevgl 移植

经过一个多月的时间的学习,rt-thread+littlevgl移植成功,现在记录一下过程。

一、准备过程

从rt-thread官方网站下载rt-thread源码文件,链接地址:GitHub - RT-Thread/rt-thread: RT-Thread is an open source IoT operating system from China.

littlevgl源码:docs/index.md at master · littlevgl/docs · GitHub
正点原子stm32f407的触摸屏实验源码。

进入到 rt-thread\bsp\stm32\stm32f407-atk-explorer 文件夹中,双击 project.uvprojx 文件,打开 MDK5 工程。


image

1.把littlevgl的源码解压后,复制到rt-thread\bsp\stm32\stm32f407-atk-explorer 文件夹中。把HARDWARE和SYSTEM文件夹也复制到rt-thread\bsp\stm32\stm32f407-atk-explorer 文件夹中

2.下载的解压后名称为lvgl-master,重命名为lvgl(下面也用lvgl也是这个文件夹),把lvgl里的lv_core等文件夹添加到工程。

3.将lvgl文件夹中的lv_conf_template.h复制到与lvgl同级目录,并重命名为lv_conf

4.打开MDK5工程文件,添加hardware文件,timer.c不用添加进来。
image.png

5.在stm32_HAL库中再添加两个库文件,并在stm32f4xx_hal_conf.h中使能这两个文件:
image.png

.打开lv_conf,将#if 0改为 # if 1,设置你的命名的宽高像素,以及颜色模式,如下代码所示

 * @file lv_conf.h
 *
 */

/*
 * COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER
 */

#if 1 /*Set it to "1" to enable content*///改为1

#ifndef LV_CONF_H
#define LV_CONF_H
/* clang-format off */

#include <stdint.h>

/*====================
   Graphical settings
 *====================*/

/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX          (480)//屏幕宽
#define LV_VER_RES_MAX          (800)//屏幕高

/* Color depth:
 * - 1:  1 byte per pixel//如果是黑白屏就设置这个
 * - 8:  RGB233
 * - 16: RGB565
 * - 32: ARGB8888
 */
#define LV_COLOR_DEPTH     16//这里选择 RGB565模式

/* Swap the 2 bytes of RGB565 color.

......

main.c中的代码:

/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-11-06     SummerGift   first version
 * 2018-11-19     flybreak     add stm32f407-atk-explorer bsp
 */

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>
//#include "lcd.h"
#include "lvgl/lvgl.h"
//#include "touch.h"




#include "sys.h"
#include "delay.h"
#include "usart.h"
//#include "led.h"
//#include "key.h"
#include "lcd.h"
//#include "usmart.h"
#include "touch.h"
#include "lv_examples.h"

/* RT-Thread相关函数和变量 */
static rt_thread_t key_thread = RT_NULL;
static void key_thread_entry(void *parameter);

static rt_thread_t gui_thread = RT_NULL;
static void gui_thread_entry(void *parameter);

static rt_thread_t idle_thread = RT_NULL;
static void idle_thread_entry(void *parameter);

/**
  * @brief GUI线程函数
  * @param parameter-线程入口参数
  * @retval None
  */
void my_disp_flush(lv_disp_drv_t * disp, const lv_area_t * area, lv_color_t * color_p)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
    int32_t x, y;
    for(y = area->y1; y <= area->y2; y++) {
        for(x = area->x1; x <= area->x2; x++) {
            LCD_Fast_DrawPoint( x, y, color_p->full);       //自己的打点函数
            color_p++;
        }
    }

    /* IMPORTANT!!!
     * Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp);
}
bool my_touchpad_read(lv_indev_t * indev, lv_indev_data_t * data)
{
    static lv_coord_t last_x = 0;
    static lv_coord_t last_y = 0;

    /*Save the state and save the pressed coordinate*/
    data->state = tp_dev.sta&TP_PRES_DOWN ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
    //if(data->state == LV_INDEV_STATE_PR) touchpad_get_xy(&last_x, &last_y);
    
    if(LV_INDEV_STATE_PR)           //触摸屏被按下
        {   
            if(tp_dev.x[0]<lcddev.width&&tp_dev.y[0]<lcddev.height)
            {   
                //if(tp_dev.x[0]>(lcddev.width-24)&&tp_dev.y[0]<16)Load_Drow_Dialog();//清除
                //else TP_Draw_Big_Point(tp_dev.x[0],tp_dev.y[0],RED);      //画图
                last_x=tp_dev.x[0];
                last_y=tp_dev.y[0];
                
            }
        }

    /*Set the coordinates (if released use the last pressed coordinates)*/
    data->point.x = last_x;
    data->point.y = last_y;

    return false; /*Return `false` because we are not buffering and no more data to read*/
}
static void event_handler(lv_obj_t * obj, lv_event_t event)
{
    if(event == LV_EVENT_CLICKED) {
        printf("Clicked\n");
    }
    else if(event == LV_EVENT_VALUE_CHANGED) {
        printf("Toggled\n");
    }
}
/**
  * @brief GUI主函数
  * @param None
  * @retval None
  */
void lv_app_main(void)
{
    
    //建立一按钮
    lv_theme_t *th=lv_theme_night_init(20,NULL);
    lv_test_theme_1(th);
    
    
}
static void gui_thread_entry(void *parameter)
{

    lv_init();
    //lv_port_disp_init()中的代码如下:
    //显示缓冲区
    /*A static or global variable to store the buffers*/
    static lv_disp_buf_t disp_buf;

    /*Static or global buffer(s). The second buffer is optional*/
    static lv_color_t buf[LV_HOR_RES_MAX * 10];
    //static lv_color_t buf_2[MY_DISP_HOR_RES * 10];

    /*Initialize `disp_buf` with the buffer(s) */
    lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX*10);
    
    //像素打点,函数注册
    lv_disp_drv_t disp_drv;                 /*A variable to hold the drivers. Can be local variable*/
    lv_disp_drv_init(&disp_drv);            /*Basic initialization*/
    disp_drv.buffer = &disp_buf;            /*Set an initialized buffer*/
    disp_drv.flush_cb = my_disp_flush;        /*Set a flush callback to draw to the display*/
    lv_disp_t * disp;
    disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created display objects*/
    
    
    
    //lv_port_indev_init()的代码如下:
    //触摸输入注册
    lv_indev_drv_t indev_drv;                  /*Descriptor of a input device driver*/
    lv_indev_drv_init(&indev_drv);             /*Basic initialization*/
    indev_drv.type = LV_INDEV_TYPE_POINTER;    /*Touch pad is a pointer-like device*/
    indev_drv.read_cb = my_touchpad_read;      /*Set your driver function*/
    lv_indev_drv_register(&indev_drv);         /*Finally register the driver*/
    lv_app_main();

    while(1)
    {
        lv_task_handler();
        tp_dev.scan(0);
        rt_thread_delay(5);
    }
}

/**
  * @brief 用户空闲线程函数
  * @param parameter-线程入口参数
  * @retval None
  */
static void idle_thread_entry(void *parameter)
{
    while(1)
    {
        //iwdg_feed();
        rt_thread_delay(500);
    }
}
/**
  * @brief main主函数
  * @param None
  * @retval None
  */
void my_task_schedule(void)
{
    
    /* 数值越小优先级越高, 0代表最高优先级 */
        
    
    /* 创建GUI线程 */
    gui_thread = rt_thread_create("gui_thread",
                                    gui_thread_entry,
                                    RT_NULL,
                                    4096,
                                    2,
                                    20);
    rt_thread_startup(gui_thread);
    /* 创建用户空闲线程 */
    idle_thread = rt_thread_create("idle_thread",
                                    idle_thread_entry,
                                    RT_NULL,
                                    1024,
                                    RT_THREAD_PRIORITY_MAX-1,
                                    20);
    rt_thread_startup(idle_thread);
        
}
int main(void)
{
    
    
    //bsp_tim_init();          //BSP时基初始化
    delay_tim_init();        //延时函数定时器初始化
    uart_init(115200);              //初始化USART
    //LED_Init();                       //初始化LED    
    //KEY_Init();                       //初始化KEY
    LCD_Init();                     //初始化LCD
    tp_dev.init();                  //触摸屏初始化 
    
    
   /* 创建GUI线程 */
    my_task_schedule();
    
}


delay.c需要修改如下:

#include "delay.h"
#include "sys.h"


/* 由于时钟不固定,所以定时器的分频系数根据系统时钟定 */
extern uint32_t SystemCoreClock;

/* 函数定义 ---------------------------------------------------------*/


/**
  * @brief 初始化延时所使用定时器的参数,主要打开时钟
  * @param None
  * @retval None
  */
void delay_tim_init(void)
{
    /* RCC的APB1ENR寄存器的bit4置一,使能 TIM6 时钟 */
    RCC->APB1ENR |= (1<<4);
    
    /* HAL库方式使能定时器时钟 */
    //__HAL_RCC_TIM6_CLK_ENABLE();
}


/**
  * @brief us级延时函数
  * @param us-需要延时的us数
  * @retval None
  */
void delay_us(uint16_t us)
{
    /* 设置定时器预分频系数,TIM6时钟为90MHz,分频后时钟为1MHz即1us */
    /* 429的时钟频率不固定,为了获取稳定的定时,分频根据系统时钟确定 */
    TIM6->PSC = ((SystemCoreClock/2/1000000)-1);
    //TIM6->PSC = (90-1);
    /* 重新初始化定时器计数器并生成寄存器更新事件,确保预分频值被采用 */
    TIM6->EGR |= (1<<0);
    /* 清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零 */
    TIM6->SR = 0;
    /* 设置自动重装载值,定时器计数器的值自增到ARR时,会产生更新事件,ARR的值就是需要延时的时间 */
    TIM6->ARR = us;
    /* CR1的bit3(OPM)置一,计数器在发生下一更新事件时停止计数,单脉冲模式 */
    TIM6->CR1 |= (1<<3);
    /* CR1的bit0(CEN)置一,启动定时器开始计数 */
    TIM6->CR1 |= (1<<0);
    /* 等待更新事件到来,计数器的值自增到自动重装载寄存器的时候,会产生更新事件,此时延时时间已到 */
    while((TIM6->SR & 0x01)==0);
    /* 清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零 */
    TIM6->SR = 0;
}



/**
  * @brief ms级延时函数
  * @param ms-需要延时的ms数
  * @retval None
  * @note 最大延时时间 65535/2 = 32767.5ms
  */

void delay_ms(uint16_t ms)
{
#if 1
    /* 设置定时器预分频系数,TIM6时钟为90MHz,分频后时钟为2KHz即500us,由于PSC为16位寄存器,所以无法分频至1KHz */
    /* 429的时钟频率不固定,为了获取稳定的定时,分频根据系统时钟确定 */
    TIM6->PSC = ((SystemCoreClock/2/2000)-1);
    //TIM6->PSC = (45000-1);
    /* 重新初始化定时器计数器并生成寄存器更新事件,确保预分频值被采用 */
    TIM6->EGR |= (1<<0);
    /* 清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零 */
    TIM6->SR = 0;
    /* 设置自动重装载值,定时器计数器的值自增到ARR时,会产生更新事件,ARR的值就是需要延时的时间的一半 */
    TIM6->ARR = (ms*2);
    /* CR1的bit3(OPM)置一,计数器在发生下一更新事件时停止计数,单脉冲模式 */
    TIM6->CR1 |= (1<<3);
    /* CR1的bit0(CEN)置一,启动定时器开始计数 */
    TIM6->CR1 |= (1<<0);
    /* 等待更新事件到来,计数器的值自增到自动重装载寄存器的时候,会产生更新事件,此时延时时间已到 */
    while((TIM6->SR & 0x01)==0);
    /* 清除更新标志位,该位在发生更新事件时通过硬件置 1,但需要通过软件清零 */
    TIM6->SR = 0;
#endif
    
#if 0
    /* RTOS延时 */
#endif
    
#if 0
    //HAL库延时
    HAL_Delay(ms);
#endif
}

6.lv_init()函数和lv_task_handler()函数在main.c中的gui_thread_entry()函数中调用。
7.lv_tick_inc(1)函数在drv_common.c中的void SysTick_Handler(void)函数中调用。

8.
image.png

里面的三行代码很重要。实现触屏必须添加tp_dev.scan(0)这行代码。

通过上述步骤就移植完成了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353