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

16F877A RS232 Communication Problem with Xbee RF module

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



Joined: 16 Nov 2008
Posts: 22
Location: Sri Lanka

View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger

16F877A RS232 Communication Problem with Xbee RF module
PostPosted: Wed Nov 26, 2008 3:37 am     Reply with quote

Hi all,

I need to write a program to send and receive data in between 16F877A micro controller and Xbee RF Module ( This is module which support UART serial communication). Here i have written a code to receive data from Xbee module to micro controller. Then it displays the temperature value on the seven segment display.

Unfortunately my code does not works at all. So there can be some errors on it. If someone can help me and guide me to correct the below code, it will helps me a lot to go through my project.

Any help regarding my matters are highly appreciated.
Thanks in advance.

Kushan Sharma

Very Happy

Code:

#include <16F877A.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES XT                       //Crystal osc <= 4mhz
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES WRT_50%                  //Lower half of Program Memory is Write Protected

#use delay(clock=4000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

  #include <stdio.h>
  #include <stdlib.h>
  #include <stdlibm.h>
  #include <string.h>

char buffer[10];        //Buffer to read data.
byte rcvd_uart = False;

#int_RDA
void  RDA_isr(void)
{
   int numPos = 0;
   
   do
   {
      buffer[numPos] = getc();
      numPos++;
   }while(kbhit());
   
   rcvd_uart = true;
   clear_interrupt(INT_RDA);
}

byte CONST LED_MAP[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
#inline
void displaySevenSegment(int tempCalc){
   int D0=0, D1=0;
   D0 = tempCalc%10;
   D1 = tempCalc/10;
   
   output_b(0);
   output_d(LED_MAP[D0]);
   output_bit(PIN_B1, 1);   //enable pin B1 for activate the seven
   delay_us(7);

   output_b(0);
   output_d(LED_MAP[D1]);
   output_bit(PIN_B0,1);
   delay_us(4 );
}

void main()
{
   int16 pwm_duty = 50;
   
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DIV_BY_1,24,1);
   setup_ccp1(CCP_PWM);
   set_pwm1_duty(pwm_duty);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_TBE);
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   // TODO: USER CODE!!
   while(true){
      int temperature = 0;
     
      if(rcvd_uart){
         temperature = (int )buffer;
      }
      displaySevenSegment(temperature);          //Display the temperature on &-segment
      delay_us(10);
   }
}


_________________
Kushan Sharma
mlkushan@gmail.com
Ttelmah
Guest







PostPosted: Wed Nov 26, 2008 4:12 am     Reply with quote

You don't tell us what actual 'format' the data is arriving in, which makes it hard to know what might be wrong. However some 'comments', one of which almost certainly covers the actual main problem...
The receive interrupt, will normally be called, with just _one_ character waiting. You will only get a second character present, if it is possible for other interrupts, or periods without interrupts being handled, to exceed one character 'time'. This should not normally happen, so the interrupt will retrieve just a single byte.
Now, you are taking the value 'buffer', _which is the address of the serial receive buffer_, and turning this number, into an integer to display. You are not making any attempt to retrieve the _contents_ of the buffer. First main problem. Since the buffer will only contain one byte, unless the sensor is just returning single byte integer values, you are not going to get your required value. Yuo need I suspect to use something like the EX_SISR code, and have your routine in the main look for a 'delimiter' character (perhaps a line feed - depends on what format the chip is actually sending), and then use the 'atoi' routine to convert the txt available, into a number.
Next big problem. You are enabling INT_TBE,with _no handler present_. This is the really big 'killer'. Since you are never sending anything, the transmitter buffr will _always_ be empty and int_tbe, will continuously trigger. Nothing else will work with this happening. _Never_enable an interrupt without a handler. Since you are not transmitting, you don;t want this interrupt enabled at all.
Next thing. What _clears_ 'rcvd_uart'. If the code was working, once this is set, it will remain set for ever...
As a final comment, if your buffer handling was working, you would need to be careful to limit the maximum number used as an address, to prevent buffer overflows.

Best Wishes
mlkushan



