We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
Hi,
I'm working on a project employing an XE164. In a ISR (CAN-RX-Interrupt) I want to fill a global variable with some values, then set a flag to signal the main program ok-to-read. The variable to fill is a structure, defined in the main program:
typedef struct {
unsigned char data[8];
unsigned char flags;
} t_data;
in main.c:
volatile t_data data;
The CAN-ISR is supposed to fill data[0..7] with the incoming bytes, then set flags to (e.g.) TRUE. The main program is supposed to look for the flag, handle the data, then reset the flag.
This works fine when I use a seperate variable. It does not always work, however, if "flags" is a member of a structure, like t_data.flags in the example above: It "mostly" works, but the main programm misses some new messages, about 10% of the time. Again, this does not occur if "flags" is a seperate variable.
Can anyone make sense of this? Could it be that read access by main to some other part of the structure also kills the ISR-write to the flags-part of the structure (or vv)?
Any help would be much apreciated.
Regs, Alto Speckhardt
Unless your main loop is guaranteed to be very fast, you should avoid using a single-buffered scheme for the CAN data.
Create a traditional ring buffer (identical to what is used for most RS232 buffering) containing 2^n entries.
This has the advantage that you get one read-position variable and one write-position variable and only one owner for each of the variables. The CAN interrupt increments the write-position variable while the main loop increments the read-position variable. This makes sure that you don't get two concurrent writes to the same variable.
It also gives the main loop a bit of safety margin when it handles other events.
thanks for your reply.
I'm aware of the problems of the single-buffer concept, but in this case it should be sufficient: The particular message in question (there will be others where the situation might be different) is pretty infrequent: It is a timestamp/heartbeat that occurs once each second. Therefore, I'm pretty much certain that my application will be able to keep up with it.
It's also the reason why I can't see any read/write conflicts in this one.
Still, every once in a while, my main program doesn't react to the message for two seconds, sometimes up to three or even four seconds.
As Per already indicated, nothing prevents 2 interrupts from occurring after one another BEOFRE your main loop gets the chance to handle the first 8 bytes. if that is the case - you lost a message!
maybe you have a race condition involving your flag?
as I said, a second inbound message before the first one can be handled by main is pretty improbable in this case. I have almost a second to handle each message - my main doesn't take a tenth of this time.
However, it shouldn't matter with my particular problem, since it does work fine with a seperate variable, but not with the structure - unless "access part of structure" is synonymous with "lock whole structure".
Is this the case?
* do you have additional interrupt sources? * maybe you have an alignment problem...? * ...
Other interrupt sources: There are other message objects active (RX and TX), but the MO in question is never reconfigured and has its own, dedicated data storages. The variables are accessed nowhere else, and the object remains untouched except by the ISR.
I don't quite understand what you mean by alignment?
processors load and store instructions assuming that the address is a multiple of the type you are loading or storing. If you load or store to an address that is not aligned to its type, then the behavior depends on the particular implementation. C compilers assume that a pointer is aligned unless you say otherwise. If a pointer isn’t aligned, then the program may give unexpected results. Can this apply to your program?
I see, thanks for the explanation.
No, I doubt that this could be the case. There is nothing I have explicitly positioned, the linker should have a free reign over where to put what. The default configuration of the compiler (Keil PK166 v6.11) should be fine by itself, shouldn't it? I've made no manual adjustments.
before somebody bites my head off - yes, I know, there are processors out there that do allow unaligned accesses...!
very well then. show us concise code excerpts!
Right. This was generated by DAVE, of course:
ISR:
void CAN_viSRN0(void) interrupt CAN_SRN0INT { [...] if(uwSRN0ObjHandler & MOSTAT_NEWDAT) // if NEWDAT is set { if (uwSRN0ObjHandler & MOSTAT_MSGLST) // if MSGLST is set { } else { // USER CODE BEGIN (SRN0_OBJ32,4) // transfer data: mydata.data[0] = CAN_HWOBJ[32].ubData[0]; // new struct content: mydata.rxstatus |= UPDATED; // USER CODE END }
main:
typedef struct { ubyte rxstatus; ubyte data[8]; } t_mydata; [...] volatile t_mydata mydata; [...] void can_rx() { // new data? if (mydata.rxstatus & UPDATED) { // handle data: [... read-access mydata.data[x] ...] // mark as handled: mydata.rxstatus &= ~UPDATED; } [...]
... and that's about it. The check "if rxstatus & UPDATED) misses out about 10% of the messages, sometimes more, sometimes less. The main applications is essentially doing loops, the check occurs about every 20ms.
If I extract t_mydata.rxstatus into a seperate variable, there is no problem.
"yes, I know, there are processors out there that do allow unaligned accesses...!"
8086, 80186, 80286, i386, i486, ... ;)
In this case, Intel designed logic into the bus controller to automatically take care of unaligned access.
For more modern x86 chips, it is normally way more important to not cross a cache-line boundary than to have unaligned access.
have you tried your program by the uv3 debugger? you can trigger a conditional breakpoint when your flag changes.
The problem is that I'd rather need a breakpoint triggered when the flag _doesn't_ change... ;-)
Ok, I will just do some more debugging and report back if I find a "smoking gun".
Meanwhile, thanks for your advice!