I want to create a gateway to pass messages from a can bus and lcm and vice-versa. If a message in lcm is sent at a specific frequency, its copy on the can bus should be sent at the exact same frequency.
How would you solve this?
In my mind I thought about two threads, one for each direction converting the message from one system to another in a loop. The two threads are coordinated by a timer which is set to a frequency which is much lower than the maximum message passing frequency possible. The timer sents a signal to the threads after each cycle. The threads wait for that event at the end of each loop, i.e. they sleep and free resources until the event occures.
I implemented this idea already but the resulting frequencies are not constant. Am I doing something conceptually wrong?
A solution should run on windows, thus utilize either native windows api or boost threads for example. The gateway should be real-time capable.
For windows, timeSetEvent can be used to set an event at a regular interval, although MSDN lists it as an obsolete function. The replacement for timeSetEvent uses a callback function, so you'd have to set an event in the callback function.
You can increase the tick rate from it's default 64hz == 15.625 ms down to 1 ms using timeBeginPeriod
Some games have threads that run at fixed frequencies, and poll a high frequency counter and Sleep when there's enough delay time remaining in the current cycle. To prevent drift, the delay is based off an original reading of the high frequency counter. Example code that is Windows XP compatible, where a Sleep(1) can take up to 2 milliseconds. dwLateStep is a diagnostic aid and incremented if the code exceeds a cycle period.
/* code for a thread to run at fixed frequency */
typedef unsigned long long UI64; /* unsigned 64 bit int */
#define FREQ 400 /* frequency */
DWORD dwLateStep; /* late step count */
LARGE_INTEGER liPerfFreq; /* 64 bit frequency */
LARGE_INTEGER liPerfTemp; /* used for query */
UI64 uFreq = FREQ; /* process frequency */
UI64 uOrig; /* original tick */
UI64 uWait; /* tick rate / freq */
UI64 uRem = 0; /* tick rate % freq */
UI64 uPrev; /* previous tick based on original tick */
UI64 uDelta; /* current tick - previous */
UI64 u2ms; /* 2ms of ticks */
UI64 i;
/* ... */ /* wait for some event to start thread */
QueryPerformanceFrequency(&liPerfFreq);
u2ms = ((UI64)(liPerfFreq.QuadPart)+499) / ((UI64)500);
timeBeginPeriod(1); /* set period to 1ms */
Sleep(128); /* wait for it to stabilize */
QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
uOrig = uPrev = liPerfTemp.QuadPart;
for(i = 0; i < (uFreq*30); i++){
/* update uWait and uRem based on uRem */
uWait = ((UI64)(liPerfFreq.QuadPart) + uRem) / uFreq;
uRem = ((UI64)(liPerfFreq.QuadPart) + uRem) % uFreq;
/* wait for uWait ticks */
while(1){
QueryPerformanceCounter((PLARGE_INTEGER)&liPerfTemp);
uDelta = (UI64)(liPerfTemp.QuadPart - uPrev);
if(uDelta >= uWait)
break;
if((uWait - uDelta) > u2ms)
Sleep(1);
}
if(uDelta >= (uWait*2))
dwLateStep += 1;
uPrev += uWait;
/* fixed frequency code goes here */
/* along with some type of break when done */
}
timeEndPeriod(1); /* restore period */