摘要:I²C (内置于集成电路)是一种2线接口,实现集成电路之间的双向通信。本应用笔记介绍MAXQ2000微控制器的软件I²C驱动器,它采用微控制器的任意GPIO引脚,支持100kHz或400kHz的I²C通信。MAXQ系列微控制器具有高速、灵活的GPIO模块以及独立的I/O供电,适用于逐位控制等应用。
maxqi2c库采用扩展C语言编写,由MAXQ的IAR嵌入式平台编译。它由两个文件组成:maxqi2c.h和maxqi2c.c。当这些文件包含在MAXQ2000固件工程时,使用µC的任意GPIO引脚,便可实现100kHz或者400kHz灵活的I²C通信。
MAXQ系列的微控制器具有高速、灵活的GPIO模块以及独立的I/O供电,适用于位拆裂等应用。
本应用笔记讨论的实例工程文件可以从Maxim Integrated Products网站下载。
清单1. maxi2c.h用户定义代码。
上面清单1中的源代码在I/O端口0分配引脚0,作为SDA,在I/O端口0分配引脚1,作为SCL。
清单1的源代码通过400kHz I²C总线初始化maxqi2c库,进行通信。由于I²C接口是逐位控制,通信速率实际略低于400kHz (或者,另一种选择100kHz)。为达到全速400kHz通信,固件设计人员必须学习maxi2c库,去掉某些源代码,发挥库的灵活性。
注释:maxqi2c库包括时延以满足I²C规范。这些时延在maxqi2c.c文件的开始,假设MAXQ2000具有20MHz系统时钟;如果采用了速率较低的时钟,可以减小时延。
要使能时钟扩展,则不应注释掉I2C_CLOCK_STRETCHING定义声明。如果不需要时钟扩展,通过注释掉I2C_CLOCK_STRETCHING定义语句禁用它。禁用时钟扩展会稍微提高maxqi2c库i2cRecv()函数的速率。
上面清单1的源代码使能时钟扩展。
这些函数都不需要正式的参数,而是采用4个全局变量为这些函数存储参数:i2cData (无符号字符 *)、i2cDataLen (无符号整数)、i2cDataAddr (无符号字符)和i2cDataTerm (无符号字符)。该方法在函数调用时不进行数据复制,从而支持固件以更快的速率运行。4个用作maxqi2c库参数的全局变量是:i2cData (无符号字符 *)、i2cDataLen (无符号整数)、i2cDataAddr (无符号字符)和i2cDataTerm (无符号字符)。
为确定在I²C总线上是否有特定器件,i2cIsAddrPresent()按照下面的格式发送一个I²C命令:
表1. i2cSend()送出的I²C命令
注释:表1中的最后3个格式显示了i2cSend()怎样向I²C总线上的同一个器件连续发送数据。
如果寻址器件每一字节作出应答,i2cSend()函数返回一个等于I2C_XMIT_OK的数值(无符号字符类型),如果寻址器件每字节没有应答,返回值则等于I2C_XMIT_FAILED。当一个字节没有得到应答时,函数将立即返回。
表2. 禁用时钟扩展,i2cRecv()发送的I²C命令
注释:表2中的最后3个格式显示了i2cRecv()怎样从I²C总线上的同一个器件连续接收数据。
如果地址作为I²C命令的一部分进行发送,没有得到应答,i2cRecv()函数返回一个等于I2C_XMIT_FAILED的数值(无符号字符),否则,返回I2C_XMIT_OK。
图1. MAX1169评估板和MAXQ2000评估板(Rev B)连接原理图,将由maxqi2c库使用。
注释:MAXQ2000评估板上的MAXQ2000高频晶振(Y1)以20MHz晶振替代。MAX1169评估板的跳接器设置和MAXQ2000评估板的开关设置在表3和表4中:
表3:MAX1169评估板的跳接器设置
表4. MAXQ2000评估板(Rev B)开关设置
max1169.c文件包括两个头文件:iomaxq200x.h和maxqi2c.h。注意,实例中的iomaxq200x.h文件将忽略MAXQ包含路径IAR嵌入式平台中的iomaxq200x.h文件。iomaxq200x.h文件为maxqi2c库所需的每个端口引脚进行定义。包含了maxqi2c.h文件,以支持固件调用maxqi2c库函数。
固件分成5步,在max1169.c文件中标出(参见附录A)。
第1步初始化UART0,以19200bps进行异步通信。注意,如果MAXQ2000系统时钟不是20MHz,必须修改寄存器PR0的分配以获得所需的波特率。
第2步调用i2cInit()函数,以初始化I2C总线MAXQ2000所使用的引脚。
第3步初始化参数,调用i2cRecv()函数。参数经过初始化,按照下面的格式传送I²C命令:
附录A: max1169.c
引言
I²C (内置集成电路)是一种2线接口,实现集成电路之间的双向通信。本应用笔记介绍maxqi2c库,它是MAXQ2000微控制器(µC)的软件I²C驱动。maxqi2c库采用扩展C语言编写,由MAXQ的IAR嵌入式平台编译。它由两个文件组成:maxqi2c.h和maxqi2c.c。当这些文件包含在MAXQ2000固件工程时,使用µC的任意GPIO引脚,便可实现100kHz或者400kHz灵活的I²C通信。
MAXQ系列的微控制器具有高速、灵活的GPIO模块以及独立的I/O供电,适用于位拆裂等应用。
本应用笔记讨论的实例工程文件可以从Maxim Integrated Products网站下载。
配置maxqi2c库
用户应将maxqi2c库文件(maxqi2c.h和maxqi2c.c)复制到MAXQ2000工程目录中,对文件进行配置,建立所需的I2C接口。通过编辑以下代码(清单1)实现所有的配置,该代码位于maxqi2c.h源文件的开始:清单1. maxi2c.h用户定义代码。
/* USER MUST CUSTOMIZE THE FOLLOWING DEFINE STMTS - START */ // Enter the port used for SDA and SCL #define SDA_PORT 0 #define SCL_PORT 0 // Enter the pin used for SDA and SCL #define SDA_PORT_BIT 0 #define SCL_PORT_BIT 1 // Uncomment one of these define statements to select I2C bus speed #define I2C_400_KHZ //#define I2C_100_KHZ // Comment out the following define statement to disable clock // stretching in i2cRecv() #define I2C_CLOCK_STRETCHING /* USER MUST CUSTOMIZE THE FOLLOWING DEFINE STMTS - END */注释:用户定义代码编译时实现,因此,运行时间固定。
选择SCL和SDA引脚
需选择两个GPIO引脚用作SCL和SDA。为SCL和SDA选择了I/O之后,必须对SDA_PORT和SCL_PORT定义语句进行编辑,以反映SDA和SCL所需的端口。也必须对SDA_PORT_BIT和SCL_PORT_BIT定义语句进行编辑,以反映SDA和SCL所需的引脚(在所选端口上)。上面清单1中的源代码在I/O端口0分配引脚0,作为SDA,在I/O端口0分配引脚1,作为SCL。
选择通信速率
通过注释I2C_400_KHZ和I2C_100_KHZ两个定义语句之一来选择通信速率。清单1的源代码通过400kHz I²C总线初始化maxqi2c库,进行通信。由于I²C接口是逐位控制,通信速率实际略低于400kHz (或者,另一种选择100kHz)。为达到全速400kHz通信,固件设计人员必须学习maxi2c库,去掉某些源代码,发挥库的灵活性。
注释:maxqi2c库包括时延以满足I²C规范。这些时延在maxqi2c.c文件的开始,假设MAXQ2000具有20MHz系统时钟;如果采用了速率较低的时钟,可以减小时延。
使用时钟扩展
maxqi2c库的时钟扩展仅在调用i2cRecv()函数开始传送时(地址传输完毕,经过地址确认后,或者在传送开始时)使用。因此,I²C传送可以采用以下格式的时钟扩展:[S] [ADDR] [R] [A] [clock stretch] [DATA0] [A] ... [DATAN-1] [A] or [clock stretch] [DATA0] [A] ... [DATAN-1] [N] [P] or [clock stretch] [DATA0] [A] ... [DATAN-1] [A]使用maxqi2c一节中的i2cRecv()说明,maxqi2c库使用实例一节中的代码解释了怎样产生这些格式的I²C命令。
要使能时钟扩展,则不应注释掉I2C_CLOCK_STRETCHING定义声明。如果不需要时钟扩展,通过注释掉I2C_CLOCK_STRETCHING定义语句禁用它。禁用时钟扩展会稍微提高maxqi2c库i2cRecv()函数的速率。
上面清单1的源代码使能时钟扩展。
使用maxqi2c
由4个函数实现maxqi2c库从软件I²C驱动发送和接收数据:i2cInit()、i2cIsAddrPresent()、i2cSend()和i2cRecv()。这些函数的文档也包含在maxqi2c.h文件中。这些函数都不需要正式的参数,而是采用4个全局变量为这些函数存储参数:i2cData (无符号字符 *)、i2cDataLen (无符号整数)、i2cDataAddr (无符号字符)和i2cDataTerm (无符号字符)。该方法在函数调用时不进行数据复制,从而支持固件以更快的速率运行。4个用作maxqi2c库参数的全局变量是:i2cData (无符号字符 *)、i2cDataLen (无符号整数)、i2cDataAddr (无符号字符)和i2cDataTerm (无符号字符)。
i2cInit()
必须在调用任何其他maxqi2c函数之前调用该函数。它初始化maxqi2c.h文件中用户定义代码所选择的端口引脚。该函数不需要参数(局部或者全局),不返回数值。i2cIsAddrPresent()
该函数使MAXQ2000能够查询I²C总线,以确定是否有特定地址的器件。该函数有一个参数――全局变量i2cDataAddr,它必须由器件地址装入,查询I²C总线是否有器件。该函数还返回一个数值(无符号字符类型)。如果找到了给定地址的器件,该数值等于I2C_XMIT_OK,如果没有找到给定地址的器件,则等于I2C_XMIT_FAILED。为确定在I²C总线上是否有特定器件,i2cIsAddrPresent()按照下面的格式发送一个I²C命令:
[S] [ADDR] [W] [A] [P]
i2cSend()
该函数使MAXQ2000能够通过软件I2C驱动向器件传送数据。i2cSend()需要以下4个参数(均为全局变量)来初始化:- i2cData (无符号字符 *):被传送字节阵列的第一个字节指针。
- i2cDataLen (无符号整数):向I²C总线传送的字节数(不包括器件地址)。
- i2cDataAddr (无符号字符):数据将被传送到的器件的地址。注意,如果该变量设置为0,将不发送地址,传送I²C数据。
- i2cDataTerm (无符号字符):I²C传送如何结束。调用i2cSend(): I2C_TERM_NONE或者I2C_TERM_STOP时,该变量可以取两个值。
表1. i2cSend()送出的I²C命令
i2cDataLen(hex) | i2cDataAddr(hex) | i2cDataTerm | I²C Command Format |
0x0002 | 0x7E | I2C_TERM_STOP | [S] [ADDR] [W] [A] [DATA0] [A] [DATA1] [P] |
0x0002 | 0x7E | I2C_TERM_NONE | [S] [ADDR] [W] [A] [DATA0] [A] [DATA1] [A] |
0x0002 | 0x00 | I2C_TERM_NONE | [DATA0] [A] [DATA1] [A] |
0x0002 | 0x00 | I2C_TERM_STOP | [DATA0] [A] [DATA1] [A] [P] |
注释:表1中的最后3个格式显示了i2cSend()怎样向I²C总线上的同一个器件连续发送数据。
如果寻址器件每一字节作出应答,i2cSend()函数返回一个等于I2C_XMIT_OK的数值(无符号字符类型),如果寻址器件每字节没有应答,返回值则等于I2C_XMIT_FAILED。当一个字节没有得到应答时,函数将立即返回。
i2cRecv()
该函数使MAXQ2000能够使用软件I²C驱动从器件接收数据。i2cRecv()函数需要以下4个参数(均为全局变量)来初始化:- i2cData (无符号字符 *):存储接收数据阵列的第一个字节指针。
- i2cDataLen (无符号整数):从I²>C总线上接收到的字节数(不包括器件地址)。
- i2cDataAddr (无符号字符):将要接收数据的器件地址。注意,如果该变量设置为0,将不发送地址,接收I²C数据。
- i2cDataTerm (无符号字符):I²C传送如何结束。调用i2cRecv(): I2C_TERM_NONE、 I2C_TERM_ACK或者I2C_TERM_NACK_AND_STOP时,该变量可以取三个值。
表2. 禁用时钟扩展,i2cRecv()发送的I²C命令
i2cDataLen(hex) | i2cDataAddr(hex) | i2cDataTerm | I2C Command Format |
0x0002 | 0x7E | I2C_TERM_NACK_AND_STOP | [S] [ADDR] [R] [A] [DATA0] [A] [DATA1] [N] [P] |
0x0002 | 0x7E | I2C_TERM_ACK | [S] [ADDR] [R] [A] [DATA0] [A] [DATA1] [A] |
0x0002 | 0x00 | I2C_TERM_ACK | [DATA0] [A] [DATA1] [A] |
0x0002 | 0x00 | I2C_TERM_NACK_AND_STOP | [DATA0] [A] [DATA1] [N] [P] |
注释:表2中的最后3个格式显示了i2cRecv()怎样从I²C总线上的同一个器件连续接收数据。
如果地址作为I²C命令的一部分进行发送,没有得到应答,i2cRecv()函数返回一个等于I2C_XMIT_FAILED的数值(无符号字符),否则,返回I2C_XMIT_OK。
带有时钟扩展的maxqi2c库使用实例
以下实例显示了怎样利用maxqi2c库从MAX1169 ADC接收16位采样,由MAXQ的RS-232端口,将这些数据传送至PC。原理
采用MAX1169 ADC评估板和MAXQ2000评估板(Rev B)实现了该实例。图1显示了两块评估板的连接。MAXQ2000 I/O端口的引脚0和引脚1 (分别在J2-30和J2-28)用作I2C总线上的主机SDA和SCL线。图1. MAX1169评估板和MAXQ2000评估板(Rev B)连接原理图,将由maxqi2c库使用。
注释:MAXQ2000评估板上的MAXQ2000高频晶振(Y1)以20MHz晶振替代。MAX1169评估板的跳接器设置和MAXQ2000评估板的开关设置在表3和表4中:
表3:MAX1169评估板的跳接器设置
跳接器 | 短路器位置 |
JU1 | 引脚1和2之间安装短路器 |
JU2 | 引脚1和2之间安装短路器 |
JU3 | 引脚1和2之间安装短路器 |
JU4 | 没有短路器 |
JU5 | 没有短路器 |
表4. MAXQ2000评估板(Rev B)开关设置
开关 | 位置 |
SW1-1 | 断开 |
SW1-2 | 断开 |
SW1-3 | 断开 |
SW1-4 | 打开 |
SW1-5 | 断开 |
SW1-6 | 断开 |
SW1-7 | 打开 |
SW1-8 | 断开 |
SW6-1 | 断开 |
SW6-2 | 断开 |
SW6-3 | 断开 |
SW6-4 | 断开 |
SW6-5 | 断开 |
SW6-6 | 断开 |
SW6-7 | 断开 |
SW6-8 | 打开 |
固件
该实例(max1169.c)的固件文件在附录A中给出。完整的工程资料可以在Maxim MAXQ2000网页下载,采用MAXQ IAR嵌入式平台编译。该实例中,maxqi2c库的用户定义代码(在maxqi2c.h文件的开始)与清单1的源代码完全相同。max1169.c文件包括两个头文件:iomaxq200x.h和maxqi2c.h。注意,实例中的iomaxq200x.h文件将忽略MAXQ包含路径IAR嵌入式平台中的iomaxq200x.h文件。iomaxq200x.h文件为maxqi2c库所需的每个端口引脚进行定义。包含了maxqi2c.h文件,以支持固件调用maxqi2c库函数。
固件分成5步,在max1169.c文件中标出(参见附录A)。
第1步初始化UART0,以19200bps进行异步通信。注意,如果MAXQ2000系统时钟不是20MHz,必须修改寄存器PR0的分配以获得所需的波特率。
第2步调用i2cInit()函数,以初始化I2C总线MAXQ2000所使用的引脚。
第3步初始化参数,调用i2cRecv()函数。参数经过初始化,按照下面的格式传送I²C命令:
[S] [ADDR] [R] [A] [clock stretch] [DATA0] [A] [DATA1] [A (termination)]第4步将地址参数设置为0。使i2cRecv()函数按照下面的格式传送I²C命令:
[clock stretch] [DATA0] [A] [DATA1] [A (termination)]第5步是周期不确定的循环。该循环调用i2cRecv() (按照第4步定义的格式),从MAX1169接收一个16位采样。由UART0将该16位采样传送(MSB在前)至PC。由于匹配参数i2cDataTerm总是等于I2C_TERM_ACK,循环的周期不确定,MAX1169不会看到停止状态。
附录A: max1169.c
/* * DEMO of maxqi2c Software I²C Driver * (uses evkits for the MAX1169 and MAXQ2000) * * by: Paul Holden - MAXIM INTEGRATED PRODUCTS * * * DESC: Test program for the maxqi2c.c/maxqi2c.h I²C * driver for the MAXQ2000. The program reads * 16-bit samples from the MAX1169 (running in * continuous conversion mode) and transmits them * using the UART0 port. * * NOTE - THE FOLLOWING CODE ASSUMES THE MAXQ2000 HAS * A Fsysclk=20MHz. */ #include "iomaxq200x.h" #include "maxqi2c.h" void main() { unsigned char data[2]; // 1. Init UART0 PD7_bit.bit0 = 1; // Set TX0 pin as output SCON0 = 0x42; SMD0 = 0x02; PR0 = 0x07DD; // 19200bps // 2. Init bit-banged I²C port i2cInit(); // 3. Send initial I²C request // [S] [ADDR+R] [A] [clock_stretch] [DATA0] [A] [DATA1] [A (termination)] i2cData = (unsigned char *)(&data); // cast needed! i2cDataAddr = 0x7E; i2cDataLen = 0x0002; i2cDataTerm = I2C_TERM_ACK; i2cRecv(); // 4. Init continuous conversion // [clock_stretch] [DATA0] [A] [DATA1] [A (termination)] i2cDataAddr = 0x00; // 5. Receive a 16-bit sample and transfer it to the UART0 port // one byte at a time. Repeat forever... while (1) { i2cRecv(); while(!SCON0_bit.TI); // Wait for UART0 Buffer to be empty SCON0_bit.TI = 0; // Reset TI flag SBUF0 = data[0]; // Send data byte 0 while(!SCON0_bit.TI); // Wait for UART0 Buffer to be empty SCON0_bit.TI = 0; // reset TI flag SBUF0 = data[1]; // Send data byte 1 } }
评论
查看更多