当前位置:   article > 正文

QNX Neutrino I2C驱动框架与代码分析_mmap_device_io

mmap_device_io

本文主要描述QNX I2C Drvier的相关内容,并以Texas Instruments DRA71x Jacinto6 Cortex A15处理器为例讲解

I2C 是经常用到的一种总线协议,它只占用两个IO口资源,分别是SCL时钟信号线与SDA数据线,两根线就能将连接与总线上的设备实现数据通信,由于它的简便的构造设计,于是成为一种较为常用的通信方式。在QNX系统里,也提供了I2C驱动框架

目录结构与组成部分

下面是Texas.Instruments.DRA71x.Jacinto6.Entry.BSP.for.QNX.SDP.6.6包目录,我们只展开跟I2C相关目录内容:

  1. .
  2. ├── Makefile
  3. ├── images
  4. │   ├── Makefile
  5. │   └── mkflashimage.sh
  6. ├── install
  7. ├── manifest
  8. ├── prebuilt
  9. │   ├── armle-v7
  10. │   │   ├── lib
  11. │   │   │   └── dll
  12. │   │   │   └── devu-omap5-xhci.so
  13. │   │   └── usr
  14. │   │   └── lib
  15. │   │   ├── libfs-flash3.a
  16. │   │   ├── libi2c-master.a
  17. │   │   ├── libio-char.a
  18. │   │   ├── libspi-master.a
  19. │   │   ├── libspi-masterS.a
  20. │   │   └── libutil.a
  21. │   └── usr
  22. │   └── include
  23. │   ├── avb.h
  24. │   ├── cam.h
  25. │   ├── fs
  26. │   ├── hw
  27. │   │   └── i2c.h
  28. │   ├── module.h
  29. │   ├── netdrvr
  30. │   └── xpt.h
  31. ├── readme.txt
  32. ├── source.xml
  33. └── src
  34. ├── Makefile
  35. ├── hardware
  36. │   ├── Makefile
  37. │   ├── deva
  38. │   ├── devb
  39. │   ├── devc
  40. │   ├── devi
  41. │   ├── devnp
  42. │   ├── etfs
  43. │   ├── flash
  44. │   ├── i2c
  45. │   │   ├── Makefile
  46. │   │   ├── common.mk
  47. │   │   └── omap35xx
  48. │   │   ├── Makefile
  49. │   │   ├── Usemsg
  50. │   │   ├── Usemsg-omap4
  51. │   │   ├── arm
  52. │   │   ├── bus_recover.c
  53. │   │   ├── bus_speed.c
  54. │   │   ├── clock_toggle.c
  55. │   │   ├── common.mk
  56. │   │   ├── context_restore.c
  57. │   │   ├── context_restore.h
  58. │   │   ├── fini.c
  59. │   │   ├── info.c
  60. │   │   ├── init.c
  61. │   │   ├── lib.c
  62. │   │   ├── module.tmpl
  63. │   │   ├── offsets.h
  64. │   │   ├── options.c
  65. │   │   ├── project.xml
  66. │   │   ├── proto.h
  67. │   │   ├── recv.c
  68. │   │   ├── reg_map_init.c
  69. │   │   ├── reset.c
  70. │   │   ├── send.c
  71. │   │   ├── slave_addr.c
  72. │   │   ├── version.c
  73. │   │   └── wait.c
  74. │   ├── ipl
  75. │   ├── mtouch
  76. │   ├── spi
  77. │   ├── startup
  78. │   └── support
  79. └── utils
  80. ├── Makefile
  81. └── r

I2C框架由以下部分组成:

