經(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 工程。

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)來。

.打開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)用。

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