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 support@ccsinfo.com

Rs485 problem

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



Joined: 07 Sep 2007
Posts: 41

View user's profile Send private message

Rs485 problem
PostPosted: Tue Nov 27, 2007 10:57 am     Reply with quote

Hi there! I´m using the Rs485 prot to connect 20 pics to a PC. By now i´m only using one to see if i can get results. The problem is that i get the first printf and then when i try to send a char i guess that my pic doesn´t get it. There may be two problems. the first is that the R/T pin may not be down when i try to receive, the other is that can be a problem with my schematic. Cool . Can anyone help me?
here´s the code
Code:


#ifndef RS485_DRIVER
#define RS485_DRIVER

#ifndef RS485_ID
#define RS485_ID  0x10                 // The device's RS485 address or ID
#endif

#ifndef RS485_USE_EXT_INT
#define RS485_USE_EXT_INT FALSE        // Select between external interrupt
#endif                                 // or asynchronous serial interrupt


#if(RS485_USE_EXT_INT == FALSE)
   #ifndef RS485_RX_PIN
   #define RS485_RX_PIN       PIN_C7   // Data receive pin
   #endif

   #ifndef RS485_TX_PIN
   #define RS485_TX_PIN       PIN_C6   // Data transmit pin
   #endif

   #ifndef RS485_ENABLE_PIN
   #define RS485_ENABLE_PIN   PIN_B3   // Controls DE pin.  RX low, TX high.
   #endif

   #ifndef RS485_RX_ENABLE
   #define RS485_RX_ENABLE    PIN_B3   // Controls RE pin.  Should keep low.
   #endif

   #use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, errors, stream=RS485)
   #use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, force_sw, multi_master, errors, stream=RS485_CD)

   #if getenv("AUART")
      #define RCV_OFF() {setup_uart(FALSE);}
   #else
      #define RCV_OFF() {setup_uart(FALSE);}
   #endif
#else
   #ifndef RS485_RX_PIN
   #define RS485_RX_PIN       PIN_B0   // Data receive pin
   #endif

   #ifndef RS485_TX_PIN
   #define RS485_TX_PIN       PIN_B3   // Data transmit pin
   #endif

   #ifndef RS485_ENABLE_PIN
   #define RS485_ENABLE_PIN   PIN_B4   // Controls DE pin.  RX low, TX high.
   #endif

   #ifndef RS485_RX_ENABLE
   #define RS485_RX_ENABLE    PIN_B5   // Controls RE pin.  Should keep low.
   #endif

   #use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, errors, stream=RS485)
   #use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN, enable=RS485_ENABLE_PIN, bits=9, long_data, multi_master, errors, stream=RS485_CD)

   #define RCV_OFF() {disable_interrupts(INT_EXT);}
#endif



#define RS485_wait_time 20             // Wait time in milliseconds

#bit    rs485_collision = rs232_errors.6

#ifndef RS485_RX_BUFFER_SIZE
#define RS485_RX_BUFFER_SIZE  40
#endif

int rs485_state, rs485_ni, rs485_no;
int rs485_buffer[RS485_RX_BUFFER_SIZE];


// Purpose:    Enable data reception
// Inputs:     None
// Outputs:    None
void RCV_ON(void) {
   #if (RS485_USE_EXT_INT==FALSE)
      while(kbhit(RS485)) {getc();} // Clear RX buffer. Clear RDA interrupt flag. Clear overrun error flag.
      #if getenv("AUART")
         setup_uart(UART_ADDRESS);
         setup_uart(TRUE);
      #else
         setup_uart(TRUE);
      #endif
   #else
      clear_interrupt(INT_EXT);
      enable_interrupts(INT_EXT);
   #endif
}


// Purpose:    Initialize RS485 communication. Call this before
//             using any other RS485 functions.
// Inputs:     None
// Outputs:    None
void rs485_init() {
   RCV_ON();
   rs485_state=0;
   rs485_ni=0;
   rs485_no=0;
   #if RS485_USE_EXT_INT==FALSE
   enable_interrupts(INT_RDA);
   #else
   ext_int_edge(H_TO_L);
   enable_interrupts(INT_EXT);
   #endif
   enable_interrupts(GLOBAL);
   output_low(RS485_RX_ENABLE);
}


// The index for the temporary receive buffer
int8 temp_ni;

// Purpose:    Add a byte of data to the temporary receive buffer
// Inputs:     The byte of data
// Outputs:    None
void rs485_add_to_temp(int8 b) {
   // Store the byte
   rs485_buffer[temp_ni] = b;

   // Make the index cyclic
   if(++temp_ni >= RS485_RX_BUFFER_SIZE)
   {
      temp_ni = 0;
   }
}


