当前位置:首页 > 单片机 > 单片机
[导读]触摸屏驱动在/kernel/drivers/char/s3c2410-ts.c 文件中。该驱动总要有以下重要数据结构:1.触摸屏的file_operationsstatic struct file_operations s3c2410_fops={owner: THIS_MODULE,open: s3c2410_ts_open,read:

触摸屏驱动在/kernel/drivers/char/s3c2410-ts.c 文件中。
该驱动总要有以下重要数据结构:
1.触摸屏的file_operations
static struct file_operations s3c2410_fops={
owner: THIS_MODULE,
open: s3c2410_ts_open,
read: s3c2410_ts_read,
release: s3c2410_ts_release,
#ifdef USE_ASYNC
fasync: s3c2410_ts_fasync,
#endif
poll: s3c2410_ts_poll,
};
2.全局变量TS_DEV结构体,用来保存触摸屏的相关参数、等待处理的消息队列、当前采样数据、上一次采样数据等信息
typedef struct {
unsigned int penStatus; /* PEN_UP, PEN_DOWN, PEN_SAMPLE */
TS_RET buf[MAX_TS_BUF]; /* protect against overrun(环形缓冲区) */
unsigned int head, tail;/* head and tail for queued events (环形缓冲区的头尾)*/
wait_queue_head_t wq; //* 等待队列数据结构
spinlock_t lock; //* 自旋锁
#ifdef USE_ASYNC
struct fasync_struct *aq;
#endif
#ifdef CONFIG_PM
struct pm_dev *pm_dev;
#endif
} TS_DEV;
其中TS_RET在/kernel/include/asm-arm/linuette_ioctl.h 文件中:
typedef struct {
unsigned short pressure; //* 压力,这里可定义为笔按下,笔抬起,笔拖曳
unsigned short x; //* 横坐标的采样值
unsigned short y; //* 纵坐标的采样值
unsigned short pad; //* 填充位
} TS_RET;
结构体wait_queue_head_t在/kernel/include/linux/wait.h 文件中:
struct __wait_queue_head {
wq_lock_t lock;
struct list_head task_list;
#if WAITQUEUE_DEBUG
long __magic;
long __creator;
#endif
};
typedef struct __wait_queue_head wait_queue_head_t;

TS_RET结构体中的信息就是驱动程序提供给上层应用程序使用的信息,用来存储触摸屏的返回值。上层应用程序通过读接口,从底层驱动中读取信息,并根据得到的值进行其他方面的操作。
TS_DEV结构用于记录触摸屏运行的各种状态,PenStatus包括PEN_UP、PEN_DOWN和PEN_FLEETING。buf[MAX_TS_BUF]是用来存放数据信息的事件队列,head、tail分别指向事件队列的头和尾。程序中的笔事件队列是一个环形结构,当有事件加入时,队列头加一,当有事件被取走时,队列尾加一,当头尾位置指针一致时读取笔事件的信息,进程会被安排进入睡眠。wq等待队列,包含一个锁变量和一个正在睡眠进程链表。当有好几个进程都在等待某件事时,Linux会把这些进程记录到这个等待队列。它的作用是当没有笔触事件发生时,阻塞上层的读操作,直到有笔触事件发生。lock使用自旋锁,自旋锁是基于共享变量来工作的,函数可以通过给某个变量设置一个特殊值来获得锁。而其他需要锁的函数则会循环查询锁是否可用。MAX_TS_BUF的值为16,即在没有被读取之前,系统缓冲区中最多可以存放16个笔触数据信息。

模块初始化函数是调用s3c2410_ts_init 函数来实现的,主要完成触摸屏设备的内核模块加载、初始化系统I/O、中断注册、设备注册,为设备文件系统创建入口等标准的字符设备初始化工作。
static int __init s3c2410_ts_init(void)
ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);
tsMajor = ret;
这里首先对字符设备进行注册,将触摸屏的file_operations 结构中的函数接口传入内核,注册成功后获得系统自动分配的主设备号。
set_gpio_ctrl(GPIO_YPON);
set_gpio_ctrl(GPIO_YMON);
set_gpio_ctrl(GPIO_XPON);
set_gpio_ctrl(GPIO_XMON);
在/kernel/include/asm-arm/arch-s3c2410/smdk.h 文件中:
#define GPIO_YPON (GPIO_MODE_nYPON | GPIO_PULLUP_DIS | GPIO_G15)
#define GPIO_YMON (GPIO_MODE_YMON | GPIO_PULLUP_EN | GPIO_G14)
#define GPIO_XPON (GPIO_MODE_nXPON | GPIO_PULLUP_DIS | GPIO_G13)
#define GPIO_XMON (GPIO_MODE_XMON | GPIO_PULLUP_EN | GPIO_G12)
GPIO 口的Port G 端口有4个管脚对应触摸屏的控制接口,分别是:
GPG15 --- nYPON Y+ 控制信号
GPG14 --- YMON Y- 控制信号
GPG13 --- nXPON X+ 控制信号
GPG12 --- XMON X- 控制信号
需要通过配置GPGCON 寄存器来设定该端口管脚的输出模式,对应位如下:
[31:30] [29:28] [27:26] [25:24]
GPG15 GPG14 GPG13 GPG12 ...
参考S3C2410 芯片datasheet 的I/O口章节,都要设为11(二进制)。
ADCDLY = 30000;
配置ADCDLY 寄存器,对自动转换模式来说是设定ADC 开始转换时的延时时间,以及对X轴和Y轴转换的时间间隔,对正常模式来说仅设定对X轴和Y轴转换的时间间隔。注意该值不能为0。
ret = request_irq(IRQ_ADC_DONE, s3c2410_isr_adc, SA_INTERRUPT,
DEVICE_NAME, s3c2410_isr_adc);
if (ret) goto adc_failed;
ret = request_irq(IRQ_TC, s3c2410_isr_tc, SA_INTERRUPT,
DEVICE_NAME, s3c2410_isr_tc);
if (ret) goto tc_failed;
/* Wait for touch screen interrupts */
wait_down_int();

