CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

INT_RDA never stops?

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
PICLeo



Joined: 14 Dec 2005
Posts: 11
Location: South Africa

View user's profile Send private message ICQ Number

INT_RDA never stops?
PostPosted: Wed Dec 14, 2005 3:22 am     Reply with quote

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







PostPosted: Wed Dec 14, 2005 4:12 am     Reply with quote

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

View user's profile Send private message

Your ISR is too big!
PostPosted: Wed Dec 14, 2005 4:23 am     Reply with quote

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

View user's profile Send private message ICQ Number

PostPosted: Thu Dec 15, 2005 1:08 am     Reply with quote

Puh

Thats a lot. Could take a while.

Thank you so far.
Douglas Kennedy



Joined: 07 Sep 2003
Posts: 755
Location: Florida

View user's profile Send private message AIM Address

PostPosted: Thu Dec 15, 2005 7:47 am     Reply with quote

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

View user's profile Send private message ICQ Number

PostPosted: Fri Jan 13, 2006 2:55 am     Reply with quote

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

View user's profile Send private message

PostPosted: Fri Jan 13, 2006 8:24 am     Reply with quote

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);
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
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