// Purpose:    Interrupt service routine for handling incoming RS485 data
#if (RS485_USE_EXT_INT==FALSE)
#int_rda
#else
#int_ext
#endif
void incomming_rs485() {
   int16 b;
   static int8  cs,state=0,len;
   static int16 to,source;

   b=fgetc(RS485);
   cs^=(int8)b;

   switch(state) {
      case 0:  // Get from address
         temp_ni=rs485_ni;
         source=b;
         cs=b;
         rs485_add_to_temp(source);
         break;

      case 1:  // Get to address
         to=b;
         #if (getenv("AUART")&&(RS485_USE_EXT_INT==FALSE))
            setup_uart(UART_DATA);
         #endif
         break;

      case 2:  // Get len
         len=b;
         rs485_add_to_temp(len);
         break;

      case 255:   // Get checksum
         if ((!cs)&&(bit_test(to,8))&&(bit_test(source,8))&&((int8)to==RS485_ID)) {  // If cs==0, then checksum is good
            rs485_ni=temp_ni;
         }

         #if (getenv("AUART")&&(RS485_USE_EXT_INT==FALSE))
            setup_uart(UART_ADDRESS);
         #endif

         state=0;
         return;

      default: // Get data
         rs485_add_to_temp(b);
         --len;
         break;
   }
   if ((state>=3) && (!len)) {
      state=255;
   }
   else {
      ++state;
   }
}


// Purpose:    Send a message over the RS485 bus
// Inputs:     1) The destination address
//             2) The number of bytes of data to send
//             3) A pointer to the data to send
// Outputs:    TRUE if successful
//             FALSE if failed
// Note:       Format:  source | destination | data-length | data | checksum
int1 rs485_send_message(int8 to, int8 len, int8* data) {
   int8 try, i, cs;
   int1 ret = FALSE;

   RCV_OFF();
   #if RS485_USE_EXT_INT
      disable_interrupts(GLOBAL);
   #endif

   for(try=0; try<5; ++try) {
      rs485_collision = 0;
      fputc((int16)0x100|rs485_id, RS485_CD);
      fputc((int16)0x100|to, RS485_CD);
      fputc(len, RS485_CD);

      for(i=0, cs=rs485_id^to^len; i<len; ++i) {
         cs ^= *data;
         fputc(*data, RS485_CD);
         ++data;
      }

      fputc(cs, RS485_CD);
      if(!rs485_collision) {
         ret = TRUE;
         break;
      }
      delay_ms(RS485_ID);
   }

   RCV_ON();
   #if RS485_USE_EXT_INT
      enable_interrupts(GLOBAL);
   #endif

   return(ret);
}


// Purpose:    Wait for wait time for the RS485 bus to become idle
// Inputs:     TRUE - restart the watch dog timer to prevent reset
//             FALSE - watch dog timer not restarted
// Outputs:    None
void rs485_wait_for_bus(int1 clrwdt)
{
   int16 i;

   RCV_OFF();
   for(i=0; i <= (rs485_wait_time*20); ++i)
   {
      if(!input(RS485_RX_PIN))
         i = 0;
      else
         delay_us(50);

      if(clrwdt)
         restart_wdt();
   }
}


// Purpose:    Get a message from the RS485 bus and store it in a buffer
// Inputs:     1) A pointer to a buffer to store a message
//             2) TRUE  - wait for a message
//                FALSE - only check if a message is available
// Outputs:    TRUE if a message was received
//             FALSE if wait is FALSE and no message is available
// Note:       Data will be filled in at the pointer as follows:
//             FROM_ID  DATALENGTH  DATA...
int1 rs485_get_message(int* data_ptr, int1 wait)
{
   while(wait && (rs485_ni == rs485_no)) {}

   if(rs485_ni == rs485_no)
      return FALSE;
   else {
      int n;
      n = rs485_buffer[(rs485_no+1)%sizeof(rs485_buffer)] + 2;

      for(; n>0; --n)
      {
         *data_ptr = rs485_buffer[rs485_no];
         if(++rs485_no >= sizeof(rs485_buffer))
         {
            rs485_no = 0;
         }
         ++data_ptr;
      }
      return TRUE;
   }
}

#endif

And the rest!!!
Code:


#include <16F876A.h>
#device *=16
#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, stream=PC)

#define  RS485_RX_BUFFER_SIZE 64
#define  RS485_USE_EXT_INT    TRUE

int8 OUR_RS485_ID = 0;
#define RS485_ID OUR_RS485_ID

#include <rs485.c>
#include <stdlib.h>

int8 in_char = 0;
int8 next_in = 0;
int8 next_out = 0;
int8 msg[64];