hardware/i2c/* 硬件接口

  1. ├── hardware
  2. │   ├── i2c
  3. │   │   ├── Makefile
  4. │   │   ├── common.mk
  5. │   │   └── omap35xx
  6. │   │   ├── Makefile
  7. │   │   ├── Usemsg
  8. │   │   ├── Usemsg-omap4
  9. │   │   ├── arm
  10. │   │   ├── bus_recover.c
  11. │   │   ├── bus_speed.c
  12. │   │   ├── clock_toggle.c
  13. │   │   ├── common.mk
  14. │   │   ├── context_restore.c
  15. │   │   ├── context_restore.h
  16. │   │   ├── fini.c
  17. │   │   ├── info.c
  18. │   │   ├── init.c
  19. │   │   ├── lib.c
  20. │   │   ├── module.tmpl
  21. │   │   ├── offsets.h
  22. │   │   ├── options.c
  23. │   │   ├── project.xml
  24. │   │   ├── proto.h
  25. │   │   ├── recv.c
  26. │   │   ├── reg_map_init.c
  27. │   │   ├── reset.c
  28. │   │   ├── send.c
  29. │   │   ├── slave_addr.c
  30. │   │   ├── version.c
  31. │   │   └── wait.c

lib/i2c 资源管理器层

  1. ├── prebuilt
  2. │   ├── armle-v7
  3. │   │   ├── lib
  4. │   │   │   └── dll
  5. │   │   │   └── devu-omap5-xhci.so
  6. │   │   └── usr
  7. │   │   └── lib
  8. │   │   ├── libfs-flash3.a
  9. │   │   ├── libi2c-master.a
  10. │   │   ├── libio-char.a
  11. │   │   ├── libspi-master.a
  12. │   │   ├── libspi-masterS.a
  13. │   │   └── libutil.a

<hw/i2c.h> 定义硬件和应用程序接口的公共头文件

  1. ├── prebuilt
  2. │   ├── armle-v7
  3. │   │   ├── lib
  4. │   │   │   └── dll
  5. │   │   └── usr
  6. │   │   └── lib
  7. │   └── usr
  8. │   └── include
  9. │   ├── hw
  10. │   │   └── i2c.h

2C总线最常见的应用是对从设备寄存器的低带宽、低速率访问,例如:编写音频编解码器、编程一个RTC程序、读取温度传感器数据等等。 通常,总线上只交换几个字节。可以将I2C主机实现为单线程资源管理器或专用应用程序。资源管理器接口的主要优点是:

1、它为应用程序开发人员提供了一个清晰、易于理解的思路。

2、它作为一个中介,在多个应用程序对一个或多个从设备之间进行访问,强制不同I2C接口之间的一致性。

3、对于专用的i2c总线应用程序,硬件访问库更有效;硬件接口定义了这个库的接口,有助于维护和代码可移植性。

QNX I2C驱动提供了基本的硬件操作接口,其接口名称为i2c_master_funcs_t,这些接口是驱动里要求实现,包括读写、设置地址、总线速度、版本信息等等。每个接口对应于上面的一个文件.c,这样便于模块化。

硬件管理接口

  1. typedef struct {
  2. size_t size; /* size of this structure */
  3. int (*version_info)(i2c_libversion_t *version);
  4. void *(*init)(int argc, char *argv[]);
  5. void (*fini)(void *hdl);
  6. i2c_status_t (*send)(void *hdl, void *buf, unsigned int len,
  7. unsigned int stop);
  8. i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len,
  9. unsigned int stop);
  10. int (*abort)(void *hdl, int rcvid);
  11. int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);
  12. int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);
  13. int (*driver_info)(void *hdl, i2c_driver_info_t *info);
  14. int (*ctl)(void *hdl, int cmd, void *msg, int msglen,
  15. int *nbytes, int *info);
  16. int (*bus_reset)(void *hdl);
  17. } i2c_master_funcs_t;

在hardware/i2c/目录中,能找到对应函数的实现。

  1. ├── i2c
  2. │   ├── Makefile
  3. │   ├── common.mk
  4. │   └── omap35xx
  5. │   ├── Makefile
  6. │   ├── Usemsg
  7. │   ├── Usemsg-omap4
  8. │   ├── arm
  9. │   ├── bus_recover.c
  10. │   ├── bus_speed.c # int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);
  11. │   ├── clock_toggle.c
  12. │   ├── common.mk
  13. │   ├── context_restore.c
  14. │   ├── context_restore.h
  15. │   ├── fini.c # void (*fini)(void *hdl);
  16. │   ├── info.c # int (*driver_info)(void *hdl, i2c_driver_info_t *info);
  17. │   ├── init.c # void *(*init)(int argc, char *argv[]);
  18. │   ├── lib.c
  19. │   ├── module.tmpl
  20. │   ├── offsets.h
  21. │   ├── options.c
  22. │   ├── project.xml
  23. │   ├── proto.h
  24. │   ├── recv.c # i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len, unsigned int stop);
  25. │   ├── reg_map_init.c
  26. │   ├── reset.c # int (*bus_reset)(void *hdl);
  27. │   ├── send.c # i2c_status_t (*send)(void *hdl, void *buf, unsigned int len, unsigned int stop);
  28. │   ├── slave_addr.c # int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);
  29. │   ├── version.c # int (*version_info)(i2c_libversion_t *version);
  30. │   └── wait.c

