rt-thread+littlevgl 移植

經(jīng)過一個(gè)多月的時(shí)間的學(xué)習(xí),rt-thread+littlevgl移植成功,現(xiàn)在記錄一下過程。

一、準(zhǔn)備過程

從rt-thread官方網(wǎng)站下載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
正點(diǎn)原子stm32f407的觸摸屏實(shí)驗(yàn)源碼。

進(jìn)入到 rt-thread\bsp\stm32\stm32f407-atk-explorer 文件夾中,雙擊 project.uvprojx 文件,打開 MDK5 工程。


image

1.把littlevgl的源碼解壓后,復(fù)制到rt-thread\bsp\stm32\stm32f407-atk-explorer 文件夾中。把HARDWARE和SYSTEM文件夾也復(fù)制到rt-thread\bsp\stm32\stm32f407-atk-explorer 文件夾中

2.下載的解壓后名稱為lvgl-master,重命名為lvgl(下面也用lvgl也是這個(gè)文件夾),把lvgl里的lv_core等文件夾添加到工程。

3.將lvgl文件夾中的lv_conf_template.h復(fù)制到與lvgl同級目錄,并重命名為lv_conf

4.打開MDK5工程文件,添加hardware文件,timer.c不用添加進(jìn)來。
image.png

5.在stm32_HAL庫中再添加兩個(gè)庫文件,并在stm32f4xx_hal_conf.h中使能這兩個(gè)文件:
image.png

.打開lv_conf,將#if 0改為 # if 1,設(shè)置你的命名的寬高像素,以及顏色模式,如下代碼所示

 * @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//如果是黑白屏就設(shè)置這個(gè)
 * - 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相關(guān)函數(shù)和變量 */
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線程函數(shù)
  * @param parameter-線程入口參數(shù)
  * @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);       //自己的打點(diǎn)函數(shù)
            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主函數(shù)
  * @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()中的代碼如下:
    //顯示緩沖區(qū)
    /*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);
    
    //像素打點(diǎn),函數(shù)注冊
    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 用戶空閑線程函數(shù)
  * @param parameter-線程入口參數(shù)
  * @retval None
  */
static void idle_thread_entry(void *parameter)
{
    while(1)
    {
        //iwdg_feed();
        rt_thread_delay(500);
    }
}
/**
  * @brief main主函數(shù)
  * @param None
  * @retval None
  */
void my_task_schedule(void)
{
    
    /* 數(shù)值越小優(yōu)先級越高, 0代表最高優(yōu)先級 */
        
    
    /* 創(chuàng)建GUI線程 */
    gui_thread = rt_thread_create("gui_thread",
                                    gui_thread_entry,
                                    RT_NULL,
                                    4096,
                                    2,
                                    20);
    rt_thread_startup(gui_thread);
    /* 創(chuàng)建用戶空閑線程 */
    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時(shí)基初始化
    delay_tim_init();        //延時(shí)函數(shù)定時(shí)器初始化
    uart_init(115200);              //初始化USART
    //LED_Init();                       //初始化LED    
    //KEY_Init();                       //初始化KEY
    LCD_Init();                     //初始化LCD
    tp_dev.init();                  //觸摸屏初始化 
    
    
   /* 創(chuàng)建GUI線程 */
    my_task_schedule();
    
}


delay.c需要修改如下:

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


/* 由于時(shí)鐘不固定,所以定時(shí)器的分頻系數(shù)根據(jù)系統(tǒng)時(shí)鐘定 */
extern uint32_t SystemCoreClock;

/* 函數(shù)定義 ---------------------------------------------------------*/


/**
  * @brief 初始化延時(shí)所使用定時(shí)器的參數(shù),主要打開時(shí)鐘
  * @param None
  * @retval None
  */
void delay_tim_init(void)
{
    /* RCC的APB1ENR寄存器的bit4置一,使能 TIM6 時(shí)鐘 */
    RCC->APB1ENR |= (1<<4);
    
    /* HAL庫方式使能定時(shí)器時(shí)鐘 */
    //__HAL_RCC_TIM6_CLK_ENABLE();
}


/**
  * @brief us級延時(shí)函數(shù)
  * @param us-需要延時(shí)的us數(shù)
  * @retval None
  */
