View previous topic :: View next topic |
Author |
Message |
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
How I should define interrupt priorities on a PIC24F? |
Posted: Fri Mar 01, 2024 8:47 pm |
|
|
I have 5 interrupt vectors enabled, 2 for each USART and the USB CDC library use at least one more.
I'm having some issues with my code because some characters are missing or corrupted when I plug the USB to the board.
The USB CDC works ok but the UART1 don't.
If I unplug the USB the UART1 works OK, so, I want to try to set up the interrupt priority because I know that at low level PIC24F devices are way different than PIC18F devices.
The microcontroller is a PIC24FJ1024GB606
CCS compiler version is 5.091 _________________ Electric Blue |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1909
|
|
Posted: Fri Mar 01, 2024 9:29 pm |
|
|
The glib answer would be to place the UART interrupt at a higher priority than the USB. But that may cause problems to pop up with the USB. It could also, squeezing a stress ball style, cause some new problem to pop up.
The better approach would be to ensure that there are absolutely no blocking code portions in your program. From your description it appears as though the symptoms only manifest when USB is attached or removed. That's a red flag to me as the drivers I've seen tend to rely on blocking code, particularly during initialization.
The difficult part is to break apart such code portions, if they exist, into many much smaller portions. Anything that involves a while () or do { } while () loop should be carefully examined to determine a feasible path forward that does not involve blocking. Instead of a while (), can you invoke a timer which then sets a flag, and that in turn causes whatever was inside the original while () to periodically run?
This approach is complex. It is, however, absolutely bulletproof in practice. Your device won't hang, reset, or otherwise act funny.
By all means try playing with the interrupt priority first. There's no danger in experimentation. If that makes the problem go away and you're satisfied with the result, excellent. If not, I encourage you to look for and eradicate blocking code in your project. |
|
|
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
|
Posted: Fri Mar 01, 2024 10:09 pm |
|
|
The blocking code that mention is in the USB library or do you say that is in my code?
This code is a bit old, from 2019 or so, but I'm 90% sure that there's no while that disable interrupts on my main code and 99% that there's no loop on my interrupts.
The main code runs on a classic superloop that calls some routines depending on the device current status.
To set the interrupt priorities should I use the #priority directive as in PIC18F?
Because the priorities in PIC24F has a lot of levels not like PIC18F that has only two.
Do you know how that work?
Because I want that the UART1 RX ISR interrupt the other 4 interrupts and not the old legacy mode that has "priority" when the interrupt service checks what flag is raised. _________________ Electric Blue |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1909
|
|
Posted: Fri Mar 01, 2024 10:35 pm |
|
|
I've created plenty of dsPIC33 based projects with tweaked interrupt priority levels. From memory I believe it's as simple as adding a "LEVEL = 5" to the ISR. It's in the manual.
There were 7 levels to the ISRs for the processors I've used in the past. The data sheet is a good source of information. I believe that LEVEL=7 was the highest. The way I set things up was to choose all the ISRs that weren't super critical to be 4 and then I tried 5 for the one that was. Some tweaking may be necessary.
The blocking I was talking about is always in some library or driver. If you wrote the USB code yourself, then you're good. If not, suspect such code. |
|
|
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
|
Posted: Fri Mar 01, 2024 11:00 pm |
|
|
No, I didn't wrote any USB library, I'm using the CCS one.
I have the CCS C manual from 2021 and found this line but I don't understand the description.
Quote: | #INT_xxxxLevel=3 - Interrupt is enabled when levels 3 and below are enabled. |
Before that line it says something about enable interrupt levels.
I've must to enable those levels? How? _________________ Electric Blue |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sat Mar 02, 2024 7:45 am |
|
|
You need to add up near the top of the code:
#DEVICE NESTED_INTERRUPTS=TRUE
This tells the compiler to add support for nested interrupts.
Then you just add for the interrupt you want more of less priority for,
the LEVEL=xx at the end of it's declaration line. The default level is 4,
so just raise the one that has problems, and leave the rest alone.
However you need to be excruciatingly careful about the variables used in
this interrupts. Historically (about 20 versions or more ago), the compiler did
defend variables between the interrupt levels. Now it does not. I complained
to CCS about this (look at the known issues part of the forum), but it has
never been fixed.
You should also probably increase the stack size. Remember in one go
this adds about 50 bytes to what may need to be saved on the stack. |
|
|
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
|
Posted: Sat Mar 02, 2024 11:32 am |
|
|
Thanks. I think have all the info I need.
On Monday I will try all this.
Thanks a lot. _________________ Electric Blue |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9243 Location: Greensville,Ontario
|
|
Posted: Sat Mar 02, 2024 5:30 pm |
|
|
comment.
I don't know if the USB driver has a timeout,or your UARTS but....
when using any/all serial communications be sure to have a 'bailout timer' ! CCS in their FAQ section have an example. Say you're expecting a string of 10 characters, set the timeout value to be 2X the time 10 characters will take.
This way, when the RS-232 connector 'magically' gets unplugged, your program can INFORM you ' what we have here, is a failure to communicate'. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Sun Mar 03, 2024 2:34 am |
|
|
A couple of comments:
First, you can run the USB without interrupts. Select the option
#define USB_ISR_POLLING //select to not use interrupts for USB
Before you load the USB include files. Then all you need to do is ensure
you call usb_task at an interval of no more than a couple of mSec.
I do this on a board where there is a hardware event that absolutely
must be handled with the minimum latency, so have a single interrupt
setup as 'FAST' to do this, but still want USB.
Then second comment, the problem may be your UART handler. The
'old' CCS example one is not optimal for the DsPIC's. Use something
like:
Code: |
#define BUFF_SIZE 32
byte bdata[BUFF_SIZE];
uint8_t bin=0, bout=0;
#INT_RDA
void character(void)
{
static uint8_t chr; //needs stdint loaded
do
{
chr=fgetc(STREAMNAME);
bdata[bin++]=chr;
if (bin==BUFF_SIZE)
bin=0;
if (bin==bout)
{
bout++;
if (bout==BUFF_SIZE)
bout=0;
}
} while (kbhit(STREAMNAME));
}
|
This showing a 32 character circular buffer.
Key point is that it keeps transferring bytes until the hardware buffer
is empty. The DsPIC's all generally have at least 4 bytes of hardware
serial buffering, and the standard interrupt handling only one, wastes
a lot of time if more than one character is waiting, which can make it
hard to catch up. DsPIC serial interrupt handlers need to be written to
handle all the characters waiting.
Also, final comment, how do you handle UART errors?. I use the hardware
UART error interrupt. |
|
|
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
|
Posted: Mon Mar 04, 2024 11:29 am |
|
|
temtronic wrote: | comment.
I don't know if the USB driver has a timeout,or your UARTS but....
when using any/all serial communications be sure to have a 'bailout timer' ! CCS in their FAQ section have an example. Say you're expecting a string of 10 characters, set the timeout value to be 2X the time 10 characters will take.
This way, when the RS-232 connector 'magically' gets unplugged, your program can INFORM you ' what we have here, is a failure to communicate'. |
I have a timer outside the UART RX ISR but the device connected to it is a GPRS modem that, when you send something to it, can take from miliseconds to a whole minute to answer and sometime the data from the modem are unexpected messages like +PB DONE that means that phone book is ready to be used.
So I'm using RTS pin to stop the modem on each line received.
That's not happening when I plug the USB; from around 3/4 lines I saw one RTS pulse, and when the USB is not connected I saw one RTS pulse on each carriage return as I programmed, so, my guess is that the carriage return is never processed by the ISR when the USB is connected. _________________ Electric Blue |
|
|
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
|
Posted: Mon Mar 04, 2024 11:36 am |
|
|
@Ttelmah
I'm using this code on the RDA ISR.
Code: | #INT_RDA
void RX1_ISR()
{
unsigned int8 Dummy_RX1;
short Error=FALSE;
if(FERR1==TRUE)
{
Dummy_RX1=fgetc(GSM);
Error=TRUE;
}//Garbage
if(OERR1==TRUE)
{
OERR1=FALSE;
Error=TRUE;
// return;
}
if(!Error && kbhit(GSM))
{
ModemWDT=0;
Dummy_RX1=fgetc(GSM);
if(Dummy_RX1<127)
{
if(!(RX1_Offset==0 && Dummy_RX1<' '))//About to receive remote data
{
if(RX1_Offset<GSM_RxBufferSize)
{
if(RX1_Offset>(GSM_RxBufferSize-40))
{
output_high(RTS);
}
GSM_RxBuffer[RX1_Offset]=Dummy_RX1;
if(RX1_Offset==2 && GSM_RxBuffer[0]=='>' && GSM_RxBuffer[1]==' ')
{
RX1_Offset=0;
}
else if(Dummy_RX1==0x0A || Dummy_RX1==0x0D)
{
output_high(RTS);
GPRS_Status.ParseGSM=TRUE;
#warning "Falta agregar una rutina que almacene los datos mientras ParseGSM=TRUE en caso de que entren mas datos y no se hayan procesado los anteriores"
RX1_Offset=0;
}
else
{
RX1_Offset++;
}
}
else
{ //Futil ?
output_low(RTS);
RX1_Offset=0;
RxGSM_OV=TRUE;//Overflow! - Byte loss
}
}
}
}
} |
_________________ Electric Blue |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Mon Mar 04, 2024 11:52 pm |
|
|
OK. There are issues here, which explain the problem.
Understand, as written, it'll work fine provided the receive interrupt
is called one character at a time. However, visualise that because the
code is busy handling the USB interrupt, _two_ characters are sitting
in the input buffer when the receive interrupt is called. Then assume
that the first of these is the end of line marker.
The interrupt exits, with the flag set to say you have a complete line,
but before the main code can do anything, is immediately called again.
This character gets written to the start of the buffer.
The main then calls it's data handling code, but the first character of
the buffer has been overwritten by this character,
Your approach is fundamentally flawed. Store the data into a standard
circular buffer. Parse the data from this outside to see when the line
is complete. Alternatively, if you want to do the per line approach, have
two line buffers, and when the line is complete switch to the second
buffer. This way data can be received before the processing is done. |
|
|
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
|
Posted: Wed Mar 06, 2024 10:00 am |
|
|
When a "\r" or a "\n" is received the RTS line is raised up, so it will be no more characters coming into the buffer until the main routine process the line and release the RTS line.
I tested it with a logic analyzer.
The problem was the silicon version that was version 1.
That version is not even in the Errata; probably because it was a trial version of the silicon. I don't know how it ended in the production.
Same program on a silicon version 5 works perfectly.
The silicon version has two problems, some times doesn't detect the end of the line at all and some time the characters are corrupt, like I got an "M" where should be numbers. That's not solvable with any approach because is random garbage. _________________ Electric Blue |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19539
|
|
Posted: Wed Mar 06, 2024 10:08 am |
|
|
That is the point you are missing.
The second character _has already been received_. It is in the
hardware buffer.
Raising RTS, does not stop this from triggering another interrupt.
In any setup like this your code has to be able to handle that a character
may already have been received (and in fact that the modem will not
stop if a character has already been started). |
|
|
E_Blue
Joined: 13 Apr 2011 Posts: 417
|
|
Posted: Wed Mar 06, 2024 11:40 am |
|
|
The next character usually is a "\n" and the next 2 characters a "\r\n" and if a line start with non printable characters the byte is ignored.
This is a capture of a device working ok
Click on the image to get full size.
I understand your point and added a while to get more bytes before exit the RDA interrupt and take notes if something similar happens in the future because they are using 3 different GPRS modems currently and could change it in the future for a 5G version. Anyway I'm not getting any issue with a device with silicon revision 5 or newer.
As far I understand the corrupt characters are not a code fault is a silicon version issue. _________________ Electric Blue |
|
|
|