前言
- 做个threadx+guix的简单记录
- guix使用的内存分配采用外部sram
代码
GUIX组件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-4-22 shchl first version
*/
#include "includes.h"
#if 1
#include "appgui_specifications.h"
/*
*******************************************************************************************************
* 外部引入变量
*******************************************************************************************************
*/
/*
*******************************************************************************************************
* 变量
*******************************************************************************************************
*/
/*
*********************************************************************************************************
* 静态全局变量
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 函数声明
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 外部函数
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 动态内存分配
*********************************************************************************************************
*/
GX_COLOR *canvas_mem;// 画布内存
/*
*********************************************************************************************************
* 变量
*********************************************************************************************************
*/
extern GX_STUDIO_DISPLAY_INFO appgui_display_table[1];
/*
*********************************************************************************************************
* 动态内存函数
*********************************************************************************************************
*/
VOID *memory_allocate(ULONG size) {
return app_sram_malloc(size);
}
void memory_free(VOID *mem) {
app_free(mem);
}
int gui_gx_application_define(void) {
/* 分配画布内存 */
canvas_mem = app_sram_malloc(320 * 480 * 2);
/* 初始化GUIX */
gx_system_initialize();
/* 注册动态内存申请和释放函数 */
gx_system_memory_allocator_set(memory_allocate, memory_free);
/* 自适应不同分辨率显示屏 */
appgui_display_table[0].x_resolution = 320;
appgui_display_table[0].y_resolution = 480;
appgui_display_table[0].canvas_memory = (GX_COLOR *) canvas_mem;
return FX_SUCCESS;
}
TX_APP_DEFINE_EXPORT(gui_gx_application_define); /*首先创建模块应用*/
/*
*********************************************************************************************************
* 内部函数
*********************************************************************************************************
*/
#endif
GUIX 任务线程 (主要是用于配置guix和监听触摸驱动)
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-4-20 shchl first version
*/
#include "includes.h"
#include "appgui_specifications.h"
#include "appgui_resources.h"
#include "gx_stm32_display_driver_16bpp.h"
#if 1
#include "bsp_touch.h"
#define APP_TASK_GUI_GX_PRIO 15
#define APP_TASK_GUI_GX_STK_SIZE 4096
/*
*******************************************************************************************************
* 外部引入变量
*******************************************************************************************************
*/
/*
*******************************************************************************************************
* 变量
*******************************************************************************************************
*/
GX_WINDOW *pScreen;
GX_WINDOW_ROOT *root;
TX_THREAD gui_gx_thread;
VOID *gui_thread_stack_area;
/*
*********************************************************************************************************
* 静态全局变量
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 函数声明
*********************************************************************************************************
*/
static VOID gui_gx_thread_entry(ULONG input);
static void gx_touch_event_call(uint8_t event_id, uint16_t x, uint16_t y);
/*
*********************************************************************************************************
* 外部函数
*********************************************************************************************************
*/
/**
* @brief cpu 状态任务
* @param first_thread 第一个启动的任务线程首地址
*/
int tx_task_gui_gx_create() {
UINT status;
gui_thread_stack_area = app_malloc(APP_TASK_GUI_GX_STK_SIZE);
if (!gui_thread_stack_area) {
tx_printf("app malloc error\r\n");
return -1;
}
status = tx_thread_create(&gui_gx_thread, /* 任务控制块地址 */
"gui gx thread", /* 任务名 */
gui_gx_thread_entry, /* 启动任务函数地址 */
0, /* 传递给任务的参数 */
gui_thread_stack_area, /* 堆栈基地址 */
APP_TASK_GUI_GX_STK_SIZE, /* 堆栈空间大小 */
APP_TASK_GUI_GX_PRIO, /* 任务优先级*/
APP_TASK_GUI_GX_PRIO, /* 任务抢占阀值 */
TX_NO_TIME_SLICE, /* 不开启时间片 */
TX_AUTO_START); /* 创建后立即启动 */
if (status) {
tx_printf("create error:%d\r\n", status);
}
/*配置触摸驱动,使用事件方式*/
g_lcd_touch.touch_send_event = gx_touch_event_call;
return TX_SUCCESS;
}
TX_THREAD_EXPORT(tx_task_gui_gx_create);
/*
*********************************************************************************************************
* 内部函数
*********************************************************************************************************
*/
static VOID gui_gx_thread_entry(ULONG input) {
/* 配置显示屏 */
gx_studio_display_configure(DISPLAY_1, stm32f4_graphics_driver_setup_565rgb,
LANGUAGE_CHINESE, DISPLAY_1_THEME_1, &root);
/* 创建窗口 */
gx_studio_named_widget_create("window", (GX_WIDGET *) root, (GX_WIDGET **) &pScreen);
/* 显示根窗口 */
gx_widget_show(root);
/* 启动GUIX */
gx_system_start();
while (1) {
lcd_touch_scan2();
tx_thread_sleep(5);
}
}
static void gx_touch_event_call(uint8_t event_id, uint16_t x, uint16_t y) {
GX_VALUE xx, yy;
GX_EVENT event;
/* 无需转换, 直接是坐标值 */
xx = (GX_VALUE)x;
yy = (GX_VALUE)y;
/* 横屏和竖屏方向识别 */
switch (event_id) {
case TOUCH_DOWN_EVENT_ID:
event.gx_event_type = GX_EVENT_PEN_DOWN;
event.gx_event_payload.gx_event_pointdata.gx_point_x = xx;
event.gx_event_payload.gx_event_pointdata.gx_point_y = yy;
event.gx_event_sender = 0;
event.gx_event_target = 0;
event.gx_event_display_handle = STM32_SCREEN_HANDLE; // 显示屏标识符,自定义的,保证在驱动中注册的一致
gx_system_event_send(&event);
break;
case TOUCH_MOVE_EVENT_ID:
event.gx_event_type = GX_EVENT_PEN_DRAG;
event.gx_event_payload.gx_event_pointdata.gx_point_x = xx;
event.gx_event_payload.gx_event_pointdata.gx_point_y = yy;
event.gx_event_sender = 0;
event.gx_event_target = 0;
event.gx_event_display_handle = STM32_SCREEN_HANDLE;
gx_system_event_fold(&event);
break;
case TOUCH_RELEASE_EVENT_ID:
event.gx_event_type = GX_EVENT_PEN_UP;
event.gx_event_payload.gx_event_pointdata.gx_point_x = xx;
event.gx_event_payload.gx_event_pointdata.gx_point_y = yy;
event.gx_event_sender = 0;
event.gx_event_target = 0;
event.gx_event_display_handle = STM32_SCREEN_HANDLE;
gx_system_event_send(&event);
break;
default:
break;
}
}
#endif
## GUIX显示屏驱动(弃用,最新见最后)
- guix和屏幕之间的驱动,这里提供了两个,一个是屏幕全部刷新,一个是脏数据区域刷新(也是局部刷新;这个在本地测试有问题,出现问题的直接原因就是在数据计算指针的问题。如果有知道是什么原因,麻烦告知一下)。
#include "includes.h"
#if 1
#include "gx_stm32_display_driver_16bpp.h"
#include "tx_api.h"
#include "gx_api.h"
#include "gx_display.h"
#include "gx_utility.h"
/*
*********************************************************************************************************
* 变量和函数
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 函 数 名: stm32f4_565rgb_buffer_toggle
* 功能说明: 更新canvas内容到LCD显存
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static INLINE_DECLARE void dbg_rectangle_inf(char *name, GX_RECTANGLE *rec) {
tx_printf("%s:(%d,%d)-----(%d,%d)\r\n", name,
rec->gx_rectangle_left, rec->gx_rectangle_top, /*左上顶点*/
rec->gx_rectangle_right, rec->gx_rectangle_bottom /*右下顶点*/
);
}
/**
* @brief lcd 局部刷新
* @param canvas
* @param dirty
*/
static void stm32f4_565rgb_buffer_toggle(GX_CANVAS *canvas, GX_RECTANGLE *dirty) {
GX_RECTANGLE Limit;
ULONG offset;
INT copy_width;
INT copy_height;
gx_utility_rectangle_define(&Limit, 0, 0,
(GX_VALUE) (canvas->gx_canvas_x_resolution - 1),
(GX_VALUE) (canvas->gx_canvas_y_resolution - 1));
if (gx_utility_rectangle_overlap_detect(&Limit, dirty, dirty)) {
dirty->gx_rectangle_left &= 0xfffe;
dirty->gx_rectangle_right |= 0x01;
/*重新刷新区域*/
// 设置绘画区域
copy_width = dirty->gx_rectangle_right - dirty->gx_rectangle_left;
copy_height = dirty->gx_rectangle_bottom - dirty->gx_rectangle_top;
// 计算偏移地址
dbg_rectangle_inf("dirty", dirty);
// 画布内存大小为(宽*高*像素大小(2字节))
uint16_t *color_ptr = (uint16_t *) canvas->gx_canvas_memory; /*颜色数据首地址:数组*/
offset = (dirty->gx_rectangle_top * canvas->gx_canvas_x_resolution); // 行偏移量
offset += dirty->gx_rectangle_left; // 列偏移量
color_ptr += offset; /*起始偏移位置*/
lcd_tft_setWin(dirty->gx_rectangle_left, dirty->gx_rectangle_top,
copy_width, copy_height);
lcd_tft_write_ram_prepare();
for (int i = dirty->gx_rectangle_top; i <= dirty->gx_rectangle_bottom; i++) { // 刷新行数
for (int j = 0; j <= copy_width; j++) { // 列数据
// 从左到右
lcd_tft_write_color(color_ptr[j]);
}
// 换行(在地址上进行地址显示屏宽度-->这里是宽度*2: uint16* 偏移跨度为2字节)
color_ptr += canvas->gx_canvas_x_resolution;
}
}
}
/**
* @brief 更新整个画布区域(直接,效率来说最低)
* @param canvas
* @param dirty
*/
static void stm32f4_565rgb_update_canvas_area(GX_CANVAS *canvas, GX_RECTANGLE *dirty) {
GX_RECTANGLE Limit;
/*获取限制区域(即当前画布的区域范围),值赋值给 Limit 结构体*/
gx_utility_rectangle_define(&Limit, 0, 0,
(GX_VALUE) (canvas->gx_canvas_x_resolution - 1),
(GX_VALUE) (canvas->gx_canvas_y_resolution - 1));
lcd_tft_draw_fast_rgb_color(Limit.gx_rectangle_left,
Limit.gx_rectangle_top,
Limit.gx_rectangle_right,
Limit.gx_rectangle_bottom,
(uint16_t *) canvas->gx_canvas_memory);
}
/*
*********************************************************************************************************
* 函 数 名: stm32f4_graphics_driver_setup_565rgb
* 功能说明: 驱动接口函数
* 形 参: ---
* 返 回 值: GX_SUCCESS
*********************************************************************************************************
*/
UINT stm32f4_graphics_driver_setup_565rgb(GX_DISPLAY *display) {
_gx_display_driver_565rgb_setup(display, (VOID *) STM32_SCREEN_HANDLE, stm32f4_565rgb_update_canvas_area);
return (GX_SUCCESS);
}
#endif
触摸驱动(原有基础上,增加回调,在外部进行扩展逻辑)
头文件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-4-7 shchl first version
*/
#ifndef APP_NO_OS_BSP_TOUCH_H
#define APP_NO_OS_BSP_TOUCH_H
#include "bsp.h"
#define TP_PRES_DOWN 0x80 //触屏被按下
#define TP_CATH_PRES 0x40 //有按键按下了
#define CT_MAX_TOUCH 5 //电容屏支持的点数,固定为5点
#define TOUCH_READ_TIMES 5
#define TOUCH_READ_DISCARD 1
#define ERR_RANGE 50 //误差范围
enum touch_event_id_enum {
TOUCH_DOWN_EVENT_ID, /*按下事件*/
TOUCH_MOVE_EVENT_ID, /*移动事件*/
TOUCH_RELEASE_EVENT_ID, /*释放事件*/
};
//触摸屏控制器
typedef struct lcd_touch_dev_struct {
uint16_t x[CT_MAX_TOUCH]; //当前坐标
uint16_t y[CT_MAX_TOUCH]; //电容屏有最多10组坐标,电阻屏则用x[0],y[0]代表:此次扫描时,触屏的坐标,用
//x[9],y[9]存储第一次按下时的坐标.
volatile uint16_t sta; //笔的状态
//b15:按下1/松开0;
//b14:0,没有按键按下;1,有按键按下.
//b13~b10:保留
//b9~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下)
/触摸屏校准参数(电容屏不需要校准)//
float xfac;
float yfac;
short xoff;
short yoff;
//新增的参数,当触摸屏的左右上下完全颠倒时需要用到.
//b0:0,竖屏(适合左右为X坐标,上下为Y坐标的TP)
// 1,横屏(适合左右为Y坐标,上下为X坐标的TP)
//b1~6:保留.
//b7:0,电阻屏
// 1,电容屏
uint8_t touchtype;
/**
* @brief 触摸发送事件
* @param event_id 事件id
* @param x x坐标
* @param y y坐标
*/
void (*touch_send_event)(uint8_t event_id, uint16_t x, uint16_t y);
struct {
uint8_t rd_x_cmd;
uint8_t rd_y_cmd;
} CNF;
} lcd_touch_dev;
#define PEN PBin(1) //T_PEN
#define DOUT PBin(2) //T_MISO
#define TDIN PFout(11) //T_MOSI
#define TCLK PBout(0) //T_SCK
#define TCS PCout(13) //T_CS
extern lcd_touch_dev g_lcd_touch; /*全局lcd触摸控制器设备*/
uint8_t bsp_InitLcdTouch(void);
uint8_t lcd_touch_scan(uint8_t tp);
// 事件驱动扫描
void lcd_touch_scan2(void);
void lcd_touch_adjust(void);
uint16_t lcd_touch_read_ad(uint8_t cmd);
#endif //APP_NO_OS_BSP_TOUCH_H
源文件
/*
* Copyright (c) 2024-2024,shchl
*
* SPDX-License-Identifier: Apache-2.0
*
* Change Logs:
* Date Author Notes
* 2024-4-7 shchl first version
*/
#include "bsp_touch.h"
#include <math.h>
#include <stdlib.h>
lcd_touch_dev g_lcd_touch = {
.CNF={0X90, 0XD0}
};
static void spi_us_delay(uint32_t us) { bsp_DelayDWTUS(us); }
//读取一个坐标值(x或者y)
//连续读取READ_TIMES次数据,对这些数据升序排列,
//然后去掉最低和最高LOST_VAL个数,取平均值
//xy:指令(CMD_RDX/CMD_RDY)
//返回值:读到的数据
uint16_t lcd_touch_read(uint8_t cmd) {
uint16_t i, j;
uint16_t buf[TOUCH_READ_TIMES];
uint32_t sum = 0;
uint16_t temp;
for (i = 0; i < TOUCH_READ_TIMES; i++) {
buf[i] = lcd_touch_read_ad(cmd);
}
for (i = 0; i < TOUCH_READ_TIMES - 1; i++)//排序
{
for (j = i + 1; j < TOUCH_READ_TIMES; j++) {
if (buf[i] > buf[j])//升序排列
{
temp = buf[i];
buf[i] = buf[j];
buf[j] = temp;
}
}
}
sum = 0;
for (i = TOUCH_READ_DISCARD; i < TOUCH_READ_TIMES - TOUCH_READ_DISCARD; i++)sum += buf[i];
temp = sum / (TOUCH_READ_TIMES - 2 * TOUCH_READ_DISCARD);
return temp;
}
//读取x,y坐标
//最小值不能少于100.
//x,y:读取到的坐标值
//返回值:0,失败;1,成功。
uint8_t lcd_touch_read_xy(uint16_t *x, uint16_t *y) {
uint16_t xtemp, ytemp;
xtemp = lcd_touch_read(g_lcd_touch.CNF.rd_x_cmd);
ytemp = lcd_touch_read(g_lcd_touch.CNF.rd_y_cmd);
if (xtemp < 100 || ytemp < 100)return 0;//读数失败
*x = xtemp;
*y = ytemp;
return 1;//读数成功
}
//连续2次读取触摸屏IC,且这两次的偏差不能超过
//ERR_RANGE,满足条件,则认为读数正确,否则读数错误.
//该函数能大大提高准确度
//x,y:读取到的坐标值
//返回值:0,失败;1,成功。
uint8_t lcd_touch_read_xy2(uint16_t *x, uint16_t *y) {
uint16_t x1, y1;
uint16_t x2, y2;
uint8_t flag;
flag = lcd_touch_read_xy(&x1, &y1);
if (flag == 0)return (0);
flag = lcd_touch_read_xy(&x2, &y2);
if (flag == 0)return (0);
if (((x2 <= x1 && x1 < x2 + ERR_RANGE) || (x1 <= x2 && x2 < x1 + ERR_RANGE))//前后两次采样在+-50内
&& ((y2 <= y1 && y1 < y2 + ERR_RANGE) || (y1 <= y2 && y2 < y1 + ERR_RANGE))) {
*x = (x1 + x2) / 2;
*y = (y1 + y2) / 2;
return 1;
} else return 0;
}
/**
* @brief lcd 触摸初始化
* @return
*/
uint8_t bsp_InitLcdTouch(void) {
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOF_CLK_ENABLE(); //开启GPIOF时钟
//GPIOB1,2初始化设置
GPIO_InitStructure.Pin=GPIO_PIN_1|GPIO_PIN_2; //PB1/PB2 设置为上拉输入
GPIO_InitStructure.Mode=GPIO_MODE_INPUT; //输入模式
GPIO_InitStructure.Pull=GPIO_PULLUP; //上拉
GPIO_InitStructure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化
//PB0
GPIO_InitStructure.Pin=GPIO_PIN_0; //PB0设置为推挽输出
GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
HAL_GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化
//PC13
GPIO_InitStructure.Pin=GPIO_PIN_13; //PC13设置为推挽输出
HAL_GPIO_Init(GPIOC,&GPIO_InitStructure); //初始化
//PF11
GPIO_InitStructure.Pin=GPIO_PIN_11; //PF11设置推挽输出
HAL_GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化
/*初始值*/
g_lcd_touch.xfac = -0.084720f;
g_lcd_touch.yfac = -0.12764f;
g_lcd_touch.xoff = 331;
g_lcd_touch.yoff = 496;
return lcd_touch_read_xy(&g_lcd_touch.x[0], &g_lcd_touch.y[0]) == 0 ? BSP_ERROR : BSP_EOK;
}
//触摸按键扫描
//tp:0,屏幕坐标;1,物理坐标(校准等特殊场合用)
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
uint8_t lcd_touch_scan(uint8_t tp){
if (PEN == 0)//有按键按下
{
if (tp)lcd_touch_read_xy2(&g_lcd_touch.x[0], &g_lcd_touch.y[0]);//读取物理坐标
else if (lcd_touch_read_xy2(&g_lcd_touch.x[0], &g_lcd_touch.y[0]))//读取屏幕坐标
{
g_lcd_touch.x[0] = g_lcd_touch.xfac * g_lcd_touch.x[0] + g_lcd_touch.xoff;//将结果转换为屏幕坐标
g_lcd_touch.y[0] = g_lcd_touch.yfac * g_lcd_touch.y[0] + g_lcd_touch.yoff;
}
if ((g_lcd_touch.sta & TP_PRES_DOWN) == 0)//之前没有被按下
{
g_lcd_touch.sta = TP_PRES_DOWN | TP_CATH_PRES;//按键按下
g_lcd_touch.x[4] = g_lcd_touch.x[0];//记录第一次按下时的坐标
g_lcd_touch.y[4] = g_lcd_touch.y[0];
}
} else {
if (g_lcd_touch.sta & TP_PRES_DOWN)//之前是被按下的
{
g_lcd_touch.sta &= ~(1 << 7);//标记按键松开
} else//之前就没有被按下
{
g_lcd_touch.x[4] = 0;
g_lcd_touch.y[4] = 0;
g_lcd_touch.x[0] = 0xffff;
g_lcd_touch.y[0] = 0xffff;
}
}
return g_lcd_touch.sta & TP_PRES_DOWN;//返回当前的触屏状态
}
//提示校准结果(各个参数)
void
TP_Adj_Info_Show(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t x3, uint16_t y3,
uint16_t fac) {
BSP_Printf("(%d,%d)----(%d,%d)\r\n(%d,%d)----(%d,%d)\r\n", x0, y0, x1, y1, x2, y2, x3, y3);
BSP_Printf("fac:%d\r\n", fac);
}
//与LCD部分有关的函数
//画一个触摸点
//用来校准用的
//x,y:坐标
//color:颜色
void TP_Drow_Touch_Point(uint16_t x, uint16_t y, uint16_t color) {
g_tft_lcd_data.front_color = color;
lcd_tft_draw_line(x - 12, y, x + 13, y);//横线
lcd_tft_draw_line(x, y - 12, x, y + 13);//竖线
lcd_tft_draw_point(x + 1, y + 1);
lcd_tft_draw_point(x - 1, y + 1);
lcd_tft_draw_point(x + 1, y - 1);
lcd_tft_draw_point(x - 1, y - 1);
lcd_tft_draw_circle(x, y, 6);//画中心圈
}
//触摸屏校准代码
//得到四个校准参数
void lcd_touch_adjust(void) {
uint16_t pos_temp[4][2];//坐标缓存值
uint8_t cnt = 0;
uint16_t d1, d2;
uint32_t tem1, tem2;
double fac;
uint16_t outtime = 0;
cnt = 0;
lcd_tft_fill_color(WHITE);//清屏
g_tft_lcd_data.front_color = BLACK;
TP_Drow_Touch_Point(20, 20, RED);//画点1
g_lcd_touch.sta = 0;//消除触发信号
g_lcd_touch.xfac = 0;//xfac用来标记是否校准过,所以校准之前必须清掉!以免错误
while (1)//如果连续10秒钟没有按下,则自动退出
{
lcd_touch_scan(1);//扫描物理坐标
if ((g_lcd_touch.sta & 0xc0) == TP_CATH_PRES)//按键按下了一次(此时按键松开了.)
{
outtime = 0;
g_lcd_touch.sta &= ~(1 << 6);//标记按键已经被处理过了.
pos_temp[cnt][0] = g_lcd_touch.x[0];
pos_temp[cnt][1] = g_lcd_touch.y[0];
cnt++;
switch (cnt) {
case 1:
TP_Drow_Touch_Point(20, 20, WHITE);//清除点1
TP_Drow_Touch_Point(g_tft_lcd_data.width - 20, 20, RED); //画点2
break;
case 2:
TP_Drow_Touch_Point(g_tft_lcd_data.width - 20, 20, WHITE); //清除点2
TP_Drow_Touch_Point(20, g_tft_lcd_data.height - 20, RED); //画点3
break;
case 3:
TP_Drow_Touch_Point(20, g_tft_lcd_data.height - 20, WHITE); //清除点3
TP_Drow_Touch_Point(g_tft_lcd_data.width - 20, g_tft_lcd_data.height - 20, RED); //画点4
break;
case 4: //全部四个点已经得到
//对边相等
tem1 = abs(pos_temp[0][0] - pos_temp[1][0]);//x1-x2
tem2 = abs(pos_temp[0][1] - pos_temp[1][1]);//y1-y2
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt(tem1 + tem2);//得到1,2的距离
tem1 = abs(pos_temp[2][0] - pos_temp[3][0]);//x3-x4
tem2 = abs(pos_temp[2][1] - pos_temp[3][1]);//y3-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt(tem1 + tem2);//得到3,4的距离
fac = (float) d1 / d2;
if (fac < 0.95 || fac > 1.05 || d1 == 0 || d2 == 0)//不合格
{
cnt = 0;
TP_Drow_Touch_Point(g_tft_lcd_data.width - 20, g_tft_lcd_data.height - 20, WHITE); //清除点4
TP_Drow_Touch_Point(20, 20, RED); //画点1
continue;
}
tem1 = abs(pos_temp[0][0] - pos_temp[2][0]);//x1-x3
tem2 = abs(pos_temp[0][1] - pos_temp[2][1]);//y1-y3
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt(tem1 + tem2);//得到1,3的距离
tem1 = abs(pos_temp[1][0] - pos_temp[3][0]);//x2-x4
tem2 = abs(pos_temp[1][1] - pos_temp[3][1]);//y2-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt(tem1 + tem2);//得到2,4的距离
fac = (float) d1 / d2;
if (fac < 0.95 || fac > 1.05)//不合格
{
BSP_Printf("need adjust again\r\n");
cnt = 0;
TP_Drow_Touch_Point(g_tft_lcd_data.width - 20, g_tft_lcd_data.height - 20, WHITE); //清除点4
TP_Drow_Touch_Point(20, 20, RED); //画点1
TP_Adj_Info_Show(pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0],
pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100);//显示数据
continue;
}//正确了
TP_Adj_Info_Show(pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0],
pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100);//显示数据
//对角线相等
tem1 = abs(pos_temp[1][0] - pos_temp[2][0]);//x1-x3
tem2 = abs(pos_temp[1][1] - pos_temp[2][1]);//y1-y3
tem1 *= tem1;
tem2 *= tem2;
d1 = sqrt(tem1 + tem2);//得到1,4的距离
tem1 = abs(pos_temp[0][0] - pos_temp[3][0]);//x2-x4
tem2 = abs(pos_temp[0][1] - pos_temp[3][1]);//y2-y4
tem1 *= tem1;
tem2 *= tem2;
d2 = sqrt(tem1 + tem2);//得到2,3的距离
fac = (float) d1 / d2;
if (fac < 0.95 || fac > 1.05)//不合格
{
cnt = 0;
TP_Drow_Touch_Point(g_tft_lcd_data.width - 20, g_tft_lcd_data.height - 20, WHITE); //清除点4
TP_Drow_Touch_Point(20, 20, RED); //画点1
TP_Adj_Info_Show(pos_temp[0][0], pos_temp[0][1], pos_temp[1][0], pos_temp[1][1], pos_temp[2][0],
pos_temp[2][1], pos_temp[3][0], pos_temp[3][1], fac * 100);//显示数据
continue;
}//正确了
//计算结果
g_lcd_touch.xfac = (float) (g_tft_lcd_data.width - 40) / (pos_temp[1][0] - pos_temp[0][0]);//得到xfac
g_lcd_touch.xoff = (g_tft_lcd_data.width - g_lcd_touch.xfac * (pos_temp[1][0] + pos_temp[0][0])) / 2;//得到xoff
g_lcd_touch.yfac = (float) (g_tft_lcd_data.height - 40) / (pos_temp[2][1] - pos_temp[0][1]);//得到yfac
g_lcd_touch.yoff = (g_tft_lcd_data.height - g_lcd_touch.yfac * (pos_temp[2][1] + pos_temp[0][1])) / 2;//得到yoff
if (abs(g_lcd_touch.xfac) > 2.0 || abs(g_lcd_touch.yfac) > 2.0)//触屏和预设的相反了.
{
cnt = 0;
TP_Drow_Touch_Point(g_tft_lcd_data.width - 20, g_tft_lcd_data.height - 20, WHITE); //清除点4
TP_Drow_Touch_Point(20, 20, RED); //画点1
BSP_Printf("TP Need readjust!TP Need readjust!\r\n");
g_lcd_touch.touchtype = !g_lcd_touch.touchtype;//修改触屏类型.
if (g_lcd_touch.touchtype)//X,Y方向与屏幕相反
{
g_lcd_touch.CNF.rd_x_cmd = 0X90;
g_lcd_touch.CNF.rd_y_cmd = 0XD0;
} else //X,Y方向与屏幕相同
{
g_lcd_touch.CNF.rd_x_cmd = 0XD0;
g_lcd_touch.CNF.rd_y_cmd = 0X90;
}
continue;
}
g_tft_lcd_data.front_color = BLUE;
lcd_tft_fill_color(WHITE);//清屏
HAL_Delay(1000);
return;//校正完成
}
}
HAL_Delay(10);
// outtime++;
// if (outtime > 1000) {
// break;
// }
}
}
/*--------------------------------------------------模拟通讯协议------------------------------------------*/
static inline void spi_write_byte(uint8_t dat);
//SPI读数据
//从触摸屏IC读取adc值
//cmd:指令
//返回值:读到的数据
uint16_t lcd_touch_read_ad(uint8_t cmd) {
uint8_t count=0;
uint16_t Num=0;
TCLK=0; //先拉低时钟
TDIN=0; //拉低数据线
TCS=0; //选中触摸屏IC
spi_write_byte(cmd);//发送命令字
bsp_DelayDWTUS(6);//ADS7846的转换时间最长为6us
TCLK=0;
bsp_DelayDWTUS(1);
TCLK=1; //给1个时钟,清除BUSY
bsp_DelayDWTUS(1);
TCLK=0;
for(count=0;count<16;count++)//读出16位数据,只有高12位有效
{
Num<<=1;
TCLK=0; //下降沿有效
bsp_DelayDWTUS(1);
TCLK=1;
if(DOUT)Num++;
}
Num>>=4; //只有高12位有效.
TCS=1; //释放片选
return(Num);
}
static inline void spi_write_byte(uint8_t dat) {
uint8_t count=0;
for(count=0;count<8;count++)
{
if(dat&0x80)TDIN=1;
else TDIN=0;
dat<<=1;
TCLK=0;
bsp_DelayDWTUS(1);
TCLK=1; //上升沿有效
}
}
/**
* @brief 触摸扫描,事件驱动
* @note 需要指定对应的函数回调 @see g_lcd_touch.touch_send_event
*/
void lcd_touch_scan2(void) {
if (PEN == 0)//有按键按下
{
if (lcd_touch_read_xy2(&g_lcd_touch.x[0], &g_lcd_touch.y[0]))//读取屏幕坐标
{
g_lcd_touch.x[0] = g_lcd_touch.xfac * g_lcd_touch.x[0] + g_lcd_touch.xoff;//将结果转换为屏幕坐标
g_lcd_touch.y[0] = g_lcd_touch.yfac * g_lcd_touch.y[0] + g_lcd_touch.yoff;
}
if ((g_lcd_touch.sta & TP_PRES_DOWN) == 0)//之前没有被按下--->按下事件
{
g_lcd_touch.sta = TP_PRES_DOWN | TP_CATH_PRES;//按键按下
g_lcd_touch.x[4] = g_lcd_touch.x[0];//记录第一次按下时的坐标
g_lcd_touch.y[4] = g_lcd_touch.y[0];
if (g_lcd_touch.touch_send_event) {
// BSP_Printf("TOUCH_DOWN_EVENT_ID:%d,%d\r\n", g_lcd_touch.x[0], g_lcd_touch.y[0]);
g_lcd_touch.touch_send_event(TOUCH_DOWN_EVENT_ID, g_lcd_touch.x[0], g_lcd_touch.y[0]);
}
} else { // 之前按下--> 移动事件
if (g_lcd_touch.touch_send_event) {
// BSP_Printf("TOUCH_MOVE_EVENT_ID:%d,%d\r\n", g_lcd_touch.x[0], g_lcd_touch.y[0]);
g_lcd_touch.touch_send_event(TOUCH_MOVE_EVENT_ID, g_lcd_touch.x[0], g_lcd_touch.y[0]);
}
}
} else {
if (g_lcd_touch.sta & TP_PRES_DOWN)//之前是被按下的--释放事件
{
g_lcd_touch.sta &= ~(1 << 7);//标记按键松开
if (g_lcd_touch.touch_send_event) {
// BSP_Printf("TOUCH_RELEASE_EVENT_ID:%d,%d\r\n", g_lcd_touch.x[0], g_lcd_touch.y[0]);
g_lcd_touch.touch_send_event(TOUCH_RELEASE_EVENT_ID, g_lcd_touch.x[0], g_lcd_touch.y[0]);
}
} else//之前就没有被按下
{
g_lcd_touch.x[4] = 0;
g_lcd_touch.y[4] = 0;
g_lcd_touch.x[0] = 0xffff;
g_lcd_touch.y[0] = 0xffff;
}
}
}
GUIX 图形化开发工具
测试
驱动中添加日志
结果
问题解决
导致原因(设置窗口区域函数参数)
- 原有的提供函数,参数注释后面两个参数代码宽和高,实际上确实代表坐标点
/**
* @brief 设置窗口
*
* (x1,y1)-------------
* --------------------
* --------------------
* --------------------
* --------------(x2,y2)
*
*
*
* @param sx 左上点(x轴坐标) x1
* @param sy 左上点(y轴坐标) y1
* @param ex 右下点(x轴坐标) x2
* @param ey 右下点(y轴坐标) y2
*/
void lcd_tft_setWin(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey) {
write_cmd(0x2a);
write_data(sx >> 8);
write_data(sx & 0XFF);
write_data(ex >> 8);
write_data(ex & 0XFF);
write_cmd(0x2b);
write_data(sy >> 8);
write_data(sy & 0XFF);
write_data(ey>> 8);
write_data(ey & 0XFF);
}
最新的gx显示屏驱动函数(测试已ok)
#include "includes.h"
#if 1
#include "gx_stm32_display_driver_16bpp.h"
#include "tx_api.h"
#include "gx_api.h"
#include "gx_display.h"
#include "gx_utility.h"
/*
*********************************************************************************************************
* 变量和函数
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* 函 数 名: stm32f4_565rgb_buffer_toggle
* 功能说明: 更新canvas内容到LCD显存
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
static inline void dbg_rectangle_inf(char *name, GX_RECTANGLE *rec) {
tx_printf("%s:(%d,%d)-----(%d,%d)\r\n", name,
rec->gx_rectangle_left, rec->gx_rectangle_top, /*左上顶点*/
rec->gx_rectangle_right, rec->gx_rectangle_bottom /*右下顶点*/
);
}
/**
* @brief 更新整个画布区域(直接,效率来说最低)
* @param canvas
* @param dirty
*/
static void stm32f4_565rgb_update_canvas_area(GX_CANVAS *canvas, GX_RECTANGLE *dirty) {
GX_RECTANGLE Limit;
/*获取限制区域(即当前画布的区域范围),值赋值给 Limit 结构体*/
gx_utility_rectangle_define(&Limit, 0, 0,
(GX_VALUE) (canvas->gx_canvas_x_resolution - 1),
(GX_VALUE) (canvas->gx_canvas_y_resolution - 1));
// 添加日志
dbg_rectangle_inf("dirty", dirty);
lcd_tft_draw_fast_rgb_color(Limit.gx_rectangle_left,
Limit.gx_rectangle_top,
Limit.gx_rectangle_right,
Limit.gx_rectangle_bottom,
(uint16_t *) canvas->gx_canvas_memory);
}
/**
* @brief lcd 局部刷新
* @param canvas
* @param dirty
*/
static void stm32f4_565rgb_buffer_toggle(GX_CANVAS *canvas, GX_RECTANGLE *dirty) {
GX_RECTANGLE Limit;
GX_RECTANGLE Copy;
INT copy_width;
INT copy_height;
gx_utility_rectangle_define(&Limit, 0, 0,
canvas->gx_canvas_x_resolution,
canvas->gx_canvas_y_resolution);
/*脏矩形与限制矩形做比较,得出需要修改的部分*/
if (gx_utility_rectangle_overlap_detect(dirty, &Limit, &Copy)) {
USHORT *memptr;
int xsrc = Copy.gx_rectangle_left;
int ysrc = Copy.gx_rectangle_top;
/*修正与显示屏的偏移量*/
gx_utility_rectangle_shift(&Copy, canvas->gx_canvas_display_offset_x, canvas->gx_canvas_display_offset_y);
/*限制矩形与修正后的矩形作比较,比较返回结果覆盖原有的矩形数据*/
if (gx_utility_rectangle_overlap_detect(&Limit, &Copy, &Copy)) {
/*获取 帆布的内存首地址(内部存储颜色数据)*/
memptr = (USHORT *) (canvas->gx_canvas_memory);
memptr += ysrc * canvas->gx_canvas_x_resolution; /*对首地址进行行偏移量递增(这里需要注意指针偏移的计算)*/
memptr += xsrc;/*进行列偏移量------->这里就得到脏数据颜色的首地址*/
// 计算 需要覆盖的 矩形的宽和高
copy_width = Copy.gx_rectangle_right - Copy.gx_rectangle_left + 1;
copy_height = Copy.gx_rectangle_bottom - Copy.gx_rectangle_top + 1;
// logInfo("buffer_toggle %p,(%d,%d)--width:%d,height:%d", memptr,
// Copy.gx_rectangle_left,Copy.gx_rectangle_top,
// copy_width,copy_height);
lcd_tft_setWin(Copy.gx_rectangle_left,
Copy.gx_rectangle_top,
Copy.gx_rectangle_right,
Copy.gx_rectangle_bottom);
lcd_tft_write_ram_prepare();
//gx采用 z字形存储方式
for (int y = 0; y < copy_height; y++) {
for (int i = 0; i < copy_width; ++i) {
lcd_tft_write_color(memptr[i]);
}
memptr += canvas->gx_canvas_x_resolution;
}
}
}
}
/*
*********************************************************************************************************
* 函 数 名: stm32f4_graphics_driver_setup_565rgb
* 功能说明: 驱动接口函数
* 形 参: ---
* 返 回 值: GX_SUCCESS
*********************************************************************************************************
*/
UINT stm32f4_graphics_driver_setup_565rgb(GX_DISPLAY *display) {
_gx_display_driver_565rgb_setup(display, (VOID *) STM32_SCREEN_HANDLE, stm32f4_565rgb_buffer_toggle);
return (GX_SUCCESS);
}
#endif
使用说明
总结
- 本次guix的显示驱动测试已经没有问题了,提供了两种方式,一种是全部刷新,一种是局部刷新