微控制器 (MCU) 最基本的元素之一是 I/O——引脚数量、接收器和源强度以及可用的功能。在某些情况下,系统的 I/O 需求与微控制器的内存大小可能会出现不匹配。通常,可用的 I/O 越多,包含的内存就越多,这使得微控制器更昂贵。
I/O 扩展器通过串行通信(通常是I2C或 SPI )向主微控制器提供额外的 I/O bank 。I/O 扩展器的常见用例包括简化 PCB 布线、改善功耗和缩小主微控制器的物理尺寸。
I/O 扩展器 ASIC是一种常见商品,但每个 ASIC 的功能和用例略有不同,这可能会在设计过程中引起头痛。然而,I/O 扩展器是一种相对简单的数字设备,可以使用微控制器进行仿真,例如新的PIC16F15244 系列。与相关的基于 ASIC 的解决方案相比,这可以产生更多的设计灵活性、功能,甚至更低的物料清单 (BOM) 成本。然而,由于更专业的硬件,基于 ASIC 的解决方案在空间和功率效率方面具有优势。
为了展示这个概念的灵活性,我们创建了 2 个单独的代码示例。第一个代码示例是一个非常简单的 I/O 扩展器。可以轻松定制与该项目相关的固件,以进一步简化通信。第二个代码示例是一个非常灵活的“高级”I/O 扩展器,其设计目的是允许通过 I2C 控制几乎每个 I/O 设置,而不是依赖于预设功能。
这两个 I/O 扩展器示例具有共同的特性和大部分代码库。与许多 I2C 设备一样,寻址线被分配给一些未使用的 I/O 以设置 I2C 地址的一些位,这在设计中提供了灵活性,而无需额外编程。与 ASIC 不同,这些分配可以在软件中更改、重新路由、删除或重新排序。此外,该器件还提供了一条外部中断线 (INT) 来发出 I/O 电平变化的信号。INT 可以是公共线路的漏极开路,如果该线路是独占的,则可以是推挽式。
在深入了解高级 I/O 扩展器之前,让我们看一下简单的扩展器。在本例中,每条 I/O 线有两种连续状态(假设代码未修改):弱上拉输入或输出 LOW。弱上拉允许 I/O 扩展器在用作输入时定义逻辑电平,并且它还关闭通常会将电流吸入扩展器的连接电路。该器件的内部弱上拉强度约为 10s 或 100s µA。
LOW 输出是一个更强大的驱动器,每个 I/O 能够吸收高达25mA 的电流,整个设备的电流为 300mA(-40C 至 +85C)。有关这些限制的更多信息,请参阅器件数据表的电气特性部分。从输出状态转换到输入状态时可能遇到的一个问题是 I/O 线上存在的寄生电容的充电时间。该电容会导致从 0 到 1 的转换延迟,这可能会触发变化时中断电路。为了最大限度地减少这些影响,可以将简单的 I/O 扩展器配置为在切换到弱上拉之前暂时打开高侧 I/O 驱动器以快速对该电容进行充电。
高侧 I/O 驱动程序的运行时间可在软件中配置,并且可以根据需要禁用。下图显示了这种方法对上升时间的增强——注意图像中时间尺度从 20µs/div 到 40ns/div 的变化。注意:我们不建议在任何配置中直接驱动电容器。
这个例子的 I2C 通信也非常简单。完整的 I2C 通信只需要 2 个字节用于读取和写入。在 I2C 写入中,数据的第一个字节是一个位图,它将由单个位表示的每个 I/O 线设置为输入或输出。发送的所有其他数据字节都被丢弃,开发人员可以省略。
在 I2C 读取中,从设备读取的字节是一个位图,表示每个 I/O 线上的数字值,可通过 PORT 寄存器在内部访问。
开发高级 I/O 扩展器的目标是提供比上面显示的简单变体功能更全面的程序。由于极其简单的串行通信,简单的 I/O 扩展器具有许多限制,并且无法利用PIC16F15244 系列中提供的以 I/O 为中心的功能,例如输入电平控制、开漏输出、可屏蔽的变化中断和弱上拉使能。
相比之下,这个更复杂的示例可以配置板载 I/O 的几乎所有功能。为了实现这个功能集,程序使用查找表来确定访问哪个寄存器或执行什么功能。图 7 显示了一个示例应用程序,其中微控制器充当键盘控制器,在按键被按下时通知主微控制器。(这个例子将在以后的文章中展开。)
高级 I/O 扩展器 - 程序结构
图 8 是器件中“寄存器”的查找表。表中的某些寄存器物理存在于每个 PIC16F15244 系列器件上,例如 TRIS 和 LAT,而其他寄存器(例如 ERROR)是“虚拟的”。虚拟寄存器特定于该程序并且仅存在于 RAM 中。
这种查找表结构的一个好处是可以灵活地重新排列寄存器,这可用于优化串行通信或减少软件故障干扰关键设置的机会。
查找表分为两个单独的函数用于读取和写入。这以代码大小为代价创建了一个简单可靠的权限方案。例如,要创建一个只读函数,只需从写查找表中省略地址即可。
高级 I/O 扩展器 - 通信
与此示例的通信比简单的 I/O 扩展器更复杂。在执行读取或写入之前,控制器必须已指示开始读取或写入的地址。
I2C 写入使用第一个数据字节来设置起始地址。然后,下一个字节用于加载该地址的数据。然后为发送的下一个数据字节增加地址。只要地址仍然可写且有效,此过程就可以重复,如下所示。如果写入无效或只读地址,则该地址不会递增,并且设备不会确认 (NACK) 以发出错误已发生的信号。
对于 I2C 读取,必须首先设置要读取的地址。通常这是通过将单个数据字节写入设备来完成的,尽管为方便起见,某些操作可能会自动设置地址。I2C 总线停止,然后以读取模式再次启动。
读取的第一个数据字节出现在地址集。读取此字节后,地址会递增,因此下一次读取发生在地址 + 1、地址 + 2 处,依此类推。如果遇到无效或只写区域,则程序停止增加地址并返回 0x00,直到停止读取。图 12 显示了两个寄存器的读取示例。
该程序存储了 8 个与 I/O 相关的设置,所有这些设置都在器件数据表中进行了更详细的说明。唯一未保存的 I/O 设置是模拟选择 (ANSEL) 和 PORT。假设 I/O 扩展器始终是数字的,因此 ANSEL 是一个常数值。PORT 值是存在于 I/O bank 上的逻辑电平,它取决于电路条件。
引用这些寄存器时,“x”(例如:TRISx)指的是可以分配的可选 I/O bank。默认情况下,此示例使用银行 C。
高级 I/O 扩展器 - 内存操作
作为一项附加功能,此代码示例支持恢复默认设置、保存、加载或保存并将其设置加载到内部非易失性存储器的功能。PIC16F15244 系列没有 EEPROM,但可以通过启用存储区闪存 (SAF) 将一小部分程序闪存 (PFM) 标记为不可执行。
图 13 显示了配置是如何在内部存储的。只有一个存储器行(32 个 14 位字)用于存储设置。此配置简化了内存管理,并将剩余的不可执行内存留作其他用途。
可配置的 I/O 设置与一个简单(软件创建的)CRC-8 校验和一起保存到 PFM,以验证配置的完整性。为了将 CRC 打包到可用空间中,它被分成 2 位的块并附加到每个配置的前 4 个设置。
如果加载的配置无法通过其校验和验证,则不会将其加载到适当的寄存器中。对于内存加载,可以在内存操作字节中指定 I/O 引脚行为,如图 14 所示。如果加载失败,引脚将保持该状态,直到重新配置。
I/O 设置可以在运行时保存并加载到四种配置中的任何一种。作为编译选项,开发人员或设计人员可以将设备设置为在启动时尝试加载配置 0 而不是默认值,这可以用作不需要完全重新编程操作的简单固件更新。如果配置 0 未通过 CRC 验证,程序将使用默认 I/O 设置启动。
设备上的默认 I/O 设置被定义为常量,开发人员可以根据应用程序的需要进行设置。这些默认值不会被程序修改,并且被认为是已知的良好值。运行内存操作“load defaults”将使用这些默认值加载寄存器。
作为故障保险,所有内存操作(不包括启动时的加载)都需要特定的字节序列来“解锁”操作。这减少了软件故障擦除或覆盖设置的机会。
如本文所示,在需要灵活 I/O 配置或在单个系统中使用大量 I/O 扩展器的应用中,简单的 8 位微控制器可以合理地替代通用 I/O 扩展器 ASIC。这两个示例所需的固件可通过以下链接在 Github 上获得。使用 Arduino Uno 的高级 I/O 扩展器的演示库和代码也在 Github 中提供。
这是 3 部分博客文章系列的第 1 部分。下一篇文章将介绍如何利用先进的 I/O 扩展器来控制 7 段显示器。
对于本系列的第 2 部分,介绍使用I/O 扩展器来控制 7 段显示器,请参阅此 Hackster Post。
对于本系列的最后一部分,包括使用 I/O 扩展器创建键盘控制器,请参阅此 Hackster 帖子。
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
全部0条评论
快来发表一下你的评论吧 !