通用i2c驱动

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/huangminqiang201209/article/details/8506593

    了解I2C的同志都知道,常规I2C驱动中的最重点就是这4个结构体:i2c_driver、i2c_client、i2c_adapter 和i2c_algorithm,而且他们之间的关系错综复杂,我看了好长一段时间,也没看出多少头绪来,而且代码的可移植性很差劲,换个平台,要该的地方一大堆,改了之后还不一定对呢委屈,所以,下面介绍的通用I2C驱动就很必要了。

    大家都知道, I2C总线仅仅使用SCL、SDA这两根信号线就实现了设备之间的数据交互,极大地简化了对硬件资源和PCB板布线空间的占用。因此,I2C总线应用非常的广泛。而工作中,我需要通过I2C实现Hi35XX控制视频芯片CX25828,我是这样做的,首先,把I2C当做一个字符设备,然后用2个GPIO管脚模拟数据线和时钟线,一个管脚控制方向,实现电平的高低控制,这样就可以模拟I2C通信了(CPU与设备间的通信)。

 

 

1.当做一个字符设备

     看到这个,相信大家一定很熟悉了,对了,这就是我们入门时再熟悉不过的东东了,上层可直接对dev下的“gpioi2c”的这个节点open、write、read、close(不过,就目前这个,无read、write接口,只有ioctl了),接下来,你懂的。(在网上有很多比较成熟的I2C驱动,也是同我这个原理实现的,改改GPIO口地址就可以了大笑)。


 
 
  1. //打开和关闭设备
  2. int gpioi2c_open(struct inode * inode, struct file * file)
  3. {
  4. return 0;
  5. }
  6. int gpioi2c_close(struct inode * inode, struct file * file)
  7. {
  8. return 0;
  9. }
  10. //最重要的结构体,文件描述符指针,上层直接ioctl就可通过底层调用gpioi2c_ioctl函数。
  11. //当然,可以有其他的成员,如read、write等。
  12. static struct file_operations gpioi2c_fops =
  13. {
  14. .owner = THIS_MODULE,
  15. .ioctl = gpioi2c_ioctl,
  16. .open = gpioi2c_open,
  17. .release = gpioi2c_close
  18. };
  19. static struct miscdevice gpioi2c_dev =
  20. {
  21. .minor = MISC_DYNAMIC_MINOR,
  22. .name = "gpioi2c", //在dev下可以找到这个节点,上层就是通过open、read/write这个节点而对设备操作的。
  23. .fops = &gpioi2c_fops,
  24. };
  25. //导入导出
  26. static int __ init gpio_i2c_init(void)
  27. {
  28. int ret;
  29. printk( "hello dvr iic\n");
  30. //注册设备
  31. ret = misc_register(&gpioi2c_dev);
  32. if( 0 != ret)
  33. return -1;
  34. i2c_set(SCL | SDA);
  35. return 0;
  36. }
  37. static void __ exit gpio_i2c_exit(void)
  38. {
  39. //卸载设备
  40. misc_deregister(&gpioi2c_dev);
  41. }
  42. //导入导出内核设备
  43. module_init(gpio_i2c_init);
  44. module_exit(gpio_i2c_exit);
  45. MODULE_INFO(build, UTS_VERSION);
  46. MODULE_LICENSE( "GPL");

杂项设备(misc device):
    在 Linux 内核的include\linux\miscdevice.h文件,要把自己定义的misc device从设备定义在这里。其实是因为这些字符设备不符合预先确定的字符设备范畴,所有这些设备采用主设备号10 ,一起归于misc device,其实misc_register就是用主设备号10调用register_chrdev()的。也就是说,misc设备其实也就是特殊的字符设备。
    misc_device是特殊的字符设备。注册驱动程序时采用misc_register函数注册,此函数中会自动创建设备节点,即设备文件。无需mknod指令创建设备文件。因为misc_register()会调用class_device_create()或者device_create()。

 

