|
|
View previous topic :: View next topic |
Author |
Message |
ccsfred
Joined: 15 Jun 2016 Posts: 33
|
CN Interrupt problem |
Posted: Wed Jan 25, 2017 9:53 am |
|
|
Hi,
I have two PIC's dsPIC33fj256MC710A's talking to each other over rs232
Compiler version: V4.078
What I'm trying to do is:
PIC1:
When encoder reaches predetermined positions:-
* Send read pulse from PIC1 to PIC2 & Send encoder position
PIC2:
* When CN interrupt triggered, read adc straight away to get closest reading to required encoder position
* Decode position
* Send encoder position and adc reading to PC
Much of the day has been trying to solve what was another bug, I think which was a stack overflow problem and it was receiving the encoder value, sending first few chars out then just noise like wrong baud rate. When I debugged it, the PC was at line one each time I tried to receive and send.
I had several nested functions with the final having a printf with a float in it. I am now using a flag to perform the function in main and not in the ISR.
So after much reading of this forum, I've resolved that thanks!
Initially I was doing this just fine by polling in the main loop, but thought I would try using interrupt for my first time, which isn't going so well..
If I have the SAMPLE_SYNC if section un-commented then I can receive encoder position just fine and send out adc value, hardcoded currently to reduce code size. If I swap over and use the cn interrupt instead of the main if(SAMPLE_SYNC) I get an overrun error.
I have included the ERRORS keyword in #use rs232, to try and automatically clear them, but this has not helped.
Code: | setup()
{
set_tris_c(0xFFFF); // all inputs
CNPU2=0x0020; // Enable weak pull-up on CN21 as connected with U2RX
//enable_interrupts(INTR_CN_PIN | PIN_C13);
// enable_interrupts(INTR_GLOBAL);
}
#int_CNI
void CCNI_Interrupt() // motion board sends pulse to read adc
{
if(SAMPLE_SYNC == 1) // make sure it was rising edge on CN1
{
interruptFlag = true;
}
CNIF = 0; // Clear CN interrupt flag
}
void main()
{
setup();
printf("\r\n\Running...\r\n");
while(true)
{
if(SAMPLE_SYNC == 1) // make sure it was rising edge on CN1
{
interruptFlag = true;
}
if(interruptFlag == true)
{
//readAdc();
decode_enc_rs232();
interruptFlag = false;
}
}
}
|
I've been looking at this for a day and a half and can't see the wood for the trees!
Can anyone point me in the right direction please?
Thanks in anticipation! |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9215 Location: Greensville,Ontario
|
|
Posted: Wed Jan 25, 2017 10:38 am |
|
|
this...
decode_enc_rs232();
could be the killer...
I don't know what's inside it !
ISR mantra...
short and fast !!
ONLY set flags within all ISRs....
Do NOT use delays of any kind
Do NOT use prints of any kind
Do NOT use ANY maths,especially FP
Do NOT use any 'functions'...
if decode_enc_rs232(); is more than 3 lines long, it'll kill the ISR.
also some(most) interrupts automatically clear their own interrupt,though you may have to read a port( check the datasheet !)
Jay |
|
|
ccsfred
Joined: 15 Jun 2016 Posts: 33
|
|
Posted: Wed Jan 25, 2017 10:52 am |
|
|
Hi Jay,
Thanks for comments, I've included decode_enc_rs232(); below for reference, I was trying to keep post short.
I am using the ISR to set the interruptFlag, then handling the rest in the main loop... is not right?
Code: | void decode_enc_rs232()
{
uint8_t i = 1;
long timeout =0;
fprintf(USB,"d");
// Check for receive errors
if(U2FERR == 1)
{
fprintf(USB,"Receive errors\n\r");
continue;
}
// Must clear the overrun error to keep UART receiving
if(U2OERR == 1)
{
U2OERR = 0;
fprintf(USB,"Overrun error\n\r");
continue;
}
if(U2RXDA) // data available
{
RxBufEnc[0] = U2RXREG; // read initial char
if(RxBufEnc[0] == 0x64) // if encoder message
{
while ((RxBufEnc[i-1] != 0x0D) && (timeout<50000)) // continue to read until "\r"
{
if(U2RXDA) // data available
{
RxBufEnc[i] = U2RXREG;
i++;
}
else
{
delay_us(2);
timeout++;
}
}
j=0;
// take "d" off
for(i=1; i<8; i++)
{
if((RxBufEnc[i] != 0x0A) || (RxBufEnc[i] != 0x0D)) // ignore LF & CR
{
value[j] = RxBufEnc[i];
j++;
}
}
encoder = atoi(value);
fprintf(USB, "e%d B%1.4f \n\r", encoder, adcVoltage[chToRead]);
}
else
{
return;
}
}
} |
Note: I changed over to using the registers to rule out any compiler issues...
I was finding the fprintf with the float was attributing to the stackoverflow error I previously had.
And I'll look into the interrupt clearing, thanks. |
|
|
newguy
Joined: 24 Jun 2004 Posts: 1907
|
|
Posted: Wed Jan 25, 2017 11:02 am |
|
|
I've been using a very similar chip, the dsPIC33FJ256GP710A, for ~5 years now. I had real trouble with v4.099 which forced me to migrate to v4.141 at the time. I've stuck with v4.141 since then. I have to say that even v4.141 doesn't properly support the processor but I've written so many different "low level" drivers that I'm reluctant to upgrade to v5.
With that said, I have grave reservations about v4.078. What I'm wondering is what % of strange things you're fighting is the compiler, your code, or compiler support for chip peripherals not being what you think it is.
I'd recommend upgrading your compiler if you have the funds for it. |
|
|
ccsfred
Joined: 15 Jun 2016 Posts: 33
|
|
Posted: Wed Jan 25, 2017 11:12 am |
|
|
Thanks for your comments newguy, I have suggested we upgrade, the device header I have isn't even for the 'A' version of the chip, you could be onto something.. I have had numerious funny goings on and as you say have written my own "low level" drivers.
Thanks! |
|
|
temtronic
Joined: 01 Jul 2010 Posts: 9215 Location: Greensville,Ontario
|
|
Posted: Wed Jan 25, 2017 11:15 am |
|
|
sigh, guess I need TRIfocals now..I have to remove my glasses when at keyboard, put them on to do board work/read real books, on to driver see across the room. This getting old ain't fun....it looked to me that the decode_enc_rs232(); was in the ISR, my mistake. |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19477
|
|
Posted: Wed Jan 25, 2017 12:56 pm |
|
|
The obvious thing is the interrupt enables are both remarked out....
Also you always need to read the pin in the interrupt or the flag cannot be cleared (you don''t need to do this yourself, the compiler automatically does this, unless you tell it not to), but neither will work if the pin has not been read.
As another comment added later, you almost certainly need to enlarge the stack. Printf's in particular use a lot of stack. #build stack=512 |
|
|
ccsfred
Joined: 15 Jun 2016 Posts: 33
|
|
Posted: Thu Jan 26, 2017 2:32 am |
|
|
Thanks Ttelmah for your reply!
I've been swapping over between using the interrupt, unremarking the:
Code: | //enable_interrupts(INTR_CN_PIN | PIN_C13);
// enable_interrupts(INTR_GLOBAL); |
thus enabling the interrupt & remarking:
Code: | if(SAMPLE_SYNC == 1) // make sure it was rising edge on CN1
{
interruptFlag = true;
} |
to test that it's still receiving ok from the other PIC and it is only the interrupt that is causing the issue.
I'm reading the pin in the interrupt, SAMPLE_SYNC is defined as:
Code: | #define SAMPLE_SYNC input(PIN_C13) |
so I don't need to clear the interrupt flag myself then?:
Code: | CNIF = 0; // Clear CN interrupt flag |
I've now increased the stack size to 1024, with my version I had to use brackets around:
Code: | #BUILD (STACK=1024) |
and I can see the size changing in the sym file, something else I've learnt!
Unfortunately the problem still exists, when the pin is checked in the main it reads the other PIC just fine everytime, comment that out, uncomment the enable interrupt lines and doesn't work. It is going into the ISR ok and into my decode_enc_rs232 function, I'm trying to debug further, but can't see the data available but changing U2RXDA low (U2STA bit 0 is low) in the watch window (with a PICkit3) when its working or not... When using the interrupt, after the first interrupt it does have overrun error U2OERR high. It does seem that the interrupt is messing with the UART somehow. |
|
|
ccsfred
Joined: 15 Jun 2016 Posts: 33
|
|
Posted: Thu Jan 26, 2017 2:39 am |
|
|
If I comment out the overrun check and clear section:
Code: |
/* // Must clear the overrun error to keep UART receiving
if(U2OERR == 1)
{
U2OERR = 0;
fprintf(USB,"Overrun error\n\r");
continue;
}
*/ |
I do get a successful read the second time, and the second time only! |
|
|
ccsfred
Joined: 15 Jun 2016 Posts: 33
|
|
Posted: Thu Jan 26, 2017 4:10 am |
|
|
Well... I've got it working now, haven't solved why the CN interrupt was breaking the serial.
I've now used a serial interrupt, and in the ISR I get the chars and put into a software buffer (based on example EX_SISR.C), sets a flag for the CN interrupt handler in the main, which then sends out the encoder value and adc value.
Code: | #include "CD91493.h"
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#define SAMPLE_SYNC input(PIN_C13)
#define BUFFER_SIZE 32
bool interruptFlag = false;
bool readyToSendOut = false;
uint8_t j = 0;
int32_t encoder =0;
char value [10];
float adcVoltage[10] ={0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00};
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
void getEncoderReading()
{
int i;
j=0;
if(buffer[0] == 0x64) // if encoder message
{
// take "d" off
for(i=1; i<8; i++)
{
if((buffer[i] != 0x0A) || (buffer[i] != 0x0D)) // ignore LF & CR
{
value[j] = buffer[i];
j++;
}
}
encoder = atoi(value);
readyToSendOut = true;
}
else
{
fprintf(USB,"Encoder recieve error\n\r");
}
}
#int_rda2
void serial_isr()
{
int t, i, j;
next_in = 0;
for(i=0; i<BUFFER_SIZE; i++) // clear buffer out
{
buffer[i] = 0x00;
}
while(buffer[next_in-1] != 0x0D) // while not CR
{
buffer[next_in]=fgetc(ENC); // get char and put in buffer
t=next_in; // t = 0
next_in=(next_in+1) % BUFFER_SIZE; // when next_in = 31, the modulus of BUFFER_SIZE wil be 0
if(next_in==next_out) // so here 0 = 0
next_in=t; // Buffer full
}
getEncoderReading();
}
setup()
{
set_tris_c(0xFFFF); // all inputs
CNPU2=0x0020; // Enable weak pull-up on CN21 as connected with U2RX
enable_interrupts(INTR_CN_PIN | PIN_C13);
enable_interrupts(int_rda2); // UART2
enable_interrupts(INTR_GLOBAL);
}
#int_CNI
void CCNI_Interrupt() // motion board sends pulse to read adc
{
if(SAMPLE_SYNC == 1) // make sure it was rising edge on CN1
{
interruptFlag = true;
}
}
void main()
{
char string[30];
setup();
printf("\r\n\Running...\r\n");
while(true)
{
if(interruptFlag == true)
{
//readAdc();
while(!readyToSendOut); // wait until string recieved from the motion board
fprintf(USB, "e%d B%1.4f \n\r", encoder, adcVoltage[0]);
interruptFlag = false;
}
}
} |
Any comments to improve my code greatly appreciated!
Thanks for your help! |
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|