1、前面的话
既上一篇【昉·星光 2 高性能RISC-V单板计算机体验】用自制的转接板学习lcd驱动点亮屏幕之后,其实在鼓捣两件事,加上图形界面和触摸驱动,实现一个小小的智能夹具中控系统,不过自从10月之后,忙的不行,虽然触摸的实验做出来了,但是感觉这玩意当不了一篇帖子,索性就想等把图形界面QT一起整出来再一起发出来,结果奇怪的是在VF2上交叉编译Qt一直跑不起来,而同样的Qt版本,在算能华山派上没有问题,所以很费解,确实也花了不少时间在上面,发邮件求救官方也没得到解决方案,
最后无奈换了LVGL,好在LVGL比较顺利,没有太多曲折,以下简单记录一下折腾的过程吧。
二、编译QT
1。下载Qt源码,网址:https://download.qt.io/,找到离线安装包或文件,之前下载的5.12.12版本,现在找不到了,用5.15代替:
2。下载之后拖到开发环境里,解压出来,在qtbase/mkspecs/文件夹创建linux-riscv64-starfive2-g++文件夹,从别处复制两个文件qmake.conf和qplatfirmdefs.h文件:
此处为定义交叉编译链,
3.然后在根目录创建compile.sh文件,填入内容:
#!/bin/sh
./configure
-prefix $PWD/qtbase
-confirm-license
-opensource
-release
-make libs
-xplatform linux-riscv64-starfive2-g++
-pch
-widgets
-dbus-runtime
--pcre=qt
--zlib=qt
--freetype=qt
--harfbuzz=qt
-linuxfb
-qt-libjpeg
-qt-libpng
-qt-zlib
-no-sse2
-no-openssl
-no-opengl
-no-cups
-no-glib
-no-dbus
-no-xcb
-no-separate-debug-info
-c++std c++11
-L/usr/riscv64-linux-gnu/lib/
添加执行权限chmod a+x ./compile.sh
然后可以执行./compile.sh
启动配置,出现下图所示:
然后可以执行gmake -j 12
,-j后面表示使用多少线程参与编译,越大越快.
编译过程举例:
在编译的过程中可能会出现一些问题:
1)没有atomic库,如下:
参考解决方案:
2)limits错误:
参考解决方案如下:
其他的问题可能没碰到也无无从知晓了
三.编译完成
编译完成之后如图:
会在qtbase/lib出现库:
把这些库拷贝到开发板上的/home/user文件夹内,我用的scp方式:
然后是plugins的动态库:
然后是声明一些与qt相关的变量,可以直接写在~/.bashrc里:
export LD_LIBRARY_PATH=/usr/local/lib
export GST_PLUGIN_PATH=/usr/local/lib/gstreamer-1.0
export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0:size=320x480:mmSize=320x480:offset=0x0:tty=/dev/ttyS0
export QT_QPA_FONTDIR=/home/user/lib/fonts
export QT_QPA_FB_FORCE_FULLSCREEN=1
export QT_ONSCREEN_PAINT=1
接着编译一个案例,比如calculate:
直接make
:
然后同样使用scp传输到开发板:
然后在开发板上运行就会出问题:
就是这个问题 抓破脑袋也想不出来原因,最后无奈放弃了.
四.改用lvgl实现图形界面
到底qt放弃了,了解到就图形界面来说LVGL也可以到达效果,所以最后采用LVGL实现了,整体参考的韦老师的帖子,很顺利,而且这次是在板子上编译的,因为使用libinput的话会需要几个依赖库,拷起来太费劲了,需要在板子上安装libinput-dev:apt-get install libinput-dev
然后大体步骤就和韦老师一致了,帖子地址:https://mp.weixin.qq.com/s?__biz=MzAxNTAyOTczMw==&mid=2649344034&idx=1&sn=6782bcb407e8dad790b1931176c9f4b6&chksm=83972ca8b4e0a5bef0ba61cd4370a2471b4b3d794ef2dd144b3692d32c9f78d7a6b7c5c24c5b&scene=27,大家直接参考就行,此处不就赘述了,改了几个地方而已,把libevdev改成了libinput,然后源码里面也有小小改动:
然后编译运行就可以显示界面了:
五.添加触摸驱动
到上步为止图形界面是搞定了,但是缺少触摸驱动,我用的芯片的ft6236,需要在设备树添加对应描述:
使用了一个中断引脚和reset引脚,同时x,y轴需要反转一下,
然后就是修改驱动文件,这里比较友好的是已经有现成的驱动文件了
但是由于linux内核版本升级,原先的驱动并不适用了,典型的就是中断的写法变了,主要体现在这里,因为不清楚内核的机制,这里卡住了2天左右吧,难受:
static int edt_ft5x06_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct edt_i2c_chip_data *chip_data;
struct edt_ft5x06_ts_data *tsdata;
u8 buf[2] = { 0xfc, 0x00 };
struct input_dev *input;
unsigned long irq_flags;
int error;
char fw_version[EDT_NAME_LEN];
dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
if (!tsdata) {
dev_err(&client->dev, "failed to allocate driver data.\n");
return -ENOMEM;
}
chip_data = device_get_match_data(&client->dev);
if (!chip_data)
chip_data = (const struct edt_i2c_chip_data *)id->driver_data;
if (!chip_data || !chip_data->max_support_points) {
dev_err(&client->dev, "invalid or missing chip data\n");
return -EINVAL;
}
tsdata->max_support_points = chip_data->max_support_points;
tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
if (IS_ERR(tsdata->vcc)) {
error = PTR_ERR(tsdata->vcc);
if (error != -EPROBE_DEFER)
dev_err(&client->dev,
"failed to request regulator: %d\n", error);
return error;
}
tsdata->iovcc = devm_regulator_get(&client->dev, "iovcc");
if (IS_ERR(tsdata->iovcc)) {
error = PTR_ERR(tsdata->iovcc);
if (error != -EPROBE_DEFER)
dev_err(&client->dev,
"failed to request iovcc regulator: %d\n", error);
return error;
}
error = regulator_enable(tsdata->iovcc);
if (error < 0) {
dev_err(&client->dev, "failed to enable iovcc: %d\n", error);
return error;
}
/* Delay enabling VCC for > 10us (T_ivd) after IOVCC */
usleep_range(10, 100);
error = regulator_enable(tsdata->vcc);
if (error < 0) {
dev_err(&client->dev, "failed to enable vcc: %d\n", error);
regulator_disable(tsdata->iovcc);
return error;
}
error = devm_add_action_or_reset(&client->dev,
edt_ft5x06_disable_regulators,
tsdata);
printk(">>%s:%d _ %s()\n",__FILE__,__LINE__, __func__);
if (error)
return error;
error = request_one_gpio(&client->dev, "reset-gpios", 0, &tsdata->reset_gpio);
// tsdata->reset_gpio = of_get_named_gpio_flags(&client->dev.of_node,"reset-gpios", 0);
if (IS_ERR(tsdata->reset_gpio)) {
error = PTR_ERR(tsdata->reset_gpio);
dev_err(&client->dev,
"Failed to request GPIO reset pin, error %d\n", error);
return error;
}
error = request_one_gpio(&client->dev, "wakeup-gpios", 0, &tsdata->wake_gpio);
// tsdata->wake_gpio = of_get_named_gpio_flags(&client->dev.of_node,"wakeup-gpios", 0);
if (IS_ERR(tsdata->wake_gpio)) {
error = PTR_ERR(tsdata->wake_gpio);
dev_err(&client->dev,
"Failed to request GPIO wake pin, error %d\n", error);
// return error;
}
error = request_one_gpio(&client->dev, "interrupt-gpios", 0, &tsdata->irq_pin);
// tsdata->irq_pin = of_get_named_gpio_flags(&client->dev.of_node,"interrupt-gpios", 0);
if (IS_ERR(tsdata->irq_pin)) {
error = PTR_ERR(tsdata->irq_pin);
dev_err(&client->dev,
"Failed to request GPIO irq pin, error %d\n", error);
return error;
}
gpiod_direction_input(tsdata->irq_pin);
/*
* Check which sleep modes we can support. Power-off requieres the
* reset-pin to ensure correct power-down/power-up behaviour. Start with
* the EDT_PMODE_POWEROFF test since this is the deepest possible sleep
* mode.
*/
if (tsdata->reset_gpio)
tsdata->suspend_mode = EDT_PMODE_POWEROFF;
else if (tsdata->wake_gpio)
tsdata->suspend_mode = EDT_PMODE_HIBERNATE;
else
tsdata->suspend_mode = EDT_PMODE_NOT_SUPPORTED;
if (tsdata->wake_gpio) {
usleep_range(5000, 6000);
gpiod_set_value_cansleep(tsdata->wake_gpio, 1);
}
printk(">>%s:%d _ %s() %p %d \n",__FILE__,__LINE__, __func__,tsdata->reset_gpio ,tsdata->suspend_mode );
if (tsdata->reset_gpio) {
usleep_range(5000, 6000);
printk(">>%s:%d _ %s() reset gpio set sleep\n",__FILE__,__LINE__, __func__);
gpiod_set_value_cansleep(tsdata->reset_gpio, 1);
msleep(300);
}
input = devm_input_allocate_device(&client->dev);
if (!input) {
dev_err(&client->dev, "failed to allocate input device.\n");
return -ENOMEM;
}
mutex_init(&tsdata->mutex);
tsdata->client = client;
tsdata->input = input;
tsdata->factory_mode = false;
error = edt_ft5x06_ts_identify(client, tsdata, fw_version);
printk(">>%s:%d _ %s() %d\n",__FILE__,__LINE__, __func__,error);
if (error) {
dev_err(&client->dev, "touchscreen probe failed\n");
return error;
}
/*
* Dummy read access. EP0700MLP1 returns bogus data on the first
* register read access and ignores writes.
*/
edt_ft5x06_ts_readwrite(tsdata->client, 2, buf, 2, buf);
edt_ft5x06_ts_set_regs(tsdata);
edt_ft5x06_ts_get_defaults(&client->dev, tsdata);
edt_ft5x06_ts_get_parameters(tsdata);
printk(">>%s:%d _ %s() x:%d y:%d\n",__FILE__,__LINE__, __func__,tsdata->num_x, tsdata->num_y);
dev_dbg(&client->dev,
"Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
tsdata->name, fw_version, tsdata->num_x, tsdata->num_y);
input->name = tsdata->name;
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
__set_bit(EV_ABS,input->evbit);
__set_bit(BTN_TOUCH,input->keybit);
input_set_abs_params(input, ABS_X,
0, 320, 0, 0);
input_set_abs_params(input, ABS_Y,
0, 480, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_X,
0, 320, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, 480, 0, 0);
edt_ft5x06_ts_get_parameters(tsdata);
printk(">>2%s:%d _ %s() x:%d y:%d\n",__FILE__,__LINE__, __func__,tsdata->num_x, tsdata->num_y);
touchscreen_parse_properties(input, true, &tsdata->prop);
printk(">>%s:%d _ %s() ypoints:%d\n",__FILE__,__LINE__,
__func__,tsdata->max_support_points );
error = input_mt_init_slots(input, tsdata->max_support_points,
INPUT_MT_DIRECT);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
return error;
}
input_set_drvdata(input , tsdata);
i2c_set_clientdata(client, tsdata);
error = devm_request_threaded_irq(&client->dev,gpiod_to_irq(tsdata->irq_pin),// client->irq,
NULL, edt_ft5x06_ts_isr, IRQF_TRIGGER_FALLING |IRQF_ONESHOT,
client->name, tsdata);
if (error) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return error;
}
error = devm_device_add_group(&client->dev, &edt_ft5x06_attr_group);
if (error)
return error;
error = input_register_device(input);
if (error)
return error;
edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev));
printk(">>%s:%d _ %s() %d\n",__FILE__,__LINE__, __func__,error);
device_init_wakeup(&client->dev , 1);
dev_dbg(&client->dev,
"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d. IRQ_Pin %d\n",
client->irq,
tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1,
tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1,
tsdata->irq_pin ? desc_to_gpio(tsdata->irq_pin) : -1);
// printk(">>%s:%d _ %s() %d %d %d %d\n",__FILE__,__LINE__, __func__,
// client->irq,
// desc_to_gpio(tsdata->wake_gpio),
// desc_to_gpio(tsdata->reset_gpio),
// desc_to_gpio(tsdata->irq_pin));
error = edt_ft5x06_register_write(tsdata, FT_DEVIDE_MODE, 0x00);
if (error) {
dev_dbg(&client->dev,
"failed to write FT_DEVIDE_MODE register, error %d\n", error);
return error;
}
error = edt_ft5x06_register_write(tsdata, FT_ID_G_THGROUP,12);
if (error) {
dev_dbg(&client->dev,
"failed to write FT_ID_G_THGROUP register, error %d\n", error);
return error;
}
error = edt_ft5x06_register_write(tsdata, FT_ID_G_PERIODACTIVE, 12);
if (error) {
dev_dbg(&client->dev,
"failed to write FT_ID_G_PERIODACTIVE register, error %d\n", error);
return error;
}
return 0;
}
主要为以下两个大坑:
解决完之后,更新设备树可以看到出现/dev/input/event0
设备:
使用cat /dev/input/event0
指令然后可以读到有输入
在网上找一个测试event0的代码也能测试:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h> // 输入子系统头文件
int ts_fd; // 触摸屏文件描述符全局变量
// 获取当前点击坐标
void get_xy(int *x, int *y);
int main()
{
// 1.打开触摸屏文件
ts_fd = open("/dev/input/event0", O_RDONLY);
if(ts_fd == -1)
{
perror("open ts failed");
return -1;
}
// 2.读取坐标(产生阻塞,等待用户点击)
int pos_x, pos_y;
while(1)
{
// 黑色底板屏幕,触摸屏坐标范围是(0~1024, 0~600),可通过计算进行缩小
get_xy(&pos_x, &pos_y);
printf("(%d, %d)\n", pos_x, pos_y);
}
// 3.关闭触摸屏
close(ts_fd);
return 0;
}
// 获取当前点击坐标
void get_xy(int *x, int *y)
{
int x_ready=0, y_ready=0;
struct input_event ts_buf;
while(1)
{
read(ts_fd, &ts_buf, sizeof(ts_buf));
// printf("type:0x%x code:0x%x value:%d\n", ts_buf.type, ts_buf.code, ts_buf.value);
// if(ts_buf.type==0x3 && ts_buf.code==0x0)
if(ts_buf.type==EV_ABS && ts_buf.code==ABS_X)
{
*x = ts_buf.value;
x_ready = 1;
y_ready = 0; // 确保x坐标获取在前
}
else if(ts_buf.type==EV_ABS && ts_buf.code==ABS_Y)
{
*y = ts_buf.value;
y_ready = 1;
}
if(x_ready==1 && y_ready==1)
break;
}
}
,现象如下:
然后就可以使用lvgl的demo实现触摸的读取并播放歌曲了,可以看到歌曲进度条动了:
六.总结
虽然不甘心qt不能用只能用lvgl,但是好歹图形界面加触摸是出来了,这其中也学到了不少的东西,还是比较感谢这次经历的,下一篇收官篇,就用图形界面搞点事情吧,敬请期待.
更多回帖