硬件访问接口

这是整个硬件接口函数,提供了10个接口函数,分别对10个接口函数进行介绍:

version_info函数

version_info函数来获取关于库版本的信息。这个函数的原型是:

int (*version_info)(i2c_libversion_t *version);

version参数是指向这个函数必须填充的i2c_libversion_t结构的指针。该结构定义如下:

  1. typedef struct {
  2. unsigned char major;
  3. unsigned char minor;
  4. unsigned char revision;
  5. } i2c_libversion_t;

具体驱动实现:

  1. int omap_version_info(i2c_libversion_t *version)
  2. {
  3. version->major = I2CLIB_VERSION_MAJOR;
  4. version->minor = I2CLIB_VERSION_MINOR;
  5. version->revision = I2CLIB_REVISION;
  6. return 0;
  7. }

init函数

init函数初始化主接口。该函数的原型是:

void *(*init)(int argc, char *argv[]);

参数是在命令行上传递的。函数返回传递给所有其他函数的句柄,如果发生错误则返回NULL。这个函数主要根据命令行解析来进行I2C初始化。

具体驱动实现:

  1. void *omap_init(int argc, char *argv[])
  2. {
  3. omap_dev_t *dev;
  4. uint16_t s;
  5. if (-1 == ThreadCtl(_NTO_TCTL_IO, 0)) {
  6. perror("ThreadCtl");
  7. return NULL;
  8. }
  9. dev = malloc(sizeof(omap_dev_t));
  10. if (!dev)
  11. return NULL;
  12. dev->speed = 100000;
  13. //根据命令 配置I2C相关参数包括基地址 中断号
  14. if (-1 == omap_options(dev, argc, argv)) {
  15. fprintf(stderr, "omap_options: parse I2C option failed\n");
  16. goto fail_free_dev;
  17. }
  18. //I2C 物理地址映射
  19. dev->regbase = mmap_device_io(dev->reglen, dev->physbase);
  20. if (dev->regbase == (uintptr_t)MAP_FAILED) {
  21. perror("mmap_device_io");
  22. goto fail_free_dev;
  23. }
  24. /* Initialize interrupt handler */
  25. if ((dev->chid = ChannelCreate(_NTO_CHF_DISCONNECT | _NTO_CHF_UNBLOCK)) == -1) {
  26. perror("ChannelCreate");
  27. goto fail_unmap_io;
  28. }
  29. if ((dev->coid = ConnectAttach(0, 0, dev->chid, _NTO_SIDE_CHANNEL, 0)) == -1) {
  30. perror("ConnectAttach");
  31. goto fail_chnl_dstr;
  32. }
  33. dev->intrevent.sigev_notify = SIGEV_PULSE;
  34. dev->intrevent.sigev_coid = dev->coid;
  35. dev->intrevent.sigev_code = OMAP_I2C_EVENT;
  36. dev->intrevent.sigev_priority = dev->intr_priority;
  37. /*
  38. * Attach interrupt
  39. */
  40. dev->iid = InterruptAttach(dev->intr, i2c_intr, dev, 0, _NTO_INTR_FLAGS_TRK_MSK);
  41. if (dev->iid == -1) {
  42. perror("InterruptAttachEvent");
  43. goto fail_con_dtch;
  44. }
  45. if (omap_reg_map_init(dev) == -1) {
  46. goto fail_intr_dtch;
  47. }
  48. if (context_restore_init(dev) == -1) {
  49. goto fail_ctxt_rest;
  50. }
  51. if (omap_clock_toggle_init(dev) == -1) {
  52. goto fail_ctxt_rest;
  53. }
  54. // I2C 系统时钟初始化
  55. /* Set up the fifo size - Get total size */
  56. omap_clock_enable(dev);
  57. s = (in16(dev->regbase + OMAP_I2C_BUFSTAT) >> 14) & 0x3;
  58. omap_clock_disable(dev);
  59. dev->fifo_size = 0x8 << s;
  60. /* Set up notification threshold as half the total available size. */
  61. dev->fifo_size >>=1;
  62. if (omap_i2c_reset(dev) == -1) {
  63. fprintf(stderr, "omap_i2c_reset: reset I2C interface failed\n");
  64. goto fail_ctxt_rest;
  65. }
  66. return dev;
  67. //失败退出处理
  68. fail_ctxt_rest:
  69. context_restore_fini(dev);
  70. fail_intr_dtch:
  71. InterruptDetach(dev->iid);
  72. fail_con_dtch:
  73. ConnectDetach(dev->coid);
  74. fail_chnl_dstr:
  75. ChannelDestroy(dev->chid);
  76. fail_unmap_io:
  77. munmap_device_io(dev->regbase, dev->reglen);
  78. fail_free_dev:
  79. free(dev);
  80. return NULL;
  81. }

