我们已经完成了一个大理论块,展示了如何为Redd复合体构建FPGA子系统。 如何组织FPGA与综合系统的中央处理器之间的通信; 将高速数据流保存在直接连接到FPGA的RAM中,以便随后轻松地传输到中央处理器(反之亦然,将数据放入此RAM中以便随后快速输出到通道)是多么容易。 我们回顾了Nios II处理器跟踪技术。 我们能够优化基于Nios II的处理器系统的性能,从而使工作尽可能高效。 总的来说,我们已经研究了所有必要的最低限度理论,现在是时候通过设计一种不是很复杂但实际上有用的设备来进行实践了……但是有一个BUT。
从文章的评论中,我注意到一些读者认为Redd和FPGA就像列宁和党一样。 他们之间有着千丝万缕的联系。 实际上,事实并非如此。 我只是想通过一些有趣的话题开始关于Redd的对话,但是还有什么比FPGA更有趣的呢? 好了,开始对话,一眼打断是愚蠢的。 最后,大逻辑块已完成。 为了表明FPGA与Redd的差距很大,我建议撰写大约三篇关于与它们无关的内容的文章。 好了,并且完成了此模块,已经进入了FPGA实践。

引言
最令人惊讶的是,一旦我决定对其他话题进行题外话,好老板就把我与一个正在进行VHDL语言和Xilinx FPGA工作的项目进行了艰难的战斗。 首先,这就是为什么我很长一段时间都没有拿起笔的原因,其次,很明显,准备实用的文章需要进行大量的实验。 同时处理VHDL / Verilog和Xilinx / Altera有点困难。 因此,无论如何都必须打破有关FPGA的故事。
这样啊 在该
系列的
第一篇文章中,我们已经检查了Redd复合体的结构图。 让我们再做一次。

在今天的文章中,Linux专家不太可能找到很多有价值的信息,但是有必要对这些图片进行简要介绍。 像我一样习惯于Windows的人,会发现一整套现成的技术,可让您使用该组合系统。 总的来说,本文将把这些读者和其他读者的技能带到一个共同的标准。
UART模块(串行端口)
在框图中,我们看到了实现4个串行端口(UART)的FT4232控制器:

但是,如果您在全球范围内谈论更多内容,那么Redd中心就不会有四个,而是六个串行端口。 刚才提到的四个具有CMOS电平,另外两个则焊接在主板上,因为该组件基于普通PC。

因此,它们具有电平-RS232(正负12伏)。 RS232端口-一切都很清晰,以两个标准DB-9连接器的形式显示,

在哪里寻找具有CMOS电平的线路? 通常-在公共连接器上。 其引脚排列在电路图中显示。 除其他外,还有与UART相对应的触点。

从外部看,此连接器如下所示:

如何使用它取决于任务。 您可以制作线束以连接每个设备。 如果有人使用Redd复合体来测试定期制造的同类设备,则此方法很有用。 但是综合设施的主要目的仍然是调试正在开发的设备。 在这种情况下,以临时方式连接它会更容易。 对于所有商品,此临时模式在屏保上均可见:Aruino电线直接插入连接器中。 当然,对联系人进行计数仍然是一种享受,如果它们不小心飞了出去,则恢复切换非常困难,因此更容易从头开始重新连接所有内容。 因此,为方便起见,有一块转接板,至少可以使用两排连接器(至少使用相同的Arduino接线)连接到转接板。

UART软件访问
串行端口是一个行之有效的,标准化的元素,因此使用它,不是通过某些特定的FTDI库,而是通过标准方法。 让我们看看这些工具在Linux中的外观。
端口名称
从网络上的许多文章和论坛中可以得出,USB串行适配器提供的端口名称的格式为/ dev / ttyUSB0,/ dev / ttyUSB1,依此类推。 在Linux中,可以使用与查看普通目录相同的命令来查看所有设备(实际上,设备是相同的文件)。 让我们看看系统中有什么名字。 我们给出命令:
ls /开发/
我们感兴趣的名称以红色突出显示。 其中很多东西。 哪个端口对应什么? 那些精通Linux的人在任何情况下都知道数千种咒语。 但是对于仍然使用Windows 3.1的人(与当时相当生气的老妇人RT-11并行使用),仍然很难记住,但随着年龄的增长,新的人就很难记住了。 因此,使用简单的方法每次都容易找到所有内容。 我用绿色框标明了这条简单路径的入口。 条件子目录序列。 现在我们在看
/ dev /名称空间。 让我们看看空间
/ dev / serial :

