NUC972触摸屏驱动移植过程分析(一)

32 篇文章 8 订阅

https://blog.csdn.net/b7376811/article/details/86514683

 因为下一个项目可能会用到触摸屏,所以这段时间对触摸屏的驱动的移植进行了研究,今天正好有机会,谈谈移植过程。

其实基于开发板对触摸屏驱动进行移植,需要做的工作并不是太多,因为大部分工作开发板的供应商已经提供好了,但是用起来总是不尽人意,所以需要进一步优化,废话少说,步入正题。

首先看一下硬件电路,如下图所示:

开发板上使用的是TSC2007作为驱动触摸屏(电阻屏)的驱动芯片,没有使用nuc972内置的AD,这样做的原因首先是比较好移植,其次是外置专用的触摸屏芯片往往精度更高。咱们先看一下这个芯片的几个主要的引脚,X+、Y+、X-、Y- 是连接触摸屏的,一般情况下无需考虑,A0、A1、SCL、SDA 这四个引脚定义很容易识别出是和IIC通信有关,是的,一般外置的触摸屏控制芯片(包括电阻屏和电容屏)都是通过IIC接口与主机CPU进行通信的,A0、A1是设置地址的接口,SCL和SDA分别是IIC的时钟接口与数据接口,那么问题来了,IIC通信是主从模式,任何的通信都需要由主机发起,而主机不能判断什么时候触摸屏被按下,总不能CPU一直在轮询触摸屏吧,那还干不干其他事儿了?所以,就又增加了一个/PENIRQ引脚,这个引脚一般要与CPU的一个gpio连接,如果触摸屏被按下,这个引脚会有动作,从图上可以看出来当触摸屏被按下时,这个引脚会变低,松开会变高(CPU一般是使用中断进行处理),这样就可以通知CPU读取触摸屏数据了。

看完了硬件,我们再来看一下软件。大家知道,在linux内核中,设备数据和驱动原则上是分离的,这样可以增加驱动的可移植性。首先看一下device,在新唐提供的BSP中,nuc970系列的设备数据在/arch/arm/mach-nuc970 这个文件夹里面,我们需要的信息在dev.c这个文件里。还是从下晚上看的看代码原则,先看一下CPU启动时,设备初始化函数:

 
  1.  
  2.  
  3. void __init nuc970_platform_init(struct platform_device **device, int size)

  4. {

  5. #if defined(CONFIG_LEDS_GPIO)

  6. if (gpio_request(nuc970_ek_leds[0].gpio, "led-ds2") < 0) {

  7. printk("can not request gpio[%d] for led-ds2\n", nuc970_ek_leds[0].gpio);

  8. } else {

  9. gpio_direction_output(nuc970_ek_leds[0].gpio, nuc970_ek_leds[0].active_low);

  10. }

  11. #endif

  12.  
  13. platform_add_devices(device, size);

  14. platform_add_devices(nuc970_public_dev, ARRAY_SIZE(nuc970_public_dev));

  15.  
  16. mdk970ek_add_device_buttons();

  17.  
  18. #if defined(CONFIG_MTD_M25P80) || defined(CONFIG_SPI_SPIDEV)

  19. /* register spi devices */

  20. #ifdef CONFIG_SPI_NUC970_P0

  21. spi_register_board_info(nuc970_spi0_board_info, ARRAY_SIZE(nuc970_spi0_board_info));

  22. #endif

  23. #ifdef CONFIG_SPI_NUC970_P1

  24. spi_register_board_info(nuc970_spi1_board_info, ARRAY_SIZE(nuc970_spi1_board_info));

  25. #endif

  26. #endif

  27.  
  28. #ifdef CONFIG_I2C_BUS_NUC970_P0

  29. i2c_register_board_info(0, nuc970_i2c_clients0, sizeof(nuc970_i2c_clients0)/sizeof(struct i2c_board_info));

  30. #endif

  31.  
  32. #ifdef CONFIG_GPIO_NUC970

  33. #ifdef CONFIG_I2C_ALGOBIT

  34. {

  35. nuc970_i2c_clients2[0].irq = 399; /* tsc2007 */

  36. }

  37. i2c_register_board_info(2, nuc970_i2c_clients2, ARRAY_SIZE(nuc970_i2c_clients2));

  38. // i2c_register_board_info(2, nuc970_i2c_clients2, sizeof(nuc970_i2c_clients2)/sizeof(struct i2c_board_info));

  39. #endif

  40. #endif

  41.  
  42. #ifdef CONFIG_PWM_NUC970

  43. pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup));

  44. #endif

  45. }

内核启动时,初始化设备就是调用的这个函数,我们使用的是使用GPIO模拟IIC通信的方式,所以需要选择CONFIG_GPIO_NUC970、CONFIG_I2C_ALGOBIT这两个宏定义,具体两个宏定义之间的代码先不考虑,下面会有介绍,只需要先注意一个字段:nuc970_i2c_clients2,这个是注册IIC设备的重中之重,而且肯定在上面的代码中有定义,网上翻代码,很容易找到它的定义:

 
  1. #ifdef CONFIG_GPIO_NUC970

  2. #ifdef CONFIG_I2C_ALGOBIT

  3. static struct i2c_board_info __initdata nuc970_i2c_clients2[] =

  4. {

  5. {

  6. I2C_BOARD_INFO("tsc2007", 0x48),

  7. .platform_data = &tsc2007_info,

  8. /* irq number is run-time assigned */

  9. },

  10. #ifdef CONFIG_SENSOR_OV7725

  11. {I2C_BOARD_INFO("ov7725", 0x21),},

  12. #endif

  13. #ifdef CONFIG_SENSOR_OV5640

  14. {I2C_BOARD_INFO("ov5640", 0x3c),},

  15. #endif

  16. #ifdef CONFIG_SENSOR_NT99141

  17. {I2C_BOARD_INFO("nt99141", 0x2a),},

  18. #endif

  19. #ifdef CONFIG_SENSOR_NT99050

  20. {I2C_BOARD_INFO("nt99050", 0x21),},

  21. #endif

  22. {I2C_BOARD_INFO("lm75a", 0x4e),},

  23.  
  24. {I2C_BOARD_INFO("ds1307", 0x68),},

  25. };