fini函数

fini函数清理驱动程序并释放与给定句柄关联的所有内存。该函数的原型是:

void (*fini)(void *hdl);

具体源码实现:

  1. void omap_fini(void *hdl)
  2. {
  3. omap_dev_t *dev = hdl;
  4. omap_clock_enable(dev);
  5. out16(dev->regbase + OMAP_I2C_CON, 0);
  6. out16(dev->regbase + OMAP_I2C_IE, 0);
  7. omap_clock_disable(dev);
  8. InterruptDetach(dev->iid); // 释放中断映射
  9. ConnectDetach(dev->coid); // 断开消息传递(Message-passing)Channel连接
  10. ChannelDestroy(dev->chid); // 释放消息传递(Message-passing)Channel
  11. // 时钟控制物理地址映射释放
  12. if (dev->clkctrl_base) {
  13. munmap_device_io (dev->clkctrl_base, 4);
  14. }
  15. if (dev->clkstctrl_base) {
  16. munmap_device_io (dev->clkstctrl_base, 4);
  17. }
  18. context_restore_fini(dev);
  19. // 整个I2C控制器物理地址映射释放
  20. munmap_device_io (dev->regbase, dev->reglen);
  21. free (hdl);
  22. }

send函数

发送函数启动主发送。通信完成时将发送一个可选事件(可以释放数据缓冲区)。如果此函数失败,则未启动任何通信。

该函数的原型是:

  1. i2c_status_t (*send)(void *hdl, void *buf, unsigned int len, unsigned int stop);
  2. /*
  3. * Master send.
  4. * Parameters:
  5. * (in) hdl Handle returned from init() init函数返回的句柄;
  6. * (in) buf Buffer of data to send 指向要发送的数据缓冲区的指针;
  7. * (in) len Length in bytes of buf 要发送的数据的长度(以字节为单位);
  8. * (in) stop If !0, set stop condition when send completes 要发送的数据的长度(以字节为单位);
  9. * Returns:
  10. * bitmask of status bits
  11. 返回状态为:
  12. I2C_STATUS_DONE:传输完成,并且没有错误。
  13. I2C_STATUS_ERROR:传输错误
  14. I2C_STATUS_NACK:没有ACK
  15. I2C_STATUS_ARBL:失去了仲裁。
  16. I2C_STATUS_BUSY:传输超时
  17. I2C_STATUS_ABORT:传输终止
  18. */

