使用 DMA 访问高速串口
Using DMA to access High Speed Serial Port
我在 C# 中使用串口组件,它运行良好!但问题是如何才能更快地处理高速(例如 2 Mbps)数据传输。
正如我对此所做的研究,我发现可以直接访问内存(使用 this link 之类的 DMA)。
谁能告诉我如何在我的应用程序中定义和使用它?
不,[c#] 标签使它遥不可及一百万英里。该网页上的代码片段不是真实的,它只是一个 "pattern"。它可以做 C# 做不到的事情,比如处理中断、获取缓冲区的物理内存地址、直接对设备寄存器进行编程。在那种能执行C#代码的机器上,不算Micro Framework,这只能靠设备驱动了。
这将是一种可以 运行 在微控制器上的代码,这种处理器不 运行 具有保护模式操作系统。即便如此,它仍然是伸展的,它通过未说明的魔法调用 DMA,例如从未真正开始传输。也没有 DMA 控制器的迹象,需要仲裁设备之间的总线访问。这是假代码。
当您使用真正的硬件时,您总是会得到一个设备驱动程序来负责与设备通信。如果设备实际上支持 DMA,非常不寻常,那么设备驱动程序程序员就不会避免使用它。您在 C# 程序中使用的 SerialPort class 使用操作系统 api,该操作系统对任何类型的串行端口设备都是通用的。它将您的 I/O 请求传递给设备驱动程序以完成工作。
操作系统 api 和设备驱动程序之间的接口由 IOCTL 覆盖。 This MSDN page 记录 Windows 的那些。 IOCTL 和 api 之间的匹配非常接近,api 层非常薄。当您仔细观察时,很明显其中 none 个与 DMA 有任何关系。他们不能,这完全是一个驱动程序实现细节。
我相信您不需要使串行访问更快,而是调整您的 c# 应用程序以更快地处理数据传输。 运行 您选择的分析器,并测量花在串口组件方法上的时间百分比。我预测这会很低,这意味着任何使串行端口更快的努力都将白费。
你完全错了。
首先,您所处的环境无法直接访问硬件 (Windows),因此如果不编写内核驱动程序(并且您不我不想,相信我)。
其次,操作系统及其驱动程序已经非常优化,如果需要使用 DMA 传输,它应该已经做到了。
第三,除非您的串行控制器支持,否则您将无法获得这些速度,而它们通常不支持,PC 的 RS232 控制器通常最高可达 115200 波特,但有些控制器最高可达 1Mb。
但还有另一种选择,没有 USB 的 USB :D
根据你的问题,我假设你正在将某种类型的微控制器与 PC 连接,并且你不想为控制器编写 USB 驱动程序(或者它没有 USB 功能),所以一个很好的选择是使用 RS-232 到 USB 电缆,它们通常支持非常快的速度,我个人使用了 FTDI RS-232 3v3,它达到 3Mb (http://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_TTL-232R_CABLES.pdf)。
最后你将编写一个普通的串行端口代码,但它将使用更扩展的 USB 接口(这是另一个优势,今天并不是所有的 PC 都带有串行端口)。
之后,要真正受益于加速,请记住为端口设置一个非常大的 read/write 缓冲区(至少 1Mb),执行非阻塞接收例程并发送大数据块(必须适合写入缓冲区)。
请记住,您的设备必须与所选速度匹配,因此,如果将其设置为 2-3Mbps,您的设备必须 运行 串行接口的速度完全相同。
这里是我描述的接收部分的例子:
SerialPort sp;
Queue<byte[]> buffer = new Queue<byte[]>();
AutoResetEvent dataAvailable = new AutoResetEvent(false);
Thread processThread;
public void Start()
{
//Start the processing thread
processThread = new Thread(ProcessData);
processThread.Start();
//Open the serial port at 3Mbps and with buffers of 3Mb
sp = new SerialPort("COM12", 3145728, Parity.None, 8, StopBits.One);
sp.ReadBufferSize = 1024 * 1024 * 3;
sp.WriteBufferSize = 1024 * 1024 * 3;
sp.DataReceived += sp_DataReceived;
sp.Open();
}
//This thread processes the stored chunks doing the less locking possible
void ProcessData(object state)
{
while (true)
{
dataAvailable.WaitOne();
while (buffer.Count > 0)
{
byte[] chunk;
lock (buffer)
chunk = buffer.Dequeue();
//Process the chunk here as you wish
}
}
}
//The receiving function only stores data in a list of chunks
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
while (sp.BytesToRead > 0)
{
byte[] chunk = new byte[sp.BytesToRead];
sp.Read(chunk, 0, chunk.Length);
lock (buffer)
buffer.Enqueue(chunk);
dataAvailable.Set();
}
}
我在 C# 中使用串口组件,它运行良好!但问题是如何才能更快地处理高速(例如 2 Mbps)数据传输。
正如我对此所做的研究,我发现可以直接访问内存(使用 this link 之类的 DMA)。 谁能告诉我如何在我的应用程序中定义和使用它?
不,[c#] 标签使它遥不可及一百万英里。该网页上的代码片段不是真实的,它只是一个 "pattern"。它可以做 C# 做不到的事情,比如处理中断、获取缓冲区的物理内存地址、直接对设备寄存器进行编程。在那种能执行C#代码的机器上,不算Micro Framework,这只能靠设备驱动了。
这将是一种可以 运行 在微控制器上的代码,这种处理器不 运行 具有保护模式操作系统。即便如此,它仍然是伸展的,它通过未说明的魔法调用 DMA,例如从未真正开始传输。也没有 DMA 控制器的迹象,需要仲裁设备之间的总线访问。这是假代码。
当您使用真正的硬件时,您总是会得到一个设备驱动程序来负责与设备通信。如果设备实际上支持 DMA,非常不寻常,那么设备驱动程序程序员就不会避免使用它。您在 C# 程序中使用的 SerialPort class 使用操作系统 api,该操作系统对任何类型的串行端口设备都是通用的。它将您的 I/O 请求传递给设备驱动程序以完成工作。
操作系统 api 和设备驱动程序之间的接口由 IOCTL 覆盖。 This MSDN page 记录 Windows 的那些。 IOCTL 和 api 之间的匹配非常接近,api 层非常薄。当您仔细观察时,很明显其中 none 个与 DMA 有任何关系。他们不能,这完全是一个驱动程序实现细节。
我相信您不需要使串行访问更快,而是调整您的 c# 应用程序以更快地处理数据传输。 运行 您选择的分析器,并测量花在串口组件方法上的时间百分比。我预测这会很低,这意味着任何使串行端口更快的努力都将白费。
你完全错了。
首先,您所处的环境无法直接访问硬件 (Windows),因此如果不编写内核驱动程序(并且您不我不想,相信我)。
其次,操作系统及其驱动程序已经非常优化,如果需要使用 DMA 传输,它应该已经做到了。
第三,除非您的串行控制器支持,否则您将无法获得这些速度,而它们通常不支持,PC 的 RS232 控制器通常最高可达 115200 波特,但有些控制器最高可达 1Mb。
但还有另一种选择,没有 USB 的 USB :D
根据你的问题,我假设你正在将某种类型的微控制器与 PC 连接,并且你不想为控制器编写 USB 驱动程序(或者它没有 USB 功能),所以一个很好的选择是使用 RS-232 到 USB 电缆,它们通常支持非常快的速度,我个人使用了 FTDI RS-232 3v3,它达到 3Mb (http://www.ftdichip.com/Support/Documents/DataSheets/Cables/DS_TTL-232R_CABLES.pdf)。
最后你将编写一个普通的串行端口代码,但它将使用更扩展的 USB 接口(这是另一个优势,今天并不是所有的 PC 都带有串行端口)。
之后,要真正受益于加速,请记住为端口设置一个非常大的 read/write 缓冲区(至少 1Mb),执行非阻塞接收例程并发送大数据块(必须适合写入缓冲区)。
请记住,您的设备必须与所选速度匹配,因此,如果将其设置为 2-3Mbps,您的设备必须 运行 串行接口的速度完全相同。
这里是我描述的接收部分的例子:
SerialPort sp;
Queue<byte[]> buffer = new Queue<byte[]>();
AutoResetEvent dataAvailable = new AutoResetEvent(false);
Thread processThread;
public void Start()
{
//Start the processing thread
processThread = new Thread(ProcessData);
processThread.Start();
//Open the serial port at 3Mbps and with buffers of 3Mb
sp = new SerialPort("COM12", 3145728, Parity.None, 8, StopBits.One);
sp.ReadBufferSize = 1024 * 1024 * 3;
sp.WriteBufferSize = 1024 * 1024 * 3;
sp.DataReceived += sp_DataReceived;
sp.Open();
}
//This thread processes the stored chunks doing the less locking possible
void ProcessData(object state)
{
while (true)
{
dataAvailable.WaitOne();
while (buffer.Count > 0)
{
byte[] chunk;
lock (buffer)
chunk = buffer.Dequeue();
//Process the chunk here as you wish
}
}
}
//The receiving function only stores data in a list of chunks
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
while (sp.BytesToRead > 0)
{
byte[] chunk = new byte[sp.BytesToRead];
sp.Read(chunk, 0, chunk.Length);
lock (buffer)
buffer.Enqueue(chunk);
dataAvailable.Set();
}
}