可以很明显的看出来nuc970_i2c_clients2是一个i2c_board_info 类型的结构体数组,里面包含了好几个成员,大部分都是条件编译选项,咱们只考虑触摸屏,只看第一个元素,这个结构体里面一共有两个变量,第一个是一个宏,其实就是填充该结构体type,addr两个字段,type一般就是I2C设备的名字,addr是该设备在I2C总线上的地址(注意,这个地址需要查看技术手册,每种设备都有不同的定义地址的规则)。platform_data  字段填充的是需要传入驱动的私有数据。下面看一下这个私有数据,继续往上翻代码,很容易找到这个结构:

 
  1. static struct tsc2007_platform_data tsc2007_info = {

  2. .model = 2007,

  3. .x_plate_ohms = 200,

  4. .get_pendown_state = tsc2007_get_pendown_state,

  5. .init_platform_hw = tsc2007_init_platform_hw,

  6. .clear_penirq = tsc2007_clear_penirq,

  7. };

model和x_plate_ohms是触摸屏自己相关的参数,get_pendown_state是获取触摸屏有没有被按下的状态指示的函数指针,这个指针指向的函数其实就是判断触摸屏芯片TSC2007的/PENIRQ引脚是高电平还是低电平。init_platform_hw是对触摸屏硬件初始化的函数指针,其实指向的就是和/PENIRQ引脚相连的CPU的gpio相关的设置函数,同样的,clear_penirq指向的就是清除该gpio中断的函数。说到这里,大家其实就应该明白了,触摸屏驱动牵扯到两方面的知识,一方面是I2C接口通信,一方面是gpio输入中断处理。再往上追,就很容易看到这三个函数的内容:

 
  1. static int tsc2007_get_pendown_state(void)

  2. {

  3. static int pre_val = 1;

  4.  
  5. int gpio_1_val = gpio_get_value(NUC970_PE15);

  6. if (pre_val != gpio_1_val) {

  7. mdelay(20);

  8. gpio_1_val = gpio_get_value(NUC970_PE15);

  9. if (pre_val != gpio_1_val) {

  10. pre_val = gpio_1_val;

  11. }

  12. }

  13.  
  14. return !pre_val;

  15. //return !gpio_get_value(NUC970_PE15);

  16. }

  17.  
  18. static void tsc2007_clear_penirq(void)

  19. {

  20. __raw_writel(__raw_readl(REG_GPIOE_ISR) | 0x8000, REG_GPIOE_ISR);

  21. //printk("clear irq for tsc2007\n");

  22. }

  23.  
  24. static int tsc2007_init_platform_hw(void)

  25. {

  26. printk("REG_GPIOE_PUEN=%x\n", __raw_readl(REG_GPIOE_PUEN));

  27. /* enable pull up on PE14, PE15 */

  28. __raw_writel(__raw_readl(REG_GPIOE_PUEN) | 0xc000, REG_GPIOE_PUEN);

  29. /* disable pull down on PE14, PE15 */

  30. __raw_writel(__raw_readl(REG_GPIOE_PDEN) & 0x3FFF, REG_GPIOE_PDEN);

  31. /* enable debounce on PE14, PE15 */

  32. __raw_writel(__raw_readl(REG_GPIOE_DBEN) | 0xc000, REG_GPIOE_DBEN);

  33. printk("REG_GPIOE_PUEN=%x\n", __raw_readl(REG_GPIOE_PUEN));

  34. gpio_request_one(NUC970_PE15, GPIOF_IN, "nuc970-ts2007");

  35. gpio_direction_input(NUC970_PE15);

  36. /* only enable fallen edge int on PE15 */

  37. __raw_writel(__raw_readl(REG_GPIOE_IFEN) | 0x8000, REG_GPIOE_IFEN);

  38. printk("gpio_to_irq(NUC970_PE15)=%d\n", gpio_to_irq(NUC970_PE15));

  39. return 0;

  40. }

好了,分析到此,触摸屏注册需要的数据基本上已经全部包含了,这些数据会在内核启动时,初始化平台的设备时调用void __init nuc970_platform_init(struct platform_device **device, int size) 这个函数,在这个函数中,通过i2c_register_board_info(2, nuc970_i2c_clients2, ARRAY_SIZE(nuc970_i2c_clients2))这个函数对结构体数组nuc970_i2c_clients2中的内容进行注册,注意,这个函数的第一个参数在这里设置为2,这个参数代表的含义是i2c总线的编号,这里为什么要设置成2呢?因为nuc970平台上包含两个I2C硬件接口,咱们这里是使用内核中使用gpio模拟I2C通信时序来完成通信的,所以为了和硬件I2C接口不冲突,设置为2。

分析到这里,基本上就把触摸屏驱动相关的device的数据搞清楚了,今天就写这么多吧,拜拜!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值