OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5220|回复: 1
打印 上一主题 下一主题

【正点原子Linux连载】第二十六章I2C实验--摘自【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.0

[复制链接]

1163

主题

1175

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
5017
金钱
5017
注册时间
2019-5-8
在线时间
1262 小时
跳转到指定楼层
楼主
发表于 2019-12-18 12:32:47 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 正点原子01 于 2019-12-18 12:01 编辑

1)实验平台:正点原子阿尔法Linux开发板
2)平台购买地址::https://item.taobao.com/item.htm?id=603672744434
2)全套实验源码+手册+视频下载地址:http://www.openedv.com/thread-300792-1-1.html
3)本章实例源码下载: I2C实验.zip (2.96 MB, 下载次数: 16)
4)对正点原子Linux感兴趣的同学可以加群讨论:935446741
5)关注正点原子公众号,获取最新资料

第二十六章I2C实验


       I2C是最常用的通信接口,众多的传感器都会提供I2C接口来和主控相连,比如陀螺仪、加速度计、触摸屏等等。所以I2C是做嵌入式开发必须掌握的,I.MX6U有4个I2C接口,可以通过这4个I2C接口来连接一些I2C外设。I.MX6U-ALPHA使用I2C1接口连接了一个距离传感器AP3216C,本章我们就来学习如何使用I.MX6U的I2C接口来驱动AP3216C,读取AP3216C的传感器数据。


26.1 I2C & AP3216C简介26.1.1 I2C简介   
       I2C是很常见的一种总线协议,I2C是NXP公司设计的,I2C使用两条线在主控制器和从机之间进行数据通信。一条是SCL(串行时钟线),另外一条是SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候SCL和SDA处于高电平。I2C总线标准模式下速度可以达到100Kb/S,快速模式下可以达到400Kb/S。I2C总线工作是按照一定的协议来运行的,接下来就看一下I2C协议。
       I2C是支持多从机的,也就是一个I2C控制器下可以挂多个I2C从设备,这些不同的I2C从设备有不同的器件地址,这样I2C主控制器就可以通过I2C设备的器件地址访问指定的I2C设备了,一个I2C总线连接多个I2C设备如图26.1.1.1所示:
图26.1.1.1 I2C多个设备连接结构图
       图26.1.1.1中SDA和SCL这两根线必须要接一个上拉电阻,一般是4.7K。其余的I2C从器件都挂接到SDA和SCL这两根线上,这样就可以通过SDA和SCL这两根线来访问多个I2C设备。
       接下来看一下I2C协议有关的术语:
       1、起始位
       顾名思义,也就是I2C通信起始标志,通过这个起始位就可以告诉I2C从机,“我”要开始进行I2C通信了。在SCL为高电平的时候,SDA出现下降沿就表示为起始位,如图26.1.1.2所示:
图26.1.1.2 I2C通信起始位
       2、停止位
       停止位就是停止I2C通信的标志位,和起始位的功能相反。在SCL位高电平的时候,SDA出现上升沿就表示为停止位,如图26.1.1.3所示:
图26.1.1.3 I2C通信停止位
       3、数据传输
       I2C总线在数据传输的时候要保证在SCL高电平期间,SDA上的数据稳定,因此SDA上的数据变化只能在SCL低电平期间发生,如图26.1.1.4所示:
图26.1.1.4 I2C数据传输
       4、应答信号
       当I2C主机发送完8位数据以后会将SDA设置为输入状态,等待I2C从机应答,也就是等到I2C从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完8位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将SDA拉低来表示发出应答信号,表示通信成功,否则表示通信失败。
       5、I2C写时序
       主机通过I2C总线与从机之间进行通信不外乎两个操作:写和读,I2C总线单字节写时序如图26.1.1.5所示:
图26.1.1.5 I2C写时序
       图26.1.1.5就是I2C写时序,我们来看一下写时序的具体步骤:
       1)、开始信号。
       2)、发送I2C设备地址,每个I2C器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个I2C器件。这是一个8位的数据,其中高7位是设备地址,最后1位是读写位,为1的话表示这是一个读操作,为0的话表示这是一个写操作。
       3)、I2C器件地址后面跟着一个读写位,为0表示写操作,为1表示读操作。
       4)、从机发送的ACK应答信号。
       5)、重新发送开始信号。
       6)、发送要写写入数据的寄存器地址。
       7)、从机发送的ACK应答信号。
       8)、发送要写入寄存器的数据。
       9)、从机发送的ACK应答信号。
       10)、停止信号。
       6、I2C读时序
       I2C总线单字节读时序如图26.1.1.6所示:
图26.1.1.6 I2C单字节读时序
       I2C单字节读时序比写时序要复杂一点,读时序分为4大步,第一步是发送设备地址,第二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是I2C从器件输出要读取的寄存器值,我们具体来看一下这步。
       1)、主机发送起始信号。
       2)、主机发送要读取的I2C从设备地址。
       3)、读写控制位,因为是向I2C从设备发送数据,因此是写信号。
       4)、从机发送的ACK应答信号。
       5)、重新发送START信号。
       6)、主机发送要读取的寄存器地址。
       7)、从机发送的ACK应答信号。
       8)、重新发送START信号。
       9)、重新发送要读取的I2C从设备地址。
       10)、读写控制位,这里是读信号,表示接下来是从I2C从设备里面读取数据。
       11)、从机发送的ACK应答信号。
       12)、从I2C器件里面读取到的数据。
       13)、主机发出NO ACK信号,表示读取完成,不需要从机再发送ACK信号了。
       14)、主机发出STOP信号,停止I2C通信。
       7、I2C多字节读写时序
       又时候我们需要读写多个字节,多字节读写时序和单字节的基本一致,只是在读写数据的时候可以连续发送多个自己的数据,其他的控制时序都是和单字节一样的。