Joined: 16 Nov 2008
Posts: 22
Location: Sri Lanka

View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger

PostPosted: Wed Nov 26, 2008 5:17 am     Reply with quote

Hi Ttelmah,

Thank you very much for your help.
I did some corrections according to your suggestions. Actually I'm developing a wireless remote controller for air conditioner. i need to send the temperature value (as an example "20") to the micro controller. After getting the temperature values which should be needed to setup the air conditioner temperature. micro controller unit sends IR bit pattern according to the reading. I didn't post the code which is related to create IR bit pattern for the simplicity of the above program. I hope, if the seven segment display shows the temperature readings correctly, i can send IR signals corresponding to that value.

After changing the code according to your suggestions. it shows an integer value on the 7-segment display. for example, when i send any character, it prints 48 on the display. On my simulator when i send a string it takes only first character of the string. So to send a string how should a change the below code?

Then finally i should send back the environment temperature readings which capture by a sensor back to my computer also.

Then again do i have to use "TBE_isr" transmitter buffer over flow interrupts? Then how should i change my code to implement that feature also?

Thank you very much and i highly appreciate your help...

Kushan Sharma



Code:

#include <16F877A.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES XT                       //Crystal osc <= 4mhz
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES WRT_50%                  //Lower half of Program Memory is Write Protected

#use delay(clock=4000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

  #include <stdio.h>
  #include <stdlib.h>
  #include <stdlibm.h>
  #include <string.h>

char buffer[10];        //Buffer to read data.
byte rcvd_uart = False;

#int_RDA
void  RDA_isr(void)
{
   int numPos = 0;   
   do
   {
      buffer[numPos] = getc();
      numPos++;
   }while(kbhit() && (numPos < 10 ));
   
   rcvd_uart = true;
   clear_interrupt(INT_RDA);
}

byte CONST LED_MAP[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
#inline
void displaySevenSegment(int tempCalc){
   int D0=0, D1=0;
   D0 = tempCalc%10;
   D1 = tempCalc/10;
   
   output_b(0);
   output_d(LED_MAP[D0]);
   output_bit(PIN_B1, 1);   //enable pin B1 for activate the seven
   delay_ms(7);

   output_b(0);
   output_d(LED_MAP[D1]);
   output_bit(PIN_B0,1);
   delay_ms(4 );
}

void main()
{
   int16 pwm_duty = 50;
   int temperature = 0;
   int a;
   
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DIV_BY_1,24,1);
   setup_ccp1(CCP_PWM);
   set_pwm1_duty(pwm_duty);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   // TODO: USER CODE!!
   while(true){
      if(rcvd_uart){
         temperature = (int )buffer;
         rcvd_uart = False;
         for(a=0;a<10;a++){
            printf("%c\n",buffer[a]);
         }
         
      }
      displaySevenSegment(temperature);          //Display the temperature on &-segment
      delay_ms(10);
   }
}

_________________
Kushan Sharma
mlkushan@gmail.com
mlkushan



Joined: 16 Nov 2008
Posts: 22
Location: Sri Lanka

View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger

PostPosted: Wed Nov 26, 2008 5:34 am     Reply with quote

Hi Ttelmah,

Finally I was able to print a number within 0 - 9 without any garbage.
I changed the type casting code segment as below:
Code:

temperature = (int )buffer;


change to

Code:
temperature = atoi(buffer);


Still some of the above problems are still there.
Thank you very much and your help is kindly appreciated.

Kushan Sharma
Ttelmah
Guest







PostPosted: Wed Nov 26, 2008 6:09 am     Reply with quote

The main problem left, is going to be the one of only receiving one character.

Now, you say you are sending a number like '20'. What is _between_one number and the next?.

So if two numbers are sent one after the other, how can you tell that it is '20' '20', not '2' '02', '2'. The character that seperates the numbers is the 'delimiter'. Normally you would send perhaps a line feed, or carriage return, or a space, to provide this distinction.

Then:
Code:

#define MAXBUF (10)
char buffer[MAXBUF+1];   //Buffer to read data.
//I'll explain one _one larger_ latter.

