Dmitry Pervushin 介绍了如何通过使用板的扩展连接器创建的串行外设接口 (SPI) 将实时时钟芯片连接到设计,从而为基于 OMAP 的 PandaBoard 添加功能。
基于 OMAP 的 PandaBoard 作为 Android 开源平台 (AOSP) 包含的个开放式移动开发平台向开发人员开放后,热情的开源社区开始在该平台上构建激动人心的项目,并提出有关扩展平台功能超越传统平台的问题方法。
虽然使用 PandaBoard 的开发人员的经验水平各不相同——从寻求编程建议的人到选择连接接口的设计师——他们的使命始终相同:创建引人注目的开源产品或应用程序,并与 PandaBoard 社区分享知识.
在这方面,这篇操作指南文章概述了可通过 PandaBoard 的扩展连接器使用的串行编程接口,并作为插图解释了如何使用它连接到实时时钟 (RTC) 芯片。
什么是 SPI?
SPI 代表“串行编程接口”,这是一个初来自摩托罗拉的简单标准。有时它被称为“4 线”接口,与 1 线、2 线和 3 线串行总线形成对比,因为该接口定义了四根线:MOSI(master-out-slave-in),MISO(master-in -slave-out)、SCLK(来自主机的串行时钟)和芯片选择信号(CS#)。使用多个片选允许多个设备连接到同一个主机。
为什么选择 SPI? 有多种接口可用于将设备连接到 PandaBoard:SPI、I2C、USB 等。然而,使用 SPI 连接外围设备(下图 1)可能是选择,因为它更便宜、易于连接和调试,并且表现出相对较好的性能。
图 1. 设置 PandaBoard SPI 连接
什么样的设备可以连接到SPI?
几乎任何类型的设备都可以连接到 SPI 总线,从简单的模数转换器到以太网适配器。甚至 SD 卡也有“SPI 模式”,允许开发人员将它们连接到 SPI 总线。
Linux 中的 SPI 子系统得到了很好的支持,并为范围广泛的 SPI 设备提供了驱动程序。有用于 MTD(SPI 闪存)、SD 控制器和以太网适配器的驱动程序。Linux 内核中的 SPI 子系统还提供通用的“spidev”驱动程序,该驱动程序向用户空间公开 API 并允许控制内核外部的外围设备。
如何将 SPI 设备连接到 PandaBoard
PandaBoard 提供了一个扩展连接器,您可以在其中找到与多通道串行端口接口 (MCSPI) 相关的引脚。这些引脚是:
引脚 4 MCSPI_CS3
引脚 10 MCSPI_CS1
引脚 12 MCSPI_SIMO
引脚 14 MCSPI_CS2
引脚 16 MCSPI_CS0
引脚 18 MCSPI_SOMI
引脚 20 MCSPI_CLK
下面的图 2显示了 CS0/CS1/CS2/CS3 线路,多允许四个设备连接到该主机。设备很可能会连接到 MCSPI_SIMO、MCSPI_SOMI、MCSPI_CLK 和 MCSPI_CS0。
图 2:PandaBoard CS0/CS1/CS2/CS3 线
同样重要的是,PandaBoard 在控制引脚上有 1.8V,但上面的示例 DS3234 芯片需要 5V。幸运的是,有诸如 PCA9306 之类的“电平转换器”,它允许双向电压电平转换器。将 1.8V 和 5V 作为 PCA9306 的 VREF1 和 VREF2 引脚施加,并将 ENABLE 引脚驱动为高电平后,该芯片在引脚 SDA1/SCL1 和 SDA2/SCL2 之间转换电平。
要将 DS3234 与 PandaBoard 一起使用,开发人员应至少购买两个电平转换器,并将 PandaBoard 扩展连接器的引脚 MCSPI_SOMI、MCSPI_SIMO、MCSPI_CS0 和 MCSPI_CLK 连接到电平转换器的一侧,将 MISO、MOSI、SS 和 CLK 连接到另一侧。示例原理图如下面的图 3 所示。
图 3. 使用 DS3234 芯片的原理图
Linux 中的 SPI 驱动程序堆栈
Linux 内核提供了以下与 SPI 子系统相关的代码片段:
* SPI 主机驱动程序
* SPI 功能设备驱动程序,通常称为“协议主机”
* SPI 板信息
SPI 主驱动程序是处理 SPI 控制器本身的驱动程序。他们知道“如何”将字节发送到任何外围设备。
幸运的是,Linux 内核已经为 OMAP 处理器的 MCSPI 控制器提供了驱动程序,因此您可能不需要创建或重新实现这一控制器,除非发现错误或需要显着提高性能。
但是,在任何一种情况下,在开始更改之前三思而后行。这些类型的设备驱动程序提供了内核级 API,该 API 对内核驱动程序可见且仅供内核驱动程序使用。
SPI 功能设备驱动程序或 SPI 协议主机处理连接到 SPI 总线的外围设备。这些驱动程序知道向/从设备发送/接收“什么”。这些设备驱动程序通常公开可由另一个子系统使用的用户级 API(如spidev那样)或内核级 API。
例如,SPI 闪存驱动程序创建 MTD(内存技术设备)驱动程序,它作为常规块设备 ( mtdblockN ) 和字符设备 ( mtdN ) 可见,因此用户可以利用 MTD 实用程序以统一的方式访问闪存.
稍后讨论的另一个例子是实时时钟芯片 DS3243,它连接到 SPI 总线并为用户空间提供通用的 RTC 接口,因此可以使用 hwclock 实用程序进行读/写 。
SPI 板信息是机器相关代码的一部分,该代码执行 SPI 设备向 SPI 子系统的注册。因为 SPI 设备通常是硬连线到电路板并且很少能够枚举它们,所以它们必须在 Linux 内核的某个地方进行硬编码。
人们可能必须将类似的代码片段添加到电路板定义文件中,在我们的示例中将添加到arch/arm/mach-omap2/board-omap4panda.c 中。
什么是必要的内核更改?
例如,SPI 总线和 USB 是不同的。USB 设备提供有关自身的信息,并且可以由总线控制器驱动程序查询。另一方面,SPI 设备无法做到这一点。SPI 主驱动程序永远没有机会知道什么连接到某些特定的 CS#(甚至知道是否有连接),除非提供一些信息。
板相关代码通过调用函数spi_register_board_info进行注册。它有两个参数:连接的设备列表和该列表的大小。该示例可能类似于以下内容:
static const struct spi_board_info panda_spi[] __initconst = {
{
.modalias = “spidev”,
.bus_num = 1,
.chip_select = 1,
.max_speed_hz = 1000,
.mode = SPI_MODE_1,
}, {
.modalias = “ds3234”,
.bus_num = 1,
.chip_select = 0,
.max_speed_hz = 400000,
},
};
另外,在函数omap4_panda_init中注册这个spi_board_info数组:
spi_register_board_info(panda_spi, ARRAY_SIZE(panda_spi));
在此代码片段中,注册了两个 SPI 设备。个设备是连接到 SPI 总线 1 (McSPI1) 上 CS#1 的通用设备,由“spidev”控制。它的速度为 1kHz,这只是为了示例,因为实际速度可以达到 48MHz。
第二个设备连接到 CS#0,速度为 400kHz(足够用于 RTC)并由“ds3234”驱动程序控制。它是一个很好的“SPI 功能设备驱动程序”,并且正在完成通过 SPI 总线交换数据的所有工作。
使用连接到 PandaBoard 的设备
首先,因为内核已被修改,所以必须重新编译。确保所有必要的选项都已打开是个好主意:
CONFIG_SPI = y
CONFIG_SPI_MASTER = y
CONFIG_SPI_OMAP_24xx = y
CONFIG_RTC_DRV_DS3234 = y
CONFIG_SPI_SPIDEV = y
一个选项可以设置为“m”,这意味着这将是一个单独的模块。在这种情况下,不要忘记更新模块目录。内核编译启动后,会出现如下信息:
ds3234 spi1.0: rtc : 将 ds3234 注册为 rtc0
这意味着 ds3234 驱动程序已附加到 SPI 设备,并通过/dev/rtc0 公开其 RTC 接口。现在可以运行任何工具来访问 RTC(hwclock是一个非常有用的实用程序):
# hwclock –显示
您还可以通过spidev接口访问设备 ,它允许用户空间应用程序自己发送/接收字节,例如,使用此脚本:
导入操作系统,系统
def spidev_test(devnode):
#
# 非常简单。没有异常处理,只有五个步骤:
#
# 1. 打开设备
spi = os.open(devnode, os.O_RDWR, 0777)
# 2. 写入单个零字节
write_bytes = “x00”
w_res = os.write(spi, write_bytes, len(write_bytes))
if written != len(write_bytes):
raise Exception(“Wrote less bytes than requested”)
# 3. 读回 8 个字节
rd = 8
rd_bytes = os.read(spi, rd)
if len(rd_bytes) != rd:
raise Exception(“Read less than expected”)
# 4. 打印结果
print [“%x” % ord(rd) for rd in rd_bytes]
# 5. 关闭句柄
os.close(spi)
if __name__ == “__main__”:
spidev_test (“/dev/spidev1.0”)
sys.exit(0)
else:
打印“你想如何使用 spidev-test?”
该脚本将尝试向设备发送一个零字节,并打印 8 个字节的响应。如果将 spidev 驱动程序而不是 ds3234 附加到 DS3234 设备(在上面的代码片段中,只需将“ds3234”替换为“ spidev ”),输出可能如下所示:
# python spi-sample.py
['5', '10', '12', '2', '1', '5', '12']
这只是一个演示 SPI 交换的示例。此数据的解析通常由 ds3234 驱动程序完成。然而,这可能是开始开发自己的协议驱动程序的一个很好的起点,尤其是当一些新的和未知的东西连接到 Linux 内核时。
调试 SPI 交换
有时会出错,并且不会发生预期的结果。这里有一些简单的规则,可以帮助揭示 SPI 交换问题的原因:
* 仔细检查接线。它可以节省大量调试时间!
* 确保设备已正确供电。
* 将时钟降低到值(1 kHz 是一个很好的设置)。
* 启用CONFIG_DEBUG_KERNEL 和CONFIG_SPI_DEBUG ,这将增加内核打印的调试信息量。* 在 SPI 主驱动程序源 ( drivers/spi/omap2_mcspi.c )的顶部
放置一行“ #define VERBOSE ”。 这将打印从 SPI 设备发送/接收的所有数据。
免责声明: 凡注明来源本网的所有作品,均为本网合法拥有版权或有权使用的作品,欢迎转载,注明出处。非本网作品均来自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。