26.1.2 I.MX6U I2C简介
       I.MX6U提供了4个I2C外设,通过这四个I2C外设即可完成与I2C从器件进行通信,I.MX6U的I2C外设特性如下:
       ①、与标准I2C总线兼容。
       ②、多主机运行
       ③、软件可编程的64中不同的串行时钟序列。
       ④、软件可选择的应答位。
       ⑤、开始/结束信号生成和检测。
       ⑥、重复开始信号生成。
       ⑦、确认位生成。
       ⑧、总线忙检测   
       I.MX6U的I2C支持两种模式:标准模式和快速模式,标准模式下I2C数据传输速率最高是100Kbits/s,在快速模式下数据传输速率最高为400Kbits/s。
       我们接下来看一下I2C的几个重要的寄存器,首先看一下I2Cx_IADR(x=1~4)寄存器,这是I2C的地址寄存器,此寄存器结构如图26.1.2.1所示:
26.1.2.1寄存器I2Cx_IADR结构
       寄存器I2Cx_IADR只有ADR(bit7:1)位有效,用来保存I2C从设备地址数据。当我们要访问某个I2C从设备的时候就需要将其设备地址写入到ADR里面。接下来看一下寄存器I2Cx_IFDR,这个是I2C的分频寄存器,寄存器结构如图26.1.2.2所示:
26.1.2.2寄存器I2Cx_IFDR结构
       寄存器I2Cx_IFDR也只有IC(bit5:0)这个位,用来设置I2C的波特率,I2C的时钟源可以选择IPG_CLK_ROOT=66MHz,通过设置IC位既可以得到想要的I2C波特率。IC位可选的设置如图26.1.2.3所示:
26.1.2.3 IC设置
       不像其他外设的分频设置一样可以随意设置,图26.1.2.3中列出了IC的所有可选值。比如现在I2C的时钟源为66MHz,我们要设置I2C的波特率为100KHz,那么IC就可以设置为0X15,也就是640分频。66000000/640=103.125KHz100KHz
       接下来看一下寄存器I2Cx_I2CR,这个是I2C控制寄存器,此寄存器结构如图26.1.2.4所示:
26.1.2.4寄存器I2Cx_I2CR结构
       寄存器I2Cx_I2CR的各位含义如下:
       IEN(bit7)I2C使能位,为1的时候使能I2C,为0的时候关闭I2C
       IIEN(bit6)I2C中断使能位,为1的时候使能I2C中断,为0的时候关闭I2C中断。
       MSTA(bit5):主从模式选择位,设置IIC工作在主模式还是从模式,为1的时候工作在主模式,为0的时候工作在从模式。
       MTX(bit4):传输方向选择位,用来设置是进行发送还是接收,为0的时候是接收,为1的时候是发送。
       TXAK(bit3):传输应答位使能,为0的话发送ACK信号,为1的话发送NO ACK信号。
       RSTA(bit2):重复开始信号,为1的话产生一个重新开始信号。
       接下来看一下寄存器I2Cx_I2SR,这个是I2C的状态寄存器,寄存器结构如图26.1.2.5所示:
26.1.2.5寄存器I2Cx_I2SR结构
       寄存器I2Cx_I2SR的各位含义如下:
       ICF(bit7):数据传输状态位,为0的时候表示数据正在传输,为1的时候表示数据传输完成。
       IAAS(bit6):当为1的时候表示I2C地址,也就是I2Cx_IADR寄存器中的地址是从设备地址。
       IBB(bit5)I2C总线忙标志位,当为0的时候表示I2C总线空闲,为1的时候表示I2C总线忙。
       IAL(bit4):仲裁丢失位,为1的时候表示发生仲裁丢失。
       SRW(bit3):从机读写状态位,当I2C作为从机的时候使用,此位用来表明主机发送给从机的是读还是写命令。为0的时候表示主机要向从机写数据,为1的时候表示主机要从从机读取数据。
       IIF(bit1)I2C中断挂起标志位,当为1的时候表示有中断挂起,此位需要软件清零。
       RXAK(bit0)应答信号标志位,为0的时候表示接收到ACK应答信号,为1的话表示检测到NO ACK信号。
       最后一个寄存器就是I2Cx_I2DR,这是I2C的数据寄存器,此寄存器只有低8位有效,当要发送数据的时候将要发送的数据写入到此寄存器,如果要接收数据的话直接读取此寄存器即可得到接收到的数据。
关于I2C的寄存器就介绍到这里,关于这些寄存器详细的描述,请参考《I.MX6ULL参考手册》第1462页的31.7小节。
26.1.3 AP3216C简介
       I.MX6U-ALPHA开发板上通过I2C1连接了一个三合一环境传感器:AP3216C,AP3216C是由敦南可以推出的一款传感器,其支持环境光强度(ALS)、接近距离(PS)和红外线强度(IR)这三个环境参数检测。该芯片可以通过IIC接口与主控制相连,并且支持中断,AP3216C的特点如下:
       ①、I2C接口,快速模式下波特率可以到400Kbit/S
       ②、多种工作模式选择:ALS、PS+IR、ALS+PS+IR、PD等等。
       ③、内建温度补偿电路。
       ④、宽工作温度范围(-30°C~+80°C)。
       ⑤、超小封装,4.1mm x 2.4mm x 1.35mm
       ⑥、环境光传感器具有16为分辨率。
       ⑦、接近传感器和红外传感器具有10为分辨率。
       AP3216C常被用于手机、平板、导航设备等,其内置的接近传感器可以用于检测是否有物体接近,比如手机上用来检测耳朵是否接触听筒,如果检测到的话就表示正在打电话,手机就会关闭手机屏幕以省电。也可以使用环境光传感器检测光照强度,可以实现自动背光亮度调节。