2.对字符设备的控制:ioctl函数

     提供给上层的接口,arg参数其实是一个地址,这个地址里面必须存放三个信息:设备地址+寄存器地址+data。通过设备地址找到设备,然后通过寄存器地址配置相应的寄存器。关于每次通信的信息量,这要看上层接口函数ioctl:int ioctl(int file_operations, int cmd, void *addr)时arg地址内存放的内容了,如果内容是int类型即4个字节,就如下。如果这个地址内存放的是结构体,而且sizeof这个结构体的大小为12byte(即设备地址、寄存器地址、以及数据各占4个字节),就不需要移动了,直接填进去。


 
 
  1. int gpioi2c_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
  2. {
  3. unsigned int val;
  4. char device_addr, reg_addr;
  5. short reg_val;
  6. switch(cmd)
  7. {
  8. case GPIO_I2C_READ:
  9. val = *( unsigned int *)arg;
  10. device_addr = (val& 0xff000000)>> 24;
  11. reg_addr = (val& 0xffff00)>> 8;
  12. reg_val = gpio_i2c_read(device_addr, reg_addr);
  13. *( unsigned int *)arg = (val& 0xffffff00)|reg_val;
  14. break;
  15. case GPIO_I2C_WRITE:
  16. val = *( unsigned int *)arg;
  17. device_addr = (val& 0xff000000)>> 24;
  18. reg_addr = (val& 0xffff00)>> 8;
  19. reg_val = val& 0xff;
  20. gpio_i2c_write(device_addr, reg_addr, reg_val);
  21. break;
  22. default:
  23. return -1;
  24. }
  25. return 0;
  26. }

 

 

3.对设备的读写:read、write

     CX25828中,寄存器的地址为16位,设备地址为8位,所有,I2C每次只能read、write1个字节的数据。。总线空闲时,上拉电阻使SDA和SCL线都保持高电平。当 SCL 稳定在高电平时,SDA 由高到低的变化将产生一个开始位,而由低到高的变化则产生一个停止位。开始位和停止位都由 I2C 主设备产生。在选择从设备时,如果从设备采用 7 位地址,则主设备在发起传输过程前,需先发送1字节的地址信息,前 7 位为设备地址,最后 1 位为读写标志。之后,每次传输的数据也是 1 个字节,从 MSB 位开始传输。每个字节传完后,在SCL的第9个上升沿到来之前,接收方应该发出 1 个 ACK 位。SCL上的时钟脉冲由I2C主控方发出, 在第8个时钟周期之后。

                 


 
 
  1. //读1个字节(其实可以2,或者4个字节,这个地方是实现读写功能以及决定字节数的,IIC支持)
  2. unsigned short gpio_i2c_read(unsigned char devaddress, unsigned int address)
  3. {
  4. unsigned short rxdata= 0
  5. unsigned int tmp= 0;
  6. int dev_addr=devaddress;
  7. //发送设备地址:8位
  8. i2c_start_bit();
  9. i2c_send_byte(( unsigned char)(dev_addr));
  10. i2c_receive_ack();
  11. //寄存器地址:16位
  12. tmp=address& 0xff00;
  13. tmp=tmp>> 8;
  14. i2c_send_byte(( unsigned char)tmp);
  15. i2c_receive_ack();
  16. tmp=address& 0xff;
  17. i2c_send_byte(( unsigned char)tmp);
  18. i2c_receive_ack();
  19. //发送:读状态
  20. i2c_start_bit();
  21. i2c_send_byte(( unsigned char)(dev_addr) | 1);
  22. i2c_receive_ack();
  23. //接收数据:8bit
  24. rxdata = i2c_receive_byte();
  25. //停止接收数据
  26. i2c_stop_bit();
  27. rxdata=rxdata& 0xff;
  28. return rxdata;
  29. }
  30. //写一个字节
  31. void gpio_i2c_write(unsigned char devaddress, unsigned int address, unsigned int data)
  32. {
  33. int tmp;
  34. unsigned short devaddr=devaddress& 0xfe; //在手册中可以看到,读写状态占1bit,读为1,写为0.
  35. //设备地址:8bit
  36. i2c_start_bit();
  37. i2c_send_byte(( unsigned char)(devaddr));
  38. i2c_receive_ack(); //ACK
  39. //寄存器地址:16bit
  40. tmp=address& 0xff00;
  41. tmp=tmp>> 8;
  42. i2c_send_byte(( unsigned char)tmp);
  43. i2c_receive_ack();
  44. tmp=address& 0xff;
  45. i2c_send_byte(( unsigned char)tmp);
  46. i2c_receive_ack();
  47. //发送数据:8bit
  48. tmp=data& 0xff;
  49. i2c_send_byte(( unsigned char)tmp);
  50. i2c_receive_ack();
  51. //停止接收数据
  52. i2c_stop_bit();
  53. }

 

