|
|
View previous topic :: View next topic |
Author |
Message |
PICLeo
Joined: 14 Dec 2005 Posts: 11 Location: South Africa
|
INT_RDA never stops? |
Posted: Wed Dec 14, 2005 3:22 am |
|
|
Hi
I use the PIC 18F242.
What I am going to do is do send data via RS232. I made a computer program with Labview that makes it possible to send and receive data.
Every 500 ms the program starts to transmit data. Then the PIC starts receiving a string and after that writing a string.
That all works so far quite well.
The only problem is that it seams to be that the PIC always stays within the RDA interrupt routine. (Even if I disconnect the USART PINS)
I assume that because of the LED at PIN_B3 glowing all the time.
And the thing is that I use also the 3 external interrupts to count puls signals coming from some sensors.
So what did I program wrong?
How am I able to stop the INT_RDA interrupt so that the outher interrupts can occur?
I thought the interrupt only occurs when receive data is available.!?!?
I hope somebody could help me.
Bye
Martin
Code: | /* RS-CAR CONTROL PROGRAMM
Reiceive and Send string [ 000,000,0D ]
*/
#include "rccar242withstring.h"
#include <stdlib.h>
#int_RDA //Datatransmission to PC via RS232\Bluetooth
RDA_isr()
{
output_high(PIN_B3);
wheelcount = wheelcount + hallwheel;
gets (inputstring);
anglein3 = ((inputstring[1] - 0x30)*100);
anglein2 = ((inputstring[2] - 0x30)*10);
anglein1 = (inputstring[3] - 0x30);
anglein = anglein3 + anglein2 + anglein1;
set_pwm1_duty(anglein);
speedin3 = ((inputstring[5] - 0x30) * 100);
speedin2 = ((inputstring[6] - 0x30) * 10);
speedin1 = (inputstring[7] - 0x30);
speedin = speedin3 + speedin2 + speedin1;
angleout = read_adc(); //send
printf("%4ld, %4ld, %4ld, %4ld, %4d \n",hallgear, hallwheel, wheelcount, angleout, anglein);
hallgear = 0;
hallwheel = 0;
timerdelay = 0;
output_low(Pin_B3);
}
#int_ext //RESET PIC
EXT_isr()
{
while(Input(PIN_B0) == 1)
{
anglein = 84;
hallgear = 0;
hallwheel = 0;
timerdelay =0;
wheelcount = 0;
Output_high(PIN_B4);
}
Output_low(PIN_B4);
}
#int_ext1 //Input signal from sensor gear
EXT1_isr()
{
if(Input(PIN_B1) == 1)
{
hallgear++;
}
}
#int_ext2 //Input signal form sensor wheel
EXT2_isr()
{
if(Input(PIN_B2) == 1)
{
hallwheel++;
}
}
void main()
{
Output_high(PIN_B4);
delay_ms(3000);
Output_low(PIN_B4);
set_tris_a(0b00000001);
set_tris_b(0b00000111);
set_tris_c(0b10000000);
setup_spi(FALSE);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_32|RTCC_8_bit);
setup_timer_1(T1_DISABLED);
setup_timer_2(T2_DIV_BY_16,255,1);
setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
setup_adc_ports(AN0_VREF_VREF);
setup_adc(ADC_CLOCK_INTERNAL);
setup_port_a( ALL_ANALOG );
set_adc_channel( 0 );
setup_ccp1(CCP_PWM);
enable_interrupts(GLOBAL);
enable_interrupts(INT_EXT);
enable_interrupts(INT_EXT1);
enable_interrupts(INT_EXT2);
enable_interrupts(INT_RTCC);
enable_interrupts(INT_RDA);
ext_int_edge(L_TO_H);
ext_int_edge(1, L_TO_H);
ext_int_edge(2, L_TO_H);
hallgear = 0;
hallwheel = 0;
timerdelay = 0;
angleout = 0;
wheelcount = 0;
anglein = 84;
anglein3 = 0;
anglein2 = 0;
anglein1 = 0;
speedin = 0;
speedin3 = 0;
speedin2 = 0;
speedin1 = 0;
set_pwm1_duty(anglein);
while(1)
{}
}
|
|
|
|
Ttelmah Guest
|
|
Posted: Wed Dec 14, 2005 4:12 am |
|
|
First comment. Dont use 'gets' in the interrupt. Just get the one character that is waiting if the interrupt occurs. 'gets', is a routine to sit an _wait_ for a complete string, with a line feed terminator, and as such, ruins the 'point' of using the interrupt in the first place. Look at the example 'ex_sisr', for how to handle the receive interrupt.
As it stands, if the incoming serial line goes low (this is the 'start' level for TTL serial), the interrupt will be called, and the code will hang inside the routine, until a line feed is seen.
Do all your other calculation work, in the main loop, when a string is seen. I'd suggest that assuming the incoming data is a packet, ending in a line feed, you make the receive routine into a 'state machine', which walks through the characters as they arrive. You can then just look for the 'state' that marks the end of string, and do your output, and change the settings, when this is seen.
Second comment. Get rid of the printf in the interrupt handler. This is a fairly strong 'no no'. The time taken to print the characters here is thirty character times. During all but two of these times, the system will be unable to receive, and data will be lost. You don't show your #use RS232 statement, but the UART will have gone into an 'error' condition, if any data arrives while you are printing, and unless this is cleared, the system will hang.
Disconnecting the serial input, unless the line is pulled 'high', may well result in a 'break' condition being seen, which will result in continuous interrupts. However provided the handler is kept short, this will not cause a problem, but with your code, will cause a continuous hang in the handler.
Best Wishes |
|
|
neil
Joined: 08 Sep 2003 Posts: 128
|
Your ISR is too big! |
Posted: Wed Dec 14, 2005 4:23 am |
|
|
Hi, there are several things you are doing in your RDA ISR which are serious no-no's!
First, you are doing a lot of maths in your interrupt. Although the PIC18 has a hardware multiplier, unlike the 16 series, each of your 'anglein' lines are taking up several program cycles.
You are reading the ADC in an interrupt. This is possibly the biggest problem. As that line stands, it waits for the ADC reads to complete before leaving the function. If you really want to read the ADC each time a serial byte comes in, replace that line with Code: | read_adc(ADC_START_ONLY); | This will start a conversion, but not wait for it to complete and get the value. You will have to read the value using Code: | angleout = read_adc(adc_read_only); | later on (outside the ISR, or in a subsequent ISR).
Finally, another thing you should NEVER do in an interrupt is a prinf, especially one this long! This statement uses a lot of cycles in a pic at the best of times when formatting data like that.
In summary, the only things you should do in an interrupt is store a value, set a flag or variable, toggle a pin, clear a flag etc. It has to be short. The problem you are having is that so much code is inside the interrupt, it never gets the chance to exit before being re-triggered!
If you read the receive buffer and put it into a received variable, then set a flag (short int) every time the serial ISR triggers and test for this flag in your main loop, you can take all this outside the interrupt. The serial interrupt is only there to signal that data has arrived so it can be read from the receive buffer immediately.
Also, I have just noticed, you are doing a 'gets()' in the interrupt. This will also wait in this function until a null termination '\0' is found over serial. If you are using interrupts for serial, you can't do this. You will need to buffer up the data yourself and check for a termination.
an example of a circular buffer is in CCS's example program 'ex_sisr.c' This isn't the most efficient but it works well. This is the sort of ISR size you should be aiming for: Code: | #int_rda
void serial_isr() {
int t;
buffer[next_in]=getc();
t=next_in;
next_in=(next_in+1) % BUFFER_SIZE;
if(next_in==next_out)
next_in=t; // Buffer full !!
}
|
To get an idea of how long your ISR is, look at the .lst file and search for #int_RDA. The end of it will be before the #int_ext comment line. This code is not the whole interrupt handler either. There is also the global save/restore code which can be quite large.
I hope this has helped, and good luck.
Neil. |
|
|
PICLeo
Joined: 14 Dec 2005 Posts: 11 Location: South Africa
|
|
Posted: Thu Dec 15, 2005 1:08 am |
|
|
Puh
Thats a lot. Could take a while.
Thank you so far. |
|
|
Douglas Kennedy
Joined: 07 Sep 2003 Posts: 755 Location: Florida
|
|
Posted: Thu Dec 15, 2005 7:47 am |
|
|
If you put a printf in the int RDA isr don't expect it to work for input while your printf is outputing characters. If your printf prints 20 chars expect to have the potential of missing at least 20 chars of inbound data. Unless you have some sort of external synchronization it is almost guaranteed at some time to lose inbound characters.
Do absolutely as little as possible in an isr... set global flags to signal your main routine and do the printf there. |
|
|
PICLeo
Joined: 14 Dec 2005 Posts: 11 Location: South Africa
|
|
Posted: Fri Jan 13, 2006 2:55 am |
|
|
Hi
I changed my program now and the transmission works quite well so far.
Thanks for the help.
I actually need this program to control a RCcar via Bluetooth. The data I send within the printf I use to calculate and indicate the speed, steering, distance and acceleration with the help of the software Labview. That works perfect.
The data I am getting with getc() is used to setup the PWM value to drive the car. That also works quite nice.
Now I have two more problems:
As long the programm stays within the send() subroutine I am not able to control the car immediately. The motor waits till the end of the send() subroutine till he can run the RDA() subroutine. In this case I am getting a maximum delay of 500ms before the value of the PWM changes.
I think the time taken for the printf and the read_adc() is just to long.
Any idea how I can change this?
The next thing is that I need 3 PWM. 2 for the speed controler and one for the steering of the car.
I do have three 18f242 microchips here and I am thinking about to use one of them for the motorcontrol one for the steering and one for the transmission to the PC. How do I get those uCs to communicate with each other?
Bye
Martin
The programm:
Code: | #include "RCcar.h"
#include <stdlib.h>
/* RS-CAR CONTROL PROGRAMM
Reiceive string [000,000]
Send string [000,000,0000,0000]
Calculation within Labview
*/
#define BUFFER_SIZE 16
BYTE buffer[BUFFER_SIZE];
BYTE next_in = 0;
BYTE next_out = 0;
char inputstring[16];
int16 hallgear = 0;
int16 hallwheel = 0;
int16 hallgearbuffer;
int16 hallwheelbuffer;
int16 angle;
int16 wheelcount = 0;
int1 sendready = 0;
int8 timerdelay = 0;
int8 speedin;
int16 speedin1;
int16 speedin2;
int16 speedin3;
int1 RDAset = 0;
char inputchar;
#int_RDA //Reads values from RS232 into buffer
RDA_isr()
{
inputchar = getc();
if(inputchar == 0x0d)
{
RDAset = 1;
}
else
{
buffer[next_in] = inputchar;
next_in = (next_in + 1);
}
}
#int_ext //Reset
EXT_isr()
{
while(Input(PIN_B0) == 1)
{
output_high(PIN_B4);
hallgear = 0;
hallwheel = 0;
hallgearbuffer = 0;
hallwheelbuffer =0;
timerdelay = 0;
wheelcount = 0;
sendready = 0;
speedin = 0;
RDAset = 0;
speedin = 0;
set_pwm1_duty(speedin);
set_pwm2_duty(speedin);
}
output_low(PIN_B4);
}
#int_ext1 //Input signal from sensor gear
EXT1_isr()
{
if(Input(PIN_B1) == 1)
{
hallgear++;
}
}
#int_ext2 //Input signal form sensor wheel
EXT2_isr()
{
if(Input(PIN_B2) == 1)
{
hallwheel++;
}
}
#int_RTCC //Time for printf specified (500ms)
RTCC_isr()
{
if(timerdelay == 49)
{
sendready = 1;
}
timerdelay++;
}
void RDA() //Subroutine handling the incoming values for speed and angle
{
disable_interrupts(INT_RDA);
strncpy (inputstring, buffer, 16);
next_in = 0;
enable_interrupts(INT_RDA);
if(inputstring[4] == '0')
{
speedin3 = ((inputstring[5] - 0x30) * 100);
speedin2 = ((inputstring[6] - 0x30) * 10);
speedin1 = (inputstring[7] - 0x30);
speedin = speedin3 + speedin2 + speedin1;
set_pwm2_duty(0);
set_pwm1_duty(speedin);
}
if(inputstring[4] == '1')
{
speedin3 = ((inputstring[5] - 0x30) * 100);
speedin2 = ((inputstring[6] - 0x30) * 10);
speedin1 = (inputstring[7] - 0x30);
speedin = speedin3 + speedin2 + speedin1;
set_pwm1_duty(0);
set_pwm2_duty(speedin);
}
RDAset = 0;
}
void send() //Send data
{
output_high(PIN_B3);
wheelcount = wheelcount + hallwheel;
angle = read_adc();
hallgearbuffer = hallgear;
hallgear = 0;
hallwheelbuffer = hallwheel;
hallwheel = 0;
printf("%2ld,%2ld,%5ld,%3ld\n",hallwheelbuffer,hallgearbuffer,wheelcount,angle);
timerdelay = 0;
sendready = 0;
output_low(PIN_B3);
}
void main()
{
Output_high(PIN_B4);
delay_ms(2000);
Output_low(PIN_B4);
set_tris_a(0b00000001);
set_tris_b(0b00000111);
set_tris_c(0b10000000);
setup_spi(FALSE);
setup_wdt(WDT_OFF);
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_32|RTCC_8_bit);
setup_timer_2(T2_DIV_BY_16,255,1);
setup_adc_ports(AN0_VREF_VREF);
setup_adc(ADC_CLOCK_INTERNAL);
setup_port_a( ALL_ANALOG );
set_adc_channel( 0 );
setup_ccp1(CCP_PWM);
setup_ccp2(CCP_PWM);
enable_interrupts(GLOBAL);
enable_interrupts(INT_EXT);
enable_interrupts(INT_EXT1);
enable_interrupts(INT_EXT2);
enable_interrupts(INT_RTCC);
enable_interrupts(INT_RDA);
ext_int_edge(L_TO_H);
ext_int_edge(1, L_TO_H);
ext_int_edge(2, L_TO_H);
speedin = 0;
set_pwm1_duty(speedin);
set_pwm2_duty(speedin);
while(1)
{
If(RDAset == 1)
{
rda();
}
If(sendready == 1)
{
send();
}
}
}
|
|
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Fri Jan 13, 2006 8:24 am |
|
|
Quote: | As long the programm stays within the send() subroutine I am not able to control the car immediately. The motor waits till the end of the send() subroutine till he can run the RDA() subroutine. In this case I am getting a maximum delay of 500ms before the value of the PWM changes.
I think the time taken for the printf and the read_adc() is just to long.
Any idea how I can change this? | The printf is very slow. Try implementing a similar method as you are using for the RS232 receive, i.e. a circular buffer that is writing the data under interrupt control. Your main loop can then write the data to the buffer at full wpeed while the interrupt driven transmit function will continue in the background.
Quote: | The next thing is that I need 3 PWM. 2 for the speed controler and one for the steering of the car.
I do have three 18f242 microchips here and I am thinking about to use one of them for the motorcontrol one for the steering and one for the transmission to the PC. How do I get those uCs to communicate with each other? | Using three of these processors seems like an overkill and you will spend a lot of time writing the code for the inter-processor communications. Easiest solution is to choose another processor which has three or more PWM units. Or use 2 x PIC18F242; one for 2 x PWM and the other for serial communication + 1 x PWM (with the 2nd PWM for future use).
Communication between the processors is easiest arranged with an I2C or SPI bus. Check this thread for a description of the differences between I2C, SPI and RS232.
I've also seen some great code examples for master-slave communications but can't find a reference quickly so maybe someone else can give you a link.
I found an error in your code. In function RDA you do 'RDAset = 0' but this variable can also be modified by the interrupt handler and must be close coupled with the resetting of the next_in pointer. Change to:
Code: | void RDA() //Subroutine handling the incoming values for speed and angle
{
disable_interrupts(INT_RDA);
strncpy (inputstring, buffer, 16);
next_in = 0;
RDAset = 0; <-- this line was moved from end of function
enable_interrupts(INT_RDA);
|
Another potential error is that you are not checking for buffer overflow when writing to the receive buffer. Code: | buffer[next_in] = inputchar;
next_in = (next_in + 1); |
Change to Code: | buffer[next_in] = inputchar;
if (next_in < BUFFER_SIZE)
next_in = (next_in + 1); |
|
|
|
|
|
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
|