具体源码实现:

  1. i2c_status_t omap_send(void *hdl, void *buf, unsigned int len, unsigned int stop)
  2. {
  3. omap_dev_t *dev = hdl;
  4. i2c_status_t ret = I2C_STATUS_ERROR;
  5. int num_bytes;
  6. if (len <= 0)
  7. return I2C_STATUS_DONE;
  8. if (-1 == omap_wait_bus_not_busy(dev, stop))
  9. return I2C_STATUS_BUSY;
  10. omap_clock_enable(dev);
  11. dev->xlen = len;
  12. dev->buf = buf;
  13. dev->status = 0;
  14. dev->intexpected = 1;
  15. /* set slave address */
  16. if (dev->slave_addr_fmt == I2C_ADDRFMT_7BIT)
  17. out16(dev->regbase + OMAP_I2C_CON, in16(dev->regbase + OMAP_I2C_CON) & (~OMAP_I2C_CON_XSA));
  18. else
  19. out16(dev->regbase + OMAP_I2C_CON, in16(dev->regbase + OMAP_I2C_CON) | OMAP_I2C_CON_XSA);
  20. out16(dev->regbase + OMAP_I2C_SA, dev->slave_addr);
  21. /* set data count */
  22. out16(dev->regbase + OMAP_I2C_CNT, len);
  23. /* Clear the FIFO Buffers */
  24. out16(dev->regbase + OMAP_I2C_BUF, in16(dev->regbase + OMAP_I2C_BUF)| OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR);
  25. /* pre-fill the fifo with outgoing data */
  26. if (dev->xlen > dev->fifo_size)
  27. num_bytes = dev->fifo_size;
  28. else
  29. num_bytes = dev->xlen;
  30. while (num_bytes)
  31. {
  32. out8(dev->regbase + OMAP_I2C_DATA, *dev->buf++);
  33. dev->xlen--;
  34. num_bytes--;
  35. }
  36. /* set start condition */
  37. out16(dev->regbase + OMAP_I2C_CON,
  38. OMAP_I2C_CON_EN |
  39. OMAP_I2C_CON_MST |
  40. OMAP_I2C_CON_TRX |
  41. OMAP_I2C_CON_STT |
  42. (stop? OMAP_I2C_CON_STP : 0)|
  43. (in16(dev->regbase + OMAP_I2C_CON)&OMAP_I2C_CON_XA));
  44. ret= omap_wait_status(dev);
  45. omap_clock_disable(dev);
  46. return ret;
  47. }

recv函数

recv函数启动主接收。传输完成时将发送一个可选事件(可以使用数据缓冲区)。如果此函数失败,则未启动任何传输。

该函数的原型是:

  1. i2c_status_t (*recv)(void *hdl, void *buf, unsigned int len, unsigned int stop);
  2. /*
  3. * Master receive.
  4. * Parameters:
  5. * (in) hdl Handle returned from init() init函数返回的句柄;
  6. * (in) buf Buffer for received data 指向要接收的数据缓冲区的指针;
  7. * (in) len Length in bytes of buf 要接收的数据的长度(以字节为单位);
  8. * (in) stop If !0, set stop condition when recv completes 如果这是非零的,函数将在发送完成时设置停止条件;
  9. * Returns:
  10. * bitmask of status bits
  11. 返回状态为:
  12. I2C_STATUS_DONE:传输完成,并且没有错误。
  13. I2C_STATUS_ERROR:传输错误
  14. I2C_STATUS_NACK:没有ACK
  15. I2C_STATUS_ARBL:失去了仲裁。
  16. I2C_STATUS_BUSY:传输超时
  17. I2C_STATUS_ABORT:传输终止
  18. */

