rs485.c
上传用户:sanfwan_06
上传日期:2007-12-18
资源大小:190k
文件大小:13k
源码类别:

串口编程

开发平台:

C/C++

  1. //**************************************************************************
  2. // Rs495.c
  3. //                      RS485 network protocol
  4. //
  5. // Version 1.0 beta
  6. //
  7. // Processor: PIC16F87x
  8. //
  9. // Network packet protocol:
  10. //
  11. // STX ( 0x02 )
  12. // NET ADDRESS HIGH  
  13. // NET ADDRESS LOW
  14. // PAYLOAD LENGTH
  15. // COMMAND
  16. // DATA ( Optional )
  17. // CRC HIGH
  18. // CRC LOW
  19. //
  20. //
  21. //
  22. //
  23. //
  24. // By: J.Winpenny
  25. // j.winpenny@ukonline.co.uk
  26. //
  27. //**************************************************************************
  28. #include <Rs485.h>
  29. #include <lcd2.h>
  30. // #include <lcd873.h>
  31. //struct
  32. //{
  33. static char cOurAddrHigh;
  34. static char cOurAddrLow;
  35. static char cRs485RxChar;
  36. static char cRS485State;
  37. static char cStart;
  38. static char cNetAddrHigh, cNetAddrLow;
  39. static char cLenExpected;
  40. static char cCommand;
  41. static char c485Buf[64];
  42. static char cRxCrcHigh, cRxCrcLow;
  43. static char cCalcCrcHigh, cCalcCrcLow;
  44. static char cBufPtr;
  45. static char cError;
  46. static char cCrcTmp, cCrcTmp5, cCrcTmp4, cCrcTmp3, cCrcTmp2;
  47. //} RS485_Protocol;
  48. //****************************************************************************
  49. // void Rs485Initialise(void)
  50. //
  51. // Initialise RS485 network driver
  52. //****************************************************************************
  53. void Rs485Initialise(char cAddrHigh, char cAddrLow)
  54. {
  55.   cOurAddrHigh = cAddrHigh;
  56.   cOurAddrLow = cAddrLow;
  57.   cRS485State = PKT_WAIT_START;
  58.   BIT_CLEAR( RS485_CONTROL, OUTPUTS_ON );           // Disable driver
  59.   BIT_SET( PIE1, RCIE );                            // Enable Receive Interrupt
  60. }
  61. //****************************************************************************
  62. // char PacketForUs(void)
  63. //
  64. // Decide if packet valid and destined for this node.
  65. // Ignore invalid packets and packets for other nodes
  66. //
  67. //****************************************************************************
  68. #separate
  69. char Rs485Process(void)
  70. {
  71. char cOurPkt, cPktReady;
  72.     cOurPkt = FALSE;
  73.     cPktReady = FALSE;
  74.     disable_interrupts(GLOBAL);
  75.     if ( cRS485State == PKT_COMPLETE )
  76.     {
  77.         if ( ( cNetAddrHigh == cOurAddrHigh )&&   // Invalid and destined for this node
  78. ( cNetAddrLow == cOurAddrLow ) )
  79. {
  80.    cOurPkt = TRUE;
  81. }
  82. else
  83. {
  84.    ClearLine2();
  85.    LcdWrite("traffic");                  // Network traffic for other nodes
  86.             delay_ms(200);
  87.        }
  88.       cRS485State = PostValidatePacket();         // Validate packet CRC
  89. if ( (cRS485State == PKT_INVALID)||(cRS485State == PKT_VALID) )
  90. {
  91. // Reject invalid packets
  92. if ( cRS485State == PKT_INVALID )          // NAK our invalid packets
  93. {
  94.    ClearLine2();
  95.    if ( cError == BAD_CRC ) LcdWrite("Bad CRC");
  96.             else if ( cError == BAD_LENGTH ) LcdWrite("Bad length");
  97.             delay_ms(200);
  98.    //if ( cOurPkt ) Rs485SendPacket( SENSOR_NAK, 0, NULL );
  99.    cRS485State = PKT_WAIT_START;
  100. }
  101. else if ( cRS485State == PKT_VALID )       // If packet valid
  102. {                                          // and destined for this node
  103.    if ( cOurPkt ) cPktReady = TRUE;
  104.    else  cRS485State = PKT_WAIT_START;
  105. }
  106. }
  107.    }
  108.    enable_interrupts(GLOBAL);
  109.  return cPktReady;
  110. }
  111. //****************************************************************************
  112. // void Rs485Decode(void)
  113. //
  114. // Decode an incomming packet on the RS485 network
  115. //
  116. // Expecting:
  117. // START,
  118. // NETWORK ADDRESS_HIGH,
  119. // NETWORK ADDRESS_LOW,
  120. // PAYLOAD LENGTH,
  121. // COMMAND,
  122. // optional DATA,
  123. // CRC HIGH,
  124. // CRC LOW
  125. //
  126. //****************************************************************************
  127. #separate
  128. char Rs485Decode( void )
  129. {
  130.    switch ( cRS485State )
  131.    {
  132.         case    PKT_WAIT_START:  cStart = cRs485RxChar;
  133.                                  if ( cRs485RxChar == PKT_START ) // Check for the start of packet byte
  134.                                  {
  135.                                     cRS485State++;
  136.                                  }
  137.                                  break;
  138.         case PKT_WAIT_ADDR_HIGH: cNetAddrHigh = cRs485RxChar;
  139.                                  cRS485State++;
  140.                                  break;
  141.         case  PKT_WAIT_ADDR_LOW: cNetAddrLow = cRs485RxChar;
  142.                                  cRS485State++;
  143.                                  break;
  144.         case       PKT_WAIT_LEN: cLenExpected = cRs485RxChar;
  145.                                  if ( cLenExpected > sizeof(c485Buf) )
  146.                                  {
  147.                                     cRS485State = PKT_INVALID;
  148.                                     cError = BAD_LENGTH;
  149.                                  }
  150.                                  else
  151.                                  {
  152.                                    cBufPtr = 0;
  153.                                    cRS485State++;
  154.                                  }
  155.                                  break;
  156.         case           PKT_CMD:  cCommand = cRs485RxChar;
  157.                                  if ( PacketHasPayload() ) cRS485State = PKT_WAIT_DATA;
  158.                                  else cRS485State = PKT_WAIT_CRC_HIGH;
  159.                                  break;
  160.         case     PKT_WAIT_DATA:  c485Buf[cBufPtr] = cRs485RxChar;
  161.                                  cBufPtr++;
  162.                                  if ( cBufPtr == cLenExpected ) // If last byte of data received
  163.                                  {
  164.                                    cRS485State++;               // next byet is the CRC high byte
  165.                                  }
  166.                                  break;
  167.         case PKT_WAIT_CRC_HIGH:  cRxCrcHigh = cRs485RxChar;
  168.                                  cRS485State++;
  169.                                  break;
  170.         case  PKT_WAIT_CRC_LOW:  cRxCrcLow = cRs485RxChar;
  171.                                  cRS485State = PKT_COMPLETE;
  172.                                  break;
  173.         case      PKT_COMPLETE:  break;       // Idle state
  174.         case         PKT_VALID:  break;       // Idle state
  175.         case       PKT_INVALID:  break;       // Idle state
  176.         default:                 cRS485State = PKT_WAIT_START;
  177.                                  break;
  178.      }
  179.  return cRS485State;
  180. }
  181. //****************************************************************************
  182. // void Rs485SendPacket( char cAddr, char cCmd, char cLen, char *cData )
  183. //
  184. // Send a packet over the RS485 link
  185. //
  186. // Input: NETWORK_ADDRESS, COMMAND, PAYLOAD_LENGTH, optional DATA
  187. //
  188. //****************************************************************************
  189. void Rs485SendPacket( char cCmd, char cLen, char *cData )
  190. {
  191. char c, d;
  192.    BIT_CLEAR( PIE1, RCIE );                          // Disable Receive Interrupt
  193.    BIT_SET( RS485_CONTROL, OUTPUTS_ON );             // Enable driver
  194.    delay_ms(1);                                      // Line turnarround time
  195.    cCalcCrcHigh = 0xff;                              // Clear CRC
  196.    cCalcCrcLow = 0xff;
  197.                                                      // Send some NULL preamblesfopr receiving UART
  198.    for ( c=0; c < NUM_TX_PREAMBLE; c++ ) Rs485SendChar( 0x00 );
  199.    Rs485UpdateCrc( PKT_START );
  200.    Rs485SendChar( PKT_START );       // Send packet start character
  201.    Rs485UpdateCrc( cOurAddrHigh );
  202.    Rs485SendChar( cOurAddrHigh );    // Send address high
  203.    Rs485UpdateCrc( cOurAddrLow );
  204.    Rs485SendChar( cOurAddrLow );     // Send address low
  205.    Rs485UpdateCrc( cLen );
  206.    Rs485SendChar( cLen );            // Send length
  207.    Rs485UpdateCrc( cCmd );
  208.    Rs485SendChar( cCmd );            // Send command
  209.    if ( cLen != 0 )                  // If payload not empty send data
  210.    {
  211.      for ( c = 0; c < cLen; c++ )
  212.      {
  213.         d = cData[c];
  214.         Rs485UpdateCrc( d );
  215.      }
  216.      for ( c = 0; c < cLen; c++ )
  217.      {
  218.         d = cData[c];
  219.         Rs485SendChar( d );          // Send data
  220.      }
  221.    }
  222.    Rs485SendChar(cCalcCrcHigh);
  223.    Rs485SendChar(cCalcCrcLow);
  224.    delay_ms(1);
  225.    BIT_CLEAR( RS485_CONTROL, OUTPUTS_ON );           // Disable driver
  226.    BIT_SET( PIE1, RCIE );                            // Enable Receive Interrupt
  227. }
  228. //****************************************************************************
  229. // void Rs485GetPacket( char *cCommand, char cLen, char *cData )
  230. //
  231. // Pass packet to main application
  232. //
  233. //****************************************************************************
  234. void Rs485GetPacket( char *cCom, char *cLen, char *cData )
  235. {
  236. char c;
  237.   *cCom = cCommand;
  238.   *cLen = cLenExpected;
  239.   for ( c=0; c < cLenExpected;c++ )  cData[c] = c485Buf[c];
  240.   cData[cLenExpected] = 0x00; // Termninate
  241. }
  242. /*************************************************************************
  243.  * Example Table Driven CRC16 Routine using 4-bit message chunks
  244.  *
  245.  * By Ashley Roll
  246.  * Digital Nemesis Pty Ltd
  247.  * www.digitalnemesis.com
  248.  * ash@digitalnemesis.com
  249.  *
  250.  * The following is an example of implementing a restricted size CRC16
  251.  * table lookup. No optimisation as been done so the code is clear and
  252.  * easy to understand.
  253.  *
  254.  * Test Vector: "123456789" (character string, no quotes)
  255.  * Generated CRC: 0x29B1
  256.  *
  257.  * Modified for CCS compiler by J.Winpenny
  258.  *************************************************************************/
  259. /*
  260.  * CRC16 Lookup tables (High and Low Byte) for 4 bits per iteration.
  261.  */
  262. const char CRC16_LookupHigh[16] = {
  263.         0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70,
  264.         0x81, 0x91, 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1
  265. };
  266. const char CRC16_LookupLow[16] = {
  267.         0x00, 0x21, 0x42, 0x63, 0x84, 0xA5, 0xC6, 0xE7,
  268.         0x08, 0x29, 0x4A, 0x6B, 0x8C, 0xAD, 0xCE, 0xEF
  269. };
  270. /*
  271.  * Before each message CRC is generated, the CRC register must be
  272.  * initialised by calling this function
  273.  */
  274. void CRC16_Init( void )
  275. {
  276. // Initialise the CRC to 0xFFFF for the CCITT specification
  277. cCalcCrcHigh = 0xFF;
  278. cCalcCrcLow = 0xFF;
  279. }
  280. /*
  281.  * Process 4 bits of the message to update the CRC Value.
  282.  *
  283.  * Note that the data must be in the low nibble of val.
  284.  */
  285. void CRC16_Update4Bits( char val )
  286. {
  287. char t;
  288. // Step one, extract the Most significant 4 bits of the CRC register
  289. t = cCalcCrcHigh >> 4;
  290. // XOR in the Message Data into the extracted bits
  291. t = t ^ val;
  292. // Shift the CRC Register left 4 bits
  293. cCalcCrcHigh = (cCalcCrcHigh << 4) | (cCalcCrcLow >> 4);
  294. cCalcCrcLow = cCalcCrcLow << 4;
  295. // Do the table lookups and XOR the result into the CRC Tables
  296. cCalcCrcHigh = cCalcCrcHigh ^ CRC16_LookupHigh[t];
  297. cCalcCrcLow = cCalcCrcLow ^ CRC16_LookupLow[t];
  298. }
  299. /*
  300.  * Process one Message Byte to update the current CRC Value
  301.  */
  302. void Rs485UpdateCrc( char cVal )
  303. {
  304. CRC16_Update4Bits( cVal >> 4 ); // High nibble first
  305. CRC16_Update4Bits( cVal & 0x0F ); // Low nibble
  306. }
  307. //****************************************************************************
  308. // void Rs485SendChar( char c )
  309. //
  310. // Driver level of RS485 protocol
  311. // Output character on RS485 driver
  312. // // Include line turn around time
  313. //****************************************************************************
  314. void Rs485SendChar( char c )
  315. {
  316.    TXREG = c;                            // Load data to send
  317.    while ( !( TXSTA & TRMT_MASK ));       // Wait for TX Empty
  318. }
  319. //****************************************************************************
  320. // char PostValidatePacket(void)
  321. //
  322. // Verify the CRC on the last packet received
  323. //
  324. // Check if the CRC is correct
  325. // and return the updated state as the result
  326. //
  327. //****************************************************************************
  328. char PostValidatePacket(void)
  329. {
  330. char c, d;
  331.   CRC16_Init();
  332.   Rs485UpdateCrc(PKT_START);
  333.   Rs485UpdateCrc(cNetAddrHigh);
  334.   Rs485UpdateCrc(cNetAddrLow);
  335.   Rs485UpdateCrc(cLenExpected);
  336.   Rs485UpdateCrc(cCommand);
  337.   if ( PacketHasPayload() )  // If the packet has a payload,
  338.   {                          // then include the data in the CRC.
  339.      for ( c = 0; c < cLenExpected; c++ )
  340.      {
  341.         d = c485Buf[c];
  342.         Rs485UpdateCrc( d );
  343.      }
  344.   }
  345.                              // Check if the CRC is correct
  346.                              // and return the updated state as the result
  347.   if ( (cRxCrcHigh == cCalcCrcHigh)&&(cRxCrcLow == cCalcCrcLow) )
  348.   {
  349.      cRS485State = PKT_VALID;
  350.   }
  351.   else
  352.   {
  353.      cError = BAD_CRC;
  354.      cRS485State = PKT_INVALID;
  355.      ClearLine2();
  356.      BinToHexAscii(cRxCrcHigh );
  357.      BinToHexAscii(cRxCrcLow );
  358.      LcdWrite(' ');
  359.      BinToHexAscii(cCalcCrcHigh);
  360.      BinToHexAscii(cCalcCrcHigh);
  361.      delay_ms(255);
  362.      delay_ms(255);
  363.      delay_ms(255);
  364.      delay_ms(255);
  365.   }
  366.   return cRS485State;
  367. }
  368. //****************************************************************************
  369. // char GetPacketCmdType(void)
  370. //
  371. // Check packet command type
  372. // Return TRUE if packet has a data payload.
  373. //
  374. //****************************************************************************
  375. char PacketHasPayload(void)
  376. {
  377.   if ( cCommand == SENSOR_GET_DATA ) return TRUE;
  378.   else return FALSE;
  379. }
  380. //****************************************************************************
  381. // void BinToHexAscii( char c )
  382. //
  383. // Contributed by: Nick De Smith
  384. //
  385. //****************************************************************************
  386. void BinToHexAscii( char c )
  387. {
  388.  const char hexMap[17] = "0123456789ABCDEF";
  389.     LcdWrite( hexMap[(c >> 4) & 0xF] );
  390.     LcdWrite( hexMap[(c & 0xF)] );
  391. }