//Using a define, makes it easier to change sizes latter if needed

int1 rcvd_uart = False; //No point in using a 'byte' for a flag
int8 numPos=0; //Store numPos as a _global_

#define DELIM ('\n') //Change this to match _your_ delimiter

#int_RDA
void  RDA_isr(void)
{
   int8 temp;
   temp=getc();
   if (rcvd_uart==false) buffer[numPos] = temp;
   //This disables reception if the 'main' has not processed the last
   //string - should never happen.
   
   if (temp==DELIM) {
       //Here we have received the delimiter
       rcvd_uart = true; //Set the flag to say a complete number is here
       buffer[numPos+1]='\0';
       //A _string_, requires a '\0' terminator. This is why the buffer
       //is one character larger than the maximum number of characters
       //the buffer will accept.
   }
   else {
      numPos++;
      if (numPos==MAXBUF) numPos=MAXBUF-1;
      //Ensure buffer cannot overflow   
   }
   //clear_interrupt(INT_RDA); - not needed the compiler does this for
   //you, unless you add 'noclear' to the declaration.
}

//Then in the main
   //Add at the front of main
   char numval[MAXBUF+1];
   int temperature=0;
//Rest of initialisation here

   while(true){
      //int temperature = 0; Do not declare the variable here
     
      if(rcvd_uart){
         strcpy(numval,bufferl); //Make a _copy_ of the received string.
         rcvd_uart=false; //Re-enable reception ASAP
         temperature = atoi(numval);
     }
     displaySevenSegment(temperature); 
     //Display the temperature on &-segment
     //There are enough delays in the display routine
   }

Hopefully I have not made too many errors, and you can understand the 'difference' in what this is doing.

Best Wishes
mlkushan



Joined: 16 Nov 2008
Posts: 22
Location: Sri Lanka

View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger

PostPosted: Wed Nov 26, 2008 8:01 am     Reply with quote

Hi Ttelmah,

Thanks a lot and thanks for your help. After correcting so many errors, I was finally able to receive a string without having any errors.

But still it does not show a number other than zero on the 7 segment display.
Below I have posted the code.

It might be due to an error of below code segment.
Code:

.........
if(rcvd_uart){
         strcpy(numval,buffer); //Make a _copy_ of the received string.
         rcvd_uart=false; //Re-enable reception ASAP
         temperature = atoi(numval);
      }
      displaySevenSegment(temperature);
........
........


So I can't identify the reason for not to display a value on the seven segment other than zero.

Complete code:
Code:

#include <16F877A.h>
#device adc=8

#FUSES NOWDT                    //No Watch Dog Timer
#FUSES XT                       //Crystal osc <= 4mhz
#FUSES NOPUT                    //No Power Up Timer
#FUSES NOPROTECT                //Code not protected from reading
#FUSES NODEBUG                  //No Debug mode for ICD
#FUSES NOBROWNOUT               //No brownout reset
#FUSES NOLVP                    //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD                    //No EE protection
#FUSES WRT_50%                  //Lower half of Program Memory is Write Protected

#use delay(clock=4000000)
#use rs232(baud=9600,parity=N,xmit=PIN_C6,rcv=PIN_C7,bits=8)

  #include <stdio.h>
  #include <stdlib.h>
  #include <stdlibm.h>
  #include <string.h>

#define MAXBUF (10)
#define DELIM ('\n') //Change this to match _your_ delimiter
char buffer[MAXBUF+1];   //Buffer to read data.
int8 numPos=0; //Store numPos as a _global_
int1 rcvd_uart = False; //No point in using a 'byte' for a flag

#int_RDA
void  RDA_isr(void)
{
   int8 temp;
   temp=getc();
   if (rcvd_uart==false){
      buffer[numPos] = temp;
   }
   if (temp==DELIM){
      rcvd_uart = true; //Set the flag to say a complete number is here
      buffer[numPos+1]='\0';
   }
   else{
      numPos++;
      if (numPos==MAXBUF) numPos=MAXBUF-1;
      //Ensure buffer cannot overflow
   }   
}       