AP3216C结构如图26.1.3.1所示:
图26.1.3.1AP3216C结构图
       AP3216的设备地址为0X1E,同几乎所有的I2C从器件一样,AP3216C内部也有一些寄存器,通过这些寄存器我们可以配置AP3216C的工作模式,并且读取相应的数据。AP3216C我们用的寄存器如表26.1.3.1所示:
表26.1.3.1本章使用的AP3216C寄存器表
       在表26.1.3.1中,0X00这个寄存器是模式控制寄存器,用来设置AP3216C的工作模式,一般开始先将其设置为0X04,也就是先软件复位一次AP3216C。接下来根据实际使用情况选择合适的工作模式,比如设置为0X03,也就是开启ALS+PS+IR。从0X0A~0X0F6个寄存器就是数据寄存器,保存着ALSPSIR这三个传感器获取到的数据值。如果同时打开ALSPSIR的读取间隔最少要112.5ms,因为AP3216C完成一次转换需要112.5ms。关于AP3216C的介绍就到这里,如果要想详细的研究此芯片的话,请大家自行查阅其数据手册。
       本章实验中我们通过I.MX6UI2C1来读取AP3216C内部的ALSPSIR这三个传感器的值,并且在LCD上显示。开机会先检测AP3216C是否存在,一般的芯片是有个ID寄存器,通过读取ID寄存器判断ID是否正确就可以检测芯片是否存在。但是AP3216C没有ID寄存器,所以我们就通过向寄存器0X00写入一个值,然后再读取0X00寄存器,判断读出得到值和写入的是否相等,如果相等就表示AP3216C存在,否则的话AP3216C就不存在。本章的配置步骤如下:
       1、初始化相应的IO
       初始化I2C1相应的IO,设置其复用功能,如果要使用AP3216C中断功能的话,还需要设置AP3216C的中断IO
       2、初始化I2C1
       初始化I2C1接口,设置波特率。
       3、初始化AP3216C
       初始化AP3216C,读取AP3216C的数据。
26.2硬件原理分析
       本试验用到的资源如下:
、指示灯LED0。
、 RGB LCD屏幕。
③、AP3216C
④、串口
       AP3216C是在I.MX6U-ALPHA开发板底板上,原理图如图26.2.1所示:

26.2.1 AP3216C原理图
       从图26.2.1可以看出AP3216C使用的是I2C1,其中I2C1_SCL使用的UART4_RXD这个IOI2C1_SDA使用的是UART4_TXD这个IO
26.3实验程序编写
本实验对应的例程路径为:开发板光盘-> 1、裸机例程->17_i2c。
       本章实验在上一章例程的基础上完成,更改工程名字为“ap3216c”,然后在bsp文件夹下创建名为“i2c”和“ap3216c”的文件。在bsp/i2c中新建bsp_i2c.c和bsp_i2c.h这两个文件,在bsp/ap3216c中新建bsp_ap3216c.c和bsp_ap3216c.h这两个文件。bsp_i2c.c和bsp_i2c.h是I.MX6U的I2C文件,bsp_ap3216c.c和bsp_ap3216c.h是AP3216C的驱动文件。在bsp_i2c.h中输入如下内容:
示例代码26.3.1 bsp_i2c.h文件代码
  1.   #ifndef _BSP_I2C_H
  2.   #define _BSP_I2C_H
  3. /***************************************************************
  4.   Copyright ? zuozhongkai Co., Ltd. 1998-2019.All rights reserved.
  5. 文件名    : bsp_i2c.h
  6. 作者      : 左忠凯
  7. 版本      : V1.0
  8. 描述      : IIC驱动文件。
  9. 其他      : 无
  10. 论坛      :www.openedv.com
  11. 日志      : 初版V1.02019/1/15 左忠凯创建
  12. ***************************************************************/
  13. #include "imx6ul.h"

  14. /* 相关宏定义 */
  15. #defineI2C_STATUS_OK                  (0)
  16. #defineI2C_STATUS_BUSY                (1)
  17. #defineI2C_STATUS_IDLE                (2)
  18. #defineI2C_STATUS_NAK                 (3)
  19. #defineI2C_STATUS_ARBITRATIONLOST     (4)
  20. #defineI2C_STATUS_TIMEOUT             (5)
  21. #defineI2C_STATUS_ADDRNAK             (6)

  22. /*
  23.   * I2C方向枚举类型
  24.   */
  25. enum i2c_direction
  26. {
  27.      kI2C_Write =0x0,             /* 主机向从机写数据  */
  28.      kI2C_Read =0x1,           /* 主机从从机读数据  */
  29. };

  30. /*
  31.   * 主机传输结构体
  32.   */
  33. struct i2c_transfer
  34. {
  35. unsignedchar slaveAddress;    /* 7位从机地址  */
  36. enum i2c_direction direction; /* 传输方向 */
  37. unsignedint subaddress;   /* 寄存器地址   */
  38. unsignedchar subaddressSize;  /* 寄存器地址长度   */
  39. unsignedchar*volatile data;   /* 数据缓冲区   */
  40. volatileunsignedint dataSize; /* 数据缓冲区长度   */
  41. };

  42. /*
  43.   *函数声明
  44.   */
  45. void i2c_init(I2C_Type *base);
  46. unsignedchar i2c_master_start(I2C_Type *base,
  47. unsignedchar address,
  48. enum i2c_direction direction);
  49. unsignedchar i2c_master_repeated_start(I2C_Type *base,
  50. unsignedchar address,
  51. enum i2c_direction direction);
  52. unsignedchar i2c_check_and_clear_error(I2C_Type *base,
  53. unsignedint status);
  54. unsignedchar i2c_master_stop(I2C_Type *base);
  55. void i2c_master_write(I2C_Type *base,constunsignedchar*buf,
  56. unsignedint size);
  57. void i2c_master_read(I2C_Type *base,unsignedchar*buf,
  58. unsignedint size);
  59. unsignedchar i2c_master_transfer(I2C_Type *base,
  60. struct i2c_transfer *xfer);

  61. #endif
复制代码
       第16到22行定义了一些I2C状态相关的宏。第27到31行定义了一个枚举类型i2c_direction,此枚举类型用来表示I2C主机对从机的操作,也就是读数据还是写数据。第36到44行定义了一个结构体i2c_transfer,此结构体用于I2C的数据传输。剩下的就是一些函数声明了,总体来说bsp_i2c.h文件里面的内容还是很简单的。接下来在文件bsp_i2c.c里面输入如下内容:
示例代码26.3.2 bsp_i2c.c文件代码
  1. /***************************************************************
  2. Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rightsreserved.
  3. 文件名   : bsp_i2c.c
  4. 作者     : 左忠凯
  5. 版本     : V1.0
  6. 描述     : IIC驱动文件。
  7. 其他     : 无
  8. 论坛     : www.openedv.com
  9. 日志     : 初版V1.0 2019/1/15 左忠凯创建
  10. ***************************************************************/
  11.    #include "bsp_i2c.h"
  12.    #include "bsp_delay.h"
  13.    #include "stdio.h"

  14. /*
  15.     * @description   : 初始化I2C,波特率100KHZ
  16.     * @param – base  : 要初始化的IIC设置
  17.     * @return          :无
  18.     */
  19. void i2c_init(I2C_Type *base)
  20. {
  21. /* 1、配置I2C */
  22.       base->I2CR &=~(1<<7);/* 要访问I2C的寄存器,首先需要先关闭I2C */

  23. /* 设置波特率为100K
  24.        * I2C的时钟源来源于IPG_CLK_ROOT=66Mhz
  25.        * IFDR设置为0X15,也就是640分频,
  26.        * 66000000/640=103.125KHz≈100KHz。
  27.        */
  28.       base->IFDR =0X15<<0;

  29. /* 设置寄存器I2CR,开启I2C */
  30.       base->I2CR |=(1<<7);
  31. }

  32. /*
  33.    * @description            :发送重新开始信号
  34.    * @param - base          :要使用的IIC
  35.    * @param - addrss        : 设备地址
  36.    * @param - direction     : 方向
  37.    * @return               :0 正常其他值出错
  38.    */
  39. unsignedchar i2c_master_repeated_start(I2C_Type *base,
  40. unsignedchar address,
  41. enum i2c_direction direction)
  42. {
  43. /* I2C忙并且工作在从模式,跳出 */
  44. if(base->I2SR &(1<<5)&&(((base->I2CR)&(1<<5))==0))
  45. return1;

  46. /*
  47.        * 设置寄存器I2CR
  48.        * bit[4]: 1 发送
  49.        * bit[2]: 1 产生重新开始信号
  50.        */
  51.       base->I2CR |=(1<<4)|(1<<2);

  52. /*
  53.        * 设置寄存器I2DR,bit[7:0] : 要发送的数据,这里写入从设备地址
  54.        */
  55.       base->I2DR =((unsignedint)address <<1)|
  56. ((direction == kI2C_Read)?1:0);
  57. return0;
  58. }

  59. /*
  60.    * @description            :发送开始信号
  61.    * @param - base           :要使用的IIC
  62.    * @param - addrss        : 设备地址
  63.    * @param - direction     : 方向
  64.    * @return               :0 正常其他值出错
  65.    */
  66. unsignedchar i2c_master_start(I2C_Type *base,
  67. unsignedchar address,
  68. enum i2c_direction direction)
  69. {
  70. if(base->I2SR &(1<<5))/* I2C忙 */
  71. return1;

  72. /*
  73.        * 设置寄存器I2CR
  74.        * bit[5]: 1 主模式
  75.        * bit[4]: 1 发送
  76.        */
  77.       base->I2CR |=(1<<5)|(1<<4);

  78. /*
  79.        * 设置寄存器I2DR,bit[7:0] : 要发送的数据,这里写入从设备地址
  80.        */
  81.       base->I2DR =((unsignedint)address <<1)|
  82. ((direction == kI2C_Read)?1:0);
  83. return0;
  84. }

  85. /*
  86.    * @description        : 检查并清除错误
  87.    * @param - base      : 要使用的IIC
  88.    * @param - status    : 状态
  89.    * @return           :状态结果
  90.    */
  91. unsignedchar i2c_check_and_clear_error(I2C_Type *base,
  92. unsignedint status)
  93. {
  94. if(status &(1<<4))            /* 检查是否发生仲裁丢失错误  */
  95. {
  96.           base->I2SR &=~(1<<4); /* 清除仲裁丢失错误位    */
  97.           base->I2CR &=~(1<<7); /* 先关闭I2C                 */
  98.           base->I2CR |=(1<<7);  /* 重新打开I2C               */
  99. return I2C_STATUS_ARBITRATIONLOST;
  100. }
  101. elseif(status &(1<<0)) /* 没有接收到从机的应答信号  */
  102. {
  103. return I2C_STATUS_NAK; /* 返回NAK(No acknowledge) */
  104. }
  105. return I2C_STATUS_OK;
  106. }

  107. /*
  108.   * @description       : 停止信号
  109.   * @param - base      : 要使用的IIC
  110.   * @param            :无
  111.   * @return              :状态结果
  112.   */
  113. unsignedchar i2c_master_stop(I2C_Type *base)
  114. {
  115. unsignedshort timeout =0XFFFF;

  116. /* 清除I2CR的bit[5:3]这三位 */
  117.      base->I2CR &=~((1<<5)|(1<<4)|(1<<3));
  118. while((base->I2SR &(1<<5)))  /* 等待忙结束   */
  119. {
  120.          timeout--;
  121. if(timeout ==0)          /* 超时跳出     */
  122. return I2C_STATUS_TIMEOUT;
  123. }
  124. return I2C_STATUS_OK;
  125. }

  126. /*
  127.   * @description       : 发送数据
  128.   * @param - base      : 要使用的IIC
  129.   * @param - buf       : 要发送的数据
  130.   * @param - size      : 要发送的数据大小
  131.   * @param - flags     : 标志
  132.   * @return              :无
  133.   */
  134. void i2c_master_write(I2C_Type *base,constunsignedchar*buf,
  135. unsignedint size)
  136. {
  137. while(!(base->I2SR &(1<<7)));       /* 等待传输完成  */
  138.      base->I2SR &=~(1<<1);           /* 清除标志位   */
  139.      base->I2CR |=1<<4;              /* 发送数据     */
  140. while(size--)
  141. {
  142.          base->I2DR =*buf++;/* 将buf中的数据写入到I2DR寄存器 */
  143. while(!(base->I2SR &(1<<1)));   /* 等待传输完成  */
  144.          base->I2SR &=~(1<<1); /* 清除标志位   */

  145. /* 检查ACK */
  146. if(i2c_check_and_clear_error(base, base->I2SR))
  147. break;
  148. }
  149.      base->I2SR &=~(1<<1);
  150.      i2c_master_stop(base);                 /* 发送停止信号  */
  151. }

  152. /*
  153.   * @description       : 读取数据
  154.   * @param - base      : 要使用的IIC
  155.   * @param - buf       : 读取到数据
  156.   * @param - size      : 要读取的数据大小
  157.   * @return              : 无
  158.   */
  159. void i2c_master_read(I2C_Type *base,unsignedchar*buf,
  160. unsignedint size)
  161. {
  162. volatileuint8_t dummy =0;

  163.      dummy++;                        /* 防止编译报错     */
  164. while(!(base->I2SR &(1<<7)));   /* 等待传输完成     */
  165.      base->I2SR &=~(1<<1); /* 清除中断挂起位   */
  166.      base->I2CR &=~((1<<4)|(1<<3));/* 接收数据   */
  167. if(size ==1)   /* 如果只接收一个字节数据的话发送NACK信号 */
  168.          base->I2CR |=(1<<3);

  169.      dummy = base->I2DR;                /* 假读        */
  170. while(size--)
  171. {
  172. while(!(base->I2SR &(1<<1)));   /* 等待传输完成  */
  173.          base->I2SR &=~(1<<1); /* 清除标志位   */

  174. if(size ==0)
  175.              i2c_master_stop(base); /* 发送停止信号  */
  176. if(size ==1)
  177.              base->I2CR |=(1<<3);
  178. *buf++= base->I2DR;
  179. }
  180. }

  181. /*
  182.   * @description   : I2C数据传输,包括读和写
  183.   * @param – base  : 要使用的IIC
  184.   * @param – xfer  : 传输结构体
  185.   * @return        : 传输结果,0 成功,其他值失败;
  186.   */
  187. unsignedchar i2c_master_transfer(I2C_Type *base,
  188. struct i2c_transfer *xfer)
  189. {
  190. unsignedchar ret =0;
  191. enum i2c_direction direction = xfer->direction;

  192.      base->I2SR &=~((1<<1)|(1<<4));      /* 清除标志位   */
  193. while(!((base->I2SR >>7)&0X1)){};   /* 等待传输完成  */
  194. /* 如果是读的话,要先发送寄存器地址,所以要先将方向改为写 */
  195. if((xfer->subaddressSize >0)&&(xfer->direction ==
  196. kI2C_Read))
  197.          direction = kI2C_Write;
  198.      ret = i2c_master_start(base, xfer->slaveAddress, direction);
  199. if(ret)
  200. return ret;
  201. while(!(base->I2SR &(1<<1))){}; /* 等待传输完成 */
  202.      ret = i2c_check_and_clear_error(base, base->I2SR);
  203. if(ret)
  204. {
  205.          i2c_master_stop(base);       /* 发送出错,发送停止信号 */
  206. return ret;
  207. }

  208. /* 发送寄存器地址 */
  209. if(xfer->subaddressSize)
  210. {
  211. do
  212. {
  213.              base->I2SR &=~(1<<1); /* 清除标志位   */
  214.              xfer->subaddressSize--;   /* 地址长度减一  */
  215.              base->I2DR =((xfer->subaddress)>>(8*
  216. xfer->subaddressSize));
  217. while(!(base->I2SR &(1<<1)));   /* 等待传输完成 */
  218. /* 检查是否有错误发生 */
  219.              ret = i2c_check_and_clear_error(base, base->I2SR);
  220. if(ret)
  221. {
  222.                  i2c_master_stop(base);    /* 发送停止信号 */
  223. return ret;
  224. }
  225. }while((xfer->subaddressSize >0)&&(ret ==
  226. I2C_STATUS_OK));

  227. if(xfer->direction == kI2C_Read)    /* 读取数据      */
  228. {
  229.              base->I2SR &=~(1<<1); /* 清除中断挂起位 */
  230.              i2c_master_repeated_start(base, xfer->slaveAddress,
  231. kI2C_Read);
  232. while(!(base->I2SR &(1<<1))){}; /* 等待传输完成  */

  233. /* 检查是否有错误发生 */
  234.              ret = i2c_check_and_clear_error(base, base->I2SR);
  235. if(ret)
  236. {
  237.                  ret = I2C_STATUS_ADDRNAK;
  238.                  i2c_master_stop(base);    /* 发送停止信号 */
  239. return ret;
  240. }
  241. }
  242. }

  243. /* 发送数据 */
  244. if((xfer->direction == kI2C_Write)&&(xfer->dataSize >0))
  245.          i2c_master_write(base, xfer->data, xfer->dataSize);
  246. /* 读取数据 */
  247. if((xfer->direction == kI2C_Read)&&(xfer->dataSize >0))
  248.          i2c_master_read(base, xfer->data, xfer->dataSize);
  249. return0;
  250. }
复制代码
    文件bsp_i2c.c中一共有8个函数,我们依次来看一下这些函数的功能,首先是函数i2c_init,此函数用来初始化I2C,重点是设置I2C的波特率,初始化完成以后开启I2C。第2个函数是i2c_master_repeated_start,此函数用来发送一个重复开始信号,发送开始信号的时候也会顺带发送从设备地址。第3个函数是i2c_master_start,此函数用于发送一个开始信号,发送开始信号的时候也顺带发送从设备地址。第4个函数是i2c_check_and_clear_error,此函数用于检查并清除错误。第5个函数是i2c_master_stop,用于产生一个停止信号。第6和第7个函数分别为i2c_master_write和i2c_master_read,这两个函数分别用于完成向I2C从设备写数据和从I2C从设备读数据。最后一个函数是i2c_master_transfer,此函数就是用户最终调用的,用于完成I2C通信的函数,此函数会使用前面的函数拼凑出I2C读/写时序。此函数就是按照26.1.1小节讲解的I2C读写时序来编写的。
       I2C的操作函数已经准备好了,接下来就是使用前面编写I2C操作函数来配置AP3216C了,配置完成以后就可以读取AP3216C里面的传感器数据,在bsp_ap3216c.h输入如下所示内容:
示例代码26.3.3 bsp_ap3216c.h文件代码
  1.   #ifndef _BSP_AP3216C_H
  2.   #define _BSP_AP3216C_H
  3. /***************************************************************
  4.   Copyright ? zuozhongkai Co., Ltd. 1998-2019.All rights reserved.
  5. 文件名    : bsp_ap3216c.h
  6. 作者      : 左忠凯
  7. 版本      : V1.0
  8. 描述      : AP3216C驱动头文件。
  9. 其他      : 无
  10. 论坛      :www.openedv.com
  11. 日志      : 初版V1.02019/3/26 左忠凯创建
  12. ***************************************************************/
  13. #include "imx6ul.h"

  14. #defineAP3216C_ADDR          0X1E/* AP3216C器件地址    */

  15. /* AP3316C寄存器 */
  16. #defineAP3216C_SYSTEMCONG   0x00/* 配置寄存器   */
  17. #defineAP3216C_INTSTATUS    0X01/* 中断状态寄存器   */
  18. #defineAP3216C_INTCLEAR     0X02/* 中断清除寄存器   */
  19. #defineAP3216C_IRDATALOW    0x0A/* IR数据低字节 */
  20. #defineAP3216C_IRDATAHIGH   0x0B/* IR数据高字节 */
  21. #defineAP3216C_ALSDATALOW   0x0C/* ALS数据低字节   */
  22. #defineAP3216C_ALSDATAHIGH  0X0D/* ALS数据高字节   */
  23. #defineAP3216C_PSDATALOW     0X0E/* PS数据低字节    */
  24. #defineAP3216C_PSDATAHIGH   0X0F/* PS数据高字节 */

  25. /* 函数声明 */
  26. unsignedchar ap3216c_init(void);
  27. unsignedchar ap3216c_readonebyte(unsignedchar addr,
  28. unsignedchar reg);
  29. unsignedchar ap3216c_writeonebyte(unsignedchar addr,
  30. unsignedchar reg,
  31. unsignedchar data);
  32. void ap3216c_readdata(unsignedshort*ir,unsignedshort*ps,
  33. unsignedshort*als);

  34. #endif
复制代码
    第45到26行定义了一些宏,分别为AP3216C的设备地址和寄存器地址,剩下的就是函数声明。接下来在bsp_ap3216c.c中输入如下所示内容:
示例代码26.3.4 bsp_ap3216c.c文件代码
  1. /***************************************************************
  2. Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rightsreserved.
  3. 文件名   : bsp_ap3216c.c
  4. 作者     : 左忠凯
  5. 版本     : V1.0
  6. 描述     : AP3216C驱动文件。
  7. 其他     : 无
  8. 论坛     : www.openedv.com
  9. 日志     : 初版V1.0 2019/3/26 左忠凯创建
  10. ***************************************************************/
  11.    #include "bsp_ap3216c.h"
  12.    #include "bsp_i2c.h"
  13.    #include "bsp_delay.h"
  14.    #include "cc.h"
  15.    #include "stdio.h"

  16. /*
  17.     * @description   : 初始化AP3216C
  18.     * @param        : 无
  19.    * @return         :0 成功,其他值错误代码
  20.    */
  21. unsignedchar ap3216c_init(void)
  22. {
  23. unsignedchar data =0;

  24. /* 1、IO初始化,配置I2C IO属性
  25.        * I2C1_SCL -> UART4_TXD
  26.        * I2C1_SDA -> UART4_RXD
  27.        */
  28.       IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL,1);
  29.       IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA,1);
  30.       IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL,0x70B0);
  31.       IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA,0X70B0);

  32. /* 2、初始化I2C1 */
  33.       i2c_init(I2C1);

  34. /* 3、初始化AP3216C  */
  35.   /* 复位AP3216C          */
  36.       ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG,0X04);
  37.       delayms(50);/* AP33216C复位至少10ms */

  38.      /* 开启ALS、PS+IR      */
  39.       ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG,0X03);

  40.   /* 读取刚刚写进去的0X03 */
  41.       data = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG);
  42. if(data ==0X03)
  43. return0;/* AP3216C正常    */
  44. else
  45. return1;/* AP3216C失败    */
  46. }

  47. /*
  48.    * @description   : 向AP3216C写入数据
  49.    * @param – addr  : 设备地址
  50.    * @param - reg   : 要写入的寄存器
  51.    * @param – data  : 要写入的数据
  52.    * @return         :操作结果
  53.    */
  54. unsignedchar ap3216c_writeonebyte(unsignedchar addr,
  55. unsignedchar reg,
  56. unsignedchar data)
  57. {
  58. unsignedchar status=0;
  59. unsignedchar writedata=data;
  60. struct i2c_transfer masterXfer;

  61. /* 配置I2C xfer结构体 */
  62.       masterXfer.slaveAddress = addr; /* 设备地址 */
  63.       masterXfer.direction = kI2C_Write;  /* 写入数据 */
  64.       masterXfer.subaddress = reg; /* 要写入的寄存器地址    */
  65.       masterXfer.subaddressSize =1;   /* 地址长度一个字节  */
  66.       masterXfer.data =&writedata; /* 要写入的数据  */
  67.       masterXfer.dataSize =1;  /* 写入数据长度1个字节  */

  68. if(i2c_master_transfer(I2C1,&masterXfer))
  69.           status=1;

  70. return status;
  71. }

  72. /*
  73.    * @description   : 从AP3216C读取一个字节的数据
  74.    * @param – addr  : 设备地址
  75.    * @param - reg   : 要读取的寄存器
  76.    * @return         :读取到的数据。
  77.    */
  78. unsignedchar ap3216c_readonebyte(unsignedchar addr,
  79. signedchar reg)
  80. {
  81. unsignedchar val=0;

  82. struct i2c_transfer masterXfer;
  83.       masterXfer.slaveAddress = addr; /* 设备地址 */
  84.       masterXfer.direction = kI2C_Read;   /* 读取数据 */
  85.       masterXfer.subaddress = reg; /* 要读取的寄存器地址    */
  86.       masterXfer.subaddressSize =1;   /* 地址长度一个字节  */
  87.       masterXfer.data =&val;   /* 接收数据缓冲区   */
  88.       masterXfer.dataSize =1;  /* 读取数据长度1个字节  */
  89.       i2c_master_transfer(I2C1,&masterXfer);

  90. return val;
  91. }

  92. /*
  93.    * @description   : 读取AP3216C的原始数据,包括ALS,PS和IR, 注意!如果
  94.    *                :同时打开ALS,IR+PS两次数据读取的时间间隔要大于112.5ms
  95.    * @param - ir    : ir数据
  96.    * @param - ps    : ps数据
  97.    * @param - ps    : als数据
  98.    * @return         :无。
  99.   */
  100. void ap3216c_readdata(unsignedshort*ir,unsignedshort*ps,unsignedshort*als)
  101. {
  102. unsignedchar buf[6];
  103. unsignedchar i;

  104. /* 循环读取所有传感器数据 */
  105. for(i =0; i <6; i++)
  106. {
  107.          buf[i]= ap3216c_readonebyte(AP3216C_ADDR,
  108. AP3216C_IRDATALOW + i);
  109. }

  110. if(buf[0]&0X80)   /* IR_OF位为1,则数据无效  */
  111. *ir =0;
  112. else    /* 读取IR传感器的数据      */
  113. *ir =((unsignedshort)buf[1]<<2)|(buf[0]&0X03);

  114. *als =((unsignedshort)buf[3]<<8)| buf[2];/* 读取ALS数据 */

  115. if(buf[4]&0x40)   /* IR_OF位为1,则数据无效  */
  116. *ps =0;
  117. else    /* 读取PS传感器的数据    */
  118. *ps =((unsignedshort)(buf[5]&0X3F)<<4)|
  119. (buf[4]&0X0F);
  120. }
复制代码
    文件bsp_ap3216c.c里面共有4个函数,第1个函数是ap3216c_init,顾名思义,此函数用于初始化AP3216C,初始化成功的话返回0,如果初始化失败就返回其他值。此函数先初始化所使用到的IO,比如初始化I2C1的相关IO,并设置其复用为I2C1。然后此函数会调用i2c_init来初始化I2C1,最后初始化AP3216C。第2个和第3个函数分别为ap3216c_writeonebyte和ap3216c_readonebyte,这两个函数分别是向AP3216C写入数据和从AP3216C读取数据。这两个函数都通过调用bsp_i2c.c中的函数i2c_master_transfer来完成对AP3216C的读写。最后一个函数就是ap3216c_readdata,此函数用于读取AP3216C中的ALS、PS和IR传感器数据。
       最后在main.c中输入如下代码:
示例代码26.3.5 main.c文件代码
  1. /**************************************************************
  2. Copyright ? zuozhongkai Co., Ltd. 1998-2019. All rightsreserved.
  3. 文件名   :   mian.c
  4. 作者     : 左忠凯
  5. 版本     : V1.0
  6. 描述     : I.MX6U开发板裸机实验18 IIC实验
  7. 其他     : IIC是最常用的接口,ALPHA开发板上有多个IIC外设,本实验就
  8. 来学习如何驱动I.MX6U的IIC接口,并且通过IIC接口读取板载
  9.            AP3216C的数据值。
  10. 论坛     : www.openedv.com
  11. 日志     : 初版V1.0 2019/1/15 左忠凯创建
  12. **************************************************************/
  13.   #include "bsp_clk.h"
  14.   #include "bsp_delay.h"
  15.   #include "bsp_led.h"
  16.   #include "bsp_beep.h"
  17.   #include "bsp_key.h"
  18.   #include "bsp_int.h"
  19.   #include "bsp_uart.h"
  20.   #include "bsp_lcd.h"
  21.   #include "bsp_rtc.h"
  22. #include "bsp_ap3216c.h"
  23. #include "stdio.h"

  24. /*
  25.   * @description    : main函数
  26.   * @param            :无
  27.   * @return          :无
  28.   */
  29. int main(void)
  30. {
  31.   unsignedshort ir, als, ps;
  32.   unsignedchar state = OFF;

  33.   int_init();   /* 初始化中断(一定要最先调用!)  */
  34.   imx6u_clkinit();  /* 初始化系统时钟   */
  35.   delay_init(); /* 初始化延时          */
  36.   clk_enable(); /* 使能所有的时钟   */
  37.   led_init();   /* 初始化led                 */
  38.   beep_init();  /* 初始化beep                   */
  39.   uart_init();  /* 初始化串口,波特率115200     */
  40.   lcd_init();   /* 初始化LCD                 */

  41.   tftlcd_dev.forecolor = LCD_RED;
  42.   lcd_show_string(30,50,200,16,16,
  43. (char*)"ALPHA-IMX6U IIC TEST");
  44.   lcd_show_string(30,70,200,16,16,(char*)"AP3216C TEST");
  45.   lcd_show_string(30,90,200,16,16,(char*)"ATOM@ALIENTEK");
  46.   lcd_show_string(30,110,200,16,16,(char*)"2019/3/26");

  47.   while(ap3216c_init())    /* 检测不到AP3216C        */
  48.   {
  49.   lcd_show_string(30,130,200,16,16,
  50. (char*)"AP3216C Check Failed!");
  51.   delayms(500);
  52.   lcd_show_string(30,130,200,16,16,
  53. (char*)"Please Check!        ");
  54.   delayms(500);
  55.   }

  56.   lcd_show_string(30,130,200,16,16,(char*)"AP3216C Ready!");
  57.   lcd_show_string(30,160,200,16,16,(char*)" IR:");
  58.   lcd_show_string(30,180,200,16,16,(char*)" PS:");
  59.   lcd_show_string(30,200,200,16,16,(char*)"ALS:");
  60.   tftlcd_dev.forecolor = LCD_BLUE;
  61.   while(1)
  62.   {
  63.   ap3216c_readdata(&ir,&ps,&als); /* 读取数据 */
  64.   lcd_shownum(30+32,160, ir,5,16);   /* 显示IR数据  */
  65.   lcd_shownum(30+32,180, ps,5,16);   /* 显示PS数据  */
  66.   lcd_shownum(30+32,200, als,5,16);  /* 显示ALS数据 */
  67.   delayms(120);
  68.   state =!state;
  69.   led_switch(LED0,state);
  70.   }
  71.   return0;
  72. }
复制代码
    第38行调用ap3216c_init来初始化AP3216C,如果AP3216C初始化失败的话就会进入循环,会在LCD上不断的闪烁字符串“AP3216CCheck Failed!”和“PleaseCheck!”,直到AP3216C初始化成功。
       第53行调用函数ap3216c_readdata来获取AP3216C的ALS、PS和IR传感器数据值,获取完成以后就会在LCD上显示出来。
文件main.c里面的内容总体上还是很简单的,实验程序的编写就到这里。
26.4编译下载验证26.4.1 编写Makefile和链接脚本
修改Makefile中的TARGET为ap3216c,然后在在INCDIRS和SRCDIRS中加入“bsp/i2c”和“bsp/ap3216c”,修改后的Makefile如下:
示例代码26.4.1.1  Makefile文件代码
  1.   CROSS_COMPILE  ?= arm-linux-gnueabihf-
  2.   TARGET            ?=ap3216c

  3. /* 省略掉其它代码...... */

  4.   INCDIRS :=  imx6ul \
  5.       stdio/include \
  6.       bsp/clk \
  7.       bsp/led \
  8.      bsp/delay \
  9.      bsp/beep \
  10.      bsp/gpio \
  11.      bsp/key \
  12.      bsp/exit \
  13.      bsp/int \
  14.      bsp/epittimer \
  15.      bsp/keyfilter \
  16.      bsp/uart \
  17.      bsp/lcd \
  18.      bsp/rtc \
  19.      bsp/i2c \
  20.      bsp/ap3216c

  21. SRCDIRS :=  project \
  22.      stdio/lib \
  23.      bsp/clk \
  24.      bsp/led \
  25.      bsp/delay \
  26.      bsp/beep \
  27.      bsp/gpio \
  28.      bsp/key \
  29.      bsp/exit \
  30.      bsp/int \
  31.      bsp/epittimer \
  32.      bsp/keyfilter \
  33.      bsp/uart \
  34.      bsp/lcd \
  35.      bsp/rtc \
  36.       bsp/i2c \
  37.      bsp/ap3216c

  38. /* 省略掉其它代码...... */

  39. clean:
  40.   rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS)$(SOBJS)
复制代码
第2行修改变量TARGET为“ap3216c”,也就是目标名称为“ap3216c”。
       第21和22行在变量INCDIRS中添加I2C和AP3216C的驱动头文件(.h)路径。
       第39和40行在变量SRCDIRS中添加I2C和AP3216C驱动文件(.c)路径。
    链接脚本保持不变。
26.4.2编译下载
       使用Make命令编译代码,编译成功以后使用软件imxdownload将编译完成的ap3216c.bin文件下载到SD卡中,命令如下:
  1. chmod 777 imxdownload                           //给予imxdownload可执行权限,一次即可
  2. ./imxdownload ap3216c.bin /dev/sdd           //烧写到SD卡中
复制代码
       烧写成功以后将SD卡插到开发板的SD卡槽中,然后复位开发板。程序运行以后LCD界面如图26.4.2.1所示:
图26.4.2.1 LCD显示界面
       图26.4.2.1中显示出了AP3216C的三个传感器的数据,大家可以用手遮住或者靠近AP3216C,LCD上的三个数据就会变化。




正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

6

主题

1127

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1656
金钱
1656
注册时间
2019-8-15
在线时间
102 小时
2#
发表于 2019-12-18 19:08:20 | 只看该作者
帮顶                                   
成功没有捷径
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-8-3 09:53

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表