光伏云项目杂记02 – 构建Ringbuff(环形缓冲)1

在做这个项目的时候,遇到一个问题。我们使用的STMF103C8T6,资源很可怜,看看:

图1 STM32F103C8T6的参数

72M的频率,内存只有20K。而这个芯片需要做的事情:

1 3个串口的收发;
2 3个灯的控制,让用户通过灯的闪烁判断处于什么状态,方便维护;
3 GPRS模块的协议管理;
4 与逆变器端的协议管理;
5 看门狗的任务处理;
6  服务器端协议的处理,包含应用端口和配置端口;

以FreeRTOS为架构,估计用了近20个任务来完成这些工作。每个任务给的堆栈是0.5K,加上串口需要的缓冲区,一番操作下来,内存基本用光光。一起开发的兄弟,暂且称为烟枪吧(抽烟很快,1分钟一根,当得起这个称呼)。每天嚷嚷,说内存严重不够用,不能让客户再提新的需求了。虽然通过map文件计算了每个任务所用的栈空间,尽可能不浪费。不过再改的话,程序就直接溢出了,问题出在哪都不知道。

我很同意他的看法,也很同情我们的处境。可是,客户又不是我家的,他想提要求,我又控制不住。何况有些要求还是很中肯的,咱们都是讲道理的,对吧?

那怎么办?

分析源代码,我盯上了串口处理的这段代码。三个串口,除去串口接收发送所需要建立的任务外,每个串口还用了256字节的缓冲区。另外,程序写得耦合性比较差,估计换个芯片,所有代码都得重写。

程序逻辑是这样的:

  1. 将缓冲区分为两段,A段128字节,B段128字节;
  2. 串口接收启用DMA传输,不占用CPU资源;
  3. 当触发串口接收DMA中断,中断处理程序将缓冲区指向A段,接收数据;
  4. 接收完毕后缓冲区指向B段;
  5. 有一个任务专门监听,发现有数据立即取走;
  6. 新的数据来了,DMA接收中断处理程序用B段缓冲区取走数据,收完后缓冲区指针再指向A段。

也就是说,当监听任务在处理数据的时候,DMA接收中断使用了另外一段缓冲区,大家互不干涉(当然程序中也利用了FreeRTOS的互斥量来实现)。

这样实现其实没有太大问题,因为精心计算了串口和DMA的处理时间。接收串口的数据越长,越不会出现问题。

问题就出在小数据上。如果串口接收大量的小数据,频繁的触发中断,CPU就有可能丢失数据了。(实际运行中没有出现过,连续发送1个字节的情况,在客户环境中可认为概率是0)

烟枪和我商量了一段时间,我们决定使用ringbuffer(环形缓冲)来实现。他把这个任务轻飘飘的交给我了,调他的板子去了。

没办法,开始干活吧。 Ringbuffer的基本原理,我大概了解。维基百科中也有说明:https://en.wikipedia.org/wiki/Circular_buffer

A 24-byte keyboard circular buffer. When the write pointer is about to reach the read pointer – because the microprocessor is not responding, the buffer will stop recording keystrokes and – in some computers – a beep will be played.

我能用的资源有限。必须实现原有的串口接收,不能丢数据;同时还有提供尽可能简单的接口,供应用层的函数调用。

因此,我选择使用Contiki中的代码,用它的核心思想来构建这个架构。当时估计错误,我不觉得能有多难,本以为一周左右能搞定。没想到对芯片的DMA以及串口不那么熟悉,整整花了我三周时间。

这是一段痛并快乐的回忆。

99 total views, 2 views today

发表评论

电子邮件地址不会被公开。 必填项已用*标注