⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ads7846.c

📁 linux2.6.16版本
💻 C
📖 第 1 页 / 共 2 页
字号:
/* * ADS7846 based touchscreen and sensor driver * * Copyright (c) 2005 David Brownell * * Using code from: *  - corgi_ts.c *	Copyright (C) 2004-2005 Richard Purdie *  - omap_ts.[hc], ads7846.h, ts_osk.c *	Copyright (C) 2002 MontaVista Software *	Copyright (C) 2004 Texas Instruments *	Copyright (C) 2005 Dirk Behme * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 as *  published by the Free Software Foundation. */#include <linux/device.h>#include <linux/init.h>#include <linux/delay.h>#include <linux/input.h>#include <linux/interrupt.h>#include <linux/slab.h>#include <linux/spi/spi.h>#include <linux/spi/ads7846.h>#ifdef	CONFIG_ARM#include <asm/mach-types.h>#ifdef	CONFIG_ARCH_OMAP#include <asm/arch/gpio.h>#endif#endif/* * This code has been lightly tested on an ads7846. * Support for ads7843 and ads7845 has only been stubbed in. * * Not yet done:  investigate the values reported.  Are x/y/pressure * event values sane enough for X11?  How accurate are the temperature * and voltage readings?  (System-specific calibration should support * accuracy of 0.3 degrees C; otherwise it's 2.0 degrees.) * * app note sbaa036 talks in more detail about accurate sampling... * that ought to help in situations like LCDs inducing noise (which * can also be helped by using synch signals) and more generally. */#define	TS_POLL_PERIOD	msecs_to_jiffies(10)/* this driver doesn't aim at the peak continuous sample rate */#define	SAMPLE_BITS	(8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */)struct ts_event {	/* For portability, we can't read 12 bit values using SPI (which	 * would make the controller deliver them as native byteorder u16	 * with msbs zeroed).  Instead, we read them as two 8-bit values,	 * which need byteswapping then range adjustment.	 */	__be16 x;	__be16 y;	__be16 z1, z2;};struct ads7846 {	struct input_dev	*input;	char			phys[32];	struct spi_device	*spi;	u16			model;	u16			vref_delay_usecs;	u16			x_plate_ohms;	u8			read_x, read_y, read_z1, read_z2;	struct ts_event		tc;	struct spi_transfer	xfer[8];	struct spi_message	msg;	spinlock_t		lock;	struct timer_list	timer;		/* P: lock */	unsigned		pendown:1;	/* P: lock */	unsigned		pending:1;	/* P: lock */// FIXME remove "irq_disabled"	unsigned		irq_disabled:1;	/* P: lock */};/* leave chip selected when we're done, for quicker re-select? */#if	0#define	CS_CHANGE(xfer)	((xfer).cs_change = 1)#else#define	CS_CHANGE(xfer)	((xfer).cs_change = 0)#endif/*--------------------------------------------------------------------------*//* The ADS7846 has touchscreen and other sensors. * Earlier ads784x chips are somewhat compatible. */#define	ADS_START		(1 << 7)#define	ADS_A2A1A0_d_y		(1 << 4)	/* differential */#define	ADS_A2A1A0_d_z1		(3 << 4)	/* differential */#define	ADS_A2A1A0_d_z2		(4 << 4)	/* differential */#define	ADS_A2A1A0_d_x		(5 << 4)	/* differential */#define	ADS_A2A1A0_temp0	(0 << 4)	/* non-differential */#define	ADS_A2A1A0_vbatt	(2 << 4)	/* non-differential */#define	ADS_A2A1A0_vaux		(6 << 4)	/* non-differential */#define	ADS_A2A1A0_temp1	(7 << 4)	/* non-differential */#define	ADS_8_BIT		(1 << 3)#define	ADS_12_BIT		(0 << 3)#define	ADS_SER			(1 << 2)	/* non-differential */#define	ADS_DFR			(0 << 2)	/* differential */#define	ADS_PD10_PDOWN		(0 << 0)	/* lowpower mode + penirq */#define	ADS_PD10_ADC_ON		(1 << 0)	/* ADC on */#define	ADS_PD10_REF_ON		(2 << 0)	/* vREF on + penirq */#define	ADS_PD10_ALL_ON		(3 << 0)	/* ADC + vREF on */#define	MAX_12BIT	((1<<12)-1)/* leave ADC powered up (disables penirq) between differential samples */#define	READ_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \	| ADS_12_BIT | ADS_DFR)#define	READ_Y	(READ_12BIT_DFR(y)  | ADS_PD10_ADC_ON)#define	READ_Z1	(READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON)#define	READ_Z2	(READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON)#define	READ_X	(READ_12BIT_DFR(x)  | ADS_PD10_PDOWN)	/* LAST *//* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered */#define	READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \	| ADS_12_BIT | ADS_SER)#define	REF_ON	(READ_12BIT_DFR(x) | ADS_PD10_ALL_ON)#define	REF_OFF	(READ_12BIT_DFR(y) | ADS_PD10_PDOWN)/*--------------------------------------------------------------------------*//* * Non-touchscreen sensors only use single-ended conversions. */struct ser_req {	u8			ref_on;	u8			command;	u8			ref_off;	u16			scratch;	__be16			sample;	struct spi_message	msg;	struct spi_transfer	xfer[6];};static int ads7846_read12_ser(struct device *dev, unsigned command){	struct spi_device	*spi = to_spi_device(dev);	struct ads7846		*ts = dev_get_drvdata(dev);	struct ser_req		*req = kzalloc(sizeof *req, SLAB_KERNEL);	int			status;	int			sample;	int			i;	if (!req)		return -ENOMEM;	INIT_LIST_HEAD(&req->msg.transfers);	/* activate reference, so it has time to settle; */	req->ref_on = REF_ON;	req->xfer[0].tx_buf = &req->ref_on;	req->xfer[0].len = 1;	req->xfer[1].rx_buf = &req->scratch;	req->xfer[1].len = 2;	/*	 * for external VREF, 0 usec (and assume it's always on);	 * for 1uF, use 800 usec;	 * no cap, 100 usec.	 */	req->xfer[1].delay_usecs = ts->vref_delay_usecs;	/* take sample */	req->command = (u8) command;	req->xfer[2].tx_buf = &req->command;	req->xfer[2].len = 1;	req->xfer[3].rx_buf = &req->sample;	req->xfer[3].len = 2;	/* REVISIT:  take a few more samples, and compare ... */	/* turn off reference */	req->ref_off = REF_OFF;	req->xfer[4].tx_buf = &req->ref_off;	req->xfer[4].len = 1;	req->xfer[5].rx_buf = &req->scratch;	req->xfer[5].len = 2;	CS_CHANGE(req->xfer[5]);	/* group all the transfers together, so we can't interfere with	 * reading touchscreen state; disable penirq while sampling	 */	for (i = 0; i < 6; i++)		spi_message_add_tail(&req->xfer[i], &req->msg);	disable_irq(spi->irq);	status = spi_sync(spi, &req->msg);	enable_irq(spi->irq);	if (req->msg.status)		status = req->msg.status;	sample = be16_to_cpu(req->sample);	sample = sample >> 4;	kfree(req);	return status ? status : sample;}#define SHOW(name) static ssize_t \name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \{ \	ssize_t v = ads7846_read12_ser(dev, \			READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \	if (v < 0) \		return v; \	return sprintf(buf, "%u\n", (unsigned) v); \} \static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL);SHOW(temp0)SHOW(temp1)SHOW(vaux)SHOW(vbatt)/*--------------------------------------------------------------------------*//* * PENIRQ only kicks the timer.  The timer only reissues the SPI transfer, * to retrieve touchscreen status. * * The SPI transfer completion callback does the real work.  It reports * touchscreen events and reactivates the timer (or IRQ) as appropriate. */static void ads7846_rx(void *ads){	struct ads7846		*ts = ads;	struct input_dev	*input_dev = ts->input;	unsigned		Rt;	unsigned		sync = 0;	u16			x, y, z1, z2;	unsigned long		flags;	/* adjust:  12 bit samples (left aligned), built from	 * two 8 bit values writen msb-first.	 */	x = be16_to_cpu(ts->tc.x) >> 4;	y = be16_to_cpu(ts->tc.y) >> 4;	z1 = be16_to_cpu(ts->tc.z1) >> 4;	z2 = be16_to_cpu(ts->tc.z2) >> 4;	/* range filtering */	if (x == MAX_12BIT)		x = 0;	if (x && z1 && ts->spi->dev.power.power_state.event == PM_EVENT_ON) {		/* compute touch pressure resistance using equation #2 */		Rt = z2;		Rt -= z1;		Rt *= x;		Rt *= ts->x_plate_ohms;		Rt /= z1;		Rt = (Rt + 2047) >> 12;	} else		Rt = 0;	/* NOTE:  "pendown" is inferred from pressure; we don't rely on	 * being able to check nPENIRQ status, or "friendly" trigger modes	 * (both-edges is much better than just-falling or low-level).	 *	 * REVISIT:  some boards may require reading nPENIRQ; it's	 * needed on 7843.  and 7845 reads pressure differently...	 *	 * REVISIT:  the touchscreen might not be connected; this code	 * won't notice that, even if nPENIRQ never fires ...	 */	if (!ts->pendown && Rt != 0) {		input_report_key(input_dev, BTN_TOUCH, 1);		sync = 1;	} else if (ts->pendown && Rt == 0) {		input_report_key(input_dev, BTN_TOUCH, 0);		sync = 1;	}	if (Rt) {		input_report_abs(input_dev, ABS_X, x);		input_report_abs(input_dev, ABS_Y, y);		input_report_abs(input_dev, ABS_PRESSURE, Rt);		sync = 1;	}	if (sync)		input_sync(input_dev);#ifdef	VERBOSE	if (Rt || ts->pendown)		pr_debug("%s: %d/%d/%d%s\n", ts->spi->dev.bus_id,			x, y, Rt, Rt ? "" : " UP");#endif	/* don't retrigger while we're suspended */	spin_lock_irqsave(&ts->lock, flags);	ts->pendown = (Rt != 0);	ts->pending = 0;	if (ts->spi->dev.power.power_state.event == PM_EVENT_ON) {		if (ts->pendown)			mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD);		else if (ts->irq_disabled) {			ts->irq_disabled = 0;			enable_irq(ts->spi->irq);		}	}	spin_unlock_irqrestore(&ts->lock, flags);}static void ads7846_timer(unsigned long handle){

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -