摘要 I2C总线的设备文件通常为/dv/i2c-n(n=0、1、2……),每个设备文件对应一组I2C总线。应用程序通过这些设备文件可以操作I2C总线上的任何从机器件。EasyARM-i.MXA提供了一路I2C接口,设备文件为/dv/i2c-0。 本章导读: 本章主要讲述非标准(相对于PC而言)硬件接口编程。像通常的串口和网口,都是标准接口,有通用的编程规范,而这章讲述的如LED、GPIO、SPI和I2C这样的接口,在嵌入式系统中非常普通,由于这些接口的特殊性,没有统一的编程规范。而在实际应用中,往往又不可缺少,所以这章的内容很重要。 本章的内容与具体的开发平台结合比较紧密,如果在非对应的平台上使用这些范例,可能需要根据实际情况进行修改和调整。 15.4用户态I2C编程 I2C总线的设备文件通常为/dv/i2c-n(n=0、1、2……),每个设备文件对应一组I2C总线。应用程序通过这些设备文件可以操作I2C总线上的任何从机器件。EasyARM-i.MXA提供了一路I2C接口,设备文件为/dv/i2c-0。 15.4.1I2C编程接口 1、打开设备 在操作I2C总线时,先调用opn()函数打开I2C设备获得文件描述符,代码如程序清单15.8所示。 程序清单15.8打开I2C设备文件 intfd;fd=opn(/dv/i2c-0,O_RDWR);if(fd0){prror(opni2c-1\n);} 2、关闭设备 当操作完成后,调用clos()函数关闭设备: clos(fd); 3、配置设备 当应用程序操作I2C总线上的从机器件时,必须先调用ioctl()函数设置从机地址和从机地址的长度。 设置从机地址 设置从机地址是使用I2C_SLAVE命令,其定义为: #dfinI2C_SLAVE0x 该命令的参数为从机地址右移一位。设置从机地址为0xA0的示例代码为: if(ioctl(GiFd,I2C_SLAVE,0xA01)0){prror(stslavaddrssfail\n);} 注意:地址需要右移一位,是因为地址的Bit0是读写控制位,在驱动中会将从机地址命令参数左移一位,并补上读写控制位。 设置地址长度 设置从机地址的长度是使用I2C_TENBIT命令,其定义为: #dfinI2C_TENBIT0x 该命令的参数可选择为:1表示设置从机地址长度为10位;0表示设置从机地址长度为8位。设置从机地址长度为10位的示例代码为: ioctl(fd,I2C_TENBIT,1); 该命令是不会返回错误的。 如果不设置地址长度,则默认为8位地址。 4、发送数据 应用程序调用writ()函数可以向I2C总线发送数据。例如在I2C总线发送“hllo”字符串的代码如程序清单15.9所示。 程序清单15.9在I2C总线发送数据 intln;charbuf[]=hllo;ln=writ(fd,buf,sizof(buf));if(ln0){printf(snddatafail);xit(-1);} writ()函数调用成功后,返回成功发送数据的长度。在writ()函数调用时,数据发送过程如下: 主机在I2C总线发送始起信号(S),然后发送从机地址(slavaddr); 从机成功接收到属于自己的从机地址后,返回应答信号(ACK); 主机接收到应答信号后,把buf缓冲区中的数据逐个在I2C总线发送; 从机每成功接收到一个从主机发来的数据都返回应答信号; 当主机的数据发送完毕后,在I2C上发送结束信号(P)。 具体过程如图15.10所示。 图15.10数据发送过程示意图 5、接收数据 应用程序调用rad()函数可以在I2C总线接收数据。例如在I2C总线接收10个字节的代码如程序清单15.9所示。 程序清单15.10在I2C总线读取数据 charbuf[10];intln;ln=rad(fd,buf,10);if(ln0){printf(radi2cdatafail);xit(-1);} rad()调用成功后,返回接收数据的长度。 rad()函数调用时,数据接收过程如下: 主机在I2C总线发送始起信号(S),然后发送从机地址(slavaddr); 从机成功接收到属于自己的从机地址后,返回应答信号(ACK); 主机接收到应答信号后,准备接收从机发来的数据; 从机把数据逐个向主机发送; 主机每成功接收到一个在从机发来的数据都返回应答信号; 当主机接收到最后一个数据时并返回应答信号,而是I2C上发送结束信号(P)。 具体过程如图15.11所示。 图15.11数据接收过程示意图 15.4.2编程范例 AP-Dmo板上的FM24C02A是I2C接口的EEPROM芯片。FM24C02A是2Kb(字节)大小的EEPROM,分为32个页,每页8字节。 这里通过演示读/写I2C接口的EEPROM来进一步说明应用程序如何使用I2C编程接口。 1、FM24C02A的操作 从机寻址 当接收到起始信号后,FM24C02A需要一个8位的从机地址来启动一次读/写操作,其从机地址构成如图15.12所示。 图15.12FM24C02A的从机地址 从机地址前4位的值固定不变,第2、3、4位的值分别由FM24C02A的A0、A1、A2引脚的输入电平决定(高电平为1,低电平为0)。从机地址的第8位为读/写启动选择位(R/W):1为启动读操作;1为启动写操作。 字节写 字节写操作为每次在FM24C02A内部储存器的指定地址写入1个字节的数据。主机先发送起始信号和从机地址(R/W位为0)。在接收到FM24C02A返回的应答信号后,主机发送需要写入的数据地址(1个字节),然后发送需要写入的数据。在收到FM24C02A返回的应答信号后,主机发送结束信号。 字节写的顺序过程如图15.13。 图15.13字节写顺序 页写 FM24C02A支持在一次写操作中连续写入一页的数据(8个字节)。页写操作的启动方式和字节写操作类似,只是主机发送了第1个字节的数据后并不是马上停止,而是继续发送剩余的7个字节的数据。FM24C02A在每接收到主机发来的1个数据都返回1个应答信号。当主机的所有数据都发送完毕后,主机发送结束信号。每当FM24C02A接收到主机发来的1个数据时,数据地址的低三位加1,而高五位不会变化,保持存储器的页地址不变。当内部产生的数据地址达到页边界时,数据地址将会翻转,接下来的数据的写入地址将置为同一页的最小地址。所以若有超过8个字节数据写入FM24C02A,数据地址将回到最先写入的地址,先前写入的数据将被覆盖。 页写的具体顺序如图15.14所示。 图15.14页写顺序 当前地址读 FM24C02A的内部数据地址计数器保留最后一次访问的地址,并自动加1。只要FM24C02A处于上电状态,这个地址在操作运行期间始终有效。在读操作中,如果存储器的最后一页的最后一个字节开始读,则读下一个字节时地址将会翻转到整个储存器的最小地址。 主机发送起始信号和从机地址(R/W位为1)后,FM24C02A返回答应信号,然后向主机发送数据。这时主机接收到数据后,并不返回答应信号,而发送结束信号。 当前地址读的具体顺序如图15.15所示。 图15.15当前地址读顺序图 自由读 自由读需要通过假的字节写操作来获得数据地址。主机首先发送起始信号、从机地址和数据地址来定位需要读取的地址。当FM24C02A返回数据地址的应答信号之后,主机马上重新发送起始信号和从机地址。这时FM24C02A返回应答信号,然后发送数据。主机接收到数据后,并不返回应答信号,而发送结束信号。 自由读的具体顺序如图15.16所示。 图15.16自由读顺序 连续读 在自由读操作中,若主机在接收了FM24C02A发来的数据后,并不发送结束信号,而是立即返回应答信号,那么FM24C02A则自动把数据地址加1,并将新数据地址的数据发送给主机。当储存器的数据地址达到最大时,数据地址将翻转到最小地址,并且继续进行连续读操作。当主机不再返回应答信号,而是发送停止信号时,FM24C02A停止发送数据。 连续读的具体顺序如图15.17所示。 图15.17连续读顺序 2、电路原理 AP-Dmo板上的FM24C02A是连接到I2C1总线,电路图如图15.18所示。 图15.18FM24C02A连接电路图 在该电路图中,FM24C02A的A0、A1、A2引脚电平被拉低,所以FM24C02A的从机地址为0xA0。 3、示例程序 在程序清单15.11所示的代码中,通过I2C总线在FM24C02A内部储存器的0x00~0x07地址连续写入8个字节的数据,然后在这些地址中把数据读出来,最后把写入数据和读出数据进行对比,以检验程序的正确性。 程序清单15.11连续写/读程序代码 #includstdio.h#includstdlib.h#includunistd.h#includsys/typs.h#includsys/stat.h#includfcntl.h#includtrmios.h#includrrno.h#dfinI2C_SLAVE0x#dfinI2C_TENBIT0x#dfinI2C_ADDR0xA0#dfinDATA_LEN8#dfinI2C_DEV_NAME/dv/i2c-1intmain(intarg,char*args[]){unsigndintrt,ln;inti,flag=0;intfd;chartx_buf[DATA_LEN+1];/*用于储存数据地址和发送数据*/charrx_buf[DATA_LEN];/*用于储存接收数据*/charaddr[1];/*用于储存读/写的数据地址*/addr[0]=0;/*数据地址设置为0*/ fd=opn(I2C_DEV_NAME,O_RDWR);/*打开I2C总线设备*/if(fd0){printf(opn%sfaild\n,I2C_DEV_NAME);rturn-1;}rt=ioctl(fd,I2C_SLAVE,I2C_ADDR1);/*设置从机地址*/if(rt0){printf(stnvaddrssfailrt:%x\n,rt);rturn-1;}/*由于没有设置从机地址长度,所以使用默认的地址长度为8*/tx_buf[0]=addr[0];/*发数据时,第一个发送是数据地址*/for(i=1;iDATA_LEN;i++)/*初始化要写入的数据:0、1……7*/tx_buf[i]=i;ln=writ(fd,tx_buf,DATA_LEN+1);/*把数据写入到FM24C02A,*/if(ln0){printf(writdatafail\n);rturn-1;}uslp(*);/*需要延迟一段时间才能完成写入EEPROM*/ln=writ(fd,addr,1);/*设置数据地址*/if(ln0){printf(writdataaddrfail\n);rturn-1;}ln=rad(fd,rx_buf,DATA_LEN);/*在设置的数据地址连续读入数据*/if(ln0){printf(raddatafail\n);rturn-1;}printf(radfromprom:);for(i=0;iDATA_LEN-1;i++){/*对比写入数据和读取的数据*/printf(%x,rx_buf[i]);if(rx_buf[i]!=tx_buf[i+1])flag=1;}printf(\n); if(!flag){/*如果写入/读取数据一致,打印测试成功*/printf(promwritandradtstsussccd!\r\n);}ls{/*如果写入/读取数据不一致,打印测试失败*/printf(promwritandradtstfaild!\r\n);}rturn0;} 该代码可以交叉编译为i2c_prom_tst程序文件,测试方法: 把i2c_prom_tst上传到EasyARM-i.MXA的任何目录; 执行i2c_prom_tst程序。 若i2c_prom_tst程序执行无误,将打印信息如图15.19所示。 图15.19FM24C02A测试结果 在白癜风应该怎么样治疗北京治疗白癜风到底需要多少钱
|