原料:
1.树莓派3b+
2.微雪7寸电容触摸屏(1024*600)
3.usb转TTL模块
准备:
树莓派的中断资源没找到,用内核线程定时查询的方式来获取GT811的触摸数据。
下图是树莓派的GPIO引脚图:
下图是微雪7寸屏触摸i2c信号的引线方式(线色对应I2C的信号:橙->5V 蓝->GND 黄->SCL 绿->SDA):
将微雪自带的GD32F103芯片去掉(这个芯片通过i2c接口读取触摸屏数据,再转换成usb接口)。GD32F103的I2C信号会干扰我们的通信。
与树莓派的接线如下图,需要显示的话可以连接hdmi线,其他的就不用连了:
环境准备:
1.串口调试:
a. 修改/boot/config.txt,增加如下2行
dtoverlay=pi3-miniuart-bt
enable_uart=1
b. 修改/boot/cmdline
console=ttyAMA0,115200
2.微雪7寸hdmi触摸屏配置:
微雪7寸HDMI触摸屏工作与树莓派时,必须手动设置分辨率。
a.将系统镜像写到TF卡中。
b.编辑TF卡根目录下/boot/confi.txt文件。
在文件结尾添加:
max_usb_current=1
hdmi_group=2
hdmi_mode=1
hdmi_mode=87
hdmi_cvt 1024 600 60 0 0 0
c.插接hdmi线盒usb线连接到树莓派的usb口上,启动即可。
3.启动I2C平台
a.执行如下命令进行树莓派配置
sudo raspi-config
b.选择Advanced Options -> I2C ->yes
c.启动i2C内核驱动
4.测试连接
a.安装i2c-tool工具
sudo apt-get install i2c-tools
b.i2c-tool查询i2c设备
i2cdetect -y 1
-y 代表取消用户交互过程,直接执行指令;
1 代表I2C总线编号;
执行结果如下:
0x5d就是GT811的id了,如果显示的是UU表明该设备已经被用了。
驱动移植:
下表是gt811主要用读取触摸点的寄存器
0x0721地址是触控点和key的状态,gt811最多支持5点触控,这里只用一点,所以寄存器0x721的tp0位和0x723~0x727对我们有用。在驱动里定时读取,然后在上报input位置就行了。
开始正题贴代码。
gt811_ts.c
#include <linux/delay.h>#include <linux/property.h>#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/module.h>
#include <linux/gpio.h>
#include <linux/mutex.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/kthread.h>const static unsigned short normal_address[] = {0x5d, I2C_CLIENT_END}; static struct input_dev *ts_input; static struct i2c_client * this_client = NULL; MODULE_LICENSE("Dual BSD/GPL"); struct sensor_gt811 {struct mutex lock;struct i2c_client *client;struct completion wait;bool is_self_test;u16 x_axis;u16 y_axis;u16 z_axis;u8 sample;u8 out_rate;u8 mesura;u8 mode;u8 gain;struct task_struct * thread;
};
static struct sensor_gt811 *gt811;static int i2c_read_bytes(struct i2c_client *client, uint8_t *buf, int len)
{ struct i2c_msg msgs[2]; int ret=-1; msgs[0].flags=!I2C_M_RD; msgs[0].addr=client->addr; msgs[0].len=2; msgs[0].buf=&buf[0]; msgs[1].flags=I2C_M_RD; msgs[1].addr=client->addr; msgs[1].len=len-2; msgs[1].buf=&buf[2]; ret=i2c_transfer(client->adapter,msgs, 2); return ret;
} static int i2c_write_bytes(struct i2c_client *client,uint8_t *data,int len)
{ struct i2c_msg msg; int ret=-1; msg.flags=!I2C_M_RD; msg.addr=client->addr; msg.len=len; msg.buf=data; ret=i2c_transfer(client->adapter,&msg, 1); return ret;
} static int gt811_thread(void *arg)
{unsigned char point_data[25] = {0x07, 0x21, 0}; unsigned short input_x = 0; unsigned short input_y = 0; unsigned short input_p = 0; static unsigned int status = 0; int ret;while(!kthread_should_stop()){// 50ms查询一次msleep_interruptible(50);//读取gt811寄存器信息ret=i2c_read_bytes(this_client, point_data, sizeof(point_data)/sizeof(point_data[0])); if(ret <= 0){ printk("Failed\n"); return 0; } //point_data数组的前两位是要读取寄存器的起始地址,point_data[2]对应gt811芯片寄存器的0x0721寄存器内容if(point_data[2]&0x1f){ status=1; //标记有触点触发input_y = ((point_data[4]<<8)|point_data[5]); input_x = ((point_data[6]<<8)|point_data[7]); input_p = point_data[8]; } //如果没有触摸动作 并且上次有触摸 表明触摸松开 else if(status){ status = 0; //触摸点被释放input_mt_report_slot_state(ts_input, MT_TOOL_FINGER, false);input_mt_sync_frame(ts_input);input_sync(ts_input); } if(status){ if(input_x >=0 &&input_x<1024 &&input_y>=0&&input_y<600){//printk("x=%d,y=%d,w=%d\n",input_x,input_y,input_p); //触摸点被按下input_mt_report_slot_state(ts_input, MT_TOOL_FINGER, true);input_report_abs(ts_input, ABS_MT_POSITION_X, input_x);input_report_abs(ts_input, ABS_MT_POSITION_Y, 600-input_y); input_report_abs(ts_input, ABS_MT_TOUCH_MAJOR, input_p);input_mt_sync_frame(ts_input);input_sync(ts_input);}} } return 1;
}//初始化GT811
static int ts_init_panel(struct i2c_client *client){ short ret=-1; uint8_t config_info[] = { 0x06,0xA2, 0x12,0x10,0x0E,0x0C,0x0A,0x08,0x06,0x04,0x02,0x00,0x02,0x22,0x12,0x22,0x22,0x22,0x32,0x22,0x42,0x22,0x52,0x22,0x62,0x22,0x72,0x22,0x83,0x22,0x92,0x22,0xA2,0x22,0xB2,0x22,0xC2,0x22,0xD2,0x22,0xE2,0x22,0xF2,0x22,0x1B,0x03,0x28,0x28,0x28,0x20,0x20,0x20,0x0F,0x0F,0x0A,0x45,0x30,0x04,0x03,0x00,0x05,0xE0,0x01,0x20,0x03,0x00,0x00,0x38,0x33,0x35,0x30,0x00,0x00,0x26,0x14,0x02,0x0A,0x00,0x00,0x00,0x00,0x00,0x14,0x10,0x30,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 }; //配置gt811触摸点分辨率config_info[62] = 600 >> 8; config_info[61] = 600 & 0xff; config_info[64] = 1024 >> 8; config_info[63] = 1024 & 0xff; ret = i2c_write_bytes(client, config_info, sizeof(config_info)/sizeof(config_info[0])); if(ret < 0) { printk(KERN_ERR "GT811 Send config failed!\n"); return ret; }
printk(KERN_ALERT"config gt811\n"); return 0;
} static int gt811_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
int retry, ret; char test_data; //i2c_set_clientdata(client, gt811);printk("ts_probe\n"); test_data = 0; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_err(&client->dev, "Must have I2C_FUNC_I2C.\n"); return -ENODEV; } gt811 = devm_kzalloc(&client->dev, sizeof(struct sensor_gt811),GFP_KERNEL);if (!gt811)return -ENOMEM;i2c_set_clientdata(client, gt811);gt811->client = client;for(retry=0;retry < 5; retry++) { msleep(100); ret =i2c_write_bytes(client, &test_data, 1); if (ret > 0) break; dev_info(&client->dev, "GT811 I2C TEST FAILED!Please check the HARDWARE connect\n"); } if(ret <= 0) { dev_err(&client->dev, "Warnning: I2C communication might be ERROR!\n"); return -ENODEV; } //初始化gt811芯片for(retry = 0; retry != 5; ++ retry){ ret = ts_init_panel(client); if(ret != 0){ continue; } else{ break; } } if(ret != 0){ printk("GT811 Configue failed!\n"); return -ENODEV; } this_client = client; //申请input设备空间 ts_input = input_allocate_device(); if(IS_ERR(ts_input)){ printk("GT811 allocate ts input device failed!\n"); return -ENOMEM; } ts_input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) ; ts_input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); ts_input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); /*input_set_abs_params(ts_input, ABS_Y, 0, 1024, 0, 0); input_set_abs_params(ts_input, ABS_X, 0, 600, 0, 0); input_set_abs_params(ts_input, ABS_PRESSURE, 0, 255, 0, 0); */__set_bit(INPUT_PROP_DIRECT, ts_input->propbit);__set_bit(EV_ABS, ts_input->evbit);input_mt_init_slots(ts_input, 1,0);input_set_abs_params(ts_input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);input_set_abs_params(ts_input, ABS_MT_POSITION_X, 0, 1024, 0, 0);input_set_abs_params(ts_input, ABS_MT_POSITION_Y, 0, 600, 0, 0); ts_input->name = "gt811-ts"; ts_input->phys = "input/ts"; ts_input->id.bustype = BUS_I2C; ts_input->id.product = 0xBEEF; ts_input->id.vendor =0xDEAD; //注册输入设备 ret = input_register_device(ts_input); if(ret < 0){ printk("Unable register %s input device!\n", ts_input->name); input_free_device(ts_input); return -ENOMEM; }
//启动内核线程,主要的触摸轮寻工作就在这里做gt811->thread = kthread_run(gt811_thread, gt811, "gt811");if(gt811->thread == NULL){printk(KERN_ALERT"gt811_thread err\n"); } return 0;
}static int gt811_remove(struct i2c_client *client)
{kthread_stop(gt811->thread);i2c_set_clientdata(client, NULL);input_unregister_device(ts_input);//kfree(gt811);return 0;
}#ifdef CONFIG_OF
static const struct of_device_id gt811_of_match[] = {{ .compatible = "goodix,gt811", },{ }
};
MODULE_DEVICE_TABLE(of, gt811_of_match);
#endifstatic const struct i2c_device_id gt811_id[] = {{"gt811", 0},{ }
};
MODULE_DEVICE_TABLE(i2c, gt811_id);static struct i2c_driver gt811_driver = {.driver = {.name = "gt811",.of_match_table = of_match_ptr(gt811_of_match),},.probe = gt811_probe,.remove = gt811_remove,.id_table = gt811_id,
};module_i2c_driver(gt811_driver);
将gt811_ts.c文件拷贝到内核目录drivers/input/touchscreen/下,并修改drivers/input/touchscreen/目录下的Kconfig和Makefile.
Makefile添加
obj-$(CONFIG_TOUCHSCREEN_GT811) += gt811_ts.o
Kconfig添加
config TOUCHSCREEN_GT811tristate "GT811 I2C touchscreen"depends on I2ChelpSay Y here if you have the Goodix touchscreen (such as oneinstalled in Onda v975w tablets) connected to yoursystem. It also supports 5-finger chip models, which can befound on ARM tablets, like Wexler TAB7200 and MSI Primo73.If unsure, say N.To compile this driver as a module, choose M here: themodule will be called goodix.
添加设备树节点:
修改arch/arm/boot/dts/bcm2708_common.dtsi文件。找到i2c节点添加gt811设备。
nano arch/arm/boot/dts/bcm2708_common.dtsi
添加以下代码:
gt811: gt811@5d {compatible = "goodix,gt811";reg = <0x5d>;
};
内核编译:
重新编译内核
内核下载及编译参考http://blog.csdn.net/xdw1985829/article/details/39077611?_t_t_t=0.15414440189488232
执行指令
KERNEL=kernel7
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
make menuconfig
选择GT811 I2c touchscreen 为“M”,选择“*”也可以。不过lsmod命令就看不到模块了。保存.config
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 zImage modules dtbs
将sd卡插入ubuntu 挂载到 /media/XX/ 下,升级sd卡的驱动模块、内核、和设备树。
sudo make INSTALL_MOD_PATH=/media/XX/3598ef8e-09be-47ef-9d01-f24cf61dff1d/ modules_install
sudo mv /media/pmj/boot/KERNEL.img /media/XX/boot/KERNEL-backup.img
sudo scripts/mkknlimg arch/arm/boot/zImage /media/XX/boot/KERNEL.img
sudo cp arch/arm/boot/dts/*.dtb /media/pmj/boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /media/XX/boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /media/XX/boot/overlays/
然后启动sd卡。
结束:
可以看到树莓派打印log里包括了反白的内容,说明设备和驱动都挂载成功了!
启动后点击触摸屏就会有打印位置信息,这里就不贴图了,因为。。。触摸屏的排线被我玩坏了 T^T
当然,打印的信息只是方便调试,用的时候要屏蔽掉。
执行lsmd能看到gt811_ts模块
执行i2cdetect -y 1 能看到之前是0x5d现在已经被占用的UU。
至此已经完成移植。
我用qt做了个测试程序,可以下载:http://download.csdn.net/download/xiaopanpanpanpan/10118464
树莓派qt环境搭建可参考http://blog.csdn.net/shenhuan1104/article/details/53486619