一、系統(tǒng)簡介 | ||||
本文介紹友善之臂一線觸摸屏接到GSC3280開發(fā)板的驅(qū)動。本文中,首先講述GSC3280內(nèi)部定時器驅(qū)動,該驅(qū)動作為一線觸摸屏數(shù)據(jù)傳送時的采樣定時器。然后講述一線觸摸屏協(xié)議驅(qū)動。最后講述通過一線觸摸屏的協(xié)議,控制觸摸屏的背光,并且通過滑動變阻器來線性控制觸摸屏的背光,提供了底層和應(yīng)用層程序。 | ||||
二、GSC3280內(nèi)部定時器驅(qū)動 | ||||
GSC3280芯片包含一個TIMER定時器模塊,TIMER定時器模塊包含TIMER0~TIMER3共4個相對獨立的32bit 定時器,每一個定時器都有自己獨立的時鐘源,均支持循環(huán)定時模式和單次定時模式兩種工作模式,每個定時器都有各自的中斷,TIMER定時器模塊將4個中斷合并為一個中斷輸出給中斷控制器,并額外提供一個int_for_adc中斷。 | ||||
首先看下定時器模塊初始化函數(shù)gsc3280_timer_init(): 1. struct gsc3280_timer { 2. bool in_use; 3. void __iomem *base; 4. unsigned long rate; 5. struct gsc3280_hard_timer *h_timer; 6. char *name; 7. }; 8. static struct gsc3280_timer gsc3280_timer_priv[GSC3280_H_TIMER_NR] = { 9. {false, (__iomem void *)GSC3280_TIMER_BASEADDR + 0x00, 0, NULL, "Timer0"}, 10. {false, (__iomem void *)GSC3280_TIMER_BASEADDR + 0x14, 0, NULL, "Timer1"}, 11. {false, (__iomem void *)GSC3280_TIMER_BASEADDR + 0x28, 0, NULL, "Timer2"}, 12. {false, (__iomem void *)GSC3280_TIMER_BASEADDR + 0x3C, 0, NULL, "Timer3"} 13.}; 14.static int __init gsc3280_timer_init(void) 15.{ 16. int i = 0; 17. struct clk *clk = NULL; 18. timer_tcr_u tcr = {.w32 = 0}; 19. 20. DBG("############"); 21. DBG("gsc3280 timer init start\n"); 22. //enable timer modules 23. sysctl_mod_enable(SYSCTL_MOD_TIMER0); 24. sysctl_mod_enable(SYSCTL_MOD_TIMER1); 25. sysctl_mod_enable(SYSCTL_MOD_TIMER2); 26. sysctl_mod_enable(SYSCTL_MOD_TIMER3); 27. //get 4 timer rate, use this cal LC 28. clk = clk_get(NULL, "timer0"); 29. gsc3280_timer_priv[TIMER0].rate = clk_get_rate(clk); 30. clk = clk_get(NULL, "timer1"); 31. gsc3280_timer_priv[TIMER1].rate = clk_get_rate(clk); 32. clk = clk_get(NULL, "timer2"); 33. gsc3280_timer_priv[TIMER2].rate = clk_get_rate(clk); 34. clk = clk_get(NULL, "timer3"); 35. gsc3280_timer_priv[TIMER3].rate = clk_get_rate(clk); 36. for (i = 0; i < GSC3280_H_TIMER_NR; i++) { 37. if (gsc3280_timer_priv[i].rate == 0) { 38. DBG("GSC3280 TIMER%d Get Rate Failed!", i); 39. return -EAGAIN; 40. } 41. DBG("timer[%d] = %ld\n", i, gsc3280_timer_priv[i].rate); 42. tcr.w32 = __raw_readl(gsc3280_timer_priv[i].base + TIMER_CR); 43. tcr.b.enable = 0; //disable timer 44. tcr.b.irq_mask = 1; //mask timer irq 45. __raw_writel(tcr.w32, gsc3280_timer_priv[i].base + TIMER_CR); 46. } 47. printk(KERN_INFO "GSC3280 TIMER0~3 Enable Success!"); 48. DBG("############"); 49. return 0; 50.} 51.core_initcall(gsc3280_timer_init); 說明: 1) 首先在系統(tǒng)控制模塊中使能四個定時器模塊。 2) 分別獲取定時器的時鐘頻率。 3) 最后,分別初始化定時器的控制寄存器。 4) 注意此處初始化使用的是core_initcall()宏,具有很高的執(zhí)行優(yōu)先級。 然后看下申請定時器函數(shù),該函數(shù)即為一線觸摸屏驅(qū)動中使用的申請定時器函數(shù)。 1. int gsc3280_request_hard_timer(struct gsc3280_hard_timer *h_timer) 2. { 3. int i = 0, ret = 0; 4. 5. for (i = 0; i < GSC3280_H_TIMER_NR; i++) { 6. if (gsc3280_timer_priv[i].h_timer == NULL) { 7. gsc3280_timer_priv[i].h_timer = h_timer; 8. ret = request_irq(EXT_GSC3280_TIMER_IRQ, &gsc3280_timer_interrupt, 9. IRQF_SHARED, gsc3280_timer_priv[i].name, 10. &gsc3280_timer_priv[i]); 11. if (ret) { 12. printk(KERN_ERR "Start hard Timer request irq failed!\n"); 13. return ret; 14. } 15. DBG("GSC3280 Request Timer Success."); 16. return 0; 17. } 18. } 19. DBG("GSC3280 Request Timer error!"); 20. return -EBUSY; 21.} 22.EXPORT_SYMBOL(gsc3280_request_hard_timer); 說明: 1) 首先通過for循環(huán)找到第一個沒有被使用的硬件定時器。 2) 找到后,申請中斷。由前面介紹可知,四個定時器共享中斷。該中斷函數(shù)接下里會講述。 3) 如果申請中斷失敗,直接退出。 gsc3280_timer_interrupt()函數(shù)如下所示: 1. static irqreturn_t gsc3280_timer_interrupt(int irq, void *_ptr) 2. { 3. irqreturn_t ret = IRQ_NONE; 4. struct gsc3280_timer *timer = (struct gsc3280_timer *)_ptr; 5. 6. if (__raw_readl(timer->base + TIMER_IS) & 0x01) { 7. //is real interrupt 8. timer->h_timer->function(timer->h_timer->data); 9. ret = IRQ_HANDLED; 10. } 11. //clr interrupt bit 12. __raw_readl(timer->base + TIMER_EOI); 13. return ret; 14.}
1) 進(jìn)入中斷后,首先讀取中斷狀態(tài)寄存器查看是否真的有中斷,不是誤觸發(fā)。 2) 如果是真的中斷,執(zhí)行中斷回調(diào)函數(shù)。 3) 通過讀取方式清除中斷標(biāo)志,以便下次能夠再次進(jìn)入中斷。 釋放硬件定時器函數(shù)如下所示: 1. void gsc3280_free_hard_timer(struct gsc3280_hard_timer *h_timer) 2. { 3. int i = 0; 4. 5. for (i = 0; i < GSC3280_H_TIMER_NR; i++) { 6. if (gsc3280_timer_priv[i].h_timer == h_timer) { 7. if (gsc3280_timer_priv[i].in_use) 8. gsc3280_timer_stop(h_timer); 9. free_irq(EXT_GSC3280_TIMER_IRQ, &gsc3280_timer_priv[i]); 10. gsc3280_timer_priv[i].h_timer = NULL; 11. } 12. } 13.} 14.EXPORT_SYMBOL(gsc3280_free_hard_timer); 說明: 1) 通過for循環(huán)和傳入的形參找到釋放的定時器 2) 判斷定時器是否在使用,如果使用,停止定時器。 3) 釋放定時器中斷。 4) 定時器變量清除。 接下來講述比較重要的函數(shù),開啟定時器gsc3280_timer_start(): 2. { 3. unsigned long tlc = 0; 4. timer_tcr_u tcr = {.w32 = 0}; 5. 6. if (timer->h_timer->value_type == 0) 7. tlc = (timer->rate * timer->h_timer->expires) /1000; 8. else 9. tlc = timer->rate / timer->h_timer->bps - 1; 10. tcr.w32 = __raw_readl(timer->base + TIMER_CR); 11. if (timer->h_timer->type == LOOP) 12. tcr.b.mode = 1; 13. else 14. tcr.b.mode = 0; 15. __raw_writel(tcr.w32, timer->base + TIMER_CR); 16. __raw_writel(tlc, timer->base + TIMER_LC); 17. //Enable Timer Interupt 18. tcr.w32 = __raw_readl(timer->base + TIMER_CR); 19. tcr.b.irq_mask = 0; //not mask timer irq 20. tcr.b.enable = 1; //enable timer 21. __raw_writel(tcr.w32, timer->base + TIMER_CR); 22. __raw_readl(timer->base + TIMER_EOI); 23. timer->in_use = true; 24.} 25.int gsc3280_timer_start(struct gsc3280_hard_timer *h_timer) 26.{ 27. int i =0; 28. 29. if ((h_timer->data == 0) || (h_timer->function == NULL)) { 30. printk(KERN_ERR "GSC3280 Request Timer, But The Param Wrong!"); 31. return -EINVAL; 32. } 33. for (i = 0; i < GSC3280_H_TIMER_NR; i++) { 34. if (gsc3280_timer_priv[i].h_timer == h_timer) { 35. if (!gsc3280_timer_priv[i].in_use) 36. start_hard_timer(&gsc3280_timer_priv[i]); 37. return 0; 38. } 39. } 40. DBG("gsc3280_timer_start error!!!!\n"); 41. return -EBUSY; 42.} 43.EXPORT_SYMBOL(gsc3280_timer_start); 說明: 1) 首先檢查形參變量,如果變量錯誤,直接返回。 2) 根據(jù)形參變量尋找相應(yīng)的定時器,調(diào)用start_hard_timer()函數(shù)開啟定時器。 3) start_hard_timer()函數(shù)首先根據(jù)需要定時的類型,計算定時器的重載值。對于本定時器驅(qū)動,支持兩種分辨率的定時時間。第一種為毫秒級,該方法定時誤差較大。第二種為bit每秒,即高精度定時,本文所使用的即是此種定時方法。然后操作控制寄存器,打開中斷,使能定時器,讓定時器開始工作。 接下來講述停止定時器函數(shù)gsc3280_timer_stop(): 1. static void stop_hard_timer(struct gsc3280_timer *timer) 2. { 3. timer_tcr_u tcr = {.w32 = 0}; 4. 5. tcr.w32 = __raw_readl(timer->base + TIMER_CR); 6. tcr.b.irq_mask = 1; //mask timer irq 7. tcr.b.enable = 0; //disable timer 8. __raw_writel(tcr.w32, timer->base + TIMER_CR); 9. timer->in_use = false; 10.} 11.void gsc3280_timer_stop(struct gsc3280_hard_timer *h_timer) 12.{ 13. int i= 0; 14. 15. for (i = 0; i < GSC3280_H_TIMER_NR; i++) { 16. if (gsc3280_timer_priv[i].h_timer == h_timer) { 17. if (gsc3280_timer_priv[i].in_use) { 18. stop_hard_timer(&gsc3280_timer_priv[i]); 19. break; 20. } 21. } 22. } 23.} 24.EXPORT_SYMBOL(gsc3280_timer_stop); 說明: 1) 首先根據(jù)形參尋找相應(yīng)的定時器。 2) 找到后,判斷定時器是否在使用,如果在使用,調(diào)用函數(shù)stop_hard_timer()停止定時器。 3) stop_hard_timer()函數(shù)中,就是操作控制寄存器,屏蔽中斷,禁止定時器。 最后介紹重新啟動定時器函數(shù)gsc3280_mod_timer(): 1. int gsc3280_mod_timer(struct gsc3280_hard_timer *h_timer) 2. { 3. int i = 0; 4. 5. for (i = 0; i < GSC3280_H_TIMER_NR; i++) { 6. if (gsc3280_timer_priv[i].h_timer == h_timer) { 7. if (gsc3280_timer_priv[i].in_use) { 8. stop_hard_timer(&gsc3280_timer_priv[i]); 9. start_hard_timer(&gsc3280_timer_priv[i]); 10. } 11. } 12. } 13. return 0; 14.} 15.EXPORT_SYMBOL(gsc3280_mod_timer); 說明: 1) 該函數(shù)首先通過形參尋找相應(yīng)的定時器。 2) 找到后,判斷定時器是否在使用,如果在使用,首先停止定時器,然后再一次開啟。 | ||||
三、一線觸摸屏協(xié)議驅(qū)動 | ||||
3.1、一線觸摸屏協(xié)議的基本內(nèi)容 | ||||
一線觸摸屏首先定義一個軟定時器,該定時器每隔25ms執(zhí)行一次,在該定時器的中斷函數(shù)中,會判斷此次是什么類型的數(shù)據(jù)傳輸,包括初始化一線觸摸屏、控制背光命令或者讀取觸摸屏位置信息命令。命令設(shè)置成功后,進(jìn)入測量函數(shù),在測量函數(shù)中,打開硬件定時器,使用位速率為9600和一線觸摸屏通信,最后取得有效數(shù)據(jù)后,如果是測量位置信息,通過input子系統(tǒng)將其報告給應(yīng)用層,如果是控制背光,則什么也不做。 | ||||
3.2、一線觸摸屏系統(tǒng)初始化和退出函數(shù) | ||||
1. #if defined(CONFIG_GSC3280_1WIRE_TS) 2. static struct platform_device ts_1wire_device = { 3. .name = "ts_1wire_device", 4. .id = -1, 5. }; 6. #endif 7. static struct platform_driver ts_1wire_device_driver = { 8. .probe = ts_1wire_probe, 9. .remove = __devexit_p(ts_1wire_remove), 10. .driver = { 11. .name = "ts_1wire_device", 12. .owner = THIS_MODULE, 13. } 14.}; 15. 16.static int __init ts_1wire_init(void) 17.{ 18. return platform_driver_register(&ts_1wire_device_driver); 19.} 20.static void __exit ts_1wire_exit(void) 21.{ 22. platform_driver_unregister(&ts_1wire_device_driver); 23.} 24.module_init(ts_1wire_init); 25.module_exit(ts_1wire_exit); 26. 27.MODULE_AUTHOR("Davied<apple_guet@126.com>"); 28.MODULE_DESCRIPTION("GSC3280 one wire ts Driver"); 29.MODULE_LICENSE("GPL"); 30.MODULE_ALIAS("gsc3280 one wire ts") 1) 將一線觸摸屏定義為平臺設(shè)備,因為它不操作任何CPU內(nèi)部資源,所以平臺設(shè)備的定義中只有名稱和id。 2) 使用平臺設(shè)備的注冊和注銷函數(shù)對其進(jìn)行操作。 1. static int ts_1wire_probe(struct platform_device *pdev) 2. { 3. int ret = 0; 4. struct ts_1wire_t *ts = NULL; 5. struct input_dev *input = NULL; 6. 7. DBG("############\n"); 8. printk(KERN_INFO "ts 1wire probe start.\n"); 9. ts = kzalloc(sizeof(struct ts_1wire_t), GFP_KERNEL); 10. if (!ts) { 11. DBG("kzalloc error\n"); 12. return -ENOMEM; 13. } 14. input = input_allocate_device(); 15. if (!input) { 16. ret = -ENOMEM; 17. goto err_free_mem; 18. } 19. spin_lock_init(&ts->slock); 20. ts->dev = &pdev->dev; 21. snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(ts->dev)); 22. input->name = "h3600_ts"; 23. input->phys = ts->phys; 24. input->dev.parent = ts->dev; 25. input->id.vendor = 0x00; //tsdev->vendor; 26. input->id.version = 0x00; //tsdev->rev; 27. input->id.product = 0x03; //tsdev->rev; 28. input->id.bustype = BUS_HOST; //should be spi 29. input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); 30. input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); 31. ts->input = input; 32. input_set_abs_params(ts->input, ABS_X, GSC_X_MIN, GSC_X_MAX, GSC_X_FUZZ, 0); 33. input_set_abs_params(ts->input, ABS_Y, GSC_Y_MIN, GSC_Y_MAX, GSC_Y_FUZZ, 0); 34. input_set_abs_params(ts->input, ABS_PRESSURE, GSC_PRESSURE_MIN, GSC_PRESSURE_MAX, 0, 0); 35. ret = input_register_device(ts->input); 36. if (ret) { 37. DBG("!!!!input register device error!\n"); 38. goto err_free_input; 39. } 40. strlcpy(ts->name, TS_1WIRE_NAME, sizeof(ts->name)); 41. ret = gpio_request(TS_1WIRE_DATA_PIN, "TS_1WIRE_DATA_PIN"); 42. if (ret) { 43. DBG("gpio request error!\n"); 44. goto err_free_input; 45. } 46. set_pin_up(); 47. set_pin_value(1); 48. set_pin_as_output(); 49. 50. init_hard_timer_for_1wire(ts); 51. ret = gsc3280_request_hard_timer(&ts->ts_hard_timer); 52. if (ret) { 53. DBG("gsc3280 request hard timer error\n"); 54. goto err_gpio_req; 55. } 56. ret = misc_register(&bl_misc); 57. if (ret != 0) { 58. DBG("misc_register bl_misc error!\n"); 59. goto err_hard_timer_req; 60. } 61. ts->one_wire_status = IDLE; 62. init_timer(&ts_1wire_timer); 63. ts_1wire_timer.data = (unsigned long)ts; 64. ts_1wire_timer.expires = jiffies + TIMER_DELAY; 65. ts_1wire_timer.function = one_wire_timer_callback; 66. add_timer(&ts_1wire_timer); 67. mutex_lock(&wire1_ts_list_lock); 68. list_add(&ts->device_entry, &wire1_ts_list); 69. mutex_unlock(&wire1_ts_list_lock); 70. platform_set_drvdata(pdev, ts); 71. printk(KERN_INFO "ts 1wire probe success.\n"); 72. DBG("############\n"); 73. return 0; 74. 75.err_hard_timer_req: 76. gsc3280_free_hard_timer(&ts->ts_hard_timer); 77.err_gpio_req: 78. gpio_free(TS_1WIRE_DATA_PIN); 79.err_free_input: 80. input_free_device(input); 81.err_free_mem: 82. kfree(ts); 83. printk(KERN_INFO "!!!!ts 1wire probe error!!!!!\n"); 84. return ret; 85.} 說明: 1) 首先申請一線觸摸屏結(jié)構(gòu)體內(nèi)存。 2) 然后申請input設(shè)備內(nèi)存。 3) 接下來初始化一線觸摸屏結(jié)構(gòu)體成員變量,包括自旋鎖、IO申請等。 4) 申請硬件定時器。將控制背光定義為混雜設(shè)備。 5) 申請軟件定時器,包括設(shè)置數(shù)據(jù)、延時時間和回調(diào)函數(shù),然后將這個軟定時器加入到系統(tǒng)中。 | ||||
3.3、軟定時器程序分析 | ||||
根據(jù)前面的一線電阻觸摸屏原理分析,首先看下回調(diào)函數(shù)one_wire_timer_callback(): 1. static void one_wire_timer_callback(unsigned long data) 2. { 3. unsigned long flags = 0; 4. struct ts_1wire_t *ts = (struct ts_1wire_t *)data; 5. 6. //mod_timer(&ts_1wire_timer, jiffies + TIMER_DELAY); 7. spin_lock_irqsave(&ts->slock, flags); 8. if (ts->lcd_type == 0) { 9. //DBG("REQ_INFO\n"); 10. ts->req = REQ_INFO; 11. } 12. else if (!ts->backlight_init_success) { 13. //DBG("backlight_init_success\n"); 14. ts->backlight_init_success = 1; 15. ts->req = BL_INIT; 16. } 17. else if (ts->backlight_req) { 18. //DBG("backlight_req\n"); 19. ts->req = ts->backlight_req; 20. ts->backlight_req = 0; 21. } else { 22. //DBG("REQ_TS\n"); 23. ts->req = REQ_TS; 24. } 25. spin_unlock_irqrestore(&ts->slock, flags); 26. start_one_wire_session(ts); 27.} 1) 首先上自旋鎖,防止多個CPU在執(zhí)行此函數(shù)。 2) 根據(jù)不同的類型,賦值不同的命令。獲取觸摸屏位置信息是默認(rèn)命令。系統(tǒng)啟動后,REQ_INFO和BL_INIT命令一般只執(zhí)行一次。 3) 釋放自旋鎖,開始一次會話。 會話函數(shù)start_one_wire_session(ts)如下所示: 1. static void start_one_wire_session(struct ts_1wire_t *ts) 2. { 3. u8 crc = 0; 4. unsigned long flags = 0; 5. 6. if (ts->one_wire_status != IDLE) { 7. DBG("one_wire_status: %d error!!!!\n", ts->one_wire_status); 8. return; 9. } 10. spin_lock_irqsave(&ts->slock, flags); 11. ts->one_wire_status = START; //IDLE to START 12. set_pin_value(1); 13. set_pin_as_output(); 14. crc8_init(crc); 15. crc8(crc, ts->req); 16. ts->io_data = (ts->req << 8) + crc; 17. ts->io_data <<= 16; 18. ts->io_bit_count = 1; 19. set_pin_as_output(); 20. spin_unlock_irqrestore(&ts->slock, flags); 21. 22. local_irq_save(flags); 23. gsc3280_timer_start(&ts->ts_hard_timer); 24. set_pin_value(0); 25. local_irq_restore(flags); 26.} 1) 首先判斷觸摸屏狀態(tài),如果是不是IDLE,錯誤退出。 2) 上自旋鎖,初始化觸摸屏狀態(tài)為開始,設(shè)置一線IO管腳狀態(tài),計算crc,組裝數(shù)據(jù)。 3) 解自旋鎖,開啟硬件定時器,啟動傳輸。 | ||||
| ||||
3.4、硬件定時器傳輸數(shù)據(jù) | ||||
1. static void init_hard_timer_for_1wire(struct ts_1wire_t *ts) 2. { 3. ts->ts_hard_timer.type = LOOP; 4. ts->ts_hard_timer.value_type = 1; 5. ts->ts_hard_timer.bps = SAMPLE_BPS; 6. ts->ts_hard_timer.function = ts_1wire_hardtimer_callback; 7. ts->ts_hard_timer.data = (unsigned long)ts; 8. } 9. static void ts_1wire_hardtimer_callback(unsigned long data) 10.{ 11. struct ts_1wire_t *ts = (struct ts_1wire_t *)data; 12. 13. //DBG("ts_1wire_hardtimer_callback start\n"); 14. ts->io_bit_count--; 15. switch(ts->one_wire_status) { 16. case START: 17. //DBG("START\n"); 18. if (ts->io_bit_count == 0) { 19. ts->io_bit_count = 16; 20. ts->one_wire_status = REQUEST; 21. } 22. break; 23. case REQUEST: 24. //Send a bit 25. //DBG("REQUEST\n"); 26. set_pin_value(ts->io_data & (1U << 31)); 27. ts->io_data <<= 1; 28. if (ts->io_bit_count == 0) { 29. ts->io_bit_count = 2; 30. ts->one_wire_status = WAITING; 31. } 32. break; 33. case WAITING: 34. //DBG("WAITING\n"); 35. if (ts->io_bit_count == 0) { 36. ts->io_bit_count = 32; 37. ts->one_wire_status = RESPONSE; 38. } 39. else if (ts->io_bit_count == 1) { 40. set_pin_as_input(); 41. set_pin_value(1); 42. } 43. break; 44. case RESPONSE: 45. //Get a bit 46. //DBG("RESPONSE\n"); 47. ts->io_data = (ts->io_data << 1) | get_pin_value(); 48. if (ts->io_bit_count == 0) { 49. ts->io_bit_count = 2; 50. ts->one_wire_status = STOPING; 51. set_pin_value(1); 52. set_pin_as_output(); 53. one_wire_session_complete(ts); 54. } 55. break; 56. case STOPING: 57. //DBG("STOPING\n"); 58. if (ts->io_bit_count == 0) { 59. ts->one_wire_status = IDLE; 60. gsc3280_timer_stop(&ts->ts_hard_timer); 61. mod_timer(&ts_1wire_timer, jiffies + TIMER_DELAY); 62. } 63. break; 64. default: 65. //DBG("default\n"); 66. gsc3280_timer_stop(&ts->ts_hard_timer); 67. mod_timer(&ts_1wire_timer, jiffies + TIMER_DELAY); 68. break; 69. } 70.} 說明: 1) 硬件定時器初始化init_hard_timer_for_1wire()函數(shù)在驅(qū)動探測函數(shù)中執(zhí)行,此函數(shù)定義了硬件定時器工作在循環(huán)模式,數(shù)據(jù)類型為位速率,位速率為9600,定義了硬件定時器的回調(diào)函數(shù)。 2) 在硬件定時函數(shù)中,通過一個switch-case結(jié)構(gòu)來區(qū)分不同的工作狀態(tài)。啟動一次會話后,其工作狀態(tài)分別經(jīng)過:START->REQUEST->WAITING->RESPONSE->STOPING。 3) 在START中,首先設(shè)置REQUEST中需要傳送的位數(shù)為16位。在REQUEST中傳送完16位數(shù)據(jù)后,設(shè)置進(jìn)入兩次WAITING狀態(tài)。第一次WAITING將IO管腳設(shè)置為輸入,準(zhǔn)備接收數(shù)據(jù)。第二次WAITING設(shè)置當(dāng)前狀態(tài)為RESPONSE,接收數(shù)據(jù)長度為32位。在RESPONSE中,接收到32位數(shù)據(jù)后,設(shè)置進(jìn)入兩次STOPING,并且調(diào)用one_wire_session_complete(ts);函數(shù)表示一次會話完成。第二次進(jìn)入STOPING后,停止硬件定時器,重新啟動軟件定時器。 one_wire_session_complete(ts);函數(shù)如下所示: 1. static inline void notify_info_data(struct ts_1wire_t *ts, unsigned char lcd_type, 2. unsigned char ver_year, unsigned char week) 3. { 4. if (lcd_type != 0xFF) { 5. ts->lcd_type = lcd_type; 6. //firmware_ver = ver_year * 100 + week; 7. } 8. } 9. static inline void notify_bl_data(struct ts_1wire_t *ts, u8 a, u8 b, u8 c) 10.{ 11. ts->bl_ready = 1; 12. wake_up_interruptible(&bl_waitq); 13.} 14.static void one_wire_session_complete(struct ts_1wire_t *ts) 15.{ 16. u8 crc = 0; 17. const unsigned char *p = (const u8*)&(ts->io_data); 18. 19. crc8_init(crc); 20. crc8(crc, p[3]); 21. crc8(crc, p[2]); 22. crc8(crc, p[1]); 23. if (crc != p[0]) { 24. DBG("one_wire_session_complete crc error\n"); 25. return; 26. } 27. switch(ts->req) { 28. case REQ_TS: 29. ts->x = ((p[3] >> 4U) << 8U) + p[2]; 30. ts->y = ((p[3] & 0xFU) << 8U) + p[1]; 31. ts->z = (ts->x != 0xFFFU) && (ts->y != 0xFFFU); 32. notify_ts_data(ts); 33. break; 34. case REQ_INFO: 35. notify_info_data(ts, p[3], p[2], p[1]); 36. break; 37. default: 38. notify_bl_data(ts, p[3], p[2], p[1]); 39. break; 40. } 41.} 說明: 1) 函數(shù)首先進(jìn)行crc校驗,如果crc錯誤,則直接退出。 2) 如果校驗正確,根據(jù)不同的命令,進(jìn)入不同的報告類型。其中REQ_TS和REQ_INFO不需要向應(yīng)用層報告數(shù)據(jù)。notify_info_data()函數(shù)通過修改ts->lcd_type變量來保證系統(tǒng)起來以后,一線觸摸屏執(zhí)行REQ_INFO命令只有一次。
1. static void gsc3280_report_event(struct ts_1wire_t *ts, u32 z) 2. { 3. #ifdef CONFIG_GSC3280_POS_PRINT 4. printk(KERN_INFO "x = %d\n", ts->x); 5. printk(KERN_INFO "y = %d\n", ts->y); 6. printk(KERN_INFO "z = %d\n", z); 7. #endif 8. 9. input_report_abs(ts->input, ABS_PRESSURE, z); 10. input_report_abs(ts->input, ABS_X, ts->x); 11. input_report_abs(ts->input, ABS_Y, ts->y); 12. if (z > 0) 13. input_report_key(ts->input, BTN_TOUCH, 1); 14. else 15. input_report_key(ts->input, BTN_TOUCH, 0); 16. input_sync(ts->input); 17.} 18.static inline void notify_ts_data(struct ts_1wire_t *ts) 19.{ 20. if (ts->z == 1) { 21. ts->x = ((ts->x -285) * gXres) / (3944 - 285); 22. ts->y = ((3936 - ts->y) * gYres) / (3936 - 102); 23. if ((ts->x > 0) && (ts->x < gXres) && (ts->y > 0) && (ts->y < gYres)) { 24. if (ts->flg == 0) { 25. ts->flg = 1; 26. gsc3280_report_event(ts, 0); 27. } 28. gsc3280_report_event(ts, ts->z); 29. } 30. } else if (ts->z == 0) { 31. if (ts->flg == 1) { 32. ts->flg = 0; 33. gsc3280_report_event(ts, 0); 34. } 35. } 36.} 說明: 1) 首先根據(jù)公式計算觸摸點的絕對坐標(biāo),如果該坐標(biāo)值在正確的范圍內(nèi),則上報該位置信息。 2) 由于是電阻觸摸屏,其電阻值和位置是線性的,公式為:(電壓- 0) / (總電壓) = (分辨?zhèn)€數(shù)- 0) / (總個數(shù))。即分辨?zhèn)€數(shù)= 總個數(shù)* (電壓) / (總電壓)。通過測量觸摸屏四個腳點的電壓值,分別得到X和Y方向上的最大總電壓,然后在實際測量中,根據(jù)測得的電壓值,帶入公式中的電壓項,分別計算出X和Y方向的絕對坐標(biāo)。本文使用的觸摸屏分辨率為800*480,所以X方向的總個數(shù)為800,Y方向的為480。 3) 位置報告函數(shù)將在第三篇文章----input核心中講述。 4) 本文設(shè)計的程序可以通過make menuconfig命令輸入分辨率,Kconfig程序如下: 1. config INPUT_TSDEV_SCREEN_X 2. int "Horizontal screen resolution" 3. depends on INPUT_TOUCHSCREEN 4. default "800" 5. help 6. If you're using a digitizer, this is the X window screen resolution you are 7. using to correctly scale the data. If you're not using a digitizer, this value 8. is ignored. 9. 10.config INPUT_TSDEV_SCREEN_Y 11. int "Vertical screen resolution" 12. depends on INPUT_TOUCHSCREEN 13. default "480" 14. help 15. If you're using a digitizer, this is the Y window screen resolution you are 16. using to correctly scale the data. If you're not using a digitizer, this value 17. is ignored. 在.c文件中通過下列程序獲得輸入的值: 1. #ifndef CONFIG_INPUT_TSDEV_SCREEN_X 2. #define CONFIG_INPUT_TSDEV_SCREEN_X 800 3. #endif 4. #ifndef CONFIG_INPUT_TSDEV_SCREEN_Y 5. #define CONFIG_INPUT_TSDEV_SCREEN_Y 480 6. #endif 7. 8. static const int gXres = CONFIG_INPUT_TSDEV_SCREEN_X; 9. static const int gYres = CONFIG_INPUT_TSDEV_SCREEN_Y; | ||||
四、控制背光驅(qū)動和應(yīng)用層程序 | ||||
4.1、驅(qū)動程序 | ||||
在系統(tǒng)探測函數(shù)中,將控制背光驅(qū)動定義為混雜設(shè)備并進(jìn)行注冊,定義混雜設(shè)備的源碼如下: 1. static struct file_operations bl_fops = { 2. owner : THIS_MODULE, 3. write : bl_write, 4. open : bl_open, 5. }; 6. static struct miscdevice bl_misc = { 7. .minor = MISC_DYNAMIC_MINOR, 8. .name = BACKLIGHT_DEVICE_NAME, 9. .fops = &bl_fops, 10.}; 說明: 1) 混雜設(shè)備的文件操作函數(shù)中有打開和寫,首先看下打開函數(shù): 1. static int bl_open(struct inode *inode, struct file *filp) 2. { 3. int status = -ENXIO; 4. struct ts_1wire_t *ts = NULL; 5. 6. mutex_lock(&wire1_ts_list_lock); 7. list_for_each_entry(ts, &wire1_ts_list, device_entry) { 8. if(strcmp(ts->name, TS_1WIRE_NAME) == 0) { 9. status = 0; 10. break; 11. } 12. } 13. mutex_unlock(&wire1_ts_list_lock); 14. if (status == 0) { 15. filp->private_data = ts; 16. } else { 17. return status; 18. } 19. return nonseekable_open(inode, filp); 20.} 說明: 1) 首先通過本地全局鏈表和鎖獲得在探測函數(shù)中定義的設(shè)備結(jié)構(gòu)體內(nèi)存指針。 2) 然后調(diào)用無定位打開函數(shù)返回。 寫數(shù)據(jù)即發(fā)送命令控制背光,程序如下: 1. static ssize_t bl_write(struct file *filp, const char __user *buffer, size_t count, loff_t *ppos) 2. { 3. int ret = 0; 4. u8 bl_data = 0; 5. struct ts_1wire_t *ts = filp->private_data; 6. 7. DBG("bl_write start\n"); 8. if (get_user(bl_data, (u8 __user *)buffer)) { 9. printk(KERN_INFO "get_user error!\n"); 10. return -EFAULT; 11. } 12. if (bl_data > 127) { 13. bl_data = 127; 14. } 15. ts->bl_ready = 0; 16. ts->backlight_req = bl_data + 0x80U; 17. ret = wait_event_interruptible_timeout(bl_waitq, ts->bl_ready, HZ); 18. if (ret < 0) { 19. printk(KERN_INFO "wait_event_interruptible_timeout error!\n"); 20. return ret; 21. } else if (ret == 0) { 22. printk(KERN_INFO "time out error!\n"); 23. return -ETIMEDOUT; 24. } else { 25. return count; 26. } 27.} 1) 首先從應(yīng)用層獲取寫入背光值。 2) 組合數(shù)據(jù),形成背光控制命令。 3) 在會話函數(shù)中由于ts->backlight_req不為0,即可發(fā)送背光控制命令。 | ||||
4.2、應(yīng)用層程序 | ||||
通過滑動變阻器來控制背光。根據(jù)協(xié)議,背光總共可以分為0~127個檔位,滑動變阻器的測量值范圍為2010~4010,也就是說,背光每變化一個檔位,滑動變阻器電阻值變化:(4010 - 2010) / 128 = 16。那么我們給背光發(fā)送的檔位值即為:(x - 2010) / 16。應(yīng)用層程序如下: 1. #include <fcntl.h> 2. #include <stdio.h> 3. #include <stdlib.h> 4. #include <sys/ioctl.h> 5. #include <linux/ioctl.h> 6. 7. 8. #define ADC_DEV_IOC_MAGIC 'a' 9. #define ADC_DEV_IOC_MAXNR 2 10.#define ADC_DEV_CON_PBAT _IOR(ADC_DEV_IOC_MAGIC, 0, int) 11.#define ADC_DEV_CON_CHX _IOWR(ADC_DEV_IOC_MAGIC, 1, int) 12. 13.int main(int argc, char **argv) 14.{ 15. unsigned int idCmd = 0; 16. //unsigned char buffer[4] = {0}; 17. unsigned char bl_data = 0; 18. int fd1 = 0, fd2 = 0, ret = 0, data = 0; 19. 20. //以阻塞方式打開設(shè)備文件,非阻塞時flags=O_NONBLOCK 21. fd1 = open("/dev/adc0", 0); 22. if (fd1 < 0) { 23. printf("Open ADC Device Faild!\n"); 24. exit(1); 25. } 26. fd2 = open("/dev/backlight-1wire", O_RDWR); 27. if (fd2 < 0) { 28. printf("Open wire1 backlight Device Faild!\n"); 29. exit(1); 30. } 31. while(1) { 32. data = 1; 33. idCmd = ADC_DEV_CON_CHX; 34. ret = ioctl(fd1, idCmd, &data); 35. if (ret != 0) { 36. printf("Read ADC Device Faild!\n"); 37. break; 38. } 39. //printf("data = %d\n", data); 40. bl_data = (data - 2010) /16; 41. //printf("bl_data = %d\n", bl_data); 42. ret = write(fd2, &bl_data, 1); 43. if (ret < 0) { 44. printf("wire1 backlight ret = %d\n", ret); 45. //break; 46. } 47. //sleep(1); 48. for (ret = 0; ret < 655360; ret++) 49. ;;;; 50. } 51. close(fd1); 52. close(fd2); 53. return 0; 54.} 編譯后,將程序設(shè)為后臺執(zhí)行,系統(tǒng)啟動后,任何時間滑動變阻器,一線觸摸屏的背光都會根據(jù)電阻的位置而做相應(yīng)的變化。 | ||||
原文參見:http://blog.chinaunix.net/uid-25445243-id-4111354.html | ||||