函数request_irq 是Linux 系统中驱动程序注册中断的方法。irq 为所要申请的硬件中断号,handler 为系统所注册的中断处理子程序,irq_flags 为申请时的选项,devname 为指向设备名称的字符指针,dev_id 为申请时告诉系统的设备标识。若中断申请成功则返回0,失败则返回负值。
ret = request_irq(IRQ_ADC_DONE, s3c2410_isr_adc, SA_INTERRUPT,
DEVICE_NAME, s3c2410_isr_adc);
调用该函数来进行A/D转换的中断注册,所要申请的硬件中断号为IRQ_ADC_DONE(62),在arch/irq s.h中定义;系统所注册的中断处理子程序为s3c2410_isr_adc 函数;申请中断选项为SA_INTERRUPT,表示中断处理程序是快速处理程序,即快速处理程序运行时,所有中断都被屏蔽;设备名称定义为DEVICE_NAME,即"s3c2410-ts";而设备标识仍然用中断处理子程序代替。

ret = request_irq(IRQ_TC, s3c2410_isr_tc, SA_INTERRUPT,
DEVICE_NAME, s3c2410_isr_tc);
接着继续调用该函数来进行触摸屏触摸的中断注册,所要申请的硬件中断号为IRQ_TC(61);系统所注册的中断处理子程序为s3c2410_isr_tc 函数;申请中断选项为SA_INTERRUPT,表示中断处理程序是快速处理程序,即快速处理程序运行时,所有中断都被屏蔽;设备名称定义为DEVICE_NAME,即"s3c2410-ts";而设备标识仍然用中断处理子程序代替。
wait_down_int();
调用该宏函数来设置触摸屏为等待中断模式【笔按下产生中断】,具体定义如下:
#define wait_down_int() { ADCTSC = DOWN_INT | XP_PULL_UP_EN | /
XP_AIN | XM_HIZ | YP_AIN | YM_GND | /
XP_PST(WAIT_INT_MODE); }
用该宏函数来设置ADC 触摸屏控制寄存器,参考S3C2410 芯片datasheet 中关于触摸屏的章节,具体设置参数如下:
DOWN_INT = 1<<8 * 0 该位保留且应该设为0 【笔按下或笔抬起中断信号控制位,设为0 表示笔按下产生中断信号】
XP_PULL_UP_EN = 1<<3 * 0 上拉开关使能,设为0 表示XP 引脚上拉使能
XP_AIN = 1<<4 * 1 选择nXPON 引脚输出值,设为1 表示nXPON 引脚输出1,则XP 引脚连接AIN[7] 引脚
XM_HIZ = 1<<5 * 0 选择XMON 引脚输出值,设为0 表示XMON 引脚输出0,则XM 引脚为高阻态
YP_AIN = 1<<6 * 1 选择nYPON 引脚输出值,设为1 表示nYPON 引脚输出1,则YP 引脚连接AIN[5] 引脚
YM_GND = 1<<7 * 1 选择YMON 引脚输出值,设为1 表示YMON 引脚输出1,则YM 引脚为接地
XP_PST(WAIT_INT_MODE); = 3 X坐标Y坐标手动测量设置,设为3 表示等待中断模式