byte CONST LED_MAP[10] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
#inline
void displaySevenSegment(int tempCalc){
   int D0=0, D1=0;
   D0 = tempCalc%10;
   D1 = tempCalc/10;
   
   output_b(0);
   output_d(LED_MAP[D0]);
   output_bit(PIN_B1, 1);   //enable pin B1 for activate the seven
   delay_ms(8);

   output_b(0);
   output_d(LED_MAP[D1]);
   output_bit(PIN_B0,1);
   delay_ms(3);
}

void main()
{
   int16 pwm_duty = 50;
   char numval[MAXBUF+1];
   int temperature=0;
   int a;
   setup_adc_ports(NO_ANALOGS);
   setup_adc(ADC_OFF);
   setup_psp(PSP_DISABLED);
   setup_spi(SPI_SS_DISABLED);
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DIV_BY_1,24,1);
   setup_ccp1(CCP_PWM);
   set_pwm1_duty(pwm_duty);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   // TODO: USER CODE!!
   while(true){
      if(rcvd_uart){
         strcpy(numval,buffer); //Make a _copy_ of the received string.
         rcvd_uart=false; //Re-enable reception ASAP
         temperature = atoi(numval);
      }
      displaySevenSegment(temperature);
      delay_ms(5);     
   }
}


Thank you for your valuable time dedicated on my stuff.
Many thanks in advance.

Kushan Sharma
_________________
Kushan Sharma
mlkushan@gmail.com
Ttelmah
Guest







PostPosted: Wed Nov 26, 2008 9:38 am     Reply with quote

Has your incoming data actually got a '\n' delimiter?.
Won't work unless it has.
Do you really need the long delays in the display routine?. Why not shorten these. Otherwise it means you only get to the test to see whether a 'message' has arrived every 11+mSec. Since it'll only potentially take just over 3mSec to send the digits, _loop faster_. Delays of 100uSec should be more than enough.
Do you have an oscilloscope?.
Do you have any spare pins on the processor?.
Write a small routine to pulse the pin at high speed a number of times defined by a variable.
Start by calling it to pulse once, when the delimiter is detected. Does thgis happen?. If so, you know you are getting this far. Then remove this, and put it to pulse when the 'atoi' is called. Does this happen?.
Etc. etc..

Best Wishes
mlkushan



Joined: 16 Nov 2008
Posts: 22
Location: Sri Lanka

View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger

PostPosted: Mon Dec 01, 2008 11:06 pm     Reply with quote

Hi Ttelmah,

I was bit late to reply you. I still couldn't figure out the reason. I still can't copy "buffer" to "numval". When I simulate the code, after giving an input string, "numval" doesn't show any text.

Code:

............
............
if(rcvd_uart){
         strcpy(numval,buffer); //Make a _copy_ of the received string.
         rcvd_uart=false; //Re-enable reception ASAP
         temperature = atoi(numval);
      }
      printf("%s",buffer);
      printf("S:%s\n", numval);
..................
..................


Can you please correct me on above code segment ?
I kindly appreciate any help.

Thanks in advance.
Kushan
_________________
Kushan Sharma
mlkushan@gmail.com
Wayne_



Joined: 10 Oct 2007
Posts: 681

View user's profile Send private message

PostPosted: Tue Dec 02, 2008 4:52 am     Reply with quote

Does buffer show the correct value ?
Does numval show anything ?

This might actually be related to the problem I have shown below!
The answer is at the bottom Smile

I hate to do this but there are a couple of issues with your code.

The first
Code:

   if (rcvd_uart==false){
      buffer[numPos] = temp;
   }
   if (temp==DELIM){
      rcvd_uart = true; //Set the flag to say a complete number is here
      buffer[numPos+1]='\0';
   }

This will actually store the delim char in the buffer and then terminate it after the char. Wouldn't buffer[numPos]='\0'; be better as this would replace the non number delim char with the null termination char Smile

Also, at some point you would need to reset the index numPos back to the start of the buffer.


When you print buffer it also prints the stored \n char, this may (depending on what you are outputting to may make the second printf overwrite the first.
Do you see "S:value" ? or "value\nS:"
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