4.模拟数据线时钟线以及需要调用的一些函数


 
 
  1. #include "gpio_i2c.h"
  2. #define GPIO_0_BASE 0x20150000
  3. #define GPIO_0_DIR IO_ADDRESS(GPIO_0_BASE + 0x400) //GPIO方向控制
  4. #define SCL (1 << 1) /* GPIO 0_1 */
  5. #define SDA (1 << 0) /* GPIO 0_0 */
  6. #define GPIO_I2C_SDA_REG IO_ADDRESS(GPIO_0_BASE + 0x4)//数据线GPIO管脚
  7. #define GPIO_I2C_SCL_REG IO_ADDRESS(GPIO_0_BASE + 0x8)//时钟线GPIO管脚
  8. #define GPIO_I2C_SCLSDA_REG IO_ADDRESS(GPIO_0_BASE + 0xc)
  9. #define HW_REG(reg) *((volatile unsigned int *)(reg))
  10. #define DELAY(us) time_delay_us(us)
  11. //拉低数据线/时钟线
  12. static void i2c_clr(unsigned char whichline)
  13. {
  14. unsigned char regvalue;
  15. //拉低时钟线
  16. if(whichline == SCL)
  17. {
  18. //方向:输入/输出
  19. regvalue = HW_REG(GPIO_0_DIR);
  20. regvalue |= SCL;
  21. HW_REG(GPIO_0_DIR) = regvalue;
  22. //置零
  23. HW_REG(GPIO_I2C_SCL_REG) = 0;
  24. return;
  25. }
  26. else if(whichline == SDA)
  27. {
  28. regvalue = HW_REG(GPIO_0_DIR);
  29. regvalue |= SDA;
  30. HW_REG(GPIO_0_DIR) = regvalue;
  31. HW_REG(GPIO_I2C_SDA_REG) = 0;
  32. return;
  33. }
  34. else if(whichline == (SDA|SCL))
  35. {
  36. regvalue = HW_REG(GPIO_0_DIR);
  37. regvalue |= (SDA|SCL);
  38. HW_REG(GPIO_0_DIR) = regvalue;
  39. HW_REG(GPIO_I2C_SCLSDA_REG) = 0;
  40. return;
  41. }
  42. else
  43. {
  44. printk( "Error input.\n");
  45. return;
  46. }
  47. }
  48. //拉高数据线/时钟线
  49. static void i2c_set(unsigned char whichline)
  50. {
  51. unsigned char regvalue;
  52. //拉高时钟线
  53. if(whichline == SCL)
  54. {
  55. regvalue = HW_REG(GPIO_0_DIR);
  56. regvalue |= SCL;
  57. HW_REG(GPIO_0_DIR) = regvalue;
  58. HW_REG(GPIO_I2C_SCL_REG) = SCL;
  59. return;
  60. }
  61. else if(whichline == SDA)
  62. {
  63. regvalue = HW_REG(GPIO_0_DIR);
  64. regvalue |= SDA;
  65. HW_REG(GPIO_0_DIR) = regvalue;
  66. HW_REG(GPIO_I2C_SDA_REG) = SDA;
  67. return;
  68. }
  69. else if(whichline == (SDA|SCL))
  70. {
  71. regvalue = HW_REG(GPIO_0_DIR);
  72. regvalue |= (SDA|SCL);
  73. HW_REG(GPIO_0_DIR) = regvalue;
  74. HW_REG(GPIO_I2C_SCLSDA_REG) = (SDA|SCL);
  75. return;
  76. }
  77. else
  78. {
  79. printk( "Error input.\n");
  80. return;
  81. }
  82. }
  83. //延时usec微秒
  84. void time_delay_us(unsigned int usec)
  85. {
  86. int i,j;
  87. for(i= 0; i<usec * 5; i++)
  88. {
  89. for(j= 0; j< 64; j++)
  90. {
  91. ;
  92. }
  93. }
  94. }
  95. //开始IIC通信
  96. static void i2c_start_bit(void)
  97. {
  98. DELAY( 1);
  99. //拉高双线,拉低数据线,开始发送数据。
  100. i2c_set(SDA | SCL);
  101. DELAY( 1);
  102. i2c_clr(SDA);
  103. DELAY( 2);
  104. }
  105. //停止IIC通信
  106. static void i2c_stop_bit(void)
  107. {
  108. //时钟响应
  109. DELAY( 1);
  110. i2c_set(SCL);
  111. DELAY( 1);
  112. i2c_clr(SCL);
  113. //数据线为高,数据线由低->高:结束通信
  114. DELAY( 1);
  115. i2c_clr(SDA);
  116. DELAY( 1);
  117. i2c_set(SCL);
  118. DELAY( 1);
  119. i2c_set(SDA);
  120. DELAY( 1);
  121. }
  122. //读取数据:1bit
  123. static unsigned char i2c_data_read(void)
  124. {
  125. unsigned char regvalue;
  126. regvalue = HW_REG(GPIO_0_DIR);
  127. regvalue &= (~SDA);
  128. HW_REG(GPIO_0_DIR) = regvalue;
  129. DELAY( 1);
  130. regvalue = HW_REG(GPIO_I2C_SDA_REG);
  131. if((regvalue&SDA) != 0)
  132. return 1;
  133. else
  134. return 0;
  135. }
  136. //发送数据:1byte
  137. static void i2c_send_byte(unsigned char c)
  138. {
  139. int i;
  140. //屏蔽中断
  141. local_irq_disable();
  142. for(i= 0; i< 8; i++)
  143. {
  144. DELAY( 1);
  145. i2c_clr(SCL);
  146. DELAY( 1);
  147. //发送1(这个是重点哦)
  148. if(c & ( 1<<( 7-i)))
  149. i2c_set(SDA);
  150. else //发送0
  151. i2c_clr(SDA);
  152. DELAY( 1);
  153. i2c_set(SCL);
  154. DELAY( 1);
  155. i2c_clr(SCL);
  156. }
  157. DELAY( 1);
  158. local_irq_enable();
  159. }
  160. //接收数据:1byte
  161. static unsigned char i2c_receive_byte(void)
  162. {
  163. int j= 0;
  164. int i;
  165. unsigned char regvalue;
  166. local_irq_disable();
  167. for(i= 0; i< 8; i++)
  168. {
  169. DELAY( 1);
  170. i2c_clr(SCL);
  171. DELAY( 2);
  172. i2c_set(SCL);
  173. regvalue = HW_REG(GPIO_0_DIR);
  174. regvalue &= (~SDA);
  175. HW_REG(GPIO_0_DIR) = regvalue;
  176. DELAY( 1);
  177. if(i2c_data_read())
  178. j+=( 1<<( 7-i));
  179. DELAY( 1);
  180. i2c_clr(SCL);
  181. }
  182. local_irq_enable();
  183. DELAY( 1);
  184. return j;
  185. }
  186. //响应(每发送一个字节,都要响应),返回值为0:成功。
  187. static int i2c_receive_ack(void)
  188. {
  189. int nack;
  190. unsigned char regvalue;
  191. DELAY( 1);
  192. regvalue = HW_REG(GPIO_0_DIR);
  193. regvalue &= (~SDA); //数据线置零
  194. HW_REG(GPIO_0_DIR) = regvalue;
  195. DELAY( 1);
  196. i2c_clr(SCL);
  197. DELAY( 1);
  198. i2c_set(SCL);
  199. DELAY( 1);
  200. nack = i2c_data_read();
  201. DELAY( 1);
  202. i2c_clr(SCL);
  203. DELAY( 1);
  204. if(nack == 0)
  205. return 1;
  206. return 0;
  207. }

 

5.上层调用

/***** IIC写数据 ********************************************************************************************/
 
 
//参数分别为设备地址、寄存器地址、值。
 
 

 
 
  1. //如果writeval为结构体(最好12字节),而非unsiged long类型,就不需要移动了(底层驱动支持情况下)
  2. void IIC_Write(int devAddr, int regAddr, int val)
  3. {
  4. unsigned long writeval, ret;
  5. writeval = (((devAddr & 0xff) << 24) | ((regAddr & 0xffff) << 8) | (val & 0xff));
  6. ret = ioctl(i2cfd, I2C_WRITE, &writeval);
  7. if( 0 > ret)
  8. {
  9. _ERROR( "write iic ERROR");
  10. }
  11. }
  12. /***** IIC读数据 ********************************************************************************************/
  13. unsigned int IIC_Read(int devAddr, int regAddr)
  14. {
  15. unsigned long readval, ret;
  16. readval = (((devAddr & 0xff) << 24) | ((regAddr & 0xffff) << 8));
  17. ret = ioctl(i2cfd, I2C_READ, &readval);
  18. if( 0 > ret)
  19. {
  20. _ERROR( "read iic ERROR");
  21. }
  22. readval &= 0xff;
  23. return readval;
  24. }


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值