太好了! 我们深入研究层次结构,查看空间
/ dev / serial / by-id 。 只是向前看,我要说的是,为了显示正确,您需要使用带有
-l开关的
ls命令 (感谢我的老板的澄清)。 也就是说,我们给出命令:
ls –l / dev / serial / by-id
一方面,一切都很好。 现在我们知道空间
/ dev / ttyUSBX中的哪个名称对应于哪个设备。 特别是,由FT4232(Quad)桥组织的端口的名称从
ttyUSB3到
ttyUSB6 。 但是,另一方面,当我考虑此站点时,我意识到在巴黎的砝码和度量衡室内必须一定有一个放置混乱标准的房间...因为某种程度上,您需要能够衡量其价值。 好吧,假设可以很容易地解释缺少端口
/ dev / ttyUSB0和
/ dev / ttyUSB1的情况 。 但是,如何解释基于已安装的FTDI网桥的后代的“本地”端口是从前三个开始编号的,为特定项目插入的第三方Prolific控制器的端口号是2? 在这样的环境中如何工作? 明天会有人将另一个控制器插入综合大楼(因为综合大楼允许不同的开发人员组同时使用不同的设备),并且端口再次移出。 对于正在运行的应用程序,我们需要在配置文件中注册哪些端口?
事实证明,一切还不错。 首先,黄色名称
/ dev / ttyUSB3和蓝色名称
/ dev / serial / by-id / usb-FTDI_Quad_RS232-HS-if00-port0是同一设备的两个别名。 第二个选项也可以显示为端口名,但是它比第一个更永久。 是的,在这种情况下,一切都有些糟糕。 可以将基于FT4232的外部控制器插入该组合系统,并且已经有必要对其编号进行处理。 这里“第二”是我们的援助。 即,另一种替代命名约定。 我们记得
/ dev / serial目录不仅包含
/ by-id子目录,还包含
/ by-path子目录。 我们检查其内容(位于下图的底部,红线下方)。

这里的一切都与物理架构相关。 我已经说过很多次了,大楼内部的所有控制器都焊接到板上了,因此内部层次结构不会改变。 因此,名称
/dev/serial/by-path/pci-0000:00:15.0-usb-0:6.5:1.0-port0将是最难的。
总的来说,我们可以通过以下方式搜索端口名称(应该执行一次,您可以将复杂实例的结果放入表中并不断使用):
- 发出命令ls –l / dev / serial / by-id 。
- 发出ls –l / dev / serial / by-path命令 。
- 从点1的结果中,找到与所需网桥的所需端口相对应的端口名称。 在段落2的结果中找到相同的端口名称。取与该段落相对应的物理名称。
对于主板上控制器所服务的端口,一切都有些复杂。 在这里,您无法从最简单的命令“
ls / dev ”开始,但是您必须记住一些内容(或者,至少记住您可以在此处联系以获得帮助)。 到处都说典型的端口名称是
ttyS0-ttyS3 。 问题仍然存在,我们系统中的真实端口是什么? 我发现以下咒语回答了这个问题:
ls / sys / class / tty / * /设备/驱动程序这是系统对此的响应:

