经过一个多月的时间的学习,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 工程。
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不用添加进来。.打开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)函数中调用。
里面的三行代码很重要。实现触屏必须添加tp_dev.scan(0)这行代码。