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)这行代码。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容