TI文檔node
AM335x觸摸屏控制器支持以下四種工做模式:linux
• 8路通用的ADCgit
• 4路做爲4線觸摸屏鏈接,4路做爲通用ADC數組
• 5路做爲5線觸摸屏鏈接,3路做爲通用ADCide
• 8路做爲8線觸摸屏鏈接函數
ADC採用的是12位SAR ADC, 採樣速率爲每秒200k次. AD採樣模擬信號是從start of conversion爲高時開始並在降低沿後的1個時鐘週期內繼續採樣. 它在採樣週期結束時捕獲模擬信號並啓動轉換. 它在12個時鐘週期內將採樣數據數字化,當end of conversion信號被使能爲高時, 代表數據ADCOUT<11:0>已可讀. 當之前的數據被讀取後一個新的轉換週期就能夠開始了。 請注意,ADC輸出的是加權的二進制數據oop
4線觸摸屏的鏈接測試
5線觸摸屏鏈接ui
8線觸摸屏鏈接編碼
Device Drivers ---> [*] Staging drivers ---> [*] Industrial I/O support ---> [*] Enable buffer support within IIO <*> Industrial I/O lock free software ring < > Industrial I/O buffering based on kfifo -*- Enable triggered sampling support (2) Maximum number of consumers per trigger Analog to digital converters ---> <*> TI's ADC driver
這裏<*> TI's ADC driver 是直接編譯進內核, 若是隻是想編譯爲模塊則能夠點擊空格更改成<M>
在arch/arm/mach-omap2/board-am335xevm.c(個人板子是board-com335x.c)裏添加以下代碼:
#include <linux/platform_data/ti_adc.h> static struct adc_data am335x_adc_data = { .adc_channels = 4, }; static struct mfd_tscadc_board tscadc = { .tsc_init = &am335x_touchscreen_data, .adc_init = &am335x_adc_data, };
若是你要同時使用adc和觸摸屏的話照上邊這樣設置便可.
若是隻使用adc則要移除觸摸屏的平臺數據(platform data),以下所示:
static struct adc_data am335x_adc_data = { .adc_channels = 8, }; /* static struct tsc_data am335x_touchscreen_data = { .wires = 4, .x_plate_resistance = 200, .steps_to_configure = 5, }; */ static struct mfd_tscadc_board tscadc = { /* .tsc_init = &am335x_touchscreen_data, */ .adc_init = &am335x_adc_data, };
將直流電壓鏈接到AIN0到AIN7的引腳上(你使用了哪一個就接在哪一個引腳),千萬注意測試電壓只能在0~1.8v之間
3.1 查看IIO設備
root@arago-armv7:~# ls -al /sys/bus/iio/devices/iio\:device0/ drwxr-xr-x 5 root root 0 Jan 1 00:00 . drwxr-xr-x 4 root root 0 Jan 1 00:00 .. drwxr-xr-x 2 root root 0 Jan 1 00:00 buffer -r--r--r-- 1 root root 4096 Jan 1 00:00 dev -r--r--r-- 1 root root 4096 Jan 1 00:00 in_voltage0_raw -r--r--r-- 1 root root 4096 Jan 1 00:00 in_voltage1_raw -r--r--r-- 1 root root 4096 Jan 1 00:00 in_voltage2_raw -r--r--r-- 1 root root 4096 Jan 1 00:00 in_voltage3_raw -r--r--r-- 1 root root 4096 Jan 1 00:00 in_voltage4_raw -r--r--r-- 1 root root 4096 Jan 1 00:00 in_voltage5_raw -r--r--r-- 1 root root 4096 Jan 1 00:00 in_voltage6_raw -r--r--r-- 1 root root 4096 Jan 1 00:00 in_voltage7_raw -rw-r--r-- 1 root root 4096 Jan 1 00:00 mode -r--r--r-- 1 root root 4096 Jan 1 00:00 name drwxr-xr-x 2 root root 0 Jan 1 00:00 power drwxr-xr-x 2 root root 0 Jan 1 00:00 scan_elements lrwxrwxrwx 1 root root 0 Jan 1 00:00 subsystem -> ../../../../../../bus/iio -rw-r--r-- 1 root root 4096 Jan 1 00:00 uevent
3.2 單次模式
檢查ADC模式, 以下所示即爲單次模式
cat /sys/bus/iio/devices/iio\:device0/mode oneshot
若是不是單次模式可使用以下命令更改
Echo onesht > /sys/bus/iio/devices/iio\:device0/mode
從某一端口讀取ADC輸出數據:
in_voltageX_raw: raw value of the channel X of the ADC
從如下命令可知in_voltageX_raw裏保存的就是ADC的原始數據, 4095即滿量程數據
root@arago:~# cat /sys/bus/iio/devices/iio\:device0/in_voltage0_raw 4095
D = Vin * (2^n - 1) / Vref
好比讀取回來ADC的數據是2298那麼輸入電壓就是
Vin = D / ((2^n - 1) / Vref)
Vin = 2298 / ((2^12 – 1) / 1.8)
Vin = 1.01V
相關資料:
1 AM335x ADC Driver's Guide http://www.deyisupport.com/question_answer/w/faq/469.am335x-linux.aspx
2 AM335X 觸摸屏的硬件鏈接及Linux驅動
http://ti.eetop.cn/viewnews-4453
3 Using resistive touch screens for human/machine interface
http://www.ti.com/lit/an/slyt209a/slyt209a.pdf
由於觸摸屏使用的是input子系統因此要講解觸摸屏以前首先要講解一下input子系統, 它的核心文件是input.c
Input子系統結構與功能實現
1 Input子系統是分層結構的,總共分爲三層: 硬件驅動層,子系統核心層,事件處理層。
(1)其中硬件驅動層負責操做具體的硬件設備,這層的代碼是針對具體的驅動程序的,須要驅動程序的做者來編寫
(2)子系統核心層是連接其餘兩個層之間的紐帶與橋樑,向下提供驅動層的接口,向上提供事件處理層的接口
(3)事件處理層負責與用戶程序打交道,將硬件驅動層傳來的事件報告給用戶程序
2各層之間通訊的基本單位就是事件,任何一個輸入設備的動做均可以抽象成一種事件,如鍵盤的按下,觸摸屏的按下,鼠標的移動等。事件有三種屬性:類型(type),編碼(code),值(value),Input子系統支持的全部事件都定義在input.h中,包括全部支持的類型,所屬類型支持的編碼等。事件傳送的方向是 硬件驅動層-->子系統核心-->事件處理層-->用戶空間
3 以觸摸屏爲例說明輸入子系統的工做流程:
注:am335x sdk6的觸摸屏驅動所用驅動層對應的模塊文件爲:ti_tsc.c,事件處理層對應的模塊文件爲 evdev.c
(1)ti_tsc模塊初始化函數中將觸摸屏註冊到了輸入子系統中,於此同時,註冊函數在事件處理層鏈表中尋找事件處理器,這裏找到的是evdev,而且將驅動與事件處理器掛載。而且在/dev/input中生成設備文件event0,之後咱們訪問這個文件就會找的咱們的觸摸屏驅動程序。
(2)應用程序打開設備文件/dev/input/event0,讀取設備文件,調用evdev模塊中read,若是沒有事件,進程就會睡眠。
(3)當觸摸屏按下,驅動層經過input子系統核心層將事件(就是X,Y座標, 壓感, 按下/鬆開)傳給事件處理層也就是evdev,evdev喚醒睡眠的進程,將事件傳給進程處理。
下邊看下代碼流程:
input_init() class_register register_chrdev(INPUT_MAJOR, "input", &input_fops); 能夠看到input子系統幫咱們寫了以上兩個函數, 那麼這個input_fops是否是就是咱們要填充的file_operations呢? static const struct file_operations input_fops = { .owner = THIS_MODULE, .open = input_open_file, .llseek = noop_llseek, };
這個file_operations並無read/write等接口, 由此咱們可知該結構體只是一個鏈接上下層的做用, 這是一種分層的思想. 那麼他就是經過open成員來鏈接上下層, 咱們來看下open成員作了什麼事情:
input_open_file handler = input_table[iminor(inode) >> 5];//使用此設備號拿到handler結構體 new_fops = fops_get(handler->fops); //獲取新的file_operations file->f_op = new_fops; //使用handler的fops成員來做爲新的file_operations new_fops->open(inode, file); //handler->fops->open(indoe, file)
由此可知: 經過次設備號拿到數組中的handler成員, 就完成了上層的input_fops到底層的真實handler->fops的轉換,
底層的handler->fops->open(indoe, file) 就是真實的open, 對應的read/write也是
如今最關鍵的就是input_table[]數組是怎麼來的呢?
經過查找代碼發如今 input_register_handler裏使用了它, 那麼誰調用了這個函數呢? 在input子系統的下邊有不少驅動都用到了這個函數, 他們首先查找有沒有能匹配的硬件,若是有就調用這個函數將input_handler放入數組裏邊.
這些驅動在如下的軟件部分能夠看到:
軟件部分
Evdev.c Joydev.c Keyboard.c Mousedev.c等這些就是input子系統向上註冊input_handler
input_register_handler input_attach_handler 判斷硬件的id_table可否與本身的id_table匹配, 若是匹配這創建鏈接
硬件部分:
input_register_device input_attach_handler 判斷軟件的id_table可否與本身的id_table匹配, 若是匹配這創建鏈接
device和handler在註冊的時候都會查找對方是否有能夠與本身匹配的, 若是有就調用connect函數來鏈接彼此.
下邊以evdev.c爲例來講明軟硬件兩部分是如何創建鏈接的:
evdev_connect() evdev->handle.dev = input_get_device(dev); evdev->handle.handler = handler;
在evdev.c中又建立了一個handle結構體他分別指向device和handler, 這樣軟硬件兩邊經過這個結構體找到對方.
static struct tsc_data am335x_touchscreen_data = { .wires = 4, //4線觸摸屏 .x_plate_resistance = 200, //阻抗200歐姆 .steps_to_configure = 5, //在產生中斷以前在fifo中的樣本個數, 最大16 };
ti_tsc_init platform_driver_register (&ti_tsc_driver); tscadc_probe input_allocate_device //分配 request_irq input_dev->name = "ti-tsc"; //設置 input_dev->dev.parent = &pdev->dev; input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);//能夠產生按鍵類事件/絕對位移類事件 input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);//觸摸屏觸摸屏事件 input_register_device//註冊
中斷處理函數
tscadc_interrupt tscadc_readl 讀取中斷狀態寄存器, adc與tsc共用一箇中斷源 判斷中斷緣由而後上報事件 input_report_abs(input_dev, ABS_X, val_x);//x座標 input_report_abs(input_dev, ABS_Y, val_y);//y座標 input_report_abs(input_dev, ABS_PRESSURE, z);//壓感 input_report_key(input_dev, BTN_TOUCH, 1);//按下/鬆開 input_sync(input_dev); 檢查是不是擡起中斷 status = tscadc_readl(ts_dev, TSCADC_REG_RAWIRQSTATUS); if (status & TSCADC_IRQENB_PENUP) input_report_key(input_dev, BTN_TOUCH, 0); input_report_abs(input_dev, ABS_PRESSURE, 0); input_sync(input_dev);