|
|
View previous topic :: View next topic |
Author |
Message |
Jamby
Joined: 02 May 2012 Posts: 4
|
UART Interrupts during I2C Interrupt |
Posted: Wed May 02, 2012 11:39 am |
|
|
Hi everybody.
Firstly, I would excuse me for my very bad English :( But I'm not English Mother tongue.
I have a question for you. It isn't really a CCS "problem", because I know PIC limits, but I would like to know if anyone has a workaround for my situations.
Let explain it.
I've a UART VGA Camera directly connected to a PIC (16F1825) that take care of communicate with it. This PIC is in turn connected to another PIC (18F26k22) using I2C protocol. This PIC takes care of receive VGA data from the first PIC and redirect it to a Wireless MCU (RN-131), it also elaborates other data from other boards and communicates to other PIC too; it can be imaged as a "MotherBoard".
Everything is assembled on a 4WD Robot.
I've a problem in the VGA PIC, I would like to have an I2C "command" that tells the PIC to request an Image to the Camera using UART and then "redirect" it to the I2C channel. I've chosen to use an I2C PIC that interface to Camera UART instead of let the "MotherPIC" do it, to be able to connect different Boards on the same I2C Channel (While with UART I can't).
The problem is that when I receive the "command" from the MotherPIC, the SlavePIC needs to send a request to the Camera and receive the response. This second communication is done with UART, so using RDA interrupt. As a 8bit PIC, it only "supports" one Interrupt a time, so I need to "exit" the SSP interrupt. This can't be done, because I cannot store the whole image data in RAM (The photo size is about 2-3Kb), I need a way to redirect the received data directly without storing the whole image.
Yeah, I know that's everything is almost incomprehensible, but I cannot explain better the situation.
Here's the actual code (stripped down):
Code: |
#byte SSP1BUF = 0x211
unsigned int8 VGA_BUF[VGA_BUF_SIZE];
unsigned int8 VGA_rear=0;
unsigned int8 VGA_front=0;
unsigned int8 state=0;
unsigned int8 command=0;
void VGA_GetLen();
int1 CONNECTED=0;
unsigned int8 TMPLEN[2];
#int_SSP
void SSP_isr(void)
{
state=i2c_isr_state();
if(CONNECTED)
{
if(state >= 0x80) //Master is Requesting Data
{
if(command==VGA_Request_LEN)
{
VGA_GetLen();
if(state < 0x83)
{
SSP1BUF=TMPLEN[state-0x80];
}
}
if(command==VGA_Request_PIC)
{
//TO DO
}
}
else
{
if(state <=0x80)
command=i2c_read();
}
}
}
#int_RDA
void RDA_isr(void)
{
VGA_BUF[VGA_front]=getc();
VGA_front=(VGA_front+1)%VGA_BUF_SIZE;
}
char VGA_getc()
{
unsigned char RET;
while(VGA_rear==VGA_front);
RET=VGA_BUF[VGA_rear];
VGA_BUF[VGA_rear]=0;
VGA_rear=(VGA_rear+1)%VGA_BUF_SIZE;
return RET;
}
void VGA_GetLen()
{
unsigned char *reply;
unsigned int16 j=0;
unsigned int8 i=0;
VGA_printf(VGA_GetImg);
delay_ms(30); //WAIT RESPONSE
reply=VGA_gets();
if(!(reply[0]==0xAA && reply[1]==0x0e && reply[2]==0x04))
return ;
reply=VGA_gets();
if(!(reply[0]==0xAA && reply[1]==0x0A && reply[2]==0x05))
return ;
else
{
TMPLEN[0]=reply[3];
TMPLEN[1]=reply[4];
}
}
void main()
{
enable_interrupts(INT_SSP);
enable_interrupts(INT_RDA);
enable_interrupts(GLOBAL);
setup_oscillator(OSC_8MHZ|OSC_NORMAL|OSC_PLL_ON,0);
delay_ms(200);
CONNECTED=VGA_init();
while(true)
{
}
}
|
Master
Code: |
void main()
{
setup_oscillator(OSC_64MHZ);
output_high(LEDR);
delay_ms(2000); //POWER-UP ALL BOARDS
output_low(LEDR);
while(true)
{
output_toggle(LEDG);
i2c_start();
F=i2c_write(0x50);
F=F || i2c_write(0x11);
if(!F)
{
i2c_start();
i2c_write(0x50 | 1);
printf("DEBUG: %d%c%c",i2c_read(0),0x0a,0x0d);
}
else
{
printf("NO RESPONSE%c%c",0x0a,0x0d);
}
i2c_stop();
delay_ms(1000);
}
}
|
I'm using PCWHD 4.130
Thanks for any suggestion.
Andrea |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed May 02, 2012 12:58 pm |
|
|
Quote: |
This second communication is done with UART, so using RDA interrupt.
As a 8bit PIC, it only "supports" one Interrupt a time, so I need to "exit"
the SSP interrupt.
|
I didn't look too closely at your overall problem, but I just want to
comment on the technical issue you gave above.
You can certainly poll the RDA interrupt to see if it's set, while you're
still inside the SSP interrupt. Then handle the RDA interrupt right there.
You still need the normal #int_rda routine that will handle RDA interrupts
when you're not inside the SSP interrupt.
Code: |
#int_ssp
void ssp_isr(void)
{
char c;
// Do INT_SSP code first.
// Then check if you received a character from RS-232.
// If so, then handle it now.
if(interrupt_active(INT_RDA))
{
c = getc(); // Get the char and put in a global buffer.
// Code to do that is not shown.
clear_interrupt(INT_RDA);
}
} |
|
|
|
Jamby
Joined: 02 May 2012 Posts: 4
|
|
Posted: Wed May 02, 2012 1:43 pm |
|
|
Oh dear! I didn't know that's possible to do in such a way.
I tried to simulate the circuit and i saw that the RCIE bit in PIE1 register was resetting when it entered in SSP interrupt, but after you said that, I've checked and I saw that I've set wrong the register address.
I'll try this solution ASAP.
Thank You.
EDIT:
It does PERFECTLY works.
I would like to know the difference between your solution and a more "readable" solution like this:
Can I just check the kbhit function?
Code: | #int_ssp
void ssp_isr(void)
{
char c;
// Do INT_SSP code first.
// Then check if you received a character from RS-232.
// If so, then handle it now.
if(kbhit())
{
c = getc(); // Get the char and put in a global buffer.
// Code to do that is not shown.
}
} |
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed May 02, 2012 4:55 pm |
|
|
I was trying to show the general case. But you're right, these two lines
produce equivalent ASM code:
Code: |
if(interrupt_active(INT_RDA))
if(kbhit())
|
There may be a problem with the compiler regarding the clear_interrupt()
routine. I will investigate that some more. |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Thu May 03, 2012 2:10 am |
|
|
Some important points: "As a 8bit PIC, it only "supports" one Interrupt a time". Being an 8 bit PIC has nothing to do with it. All single core processors, indeed practically all processors (each core in a multi-core chip are processors in their own right) can only only ever do one thing at a time. Interrupts are point events, they happen at a time, they do not spread over a time interval. Unfortunately your comment only shows that you don't really understand interrupts fully yet.
Interrupts are hardware events. When an external device, be it a built-in peripheral such as the UARTS, I2C or whatever, or something external to the PIC generates an interrupt it is merely raising a hardware flag that says it needs servicing in some way. UARTS raise interrupts when they receive characters, or detect errors or have completed sendind of a character. The receive interrupt, RDA in CCS, means the UART has received a character and it needs to be dealt with.
When an interrupt is raised and the firmware is configured to deal with it, in other words there is a suitable ISR routine and interrupts are turned on for this source and globally, then the processor, with help from CCS interrupt handling code, saves whatever its was doing and starts, after a short but not insiginificant time, running the ISR code. By default interrupts are disabled while running ISRs, and reenabled automatically by CCS interrupt handling code after the user's ISR has finished. Interrupts CAN be turned on while in ISR code. This is NOT a good idea. You'll helplessly confuse CCS's interrupt handling code, which is not designed to be re-entrant. I have, a long time ago, on a 16C74, had re-interruptible ISR code. BUT that was in assembler where I had full control of the processor context. It enabled half decent software transmit only UART code (as the hardware designer had thoughtfully not bothered to connect the RS232 to the hardware UART pins) to run while updating an LCD (no controller, just IO lines). To to this I had to make the lengthy LCD update ISR re-interruptible for about 2/3 of its code. I write this to illustrate it is possible, but absolutely NOT recommended for CCS C.
As always, the recommendation for ALL ISRs is to make them as short and as fast as possible. Its entirely possible to have several interrupts running on a single PIC. What you need to remember is that interrupts are hardware events that generally have a limited time window. They are thus realtime events: service them quickly and efficiently. Interrupts that occurs while interrupts are disabled are not "lost", they are simply delayed. The point is to delay them as little as possible.
Your UART RDA ISR is OK. It simply buffers incoming data. Its your I2C ISR that's got the problem. Really with an I2C slave you need to have everything that you need to send ready and waiting, buffered up somewhere. Its not as bad as an SPI slave however, at least with I2C the slave can hold up the transfer. Never the less, you really need to have everything ready rather than wait around for it to come in from elsewhere. As you've found, if waiting for it in an ISR involved waiting for other ISRs to run, as in your code, then you've got a big problem. Its a form of deadlock and its been around as long as interrupts have.
Your I2C ISR will run for every byte in the I2C transaction. You need that to run totally independantly of the serial data transfer. Your I2C stuff should only ever attempt to transfer data when there is a complete buffer full of data waiting for it. AND you'll need to double buffer your data to allow you to receive new serial data while transmitting the last complete dataset by I2C. All this is standard computing stuff; such interrupt driven double buffering of data has been around since the early 1960's. You may need to alter your I2C protocol to include a "Data not ready" response to be used when there is not a full buffer of data ready and waiting.
To sum up: make you transmit only send if there is a complete buffer of data. Use double buffering. Ensure ISRs are short and don't have to wait for anything.
RF Developer |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19559
|
|
Posted: Thu May 03, 2012 2:38 am |
|
|
I was going to post something similar, but you beat me to it.
Add the comment, that this sounds like a perfect job for one or more 'flags'. Single bit boolean variables, which the PIC can test/set in a single instruction. You could have a flag saying 'RS232_packet_to_be accepted', which is normally 'false', then just set it in the I2C ISR, when the particular event occurs that you want to start the RS232 transfer. In the RS232 code, just ignore characters, till this flag goes 'true'. Then perhaps another 'RS232_packet_complete', set when the packet is complete.
You need throughout, also to remember that nothing is perfect. Characters sometimes _will_ get lost or corrupted. This is why systems that rely on character 'counting', are potentially dangerous. Code that sits waiting in an ISR for a number of characters to arrive, will sometimes go wrong. If instead the arrival of 'xx' characters sets a flag signal, then in the main code, you can test a timer, and if this does not happen within a period of time, institute recovery procedures. Much harder to do if you are sitting in an ISR...
Best Wishes |
|
|
Jamby
Joined: 02 May 2012 Posts: 4
|
|
Posted: Thu May 03, 2012 6:01 am |
|
|
RF_Developer wrote: | Some important points: "As a 8bit PIC, it only "supports" one Interrupt a time". Being an 8 bit PIC has nothing to do with it. All single core processors, indeed practically all processors (each core in a multi-core chip are processors in their own right) can only only ever do one thing at a time. Interrupts are point events, they happen at a time, they do not spread over a time interval. Unfortunately your comment only shows that you don't really understand interrupts fully yet.
|
What I wanted to say with that phrase is that, opposite as 16bit PIC, there is only ONE global interrupt that's raised, while in 16bit PIC we have different interrupts for each event. CCS still "divide" them to be able to build more readable code, but the ISR is only one.
RF_Developer wrote: |
When an interrupt is raised and the firmware is configured to deal with it, in other words there is a suitable ISR routine and interrupts are turned on for this source and globally, then the processor, with help from CCS interrupt handling code, saves whatever its was doing and starts, after a short but not insiginificant time, running the ISR code. By default interrupts are disabled while running ISRs, and reenabled automatically by CCS interrupt handling code after the user's ISR has finished. Interrupts CAN be turned on while in ISR code. This is NOT a good idea. You'll helplessly confuse CCS's interrupt handling code, which is not designed to be re-entrant. I have, a long time ago, on a 16C74, had re-interruptible ISR code. BUT that was in assembler where I had full control of the processor context. It enabled half decent software transmit only UART code (as the hardware designer had thoughtfully not bothered to connect the RS232 to the hardware UART pins) to run while updating an LCD (no controller, just IO lines). To to this I had to make the lengthy LCD update ISR re-interruptible for about 2/3 of its code. I write this to illustrate it is possible, but absolutely NOT recommended for CCS C.
|
It was what I wanted to do, even if I know that's not recommended, but I hadn't any other idea..
RF_Developer wrote: | Never the less, you really need to have everything ready rather than wait around for it to come in from elsewhere. As you've found, if waiting for it in an ISR involved waiting for other ISRs to run, as in your code, then you've got a big problem. Its a form of deadlock and its been around as long as interrupts have. |
RF_Developer wrote: | You may need to alter your I2C protocol to include a "Data not ready" response to be used when there is not a full buffer of data ready and waiting. |
RF_Developer wrote: | To sum up: make you transmit only send if there is a complete buffer of data. Use double buffering. Ensure ISRs are short and don't have to wait for anything. |
As I say:
Jamby wrote: | I cannot store the whole image data in RAM (The photo size is about 2-3Kb), I need a way to redirect the received data directly without storing the whole image.
|
Mid-Level 8bit PIC has at most 4-5Kbytes of RAM. The PIC16F1825 has 1024 bytes of RAM, I cannot store the whole Image in RAM, Double Buffer is definitely not affordable.
Anyway, thanks for the suggestion |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Thu May 03, 2012 6:10 am |
|
|
Simple: buy a bigger PIC.
RF Developer. |
|
|
Jamby
Joined: 02 May 2012 Posts: 4
|
|
Posted: Thu May 03, 2012 11:01 am |
|
|
RF_Developer wrote: | Simple: buy a bigger PIC.
RF Developer. |
Do you think that if it was possible, I wouldn't have already bought a bigger PIC to accomplish this?
Unluckily, there aren't 8bit (or even 16bit) PIC with more than 1Kbytes of RAM, AND at most 14 pins. This PIC needs to be soldered directly over the VGA cam, and 14 pins is the most that can be used even with SMD package.
So, I think that there isn't any other solution (except the solution of choose another MCU Manufacturer) further than waiting during the SSP interrupt. |
|
|
asmallri
Joined: 12 Aug 2004 Posts: 1636 Location: Perth, Australia
|
|
Posted: Fri May 04, 2012 2:12 am |
|
|
The PIC18F supports two separate hardware priorities. When a normal (low priority) interrupt is being processed, the PIC18F ONLY disables the low priority interrupt. A high priority interrupt can happily interrupt a low priority interrupt handler.
PIC24 and above support multiple hardware priority interrupts and without doubt provide far more flexibility in this regard. _________________ Regards, Andrew
http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!! |
|
|
RF_Developer
Joined: 07 Feb 2011 Posts: 839
|
|
Posted: Fri May 04, 2012 6:31 am |
|
|
"Do you think that if it was possible, I wouldn't have already bought a bigger PIC to accomplish this?"
Judging by the vast majority of people who come to this forum, yes, I think that you may well have not thought of using an alternative PIC. Many haven't even read the datasheet for the device they are using, and/or have only the very barest grasp of C. A lot have no concept of how interrupts work.
You came on here and made it clear you were unclear as to what an interrupt was as compared to an interrupt service routine. Maybe that was/is a language difficulty... maybe not. Your use of English is of sufficient a level to make me think language is NOT the issue, or at least not the main issue.
Even after carefully describing the difference and making some of the implications clear, you just brush those aside and move your goal posts, by saying that you have already fixed your hardware implementation and that's not going to change.
You then, in the next sentence, tell us you are using a very amateur and frankly fault prone method, which makes me think more than ever you are not experienced enough to adopt a suitable engineering solution, i.e. to pick hardware capable of doing the job, instead you're insisting on a firmware fix to your problem.
OK, I think your best way of dealing with this is to abandon C altogether and assembler, then you'll have total control of the processor. It'll take you more effort of course, but its much more likely that you'll get a satisfactory result. Maybe you need to forget interrupts altogether and go for a completely polled solution, with a very tightly looping main loop. Controlling a single UART and I2C is straightforward enough to do in assembler, a polled implementation may well work in C come to that.
As a general note, just because the PIC can do something. that doesn't mean CCS C must be able to implement the same functionality. I'm thinking here of certain PWM operations which need extra peripheral initialisation that is not covered by CCS's PWM setup functionality. Also ALL interrupt functionality is not covered by C, its all done by extensions, which vary from compiler to compiler, so its entirely reasonable to expect some unusual functionality, and nested interrupts ARE unusual, not to be easy or possibly even supported by CCS C, or any other compiler come to that.
I've tried to help all I can. I won't comment further on this.
RF Developer |
|
|
|
|
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
|