Hi, I'm having some difficulties in determining when and how (external) interrupts remain pending. In the users manual for the XC167ci it states that hardware arbitration leaves interrupts pending if they cannot be handled at that moment (i.e. the ILVL of the PSW is too high). I use a few macros to raise or lower the ILVL temporarily. This works great, I never miss interrupts. I figure that if an interrupt occurs when the ILVL is too high, the interrupt is buffered (pending) and it is handled the moment I lower the ILVL again. But I now would like to turn of ONE specific interrupt. I do that by clearing and setting of the CC10IE bit. It seems that interrupts that occur during the time that the CC10IE bit is of are completely gone. I.e. they do not stay 'pending', they are not buffered and handled whenever I enable the CC10IE bit again. Can anyone tell me how and when the XC167 keeps interrupts pending? Did anyone else run into this same problem? Should I enable/disable one specific interrupt in another way? Thanks for your help
Hi, I got a similar problem. Protecting code by setting the IE bit of a specific interrupt did work properly. It was the serial transmit interrupt. Only after changing to disable the global interrupts or using atomic instructions it was working. Here my code:
void Tx_irq(void) interrupt S0TINT = 42 { if (s_iTxWrite != s_iTxRead) // buffer not empty? { S0TBUF = TxBuffer[s_iTxRead++]; // transmit the next character if (s_iTxRead >= BUFFER_SIZE) // handle buffer wrap around s_iTxRead = 0; } else { s_bTxRestart = 1; // transmit must be restarted } } BYTE far SerWriteByte(BYTE byte) { int iNextWriteIndex = s_iTxWrite + 1; // handle buffer wrap around if (iNextWriteIndex >= BUFFER_SIZE) iNextWriteIndex = 0; while (iNextWriteIndex == s_iTxRead); // wait until free buffer space is available _atomic_(4); // CLEAR_FLAG(S0TIC, IC_IE(1)); // disable send interrupt TxBuffer[s_iTxWrite] = byte; _atomic_(2); s_iTxWrite++; _atomic_(4); if (s_iTxWrite >= BUFFER_SIZE) // handle buffer wrap around s_iTxWrite = 0; // SET_FLAG(S0TIC, IC_IE(1)); // enable send interrupt if (_testclear_(s_bTxRestart)) { S0TIR = 1; // start transmiting } return byte; }
Can anyone tell me how and when the XC167 keeps interrupts pending? That's easy. The interrupt is pending when the xxIR bit of the interrupt's control register is set. So pending interrupts would only be lost if you clear the corresponding xxIR bits. - mike
If I remember correctly, in the C16x family of MCUs the CPU will execute one or more instructions following the flag clear instruction before it takes effect. Again, I might be wrong, but that is not the case with the XC16x family. To make sure that the interrupts are disabled when they need to be, append extra NOPs:
SOTIE = 0; _nop_(); _nop_(); _nop_();
Thanks for your reply. I understand that I can use the IR bit to see if an interrupt has occurred. But my point is: if the interrupt occurs while the xxIE bit is 0. In that case the xxIR will not get set. If I then, e.g. 1ms later, enable the interrupt by setting the xxIE bit to 1 the xxIR will not get set. In contrast, if I do the same using the global IEN bit or by raising ILVL in the PSW, the hardware seems to remember the interrupt and will handle it as soon as I enable IEN or lower ILVL.
But my point is: if the interrupt occurs while the xxIE bit is 0. In that case the xxIR will not get set. That is not the case. Here is an extract from the manual: An interrupt request sets the associated interrupt request flag xxIR. If the requesting interrupt node is enabled by the associated interrupt enable bit xxIE arbitration starts with the next clock cycle, or after completion of an arbitration cycle that is already in progress. All interrupt requests pending at the beginning of a new arbitration cycle are considered, independently from when they were actually requested. - mike
@Mike Yes you are right, there must one instruction after clearing the interrupt enable bit. I checked this in the disassembler that the next instruction doesent need protection. But my tests got also the same problem with a nop after clearing the enable bit and I dont know why? Reagarding the usage of the atomic instructions, _atomic(0); ...; _endatomic_(); supports only 4 instructions and the compiler will not stack them. Ok, if I need more than one atomic instruction, than I can also disable the global interrupt flag. Still strange why it was not working disabling the SOTIE interrupt enable flag. Bert
I ran a test using the extra NOP's. It didn't make any difference. I still miss interrupts. Using a debugger I can also determine that the xxIR bit is 0 when I detect that I missed an interrupt.
Still strange why it was not working disabling the SOTIE interrupt enable flag. After a long look at the code you posted the only thing I can come up with is that maybe s_iTxRead or s_iTxWrite are not declared as volatile?
It seems I have found the cause for this problem. Your remark about the NOP got me thinking about the interruptability of the disable/enable macro's themselves. Before they were: INTR_DISABLE: CC10IC = (CC10IC & ((UINT16) ~0x0040)); INTR_ENABLE: CC10IC = (CC10IC | 0x0040); But disabling it this way would mean the assembler first needs to read the CC10IC, and it with 0x40, and then write it back. This is no way near atomic! So I changed it into this: INTR_DISABLE: CC10IE = 0; _nop(); _nop(); INTR_ENABLE: CC10IE = 1; For now this seems to have resolved my problem! I will leave my board up and running for the night and see if it keeps running well. Thanks for all your support
Only s_iTxRead and the restartflag are declared as volatile, because only this two are change inside the interrupt function.
It sounds like you solved your problem but please allow me to offer my opinion. There is usually a simple bit operation for a global enable or disable operation( EI, DI). Encasing certain short sequences of instructions( no loops or branches!) in EI/DI protects against the kind of problem you've had. The cost is a little more delay in ISR response time now and then and usually the system will tolerate the extra delay. I don't do this casually but it's a useful method that improves system reliability. Cheers!