事实证明,我们需要使用名称
/ dev / ttyS2和
/ dev / ttyS3 。 为什么-我不知道。 但是有一件令人高兴的事:这里没有预见到任何特殊的变化,因此可以记住并使用这些常量,而不必担心它们会发生变化。
代码开发
在开发时,您应该使用出色
的POSIX操作系统串行编程指南 (获得的第一个直接链接是
https://www.cmrr.umn.edu/~strupp/serial.html ,但是没人知道它可以使用多长时间)。 告知如何使用全套信号尤为重要,因为综合系统中的端口已完全实现。 没错,今天我们将仅使用Tx和Rx线。
通常,我会给出示波器的结果,但现在事实证明我处于几乎是真实的状况:该综合设施位于我的手无法触及的位置,因此无法连接示波器探头。 为了至少看到一些结果,应我的要求,同事们根据以下经典方案在综合大楼中添加了几篇文章:

让我们尝试从一个端口转移到另一个端口。 在我们的例子中,端口
/dev/serial/by-path/pci-0000:00:15.0-usb-0:6.5:1.2-port0和
/dev/serial/by-path/pci-0000:00:15.0-已连接USB-0:6.5:1.3-PORT0 。
我们已经在
上一篇文章中讨论了Redd中央处理器的程序是如何编写的,因此今天我们将仅局限于在
POSIX操作系统串行编程指南文档的印象下编写的程序文本。 实际上,最有趣的一点是将接收策略切换为非阻塞读取,其余的则微不足道。 不过,考虑到网络上与此主题相关的示例中的混乱情况,最好准备一个简单的示例(稍后将显示,即使基于此出色文档的示例也无法100%正常工作,下面的代码不同于其中描述的规范,但下面有更多说明)。
相同的示例代码#include <cstdio> #include <unistd.h> /* UNIX standard function definitions */ #include <fcntl.h> /* File control definitions */ #include <errno.h> /* Error number definitions */ #include <termios.h> /* POSIX terminal control definitions */ int OpenUART(const char* portName, speed_t baudRate) { // int fd = open(portName, O_RDWR | O_NOCTTY | O_NDELAY); // if (fd == -1) { return fd; } // fcntl(fd, F_SETFL, FNDELAY); // termios options; tcgetattr(fd, &options); // , // , . ... cfsetspeed(&options, baudRate); // ... // 1 , , 8 options.c_cflag &= ~PARENB; options.c_cflag &= ~CSTOPB; options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; options.c_cflag |= (CLOCAL | CREAD); // , ... tcsetattr(fd, TCSANOW, &options); return fd; } int main() { printf("hello from ReddUARTTest!\n"); int fd1 = OpenUART("/dev/serial/by-path/pci-0000:00:15.0-usb-0:6.5:1.3-port0", 9600); int fd2 = OpenUART("/dev/serial/by-path/pci-0000:00:15.0-usb-0:6.5:1.2-port0", 9600); if ((fd1 != -1) && (fd2 != -1)) { static const unsigned char dataForSend[] = {0xff,0xfe,0xfd,0xfb}; // write(fd1, dataForSend, sizeof(dataForSend)); unsigned char dataForReceive[128]; ssize_t cnt = 0; // , , // int readSteps = 0; // , while (cnt < (ssize_t)sizeof(dataForSend)) { readSteps += 1; ssize_t rd = read(fd2, dataForReceive + cnt, sizeof(dataForReceive) - cnt); // - , if (rd <= 0) { usleep(1000); } else // - { cnt += rd; } } // printf("%d read operations\n", readSteps); printf("Read Data: "); for (unsigned int i = 0; i < cnt; i++) { printf("%X ", dataForReceive[i]); } printf("\n"); } else { printf("Error with any port open!\n"); } // if (fd1 != -1) { close(fd1); } if (fd2 != -1) { close(fd2); } return 0; }
运行-我们得到了预期的结果:
hello from ReddUARTTest! 14 read operations Read Data: FF FE FD FB
可以看出,4个字节占用了14次尝试,也就是说,读取没有阻塞。 有时系统返回“无新数据”状态,程序进入睡眠状态一毫秒。
总的来说,一切都很好,但是如果没有示波器,我无法确定基于同一芯片的两个端口确实可以设置速度。 我已经跳了一个事实,那就是速度相同(因为他有一个控制器),但不是我订购的那个。 让我们至少以某种方式检查它是否至少受到控制。 为此,我将接收端口的速度设置为发送端口的速度的两倍。 并且了解了数据传输过程的物理原理,您可以预测该数据在接收期间如何失真。 让我们以图形形式看一下0xff字节的传输。 S-起始位(始终为零)位,P-停止位(始终为1),0-7-数据位(对于常数0xFF-所有单位)。

现在,让此视图覆盖以两倍速度运行的接收器如何查看所有内容:

太好了 应当接受值“ 1111 1110”(数据向最低有效位发送),即0xFE。 传输值的后半部分不影响接收,因为单位对应于线路中的静音。 也就是说,我们传输了一个字节,一个字节也会来。
我们将构建相同的图形进行验证,该图形将对应于传输的0xFE值:

期望值为“ 1111 1000”或0xF8。 好吧,让我们验证一下传递值0xFD的期望值:

我们得到的值为0xE6。 好吧,对于传输的值0xFB,我们得到了接收的0x9E(您可以绘制图形并亲自查看)。 太好了! 我们在测试应用程序中更改了一行,用19200代替了9600的速度:
int fd2 = OpenUART("/dev/serial/by-path/pci-0000:00:15.0-usb-0:6.5:1.2-port0", 19200);
我们开始并得到以下工作结果:
hello from ReddUARTTest! 9 read operations Read Data: FE F8 E6 9E
顺便说一句,我没有白白进行这项检查。 起初,我使用了其他速度设置功能(cfsetispeed / cfsetospeed对),但它们没有起作用! 通过此测试,该问题得以及时发现并解决。 使用设备时,您永远无法相信直觉。 一切都要检查!
电源线管理220伏
通常,220伏特电源线与本文的主题(FTDI桥)无关,但与本节的主题(串行端口)有关。 让我们快速看一下它们。

当我们列出端口时,我们看到了这个名称:

这是一个虚拟串行端口。 它是如此虚拟,以至于它具有什么参数(端口速度,位数,奇偶校验格式等)都无关紧要。 无论他设置了什么参数,他仍将能够完美地处理命令。 这些团队控制着综合大楼的电源插座。

在开发命令系统时,决定放弃复杂的命令界面。 尽管该字节是文本的(因此可以在调试时方便地从终端传输它),但是管理占用一个字节,而没有框架字符串和其他装饰。 简洁的解释很容易:字符串接口允许您处理不安全的UART通道中的干扰。 但就我们而言,从物理上讲,工作是通过USB通道进行的,该通道受循环控制代码保护。 处理返回流需要编写其他代码或不断刷新缓冲区,这并不总是很方便。 这就是为什么没有字符串基准,没有答案的原因。 可以认为该通道是稳定的。 如果您需要响应,则可以明确要求它。 也就是说,始终可以通过在命令后发送一个额外的字节来轻松检查该块的性能。
考虑可以发送的命令:
命令“?” (问号)是唯一返回响应的代码。 作为响应,总是有3个字节,每个字节对应于插座之一的状态。 实际上,状态对应于命令。 例如,“ abc”-现在所有三个插座都已关闭,“ Abc”-第一个已打开,第二和第三个已关闭,依此类推。
对于使用该子系统的实验,我建议不要编写一个特殊的程序(与先前给出的程序没有什么不同,只有发送到端口的数据会有所不同),而是使用OS工具并与套接字进行交互播放。
经过大量的实验,通过cat命令跟踪端口并使用echo程序在并行窗口中发送命令后,我意识到由于某种原因,我无法在一对基于腻子的ssh终端上取得成果(即使仅使用那些只能使用这些端口的端口)他对程序进行了完美的实验)。 因此,我必须安装标准的minicom程序。 让我提醒您安装命令:
须藤apt-get minicom接下来,使用命令运行它:
minicom –D / dev / ttyACM0端口名称很短,因为使用手动实验最容易输入。 与往常一样,在软件工作中,最好使用与硬件层次结构相关的名称。 再一次,我注意到我没有配置任何其他端口参数,因为它是虚拟的。 它可以使用任何设置。
然后,我们在终端中按问号,然后立即(没有换行)得到响应

