Hi,
I’m having a problem with RS232 character drops on a cerberus (4.3).
No matter how fast I try to recevie the characters, when the wired network is open I have lots of overrun. When the network if closed, it doesn’t miss anything.
The speed is only 19200 bauds, and the other side doesn’t support flow control.
I have made 2 samples to reproduce the problem :
A PC program sends batches of data (0 0 0 + numbers from 1 to 250), the cerberus synchronizes on the 3 ‘0’, checks the numbers and sends the result to the PC.
It roughly simulates what the actual code do.
This first part runs on the cerberus
#define USE_NETWORK
#define CERBERUS
//#define RAPTOR
using System;
using System.IO.Ports;
using System.Text;
using System.Threading;
using Microsoft.SPOT;
#if USE_NETWORK
using GHI.Networking;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Net.NetworkInformation;
#endif
namespace TestComPort
{
public static class SerialPortLoop
{
private static readonly ManualResetEvent stopRequest = new ManualResetEvent(false);
private static readonly CircularBuffer buffer = new CircularBuffer(512);
private static SerialPort com;
private static int totalReceived, wrong;
#if USE_NETWORK
#if CERBERUS
private static readonly EthernetENC28J60 netIf
= new EthernetENC28J60(SPI.SPI_module.SPI1,
(Cpu.Pin)13, // Socket 6 pin 6
(Cpu.Pin)14, // Socket 6 pin 3
(Cpu.Pin)26, // Socket 6 pin 4
4000);
#elif RAPTOR
private static readonly EthernetENC28J60 netIf
= new EthernetENC28J60(SPI.SPI_module.SPI1,
(Cpu.Pin)28, // Socket 3 pin 6
(Cpu.Pin)33, // Socket 3 pin 3
(Cpu.Pin)87, // Socket 3 pin 4
4000);
#endif
#endif
public static void Main()
{
#if USE_NETWORK
NetworkChange.NetworkAvailabilityChanged += NetworkAvailabilityChanged;
netIf.Open();
#endif
#if CERBERUS
const string comName = "COM2"; // Socket 2
#elif RAPTOR
const string comName = "COM1"; // Socket 10
#endif
com = new SerialPort(comName, 19200, Parity.None, 8, StopBits.One);
com.Handshake = Handshake.None;
com.Open();
com.ErrorReceived += ComOnErrorReceived;
var reader = new Thread(Reader);
reader.Start();
byte value = 0;
while (!stopRequest.WaitOne(0, false))
{
int zCnt = 0;
while (!stopRequest.WaitOne(0, false))
{
// Synch on 3 0x00
if (buffer.Read(100, ref value) && (value == 0))
{
zCnt++;
if (zCnt == 3) break;
}
else
zCnt = 0;
}
DateTime timeout = DateTime.Now.AddTicks(500 * TimeSpan.TicksPerMillisecond);
int received = 0;
while (DateTime.Now < timeout)
{
received = buffer.Length;
if (received >= 250)
break;
Thread.Sleep(1);
}
bool ok = true;
for (int i = 1; i <= 250; i++)
{
if (!buffer.Read(0, ref value) || (value == 0))
{
ok = false;
break;
}
}
if (!ok) wrong++;
string msg = "Batch : " + received + ", total bytes " + totalReceived + ", wrong " + wrong;
Debug.Print(msg);
var toSend = Encoding.UTF8.GetBytes(msg + "\r\n");
com.Write(toSend, 0, toSend.Length);
buffer.Clear();
}
}
#if USE_NETWORK
private static void NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{
if (e.IsAvailable)
{
Debug.Print("Network UP");
Debug.Print("IP : " + netIf.IPAddress + " / " + netIf.SubnetMask);
Debug.Print("GW : " + netIf.GatewayAddress);
foreach (var address in netIf.DnsAddresses)
Debug.Print("DNS : " + address);
}
else
Debug.Print("Network DOWN");
}
#endif
private static void ComOnErrorReceived(object sender, SerialErrorReceivedEventArgs serialErrorReceivedEventArgs)
{
string msg;
switch (serialErrorReceivedEventArgs.EventType)
{
case SerialError.Frame: msg = "Com framing error"; break;
case SerialError.Overrun: msg = "Com buffer overrun, next char lost"; break;
case SerialError.RXOver: msg = "Com input buffer overflow"; break;
case SerialError.RXParity: msg = "Com parity error"; break;
case SerialError.TXFull: msg = "Com output buffer full"; break;
default: msg = serialErrorReceivedEventArgs.EventType.ToString(); break;
}
Debug.Print(msg);
}
private static void Reader()
{
var tmp = new byte[8];
while (!stopRequest.WaitOne(1, false))
{
if (com.BytesToRead != 0)
{
int read = com.Read(tmp, 0, 8);
totalReceived += read;
buffer.Append(tmp, read);
}
}
}
}
public class CircularBuffer
{
private readonly byte[] buffer;
private readonly ManualResetEvent dataAvailable = new ManualResetEvent(false);
private readonly int last;
private int readPos;
private int writePos;
private int free;
public CircularBuffer(int size)
{
buffer = new byte[size];
last = size - 1;
Clear();
}
public int Length { get { return buffer.Length - free; } }
public void Clear()
{
lock (buffer)
{
dataAvailable.Reset();
readPos = writePos = 0;
free = buffer.Length;
}
}
public int Append(byte[] data, int length)
{
lock (buffer)
{
if ((free == 0) || (length < 1)) return 0;
free--;
buffer[writePos] = data[0];
if (writePos == last) writePos = 0; else writePos++;
dataAvailable.Set();
length--;
if (length < free)
free -= length;
else
{
length = free;
free = 0;
}
for (int i = 1; i <= length; i++)
{
buffer[writePos] = data[i];
if (writePos == last) writePos = 0; else writePos++;
}
}
return length + 1;
}
public bool Read(int waitMs, ref byte value)
{
if (!dataAvailable.WaitOne(waitMs, false)) return false;
lock (buffer)
{
value = buffer[readPos];
if (readPos == last) readPos = 0; else readPos++;
free++;
if (free == buffer.Length)
dataAvailable.Reset();
}
return true;
}
}
}
Then the PC part :
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hit a key to start");
Console.ReadKey();
var input = new StringBuilder();
var data = new byte[253];
var com = new SerialPort("Com1", 19200, Parity.None, 8, StopBits.One);
com.DataReceived += (sender, eventArgs) =>
{
if (eventArgs.EventType == SerialData.Chars)
{
input.Append(com.ReadExisting());
var s = input.ToString();
int cr = s.IndexOf("\r\n");
if (cr >= 0)
{
Console.WriteLine(s.Substring(0, cr));
input.Remove(0, cr + 1);
}
}
};
com.Open();
int p = 0;
for (byte i = 0; i < 3; i++)
data[p++] = 0;
for (byte i = 1; i <= 250; i++)
data[p++] = i;
p = 0;
while (!Console.KeyAvailable)
{
com.DiscardOutBuffer();
com.Write(data, 0, data.Length);
Console.WriteLine("Sent batch {0}", ++p);
Thread.Sleep(500);
}
}
}
I tried on cerberus, raptor with or without network (by commenting #define), the raptor works fine in both modes.
Thanks in advance for any help…
Best regards,
Steph