具体源码实现:

  1. i2c_status_t omap_recv(void *hdl, void *buf, unsigned int len, unsigned int stop)
  2. {
  3. omap_dev_t *dev = hdl;
  4. i2c_status_t ret;
  5. if (len <= 0)
  6. return I2C_STATUS_DONE;
  7. if (-1 == omap_wait_bus_not_busy(dev, stop))
  8. return I2C_STATUS_BUSY;
  9. dev->xlen = len;
  10. dev->buf = buf;
  11. dev->status = 0;
  12. dev->intexpected = 1;
  13. omap_clock_enable(dev);
  14. /* set slave address */
  15. if (dev->slave_addr_fmt == I2C_ADDRFMT_7BIT)
  16. out16(dev->regbase + OMAP_I2C_CON, in16(dev->regbase + OMAP_I2C_CON) & (~OMAP_I2C_CON_XSA));
  17. else
  18. out16(dev->regbase + OMAP_I2C_CON, in16(dev->regbase + OMAP_I2C_CON) | OMAP_I2C_CON_XSA);
  19. out16(dev->regbase + OMAP_I2C_SA, dev->slave_addr);
  20. /* set data count */
  21. out16(dev->regbase + OMAP_I2C_CNT, len);
  22. /* Clear the FIFO Buffers */
  23. out16(dev->regbase + OMAP_I2C_BUF, in16(dev->regbase + OMAP_I2C_BUF)| OMAP_I2C_BUF_RXFIF_CLR | OMAP_I2C_BUF_TXFIF_CLR);
  24. /* set start condition */
  25. out16(dev->regbase + OMAP_I2C_CON,
  26. OMAP_I2C_CON_EN |
  27. OMAP_I2C_CON_MST |
  28. OMAP_I2C_CON_STT |
  29. (stop? OMAP_I2C_CON_STP : 0) |
  30. (in16(dev->regbase + OMAP_I2C_CON)&OMAP_I2C_CON_XA));
  31. ret= omap_wait_status(dev);
  32. omap_clock_disable(dev);
  33. return ret;
  34. }

abort函数

中止功能迫使主程序释放总线。当停止条件被发送时,它返回。

该函数的原型是:

  1. int (*abort)(void *hdl, int rcvid);
  2. /*
  3. * Force the master to free the bus.
  4. * Returns when the stop condition has been sent.
  5. * Returns:
  6. * 0 success
  7. * -1 failure
  8. */

此处I2C驱动不需要进行abort操作。所以abort函数在resmgr层注册的时候为NULL。

set_slave_addr函数

set_slave_addr函数指定目标从地址。该函数的原型是:

  1. int (*set_slave_addr)(void *hdl, unsigned int addr, i2c_addrfmt_t fmt);
  2. /*
  3. hdl: init函数返回的句柄。
  4. addr: 目标从地址。
  5. fmt:地址的格式; I2C_ADDRFMT_7BIT 和I2C_ADDRFMT_10BIT
  6. */

具体源码实现:

  1. int omap_set_slave_addr(void *hdl, unsigned int addr, i2c_addrfmt_t fmt)
  2. {
  3. omap_dev_t *dev = hdl;
  4. if(fmt != I2C_ADDRFMT_7BIT && fmt != I2C_ADDRFMT_10BIT)
  5. return -1;
  6. dev->slave_addr = addr;
  7. dev->slave_addr_fmt = fmt;
  8. return 0;
  9. }

set_bus_speed函数

set_bus_speed函数指定总线速度。如果请求的总线速度无效,该函数将返回一个失败,并保持总线速度不变。

该函数的原型是:

  1. int (*set_bus_speed)(void *hdl, unsigned int speed, unsigned int *ospeed);
  2. /*
  3. * Specify the bus speed.
  4. * If an invalid bus speed is requested, this function should return
  5. * failure and leave the bus speed unchanged.
  6. * Parameters:
  7. * (in) hdl Handle returned from init() init函数返回的句柄;
  8. * (in) speed Bus speed. Units are implementation-defined. 总线的速度。
  9. * (out) ospeed Actual bus speed (if NULL, this is ignored) NULL或指向函数应该存储实际总线速度的位置的指针。
  10. * Returns:
  11. * 0 success
  12. * -1 failure
  13. */

