Formatted print to circular buffer -- xprintf

本文探讨了STM32F3微控制器使用DMA进行UART打印时遇到的问题,并提出了一种利用环形缓冲区实现格式化输出的方法。通过采用自定义的sprintf函数和双缓冲区技巧,确保了在数据传输过程中可以继续接收传感器数据和处理数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

http://stackoverflow.com/questions/14926294/formatted-print-to-circular-buffer

 

I'm writing embedded code for STM32F3 mc (STM32F3-Discovery). I need to output some data to UART and I'm using DMA for this as this allows me to concentrate on sensors reading and data processing rather than on waiting for byte transmission completion. The issue however is that I have to combine:

  1. Formatted output (i.e. some from of printf)
  2. A number of consecutive prints (that occur before previous print has finished)

So I'm thinking about a circular buffer. But I don't think I know how to make sprintf to respect the end of buffer and continue writing to the beginning of the buffer. I can of course create another temporary buffer, print there, and copy byte-by-byte, but it doesn't look elegant to me.

One solution could be to implement your own sprintf which is able to work with ring buffers. Unfortunately this won't help you out regarding a more basic problem: What would you do if your ringbuffer is full and you invokesprintf?

If your memory situation can afford it, I'd suggest another solution for this problem:

The idea is based on two linked lists of buffers (one list for the free buffers, one list as transmit queue). The buffers are equally sized so they can store a worst case length string. The buffers build a simple heap where allocation/deallocation is only dequeuing/enqueuing an element either from the free or from the transmission list.

Having equally sized buffers guarantees you don't suffer from outer fragmentation effects like "checkerboarding" while allocating memory dynamically. Building your own heap for this job would also give you full control over the total buffer size available for transmission tasks.

I could imagine this running as follows:

  1. You allocate a buffer from the free list to render the data into.
  2. Use your render functions (suchas sprintf) to render the data in the buffer
  3. Append the data to be sent to the transmission queue (and trigger a transmission if necessary)

For the DMA transfer you handle the transfer end IRQ. There you move the buffer just transferred to the "free list" and setup the transfer for next buffer in the queue.

This solution won't be the memory efficientiest but the runtime efficiency is good as you only write to the memory once and the allocation/deallocation is only fetching/storing a pointer somewhere. Of course you'll have to make sure that you don't get race conditions between your application and the IRQ for allocation/deallocation.

Maybe this idea gives you an inspiration to solve your requirements.

One way you could approximate it is to allocate your printf buffer as 2x the size of your ringbuffer, and then use the first half as your ring buffer. Detect an overflow, copy the overflow in the latter half back to the front (the ring portion). Something like this:

 

 1 void xprintf(const char *format, ...)
 2 {
 3   int written;
 4   va_list args;
 5   va_start(args, format);
 6   written = vsnprintf(buffer, HALF_BUFFER_SIZE, format, args);
 7   va_end(args);
8 if (buffer + written > bufferStart + HALF_BUFFER_SIZE) 9 { // time to wrap 10 int overflow = (buffer + written) - (bufferStart + HALF_BUFFER_SIZE); 11 memmove(bufferStart, bufferStart + HALF_BUFFER_SIZE, overflow; 12 buffer = bufferStart + overflow; 13 } 14 }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值