这意味着当前所有插座都已关闭。 假设我们要打开第二个插座。 按大写字母“ B”。 屏幕上没有反应。 再按一次“?”,我们将在答案中换行:

一切正常。 不要忘记关闭220伏(命令“ b”)。 您可以通过依次按ctrl + A,然后按X退出终端。实验完成。
SPI和I 2 C轮胎
SPI总线(也可以在Quad-SPI模式下工作)和I
2 C结合通用桥实现。 也就是说,通常,该组合系统具有两个桥接器,每个桥接器都可以在SPI模式或I
2 C中打开。在结构图中,相应部分如下所示:

从电路图中可以看到接通最终总线的本质。 仅考虑以下两个控制器之一:

因此,SPI和I
2 C总线不会以任何方式相交。 共同使用的限制仅由FT4222H控制器中FTDI施加的限制来确定。 不幸的是,文档指出一次只能激活一个接口:

如何管理CFG1_0..CFG1_1和CFG2_0..CFG2_1行,我们将在下一篇文章中讨论。 现在我们相信它们都被取消了。
通常,在文件
FT4222H USB2.0 TO QUADSPI / I2C BRIDGE IC中对控制器的工作进行了很好的描述,因此,我们将不考虑控制器工作模式的功能。 从提到的文档中,一切都非常清楚。
至于软件支持,可以在同样引人注目的文档
AN_329 LibFT4222用户指南中找到其描述。 我们已经在FTDI桥上进行了两次工作:在
本文的下半部分和
本文的下半部分。 因此,将本文档与这些文章进行比较,您可以快速弄清楚并开始编写自己的代码。 让我仅向您展示将数据发送到SPI总线的参考代码,而无需详细说明其实现细节,令人痛苦的是,它似乎已被FT2232进行了分析。
将数据发送到SPI总线的代码。 #include "../ftd2xx/ftd2xx.h" #include "../LibFT4222/inc/LibFT4222.h" void SpiTest (int pos) { FT_HANDLE ftHandle = NULL; FT_STATUS ftStatus; FT4222_STATUS ft4222Status; // ftStatus = FT_Open(pos, &ftHandle); if (FT_OK != ftStatus) { // open failed printf ("error: Cannot Open FTDI Device\n"); return; } ft4222Status = FT4222_SPIMaster_Init(ftHandle, SPI_IO_SINGLE, CLK_DIV_4, CLK_IDLE_LOW, CLK_LEADING, 0x01); if (FT4222_OK != ft4222Status) { printf ("error: Cannot switch to SPI Master Mode\n"); // spi master init failed return; } uint8 wrBuf [] = {0x9f,0xff,0xff,0xff,0xff,0xff,0xff}; uint8 rdBuf [sizeof (wrBuf)]; uint16 dwRead; ft4222Status = FT4222_SPIMaster_SingleReadWrite (ftHandle,rdBuf,wrBuf,sizeof (wrBuf),&dwRead,TRUE); if (FT4222_OK != ft4222Status) { printf ("error: Error on ReadWrite\n"); } else { printf ("received: "); for (int i=0;i<6;i++) { printf ("0x%X ",rdBuf[i]); } printf ("\n"); } FT4222_UnInitialize(ftHandle); FT_Close(ftHandle); }
SPI总线零件
微控制器的代码开发人员通常将SPI总线用作预定频率的发生器。 实际上,通过GPIO线纯粹以编程方式生成的脉冲取决于许多因素。 首先,分支,旋转循环需要处理器周期。 其次,中断,DMA和其他不可预见的因素可能会干扰处理器。 SPI或多或少是稳定的,知道自己设法将字节放入缓冲区。 SPI模块的典型应用与RGB本身没有直接关系,它是RGB LED的控制,对此,设置脉冲持续时间的精度非常重要。
不幸的是,这对于FTDI桥是不可接受的。 上面的代码片段将在总线上生成这些脉冲:

在这种情况下,从该总线的角度来看,没有违反SPI操作规则,一切正常。 请记住,控制器上惯用的自定义解决方案在这里不起作用。 的确,该综合大楼有很多免费的USB连接器。 所有非标准模块都可以单独开发并连接到它们。
轮胎零件I 2 C
唯一有意义的是表明在组合系统的一侧没有用于I
2 C总线的上拉电阻。 但这很正常:在工作设备的侧面,仍然有一部电梯。 如今,上拉电压可以达到任何电压,因此将其设置在目标设备上是合乎逻辑的。
结论
今天,我们在使用FTDI桥实施的轮胎方面获得了实践技能。 通常,与他们合作是标准的,只是所有知识都在一篇文章中进行了总结,以免一点一点地寻找它们。 下次,我们将考虑一个基于STM32控制器实现的控制非标准设备的模块。 在结构图中,此部分对应于它:

但实际上,那里的一切都更加有趣...