具体源码实现:

  1. int
  2. omap_set_bus_speed(void *hdl, unsigned int speed, unsigned int *ospeed)
  3. {
  4. omap_dev_t *dev = hdl;
  5. unsigned long iclk;
  6. unsigned scll_plus_sclh;
  7. int scll_delta;
  8. int high_adjust;
  9. int low_adjust;
  10. int scll_to_write;
  11. int sclh_to_write;
  12. /* This driver support bus speed range from 8KHz to 400KHz
  13. * limit the low bus speed to 8KHz to protect SCLL/SCLH from overflow(large than 0xff)
  14. * if speed=8KHz, iclk=4MHz, then SCLL=0xf3, SCLH=0xf5
  15. */
  16. if (speed > 400000 || speed < 8000) {
  17. fprintf(stderr, "i2c-omap35xx: Invalid bus speed(%d)\n", speed);
  18. errno = EINVAL;
  19. return -1;
  20. }
  21. omap_clock_enable(dev);
  22. /* Set the I2C prescaler register to obtain the maximum I2C bit rates
  23. * and the maximum period of the filtered spikes in F/S mode:
  24. * Stander Mode: I2Ci_INTERNAL_CLK = 4 MHz
  25. * Fast Mode: I2Ci_INTERNAL_CLK = 9.6 MHz
  26. */
  27. if (speed <= 100000) {
  28. #ifdef VARIANT_j5
  29. out16(dev->regbase + OMAP_I2C_PSC, 11); // The sysclk is 48 MHz in J5 and 96 MHz in OMAP
  30. #else
  31. out16(dev->regbase + OMAP_I2C_PSC, 23); // I2Ci_INTERNAL_CLK = 4 MHz
  32. #endif
  33. iclk = OMAP_I2C_ICLK;
  34. high_adjust = dev->high_adjust_slow;
  35. low_adjust = dev->low_adjust_slow;
  36. // in standard mode, scll is smaller than (scll_plus_sclh>>1) by 1
  37. scll_delta = -1;
  38. } else {
  39. #ifdef VARIANT_j5
  40. out16(dev->regbase + OMAP_I2C_PSC, 4); // The sysclk is 48 MHz in J5 and 96 MHz in OMAP
  41. #else
  42. out16(dev->regbase + OMAP_I2C_PSC, 9); // I2Ci_INTERNAL_CLK = 9.6 MHz
  43. #endif
  44. iclk = OMAP_I2C_ICLK_9600K;
  45. high_adjust = dev->high_adjust_fast;
  46. low_adjust = dev->low_adjust_fast;
  47. #ifdef VARIANT_omap4
  48. // in fast mode, scll is larger than (scll_plus_sclh>>1) by 1
  49. scll_delta = 1;
  50. #else
  51. // At this point it is unclear whether omap3 also has this relationship
  52. // between SCLL and SCLH in fast mode, so we leave it alone for now to
  53. // avoid breakage.
  54. scll_delta = -1;
  55. #endif
  56. }
  57. /* Set clock based on "speed" bps */
  58. scll_plus_sclh = (iclk/speed - (SCLL_BIAS + SCLH_BIAS));
  59. scll_to_write = (scll_plus_sclh>>1) + scll_delta;
  60. sclh_to_write = scll_plus_sclh - scll_to_write;
  61. scll_to_write += low_adjust;
  62. sclh_to_write += high_adjust;
  63. if (scll_to_write < 0) scll_to_write = 0;
  64. if (sclh_to_write <= 2) sclh_to_write = 3;
  65. out16(dev->regbase + OMAP_I2C_SCLL, scll_to_write);
  66. out16(dev->regbase + OMAP_I2C_SCLH, sclh_to_write);
  67. dev->speed = iclk / (scll_to_write + SCLL_BIAS + sclh_to_write + SCLH_BIAS);
  68. if (ospeed)
  69. *ospeed = dev->speed;
  70. omap_clock_disable(dev);
  71. return 0;
  72. }

driver_info函数

driver_info函数返回有关驱动程序的信息。该函数的原型是:

  1. int (*driver_info)(void *hdl, i2c_driver_info_t *info);
  2. /*
  3. hdl init函数返回的句柄
  4. info
  5. 一个指向i2c_driver_info_t结构的指针,函数应该在该结构中存储信息:
  6. typedef struct {
  7. uint32_t speed_mode;
  8. uint32_t addr_mode;
  9. uint32_t reserved[2];
  10. } i2c_driver_info_t;
  11. */

具体源码实现:

  1. int omap_driver_info(void *hdl, i2c_driver_info_t *info)
  2. {
  3. info->speed_mode = I2C_SPEED_STANDARD | I2C_SPEED_FAST;
  4. info->addr_mode = I2C_ADDRFMT_7BIT | I2C_ADDRFMT_10BIT;
  5. return 0;
  6. }

ctl函数

