5

我正在为 STM32F3 mc (STM32F3-Discovery) 编写嵌入式代码。我需要向 UART 输出一些数据,并且为此使用 DMA,因为这使我可以专注于传感器读取和数据处理,而不是等待字节传输完成。然而问题是我必须结合:

  1. 格式化输出(即来自 printf 的一些输出)
  2. 多次连续打印(发生在上一次打印完成之前)

所以我在考虑一个循环缓冲区。但我认为我不知道如何让 sprintf 尊重缓冲区的结尾并继续写入缓冲区的开头。我当然可以创建另一个临时缓冲区,在那里打印并逐字节复制,但它对我来说并不优雅。

4

2 回答 2

2

一种解决方案可能是实现您自己sprintf的能够与环形缓冲区一起使用的解决方案。不幸的是,这不会帮助您解决更基本的问题:如果您的 ringbuffer 已满并且您调用 ,您会怎么做sprintf

如果您的记忆力负担得起,我会建议这个问题的另一种解决方案:

这个想法基于两个链接的缓冲区列表(一个列表用于空闲缓冲区,一个列表作为传输队列)。缓冲区的大小相同,因此它们可以存储最坏情况长度的字符串。缓冲区构建一个简单的堆,其中分配/解除分配只是从空闲列表或传输列表中使元素出队/入队。

具有相同大小的缓冲区可确保您在动态分配内存时不会受到外部碎片影响,例如“棋盘式”。为这项工作构建自己的堆还可以让您完全控制可用于传输任务的总缓冲区大小。

我可以想象这个运行如下:

  1. 您从空闲列表中分配一个缓冲区以将数据呈现到其中。
  2. 使用您的渲染函数(例如 sprintf)渲染缓冲区中的数据
  3. 将要发送的数据附加到传输队列(必要时触发传输)

对于 DMA 传输,您处理传输结束 IRQ。在那里,您将刚刚传输的缓冲区移动到“空闲列表”并为队列中的下一个缓冲区设置传输。

此解决方案不会是内存效率最高的,但运行时效率很好,因为您只写入一次内存并且分配/释放只是在某处获取/存储指针。当然,您必须确保您的应用程序和分配/解除分配的 IRQ 之间没有竞争条件。

也许这个想法会给你一个灵感来解决你的需求。

于 2013-02-17T22:35:27.790 回答
1

您可以近似的一种方法是将您的 printf 缓冲区分配为环形缓冲区大小的 2 倍,然后将前半部分用作环形缓冲区。检测溢出,将后半部分的溢出复制回前面(环形部分)。像这样的东西:

void xprintf(const char *format, ...)
{
  int written;
  va_list args;
  va_start(args, format);
  written = vsnprintf(buffer, HALF_BUFFER_SIZE, format, args);
  va_end(args);
  if (buffer + written > bufferStart + HALF_BUFFER_SIZE)
  { // time to wrap
    int overflow = (buffer + written) - (bufferStart + HALF_BUFFER_SIZE); 
    memmove(bufferStart, bufferStart + HALF_BUFFER_SIZE, overflow;
    buffer = bufferStart + overflow;
  }
}
于 2013-02-19T02:01:18.713 回答