PaulStoffregen
Well-known member
I have no idea why you're experiencing this problem. In fact, I don't really even know quite what the problem is, other than you have several different high priority interrupts on a tight timing schedule and when you add use of HardwareSerial (which ought to be at a lower priority) somehow your high priority interrupts miss their timing requirements. Is that about right?
Even if I could see all your code, figuring out these sorts of problems can be quite challenging. But without seeing the code, best I can do is completely blind guessing.
First of all, the HardwareSerial class is meant for the main program to call the functions like Serial3.print(). If you are calling these from interrupts, it must be from a lower priority that the priority level the serial interrupt uses. Best to instead put the data into an array and set a volatile flag or index which the main program polls.
Likewise, all sort of other data sharing or interaction between interrupts and main program is based on the concept that the public API functions are only called from main program, not from an interrupt. My guess is you may have code somewhere that's violating that intended design?
My next guess would be some sort of similar "unsafe" data sharing problem. Crafting interrupt based code is extremely difficult to do reliably because keeping data sharing between interrupts and lower priority code is a very hard problem. Only a few "safe" ways are well known which don't require blocking the interrupt while access shared data. The queue with head and tail indexes is the most commonly used.
When not using those known-safe ways, usually you must disable interrupts briefly while accessing data. If you have any such code, maybe it's playing havoc with the timing?
Now for some guesswork of things I believe are quite improbable, which are all variation of the theme that your code is flawless and some aspect of the hardware usage is adding timing problems. I would be quite surprised if that's the case, but I'm guessing you probably want to hear some of these ideas, so here goes....
Base hardware overhead for an interrupt is about 24 cycles (except when tail chain optimization gets used). That gives you half the CPU's integer registers. If other integer registers or the float registers are used, more cycles are used to save them onto the stack and restore before returning. If you're extremely pressed for tight timing, maybe those extra cycles just push everything over the edge.
Likewise on timing, access the DTCM memory (RAM1) is very fast and isn't cached because it's basically the same speed as the CPU cache. But access to RAM2 is cached, and cache misses are about 4X slower. If you're using RAM2 (either DMAMEM or memory you got from malloc or C++ new) maybe code running one way is suffering more cache misses than running another way?
And for an extremely unlikely scenario, one of the many things the hardware does to run so fast is branch prediction. Maybe running different code is evicting branch history, which later affects the speed other code which could have run faster if more of its branches were predicted?
But again, flawless code that suffers from these rather slight timing changes is pretty unlikely, especially if you have timing slack measured in a few microseconds range. Almost always with interrupt-based programs, things appear to work perfectly in one scenario but then fail mysteriously in other usage cases, due to unsafe sharing data between the interrupt and other code running at lower priority. These problems are insidious because they usually manifest when you make some unrelated change or simply have different timing of when the interrupts happen, where focusing on the thing you changed yields no benefit because it's not really the problem. Unsafe data sharing is the really hard aspect of designing interrupt-based code.
Even if I could see all your code, figuring out these sorts of problems can be quite challenging. But without seeing the code, best I can do is completely blind guessing.
First of all, the HardwareSerial class is meant for the main program to call the functions like Serial3.print(). If you are calling these from interrupts, it must be from a lower priority that the priority level the serial interrupt uses. Best to instead put the data into an array and set a volatile flag or index which the main program polls.
Likewise, all sort of other data sharing or interaction between interrupts and main program is based on the concept that the public API functions are only called from main program, not from an interrupt. My guess is you may have code somewhere that's violating that intended design?
My next guess would be some sort of similar "unsafe" data sharing problem. Crafting interrupt based code is extremely difficult to do reliably because keeping data sharing between interrupts and lower priority code is a very hard problem. Only a few "safe" ways are well known which don't require blocking the interrupt while access shared data. The queue with head and tail indexes is the most commonly used.
When not using those known-safe ways, usually you must disable interrupts briefly while accessing data. If you have any such code, maybe it's playing havoc with the timing?
Now for some guesswork of things I believe are quite improbable, which are all variation of the theme that your code is flawless and some aspect of the hardware usage is adding timing problems. I would be quite surprised if that's the case, but I'm guessing you probably want to hear some of these ideas, so here goes....
Base hardware overhead for an interrupt is about 24 cycles (except when tail chain optimization gets used). That gives you half the CPU's integer registers. If other integer registers or the float registers are used, more cycles are used to save them onto the stack and restore before returning. If you're extremely pressed for tight timing, maybe those extra cycles just push everything over the edge.
Likewise on timing, access the DTCM memory (RAM1) is very fast and isn't cached because it's basically the same speed as the CPU cache. But access to RAM2 is cached, and cache misses are about 4X slower. If you're using RAM2 (either DMAMEM or memory you got from malloc or C++ new) maybe code running one way is suffering more cache misses than running another way?
And for an extremely unlikely scenario, one of the many things the hardware does to run so fast is branch prediction. Maybe running different code is evicting branch history, which later affects the speed other code which could have run faster if more of its branches were predicted?
But again, flawless code that suffers from these rather slight timing changes is pretty unlikely, especially if you have timing slack measured in a few microseconds range. Almost always with interrupt-based programs, things appear to work perfectly in one scenario but then fail mysteriously in other usage cases, due to unsafe sharing data between the interrupt and other code running at lower priority. These problems are insidious because they usually manifest when you make some unrelated change or simply have different timing of when the interrupts happen, where focusing on the thing you changed yields no benefit because it's not really the problem. Unsafe data sharing is the really hard aspect of designing interrupt-based code.