#ifdef CONFIG_DEVFS_FS
devfs_ts_dir = devfs_mk_dir(NULL, "touchscreen", NULL);
devfs_tsraw = devfs_register(devfs_ts_dir, "0raw", DEVFS_FL_DEFAULT,
tsMajor, TSRAW_MINOR, S_IFCHR | S_IRUSR | S_IWUSR,
&s3c2410_fops, NULL);
#endif
这里调用了devfs_mk_dir 函数,在设备文件系统中创建了一个名为touchscreen 的目录,并返回一个带有目录结构的数据结构变量devfs_ts_dir。将该变量作为下一步devfs_register 函数的参数,该参数在调用设备文件系统注册清除函数devfs_unregister 时也要作为参数传入。
调用devfs_register 函数后,会在刚才创建的touchscreen 目录下再创建一个名为0raw 的设备文件节点。该函数的参数中,DEVFS_FL_DEFAULT 为该函数的标志选项,tsMajor 为注册字符设备时系统自动分配的主设备号,TSRAW_MINOR(1)为次设备号,S_IFCHR | S_IRUSR | S_IWUSR 为默认的文件模式,&s3c2410_fops 为传入内核的触摸屏file_operations 结构中的函数接口,私有数据指针为空。返回一个devfs_handle_t 数据结构的变量devfs_tsraw,这会在调用设备文件系统注册清除函数devfs_unregister 时作为参数传入。

模块的退出函数为s3c2410_ts_exit,该函数的工作就是清除已注册的字符设备,中断以及设备文件系统。
static void __exit s3c2410_ts_exit(void)
{
#ifdef CONFIG_DEVFS_FS
devfs_unregister(devfs_tsraw);
devfs_unregister(devfs_ts_dir);
#endif
unregister_chrdev(tsMajor, DEVICE_NAME);
#ifdef CONFIG_PM
pm_unregister(tsdev.pm_dev);
#endif
free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
free_irq(IRQ_TC, s3c2410_isr_tc);
#ifdef CONFIG_DEVFS_FS
devfs_unregister(devfs_tsraw);
devfs_unregister(devfs_ts_dir);
#endif
这里首先清除原先后一步创建设备文件节点0raw 的结构变量devfs_tsraw,然后再清除创建touchscreen 目录的结构变量devfs_ts_dir。
unregister_chrdev(tsMajor, DEVICE_NAME);
接下来删除字符设备的注册信息。
void free_irq(unsigned int irq, void *dev_id)
函数free_irq 与函数request_irq 相对应,通常在模块被卸载时调用,负责注销一个已经申请的中断。

free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
free_irq(IRQ_TC, s3c2410_isr_tc);
最后依次注销A/D转换和定时器这两个已经申请的中断。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