一、 触摸屏驱动程序之概念介绍(第十六课/第一节)
内核会带有(s3c2410_ts.c)触摸屏的驱动程序(平台驱动),大概浏览,然后仿造
注册一个平台驱动
若内核里面有同名的平台设备驱动程序的时候,probe函数就会被调用
分配一个input_dev结构体
设置它
注册
当有事情发生时会上报事件
1th、现在开始写触摸屏驱动代码**
先从入口函数开始
第一步:分配一个input_dev结构体
第二步:设置
设置能产生哪类事件
设置能产生该类事件里哪些事件
第三步:注册
触摸屏原理:
触摸屏使用过程
二、 触摸屏驱动程序之编写驱动(第十六课/第二节)
查看硬件原理图:
先看看内核里的触摸屏驱动程序做了哪些事情
为了省电,内核在启动时,对那些不是必须的模块都会关掉。
ADC触摸屏有四种模式
1. 正常的转换模式:
2. 分离的X/Y坐标转换模式:
3. 自动(连续)X/Y坐标转换模式:
4. 等待中断模式:
2th、在第一个基础写第二个触摸屏驱动测试(可以检测按下或松开)
定义一个时钟,并使能
映射相关寄存器
注册中断
中断函数
测试2th:
第一步:make menuconfig去掉原来的驱动程序
第二步:编译没有TS的内核
第三步:使用新的没有TS的uImage启动
第四步:编译,挂接,加载驱动
3th、 按下触摸屏打印adc的值
第一步:注册添加ADC中断
第二步:按下触摸屏后启动测量
第三步:adc转换结束后会触发中断
测试3th:
编译3th驱动程序,加载驱动
BUG来了:电压变化挺大,需要继续改进
分析:1.触摸屏巧妙的应用了"欧姆定律",当按下时就立刻产生中断,但此时的电压可能并没有稳定下来,所以测量的电压可能会不太准确;2.ADC启动和转换需要一定的时间,不可能瞬间完成,若在启动过程中松开触摸屏,这个电压值也不稳定。
4th、 改进电压不稳定
改进方法1:使用(ADCDLY)寄存器增加延时,我们设置为最大
改进方法2:(判断寄存器ADCDAT0或ADCDAT1)若在启动过程中松开触摸屏,丢弃该值
测试4th:
编译4th驱动程序,加载驱动
5th、 多次测量优化
改进方法3:多次测量求平均值
测试5th:
编译5th驱动程序,加载驱动
6th、 软件过滤
改进方法4:软件过滤思路:一共采集4个数,把前两个数求平均值后与第三个数相减求差值,若差值大于某个设定值,则说明这四个值都不可靠;若可靠,把第二个数和第三个数求平均值后与第四个数相减求差值,若差值大于某个设定值,也说明这四个值都不可靠;若也可靠则返回过滤成功,然后打印。
测试6th:
编译6th驱动程序,加载驱动
7th、 处理触摸屏滑动
改进方法5:使用定时器处理长时间按下或滑动
定义并添加一个定时器到定时器列表
在数据完整可靠的情况下修改定时器的超时时间
测试7th:
编译7th驱动程序,加载驱动
8th、 完整的触摸屏驱动程序
操作方法:把里面的"printk()"改成"上报事件"即可。
先看看内核自带的触摸屏驱动怎么做的
在自己的触摸屏驱动程序里模仿
同样在松开的时候也应该上报事件
测试8th:
编译8th驱动程序,在加载驱动前,先看看有哪些event设备结点
加载驱动,并再次查看设备结点
(hexdump /dev/event0)读取event0设备
使用tslib测试:
根据(tslib编译使用方法.TXT)文件配置
拷贝tmp到nfs文件系统的first_fs目录下,并改名为ts_dir:
r:表示递归;f:表示强制;d:表示链接仍保持原本的链接
修改(/etc/ts.conf)文件
添加环境变量
加载触摸屏驱动,加载LCD驱动
校验测试:
校验成功后生成以下校验文件
还有很多别的测试程序:
最后的触摸屏驱动程序
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/plat-s3c24xx/ts.h>
#include <asm/arch/regs-adc.h>
#include <asm/arch/regs-gpio.h>
struct s3c_ts_regs{
unsigned int adccon;
unsigned int adctsc;
unsigned int adcdly;
unsigned int adcdat0;
unsigned int adcdat1;
unsigned int adcupdn;
};
static struct clk *s3c_adc_clock;
static struct input_dev *s3c_ts_dev;
static volatile struct s3c_ts_regs* s3c_ts_regs;
static struct timer_list timer_ts;
static void enter_wait_down_mode(void)
{
s3c_ts_regs->adctsc = 0xd3;
}
static void enter_wait_up_mode(void)
{
s3c_ts_regs->adctsc = 0x1d3;
}
static void enter_measure_mode(void)
{
s3c_ts_regs->adctsc = (1<<2)|(1<<3);
}
static void start_adc(void)
{
s3c_ts_regs->adccon |= (1<<0);
}
static int filter_adc(int *a, int *b)
{
#define ERR_LIMIT 10
int avr_x,avr_y;
int del_x, del_y;
avr_x = (a[0] + a[1])/2;
avr_y = (b[0] + b[1])/2;
del_x = (avr_x > a[2])? (avr_x - a[2]):(a[2] - avr_x);
del_y = (avr_y > b[2])? (avr_y - b[2]):(b[2] - avr_y);
if((del_x > ERR_LIMIT)||(del_y > ERR_LIMIT))
return 0;
avr_x = (a[1] + a[2])/2;
avr_y = (b[1] + b[2])/2;
del_x = (avr_x > a[3])? (avr_x - a[3]):(a[3] - avr_x);
del_y = (avr_y > b[3])? (avr_y - b[3]):(b[3] - avr_y);
if((del_x > ERR_LIMIT)||(del_y > ERR_LIMIT))
return 0;
return 1;
}
static void s3c_timer_irq_function(unsigned long data)
{
if(s3c_ts_regs->adcdat0 &(1<<15))
{
/* 松开 */
input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
input_sync(s3c_ts_dev);
enter_wait_down_mode();
}
else
{
enter_measure_mode();
start_adc();
}
}
static irqreturn_t s3c_adc_irq_function(int irq, void *dev_id)
{
/* 改进措施2:在adc还在转换时松开的值应该丢弃 */
static int cnt = 0;
static int adc_x[4], adc_y[4];
/* 松开 */
if(s3c_ts_regs->adcdat0 &(1<<15))
{
cnt = 0;
input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
input_sync(s3c_ts_dev);
enter_wait_down_mode();
}
else
{
/* 优化措施3:多次测量求平均值 */
adc_x[cnt] = s3c_ts_regs->adcdat0&0x3ff;
adc_y[cnt] = s3c_ts_regs->adcdat1&0x3ff;
cnt++;
if(4==cnt)
{
if(filter_adc(adc_x, adc_y))
{
// printk("cnt = %d, adc_x = %d, adc_y = %d
",cnt++, (adc_x[0]+adc_x[1]+adc_x[2]+adc_x[3])/4, (adc_y[0]+adc_y[1]+adc_y[2]+adc_y[3])/4);
input_report_abs(s3c_ts_dev, ABS_X, (adc_x[0]+adc_x[1]+adc_x[2]+adc_x[3])/4);
input_report_abs(s3c_ts_dev, ABS_Y, (adc_y[0]+adc_y[1]+adc_y[2]+adc_y[3])/4);
input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);
input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
input_sync(s3c_ts_dev);
}
cnt = 0;
enter_wait_up_mode();
/* 启动定时器 */
mod_timer(&timer_ts, jiffies + HZ/100); //10ms后启动定时器
}
else
{
enter_measure_mode();
start_adc();
}
}
return IRQ_HANDLED;
}
static irqreturn_t s3c_ts_irq_function(int irq, void *dev_id)
{
if(s3c_ts_regs->adcdat0 & (1<<15))
{
/* 松开 */
input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
input_sync(s3c_ts_dev);
enter_wait_down_mode();
}
else
{
/* 按下 */
enter_measure_mode();
start_adc();
}
return IRQ_HANDLED;
}
static int s3c_ts_init(void)
{
/* 1.分配一个input_dev结构体 */
s3c_ts_dev = input_allocate_device();
/* 2.设置 */
/* 2.1 能产生哪种类型事件 */
set_bit(EV_KEY, s3c_ts_dev->evbit);
set_bit(EV_ABS, s3c_ts_dev->evbit);
/* 2.2 能产生该种类型事件的哪些事件 */
set_bit(BTN_TOUCH, s3c_ts_dev->keybit); //能够产生按键类里的触摸屏事件
input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
/* 3.注册 */
input_register_device(s3c_ts_dev);
/* 4.硬件相关操作 */
/* 4.1使能时钟 bit[15] */
s3c_adc_clock = clk_get(NULL, "adc");
clk_enable(s3c_adc_clock);
/* 4.3 配置寄存器 */
s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));
/* ADCCON 寄存器
* [14] : 1-A/D converter prescaler enable
* [13:6] : A/D converter prescaler value
*/
s3c_ts_regs->adccon = (1<<14)|(49<<6);
/* 4.2 注册中断 */
request_irq(IRQ_TC, s3c_ts_irq_function, IRQF_SAMPLE_RANDOM,"s3c_ts", NULL);
request_irq(IRQ_ADC, s3c_adc_irq_function, IRQF_SAMPLE_RANDOM,"s3c_adc", NULL);
/* 优化措施1:设置 adcdly */
s3c_ts_regs->adcdly = 0xffff;
/* 优化措施5:使用定时器处理长按,滑动的情况 */
init_timer(&timer_ts);
timer_ts.function = s3c_timer_irq_function;
add_timer(&timer_ts);
enter_wait_down_mode();
printk("s3c_ts_init is ok
");
return 0;
}
static void s3c_ts_exit(void)
{
free_irq(IRQ_ADC,NULL);
free_irq(IRQ_TC, NULL);
iounmap(s3c_ts_regs);
input_unregister_device(s3c_ts_dev);
input_free_device(s3c_ts_dev);
del_timer(&timer_ts);
clk_disable(s3c_adc_clock);
clk_put(s3c_adc_clock);
s3c_adc_clock = NULL;
}
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);
MODULE_LICENSE("GPL");
<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">