ctl函数处理一个驱动程序特定的devctl()命令。该函数的原型是:

  1. int (*ctl)(void *hdl, int cmd, void *msg, int msglen, int *nbytes, int *info);
  2. /*
  3. * Handle a driver-specific devctl().
  4. * Parameters:
  5. * (in) hdl Handle returned from init() init函数返回的句柄;
  6. * (in) cmd Device command 设备命令;
  7. * (i/o) msg Message buffer 一个指向消息缓冲区的指针。该函数可以更改缓冲区的内容;
  8. * (in) msglen Length of message buffer in bytes 消息缓冲区的长度,以字节为单位;
  9. * (out) nbytes Bytes to return (<= msglen) 消息缓冲区的长度,以字节为单位;
  10. * (out) info Extra status information returned by devctl 指向函数可以存储devctl()返回状态信息的位置指针;
  11. * Returns:
  12. * EOK success
  13. * errno failure
  14. */

此处I2C驱动不需要进行devctrl操作。所以ctl函数在resmgr层注册的时候为NULL。

共享库的接口

当I2C主设备专用于单个应用程序使用时,可以将硬件接口代码编译为库并将其链接到应用程序。

为了提高代码的可移植性,应用程序应该调用i2c_master_getfuncs()来访问特定于硬件的函数。通过功能表访问硬件库,应用程序更容易加载和管理多个硬件库。

资源管理器接口以类似的方式使用硬件库。

资源管理器设计

资源管理器层实现为静态链接到硬件库的库,提供了一个单线程管理器,libi2c-master。

在启动时,resmgr层执行以下操作:

i2c_master_getfuncs(&masterf) masterf.init() masterf.set_bus_speed() 然后,资源管理器让自己在后台运行,下面是资源管理器处理这些devctl()命令:

DCMD_I2C_DRIVER_INFO calls masterf.driver_info(). DCMD_I2C_SET_BUS_SPEED and DCMD_I2C_SET_SLAVE_ADDR only update the state of the current connection. DCMD_I2C_SEND, DCMD_I2C_RECV, DCMD_I2C_SENDRECV, DCMD_I2C_MASTER_SEND, and DCMD_I2C_MASTER_RECV

  1. if (bus_speed has changed)
  2. masterf.set_bus_speed()
  3. masterf.set_slave_address()
  4. masterf.send() or masterf.recv()

资源管理器线程一直占用,直到传输完成并回复客户端,可以通过向资源管理器发送SIGTERM来终止它。

实质和下面流程一样。在初始化里设置一个中断事件,这个事件将发送消息告诉主循环接收和发送数据。

  1. #include <hw/i2c.h>
  2. i2c_master_funcs_t masterf;
  3. i2c_libversion_t version;
  4. i2c_status_t status; void *hdl;
  5. i2c_master_getfuncs(&masterf, sizeof(masterf));//初始化硬件接口 masterf.version_info(&version);
  6. if ((version.major != I2CLIB_VERSION_MAJOR) || (version.minor > I2CLIB_VERSION_MINOR))
  7. {
  8. /* error */
  9. ...
  10. }
  11. hdl = masterf.init(...); //调用初始化函数
  12. masterf.set_bus_speed(hdl, ...); //设置总线速度
  13. while(1)
  14. {
  15. InterruptWait (NULL, NULL);
  16. masterf.set_slave_addr(hdl, ...); //设置从地址
  17. status = masterf.send(hdl, ...);//发送数据
  18. if (status != I2C_STATUS_DONE) { /* error */
  19. if (!(status & I2C_STATUS_DONE))
  20. masterf.abort(hdl); }
  21. status = masterf.recv(hdl, ...);//接收数据
  22. if (status != I2C_STATUS_DONE)
  23. { /* error */ ... }
  24. }
  25. masterf.fini(hdl);//完成I2C传输后,清理驱动程序并释放与给定句柄关联的所有内存
  26. InterruptUnmask(INTNUM, id);
  27. }
  28. masterf.fini(hdl);//完成I2C传输后,清理驱动程序并释放与给定句柄关联的所有内存

参考文献:

QNX----I2C驱动框架

QNX---IMX6UL I2C 驱动分析

Technical Notes I2C (Inter-Integrated Circuit) Framework(qnx.com)

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号