#INT_RDA
void serial_isr()
{
   printf("Interrupt");
   in_char = getc(PC);
}

#INT_TIMER1
void timer1_isr()
{
   int8 i;

   if(rs485_get_message(msg, FALSE))
   {
      printf("\n\r%d: ", msg[0]);

      for(i=0; i < msg[1]; ++i)
         putc(msg[i+2]);

      printf("\n\r");
   }
}

void RS485send(char* s, int8 id)
{
   int8 size;

   for(size=0; s[size]!='\0'; ++size);

   rs485_wait_for_bus(FALSE);

   while(!rs485_send_message(id, size, s))
      delay_ms(OUR_RS485_ID);
}

char PCgetc()
{
   in_char = 0;

   while(!in_char);

   return in_char;
}

int8 PCgetInt()
{
   int8 i, s[3];

   for(i=0; (s[i]=PCgetc()) != '\r' && i<3; ++i);

   return atoi(s);
}

char* PCgetMsg()
{
   int8 i;

   for(i=0; (msg[i] = PCgetc()) != '\r' && i<64; ++i);

   msg[i] = '\0';

   return &(msg[0]);
}

void main()
{
   int8 i, send_addr = 0;
   char command;

   setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);

   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

   rs485_init();

   while(OUR_RS485_ID == 0 || OUR_RS485_ID >= 255)
   {
      printf("Please choose a network ID (1-255): ");
      OUR_RS485_ID = PCgetInt();
   }

   printf("\n\rYour ID is: %d\n\r\n\rIf you require assistance, press h.\n\r", OUR_RS485_ID);

   while(command != 'Q')
   {
         enable_interrupts(INT_TIMER1);

         command = toupper(PCgetc());

         disable_interrupts(INT_TIMER1);

         switch(command)
         {
            case 'S':
               if(send_addr == 0 || send_addr > 255)
               {
                  printf("\n\r\n\rEnter send address (1-255): ");
                  send_addr = PCgetInt();
               }

               printf("\n\r%d:>", send_addr);
               RS485send(PCgetMsg(), send_addr);
               printf("\n\r");
               break;

            case 'C':
               send_addr = 0;

               while(send_addr == 0 || send_addr > 255)
               {
                  printf("\n\r\n\rEnter send address (1-255): ");
                  send_addr = PCgetInt();
                  printf("\n\r");
               }
               break;

            case 'I':
               OUR_RS485_ID = 0;

               while(OUR_RS485_ID == 0 || OUR_RS485_ID >= 255)
               {
                  printf("\n\rPlease choose a network ID (1-255): ");
                  OUR_RS485_ID = PCgetInt();
               }
               printf("\n\rYour ID is: %d\n\r\n\r", OUR_RS485_ID);
               break;

            case 'H':
               printf("\n\rCommands: (S)end message, (C)hange Send Address, ");
               printf("Change (I)D, (H)elp, (Q)uit.\n\r");
               break;

            default:
               if(command != 'Q')
                  printf("\n\rInvalid command!\n\r");
         }
   }
}


And the schematic...
http://www.mikroe.com/en/books/picbasicbook/09/rs485.gif

By the way i´m using the Bray terminal is very useful...

Thanks in advance...
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Nov 27, 2007 11:32 am     Reply with quote

Quote:
void main()
{
int8 i, send_addr = 0;


while(send_addr == 0 || send_addr > 255)

I didn't look closely at your code, but I did notice that you're testing
to see if an unsigned 8-bit integer will be greater than 255. It can't
be. It's range is only from 0 to 255.
Audi80



Joined: 07 Sep 2007
Posts: 41

View user's profile Send private message

PostPosted: Tue Nov 27, 2007 11:51 am     Reply with quote

You´re right but it doesn´t even get there. that´s to test if an user set the RS485 ID wrong. I´ve got that example in mplab and the driver too so if i only change the control pins it should work right? the problem is that with the hyperterminal i get the follow message:

"Please choose a network ID (1-255): "

And then when i press 2 it seems that the character doesnt get to the rx in the pic.

If you have any idea would be appreciated...
Very Happy

Thanks...
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Nov 27, 2007 12:12 pm     Reply with quote

Another problem is that your schematic shows the Enable pin for the
driver chip is connected to pin E0. But your source code uses pin B3.
The source code needs to match the schematic.
Audi80



Joined: 07 Sep 2007
Posts: 41

View user's profile Send private message

PostPosted: Wed Nov 28, 2007 3:53 am     Reply with quote

Once again your right but i have changed the control pin to PIN_B3 and i´m not using the 16F877 i´m using the 16F876A i think that the example that was in mplab is not controling the RD/WR pin. Do you have any working example?
Thanks
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