Mizar32/I2C
I2C 代表**集成电路间总线**,用于不同硅芯片之间的通信。更具体地说,总线是一种数字通信通道,可以被许多设备或外设共享。
I2C 具有广泛的应用,通常用于同一机箱内集成电路之间的通信,无论是在同一电路板上还是在不同的电路板上,只要不需要高速通信速度即可。
飞利浦最初开发 I2C 用于电视机内部芯片之间的通信,但随着时间的推移,该系统蓬勃发展。从 1982 年首次发布以来,它现在已用于超过 1000 种不同的集成电路。
它成功的一个原因是,它是一种实用且经济的方式,可以通过仅使用两条线 (数据线和时钟线) 将同一个总线上的所有东西连接起来来创建复杂的电路,这显着减少了必须在电路板上布线的电信号数量。
现实世界应用的示例包括连接非易失性存储器、实时时钟电路、数字传感器、I/O 扩展器、数字 LED、液晶显示器和开关等设备。
I2C 规范已多次修订,始终保持向后兼容性,从 1992 年的 v1.0、2000 年的 v2.1 到 2007 年的 v3.0。标准模式以高达 100 kbit/s 的速度运行,快速模式以高达 400 kbit/s 的速度运行,快速模式 Plus 以高达 1 Mbit/s 的速度运行,高速模式以高达 3.4 Mbit/s 的速度运行。更高速度模式是在 I2C 规范的新修订版中添加的,因此并非所有集成电路都支持所有速度。但是,该协议是向后兼容的,因此更新、更快的设备仍然可以与更旧、更慢的设备通信,并且连接到总线的一个设备的速度不会影响总线上的其他设备。Mizar32 I2C 接口的硬件支持标准模式,达到 100 kbit/s,快速模式高达 400 kbit/s。
一些制造商(例如我们的 Atmel)将 I2C 称为 TWI(双线接口),因为它没有实现完整的 I2C 的所有细节,但它们是兼容的,本质上是相同的。
为了更好地了解 I2C,让我们仔细看看 I2C 协议的工作原理。
I2C 是一种半双工协议,这意味着一次只能有一个设备通信。总线有两条线:时钟和数据。
主设备为总线提供时钟。最常见的配置是一个主设备和一个或多个从设备。对此的另一种选择是多主模式,其中多个主设备可以在同一个总线上通信而不会导致错误。主设备有一种方法可以在它们之间决定谁在每个时刻控制总线。执行此操作的机制称为总线仲裁和时钟同步。
在一个具有单个主设备和多个从设备的配置中,是主设备提供时钟信号,但从设备可以使其变慢以降低速度,如果它们需要一些额外的时间。
从电气上讲,这两条线都用一个电阻拉高,并且设备通过将这些线拉低来发送信号。在标准模式下,最大总线速度为 100 Kbit/s,电阻值为 10K 欧姆;在快速模式下,最大总线速度为 400 Kbit/s,每个电阻值为 2.2K 欧姆。
每个从设备都有一个 7 位地址,它将在总线上响应该地址,并且同一个 I2C 总线上的每个设备都必须设置为响应不同的地址。
当两个设备通信时,其中一个是主设备,另一个是从设备,其中一个是发送数据,另一个是接收数据,因此在任何特定时刻,设备可以是主发送器、主接收器、从接收器或从发送器。
以下是主发送器 (与从设备启动通信的设备) 生成的信号序列。
第一个事件是主发送器生成一个起始条件,即数据 (SDA) 线在时钟 (SCL) 线为高电平时从高电平转换为低电平。所有 I2C 消息中的第一个始终是起始条件。
起始条件会导致所有从设备唤醒并开始监听,因此主设备发送从接收器地址,该地址由 7 位组成,后跟一个方向位 (0 表示发送,1 表示接收),然后由从接收器生成一个确认 (ACK) 位,表示它已识别其地址并正在监听。如果方向位为 0,表示主设备想要向从设备发送数据,它将传输 8 位数据,从设备再次发送 ACK 位,主设备发送另一个 8 位数据,从设备发送 ACK,依此类推。在每个字节数据之后,接收数据的从设备必须通过将数据线短暂拉低来发送 ACK 位。如果缺少 ACK,则表示没有人收到数据,正在传输数据的任何一方都会停止传输。传输完所有字节数据后,主设备通过在总线上生成停止条件来关闭通信,即在时钟 (SCL) 为高电平时,SDA 线从低电平转换为高电平。所有 I2C 消息始终以停止条件结束。
以更紧凑的形式
S | 起始条件 |
ADDR | 7 位从设备地址 |
R/W | 数据方向位:1 表示读取或 0 表示写入 |
DATA | 8 位数据 |
ACK | 1 位确认 |
P | 停止条件 |
有时 ADDR 和 R/W 被视为一个 8 位值,其中地址在最高 7 位,R/W 标志在最低有效位。例如,我们可以说 7 位从设备地址为 42,方向位为 1,而在其他时刻,我们可以说 8 位从设备地址为 85,这意味着相同的内容。
以下是主设备向从设备发送两个字节数据的简化视图
S | ADDR | W | ACK | DATA | ACK | DATA | ACK | P |
AVR32UC3A 芯片有一个 I2C 控制器,Mizar32 在左侧总线连接器 BUS2 上提供其信号。
Mizar32 的主板还具有 PCA9540 双向 I2C 多路复用器芯片,它可以将 I2C 信号连接到两组 I2C 总线引脚中的任意一组,即左侧和右侧 I2C 总线,其中一组在 BUS2 上可用,另一组在 BUS5 连接器上可用。如果您使用这些而不是主 I2C 总线引脚,您可以在系统中拥有两倍数量的 I2C 设备。此外,如果您的硬件设计需要两个响应相同 I2C 地址的 I2C 设备,您可以将一个放在左侧 I2C 总线上,另一个放在右侧。
当 Mizar32 打开时,多路复用器处于非活动状态,AVR32 的 I2C 信号仅馈送到主 I2C 总线引脚。要使 I2C 总线信号出现在左侧 I2C 总线上,您需要将值 5 编程到多路复用器的控制字中,要与右侧总线通信,您需要将值 4 编程到控制字中。要禁用多路复用器,您需要编程 0。
请参阅以下代码示例,了解如何使用 eLua 完成此操作。
I2C 多路复用器以及 Mizar32 附加模块上的所有主 I2C 设备都在主 I2C 总线上,因此您不必编程 I2C 多路复用器即可访问它们。Mizar32 模块放置在左侧和右侧 I2C 总线上的唯一设备是每个附加模块上的 EEPROM。
信号 | GPIO | 总线引脚 | 说明 |
---|---|---|---|
SDA | PA29 | BUS2 引脚 10 | 主 I2C 总线数据 |
SCL | PA30 | BUS2 引脚 11 | 主 I2C 总线时钟 |
BUS_SDA_L | - | BUS2 引脚 12 | 左侧 I2C 总线数据 |
BUS_SCL_L | - | BUS2 引脚 13 | 左侧 I2C 总线时钟 |
BUS_SDA_R | - | BUS5 引脚 3 | 右侧 I2C 总线数据 |
BUS_SCL_R | - | BUS5 引脚 4 | 右侧 I2C 总线时钟 |
下表显示了 Mizar32 及其附加模块上芯片使用的 I2C 从设备地址。
在下表中,我们给出了十进制表示法的 7 位代码,以及十六进制的相应 8 位命令字节值。
设备 | 7 位地址 (十进制) |
8 位 (十六进制) |
说明 |
---|---|---|---|
LCD 显示器 (命令) | 0111110 (62) |
7C/7D |
读取返回光标位置 |
LCD 显示器 (数据) | 0111111 (63) |
7E/7F |
读取返回按钮 |
VGA 板 24LC512 | 1010000 (80) |
A0/A1 |
(1) |
以太网板上的 PCF8563 RTC | 1010001 (81) |
A2/A3 |
|
以太网板上的 DS1337 RTC | 1101000 (104) |
D0/D1 |
|
I2C 多路复用器 PCA9540 | 1110000 (112) |
E0/E1 |
(1) VGA 的 24LC512 EEPROM 包含运行 VGA 板的 Parallax Propeller 芯片的程序,该程序仅在 VGA 板上的两对触点 GS4 和 GS5 通过焊锡连接时才连接到 Mizar32 的主 I2C 总线,允许您使用 Mizar32 Propeller 编程器 从 Mizar32 编程 Propeller 芯片。
Alcor6L 有一个 i2c
模块,提供设置、启动、地址、发送/接收字节和停止原语。
-- Send a byte to the I2C multiplexer to tell it to enable the left I2C bus id = 0 -- which I2C bus to use (there is only one!) mux_addr = 112 -- the slave address of the I2C multiplexer mux_disable = 0 -- control word to disable the multiplexer mux_left = 5 -- control word to enable left bus mux_right = 4 -- control word to enable right bus i2c.start( id ) if not i2c.address( id, mux_addr, i2c.TRANSMITTER ) then print "The multiplexer did not reply" else -- enable the multiplexer onto the left bus if i2c.write( id, mux_left ) ~= 1 then print "The multiplexer did not acknowledge the write" end end i2c.stop( id )
# Send a byte to the i2c multiplexer to tell it to # enable the left i2c bus (setq id 0 # Which i2c bus to use? mux-addr 112 # The slave address of the i2c mux mux-disable 0 # Control word to disable the mux mux-left 4 # Control word to enable left bus mux-right 5 ) # Control word to enable right bus (i2c-start 0) (if (not (= T (i2c-address id mux-addr *i2c-transmitter*))) (prinl "The multiplexer did not reply") (if (not (= (i2c-write id mux-left) mux-left)) (prinl "The multiplexer did not acknowledge the write") ) ) (i2c-stop id)
请注意:您也可以从我们 github 上的示例库中下载上述代码 send-data.l
。
-- Retrieve and print the contents of the I2C splitter's control register id = 0 -- which I2C bus to use (there is only one!) mux_addr = 112 -- the slave address of the I2C multiplexer i2c.start( id ) if not i2c.address( id, mux_addr, i2c.RECEIVER ) then print "The multiplexer did not reply" else cr = i2c.read( id, 1 ) -- read the control register (one byte) -- print the contents of the control register as a decimal number print( "Control register = " .. string.byte( cr ) ) end i2c.stop( id )
# Retrieve and print the contents of the i2c splitter's # control register # misc.l in the Alcor6L codebase contains # the function PicoLisp function stringToNum. # misc.l should exist in /mmc (load "@misc.l") (setq id 0 # Which i2c bus to use? (there's only one!) mux-addr 112 ) # The slave address of the i2c multiplexer (i2c-start id) (if (not (= T (i2c-address id mux-addr *i2c-receiver*))) (prinl "The multiplexer did not reply") (let (cr (i2c-read id 1)) # Read the control register (one byte) (prinl "Control register: " (stringToNum cr)) ) ) (i2c-stop id)
请注意:上述程序需要文件 misc.l
才能正常工作。此版本的 misc.l
与默认情况下随附的 PicoLisp 不同。您也可以从我们示例库中下载 read-data.l
。
- 维基百科的 I2C 文章
- 在 Atmel AT32UC3A 数据手册 中的第 24 章:双线接口 (TWI)。
- 飞利浦半导体 PCA9540 2 通道 I2C 多路复用器的数据手册
- 飞利浦半导体 I2C 地址分配表
- eLua 参考手册中的 I2C 模块.