void delay_us(uint16_t us)
{
    /* 設(shè)置定時(shí)器預(yù)分頻系數(shù),TIM6時(shí)鐘為90MHz,分頻后時(shí)鐘為1MHz即1us */
    /* 429的時(shí)鐘頻率不固定,為了獲取穩(wěn)定的定時(shí),分頻根據(jù)系統(tǒng)時(shí)鐘確定 */
    TIM6->PSC = ((SystemCoreClock/2/1000000)-1);
    //TIM6->PSC = (90-1);
    /* 重新初始化定時(shí)器計(jì)數(shù)器并生成寄存器更新事件,確保預(yù)分頻值被采用 */
    TIM6->EGR |= (1<<0);
    /* 清除更新標(biāo)志位,該位在發(fā)生更新事件時(shí)通過硬件置 1,但需要通過軟件清零 */
    TIM6->SR = 0;
    /* 設(shè)置自動重裝載值,定時(shí)器計(jì)數(shù)器的值自增到ARR時(shí),會產(chǎn)生更新事件,ARR的值就是需要延時(shí)的時(shí)間 */
    TIM6->ARR = us;
    /* CR1的bit3(OPM)置一,計(jì)數(shù)器在發(fā)生下一更新事件時(shí)停止計(jì)數(shù),單脈沖模式 */
    TIM6->CR1 |= (1<<3);
    /* CR1的bit0(CEN)置一,啟動定時(shí)器開始計(jì)數(shù) */
    TIM6->CR1 |= (1<<0);
    /* 等待更新事件到來,計(jì)數(shù)器的值自增到自動重裝載寄存器的時(shí)候,會產(chǎn)生更新事件,此時(shí)延時(shí)時(shí)間已到 */
    while((TIM6->SR & 0x01)==0);
    /* 清除更新標(biāo)志位,該位在發(fā)生更新事件時(shí)通過硬件置 1,但需要通過軟件清零 */
    TIM6->SR = 0;
}



/**
  * @brief ms級延時(shí)函數(shù)
  * @param ms-需要延時(shí)的ms數(shù)
  * @retval None
  * @note 最大延時(shí)時(shí)間 65535/2 = 32767.5ms
  */

void delay_ms(uint16_t ms)
{
#if 1
    /* 設(shè)置定時(shí)器預(yù)分頻系數(shù),TIM6時(shí)鐘為90MHz,分頻后時(shí)鐘為2KHz即500us,由于PSC為16位寄存器,所以無法分頻至1KHz */
    /* 429的時(shí)鐘頻率不固定,為了獲取穩(wěn)定的定時(shí),分頻根據(jù)系統(tǒng)時(shí)鐘確定 */
    TIM6->PSC = ((SystemCoreClock/2/2000)-1);
    //TIM6->PSC = (45000-1);
    /* 重新初始化定時(shí)器計(jì)數(shù)器并生成寄存器更新事件,確保預(yù)分頻值被采用 */
    TIM6->EGR |= (1<<0);
    /* 清除更新標(biāo)志位,該位在發(fā)生更新事件時(shí)通過硬件置 1,但需要通過軟件清零 */
    TIM6->SR = 0;
    /* 設(shè)置自動重裝載值,定時(shí)器計(jì)數(shù)器的值自增到ARR時(shí),會產(chǎn)生更新事件,ARR的值就是需要延時(shí)的時(shí)間的一半 */
    TIM6->ARR = (ms*2);
    /* CR1的bit3(OPM)置一,計(jì)數(shù)器在發(fā)生下一更新事件時(shí)停止計(jì)數(shù),單脈沖模式 */
    TIM6->CR1 |= (1<<3);
    /* CR1的bit0(CEN)置一,啟動定時(shí)器開始計(jì)數(shù) */
    TIM6->CR1 |= (1<<0);
    /* 等待更新事件到來,計(jì)數(shù)器的值自增到自動重裝載寄存器的時(shí)候,會產(chǎn)生更新事件,此時(shí)延時(shí)時(shí)間已到 */
    while((TIM6->SR & 0x01)==0);
    /* 清除更新標(biāo)志位,該位在發(fā)生更新事件時(shí)通過硬件置 1,但需要通過軟件清零 */
    TIM6->SR = 0;
#endif
    
#if 0
    /* RTOS延時(shí) */
#endif
    
#if 0
    //HAL庫延時(shí)
    HAL_Delay(ms);
#endif
}

6.lv_init()函數(shù)和lv_task_handler()函數(shù)在main.c中的gui_thread_entry()函數(shù)中調(diào)用。
7.lv_tick_inc(1)函數(shù)在drv_common.c中的void SysTick_Handler(void)函數(shù)中調(diào)用。

8.
image.png

里面的三行代碼很重要。實(shí)現(xiàn)觸屏必須添加tp_dev.scan(0)這行代碼。

通過上述步驟就移植完成了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容