Advertisement
chasxmd

MODbus port to 18F

Nov 29th, 2014
175
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.24 KB | None | 0 0
  1. /*
  2.  * File:   main.c
  3.  * Author: Charles M Douvier
  4.  * Contact at: http://iradan.com
  5.  *
  6.  * Created on November 8, 2014, 2:37 PM
  7.  *
  8.  * Target Device:
  9.  * 18F27J53 Development Board by AtomSoft
  10.  *
  11.  * Project: MODbus/RTU slave
  12.  *
  13.  *  Notes
  14.  *  We will ignore both query high bytes for now
  15.  *  TODO:   Pull all hard coded IO out eventually and set it up via USB?
  16.  *          Maybe a terminal application for enabling points?
  17.  *
  18.  *
  19.  * Version:
  20.  * 0.02     Controlled sends caned reply on request to address on RS-232
  21.  * 0.03     CRC works on RX
  22.  * 0.04     No actual IO yet..
  23.  *          Function 0x02 (Read Input Status) Started
  24.  * 0.05     Ported to 18F17J53 for expanded IO/USB options.
  25.  *              12MHz XTAL with CPUDIV set to 16MHz
  26.  *      Memory Summary:
  27.  *          Program space        used   5F0h (  1520) of 1FFF8h bytes   (  1.2%)
  28.  *          Data space           used   19Dh (   413) of   EB0h bytes   ( 11.0%)
  29.  *
  30.  */
  31. //#ifndef _XTAL_FREQ
  32. //#define _XTAL_FREQ 16000000 //
  33. //#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
  34. //#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
  35. //#endif
  36.  
  37. // PIC18F27J53 Configuration Bit Settings
  38.  
  39. // 'C' source line config statements
  40.  
  41. #include <xc.h>
  42.  
  43. // #pragma config statements should precede project file includes.
  44. // Use project enums instead of #define for ON and OFF.
  45.  
  46. // CONFIG1L
  47. #pragma config WDTEN = OFF      // Watchdog Timer (Disabled - Controlled by SWDTEN bit)
  48. #pragma config PLLDIV = 3       // PLL Prescaler Selection (Divide by 3 (12 MHz oscillator input))
  49. #pragma config CFGPLLEN = ON    // PLL Enable Configuration Bit (PLL Enabled)
  50. #pragma config STVREN = ON      // Stack Overflow/Underflow Reset (Enabled)
  51. #pragma config XINST = OFF       // Extended Instruction Set (Enabled)
  52.  
  53. // CONFIG1H
  54. #pragma config CPUDIV = OSC3_PLL3// CPU System Clock Postscaler (CPU system clock divide by 3 from 48MHz)
  55. #pragma config CP0 = OFF        // Code Protect (Program memory is not code-protected)
  56.  
  57. // CONFIG2L
  58. #pragma config OSC = HSPLL      // Oscillator (HS+PLL, USB-HS+PLL)
  59. #pragma config SOSCSEL = HIGH   // T1OSC/SOSC Power Selection Bits (High Power T1OSC/SOSC circuit selected)
  60. #pragma config CLKOEC = OFF      // EC Clock Out Enable Bit  (CLKO output enabled on the RA6 pin)
  61. #pragma config FCMEN = OFF      // Fail-Safe Clock Monitor (Disabled)
  62. #pragma config IESO = OFF       // Internal External Oscillator Switch Over Mode (Disabled)
  63.  
  64. #define _XTAL_FREQ 16000000 //defined for delay
  65.  
  66.     char    ctxt[10], buf1, testcrc[4];    //buff serial string
  67.     volatile unsigned int     ping, isrcall, index, reading, new_rx;
  68.     int address, crc_high, crc_low, i, querylength;
  69.     unsigned int result;
  70.  
  71.     //these variables are for fuctions to be enabled/disabled and pushed into application code via later
  72.     int frame, z, bufferbits, query_reg_address;
  73.     char data_out[64];
  74.     char reg_address[255];
  75.  
  76.  
  77. /*
  78.  *
  79.  *  Interrupt Service
  80.  *
  81.  * UART RX and Timer 1
  82.  *
  83.  */
  84. void interrupt ISR() {
  85.  
  86.     if (PIR1bits.RCIF)          // see if interrupt caused by incoming data
  87.     {
  88.         isrcall = 0x01;
  89.         char temp;
  90.         temp = RCREG;     // read the incoming data
  91.         buf1 = temp;
  92.         if(temp==address && reading==0)      //if my address..
  93.         {
  94.             index = 0;                  //reset index
  95.             reading = 1;                //from now on go to else if
  96.             LATCbits.LATC1 = 1;
  97.             new_rx = 0;
  98.             ping = 1;
  99.         }
  100.         else if(reading == 1)           //in middle of GPS sentence
  101.         {
  102.             //TODO reset timeout timer
  103.             ctxt[index] = temp;         //load it up
  104.             index++;                    //increment index
  105.             ping = 1;                   //this is for debugging
  106.             if(index > 6)              //1+7 = master frame.
  107.                 {
  108.                 index = 0;              //reset index
  109.                 reading = 0;            //no longer storing the string
  110.                 new_rx = 1;             //"ding"
  111.                 T1CONbits.TMR1ON = 0;
  112.                 }
  113.         }
  114.     }
  115.  
  116.     //RCSTA2bits.FERR = 0;    //Clear errors
  117.     //RCSTA2bits.OERR = 0;
  118.  
  119.     //time out timer, if tripped new_rx=0;
  120.     if (PIR1bits.TMR1IF)
  121.     {
  122.         new_rx=0;
  123.         ping = 0;
  124.         T1CONbits.TMR1ON = 0;
  125.         PIR1bits.TMR1IF = 0;
  126.         if (reading) {
  127.         reading = 0;
  128.         LATCbits.LATC0 = 1;
  129.         }
  130.     }
  131. }
  132.  
  133. void init_io(void) {
  134.  
  135.     INTCONbits.GIE = 0;         //no interruptions please
  136.  
  137.     ADCON0 = 0b00000000;        //don't need any ADC
  138.     ADCON1 = 0b00000000;        //speed Vref=AVdd, VssRef=AVss
  139.  
  140.     LATA = 0x00;
  141.     LATB = 0x00;
  142.     LATC = 0x00;
  143.  
  144.     TRISAbits.TRISA0 = 1; // address 1
  145.     TRISAbits.TRISA1 = 1; // address 2
  146.     TRISAbits.TRISA2 = 1; // address 3
  147.     TRISAbits.TRISA3 = 0; // output
  148.     TRISAbits.TRISA5 = 0; // output
  149.  
  150.     TRISBbits.TRISB0 = 0; //
  151.     TRISBbits.TRISB1 = 1; //
  152.     TRISBbits.TRISB2 = 0; //
  153.     TRISBbits.TRISB3 = 0; //
  154.     TRISBbits.TRISB4 = 0; //
  155.     TRISBbits.TRISB5 = 1; //
  156.     TRISBbits.TRISB6 = 0; //
  157.     TRISBbits.TRISB7 = 0; //
  158.  
  159.     TRISCbits.TRISC0 = 0; // timer LED output
  160.     TRISCbits.TRISC1 = 0; // output
  161.     TRISCbits.TRISC2 = 0; // LED test output
  162.     TRISCbits.TRISC3 = 0; // output
  163.     //TRISCbits.TRISC4 = 1; // USB
  164.     //TRISCbits.TRISC5 = 1; // USB
  165.     TRISCbits.TRISC6 = 0; // output TX1
  166.     TRISCbits.TRISC7 = 1; // input  RX1
  167. }
  168.  
  169.  void __delay_10ms(unsigned char n)     //__delay functions built-in can't be used for much at this speed... so!
  170.  {
  171.      while (n-- != 0) {
  172.          __delay_ms(10);
  173.      }
  174.  }
  175.  
  176. void uart_xmit(unsigned int mydata_byte) {
  177.  
  178.     while(!TXSTAbits.TRMT);    // make sure buffer full bit is high before transmitting
  179.     TXREG = mydata_byte;       // transmit data
  180. }
  181.  
  182. void write_uart(const char *txt)
  183. {
  184.                                 //this send a string to the TX buffer
  185.                                 //one character at a time
  186.        while(*txt)
  187.        uart_xmit(*txt++);
  188.  
  189. }
  190.  
  191. void serial_init(void)
  192. {
  193.     //9600 8N1
  194.  
  195.     TXSTA1bits.BRGH=1;       // select low speed Baud Rate (see baud rate calcs below)
  196.     TXSTA2bits.TX9=0;        // select 8 data bits
  197.     TXSTA1bits.TXEN = 1;     // enable transmit
  198.     RCSTA1bits.SPEN=1;       // serial port is enabled
  199.     RCSTA1bits.RX9=0;        // select 8 data bits
  200.     RCSTA1bits.CREN=1;       // receive enabled
  201.     SPBRG1=76;
  202.     SPBRGH1=0;
  203.  
  204.  
  205.     PIR1bits.RCIF=0;        // make sure receive interrupt flag is clear
  206.     PIE1bits.RCIE=1;        // enable UART Receive interrupt
  207.     INTCONbits.PEIE = 1;    // Enable peripheral interrupt
  208.     INTCONbits.GIE = 1;     // enable global interrupt
  209.  
  210.      __delay_10ms(5);        // give time for voltage levels on board to settle
  211.  
  212. }
  213.  
  214. void run_timer (void) {
  215.     T1CONbits.TMR1ON = 0;
  216.     TMR1=0;
  217.     TMR1L=0x00;
  218.     TMR1H=0xAA;
  219.     T1CONbits.TMR1CS = 0x01;
  220.     T1CONbits.T1CKPS = 0x01;
  221.     PIE1bits.TMR1IE = 1;
  222.     T1CONbits.TMR1ON = 1;
  223.  
  224.  
  225. //        PIR1bits.TMR1IF=0;
  226. }
  227.  
  228.  
  229.  
  230. void check_my_address(void) {
  231.     /*
  232.      *  Determine what address the unit is based on DIP switches
  233.      *  PORTAbits.RA0   Switch 1
  234.      *  PORTAbits.RA1   Switch 2
  235.     */
  236.  
  237.     if (PORTAbits.RA0 & PORTAbits.RA1) address = 0x04;
  238.     if (!PORTAbits.RA0 & PORTAbits.RA1) address = 0x03;
  239.     if (PORTAbits.RA0 & !PORTAbits.RA1) address = 0x02;
  240.     if (!PORTAbits.RA0 & !PORTAbits.RA1) address = 0x01;
  241.  
  242. }
  243.  
  244. /*
  245.  * TODO
  246.  * Poll the inputs
  247.  *
  248.  * I will probably use a 74138 to address banks with 74HC245 for byte/banks of inputs
  249.  * I will likely use the same set up for "Coils"
  250.  * For input registers (analog inputs) I will consider perhaps some kind of A/D input with CD4066+74HC138
  251.  *
  252.  */
  253.  
  254. void poll_my_inputs(void) {
  255.  
  256.     //just stuff it with something for now
  257.     for (z = 0; z < 64; ++z)
  258.     {
  259.     reg_address[z] = z;
  260.     }
  261.  
  262. }
  263.  
  264.  
  265. /*
  266.  * did the chicken come before the egg?
  267. * this MODbus CRC bit was gently borrowed from the internet..
  268. * I can't determine who wrote it.. it shows up in an Ardiuno Sketch for MODbus
  269. *  Modbus over serial line - RTU Slave Arduino Sketch
  270. * with referenced of Juan Pablo Zometa, Samuel Marco, Andras Tucsni, Philip Costigan
  271. * It also shows up on a number of websites and forums uncredited... many PIC or C based ..
  272. * so who knows, but I didn't write it
  273. * The MODbus CRC-16 function is outlined on the Modicon site for reference
  274. */
  275. unsigned int modbus_crc(unsigned char buf[], int start, int cnt)
  276. {
  277.     int     i,j;
  278.    unsigned temp, temp2, flag;
  279.  
  280.    temp=0xFFFF;
  281.  
  282.    for (i=start; i<cnt; i++){
  283.     temp=temp ^ buf[i];
  284.  
  285.       for (j=1; j<=8; j++){
  286.         flag=temp & 0x0001;
  287.          temp=temp >> 1;
  288.          if (flag) temp=temp ^ 0xA001;
  289.          }
  290.       }
  291.    /*** Reverse byte order. ***/
  292.    temp2=temp >> 8;
  293.    temp= (temp << 8) | temp2;
  294.    return(temp);
  295. }
  296.  
  297. int check_master_crc (void) {
  298.  
  299.     int diff, diff1, diff2;       //what we send back
  300.     char data[6];
  301.     result = 0x0000;
  302.     crc_high = 0x00;
  303.     crc_low = 0x00;
  304.  
  305.     data[0] = address;  //this is ugly but all master queries are 6bytes+CRC so it'll do
  306.     data[1] = ctxt[0];  //function
  307.     data[2] = ctxt[1];  //start_addressH
  308.     data[3] = ctxt[2];  //start_addressL
  309.     data[4] = ctxt[3];  //# of regH
  310.     data[5] = ctxt[4];  //# of regL
  311.  
  312.     result = modbus_crc(data,0,6);
  313.     crc_high = result >> 8;
  314.     crc_low = result & 0x00FF;
  315.  
  316.     diff1 = ctxt[5] ^ crc_high;
  317.     diff2 = ctxt[6] ^ crc_low;
  318.     diff = diff1 + diff2;
  319.     //this spits back an XORed value between calculated and received CRC
  320.     return(diff);
  321. }
  322.  
  323. void respond_input_status(void) {
  324.         //ctxt[1] start_addressH
  325.         //ctxt[2] start_addressL
  326.         //ctxt[3] # of regH
  327.         //ctxt[4] # of regL
  328.     querylength = ctxt[4];  //ignoring upper byte for now (TODO)
  329.     query_reg_address = ctxt[2]; //ignoring upper address byte (TODO)
  330.  
  331.     frame = 0;     //data frame counter
  332.  
  333.     do {
  334.  
  335.         data_out[frame] = reg_address[query_reg_address];
  336.         //TODO  make this do somethng
  337.         querylength--;
  338.         frame++;
  339.     } while (querylength>0);
  340.  
  341.     querylength = ctxt[4];  //reload so we can count time transmitting data
  342.  
  343.     result = modbus_crc(data_out,0,2);
  344.     crc_high = result >> 8;
  345.     crc_low = result & 0x00FF;
  346.  
  347.     //uart_xmit response address, function, data_out[frames], CRC
  348.  
  349.     uart_xmit(address);
  350.     uart_xmit(ctxt[0]);
  351.  
  352.     frame = 0;
  353.     do {
  354.         uart_xmit(data_out[frame]);
  355.         //TODO  make this do somethng
  356.         querylength--;
  357.         frame++;
  358.     } while (querylength>0);
  359.  
  360.     uart_xmit(crc_high);
  361.     uart_xmit(crc_low);
  362. }
  363.  
  364. int main(void) {
  365.     OSCCONbits.SCS = 0x00;
  366.  
  367.     new_rx=0;
  368.  
  369.     init_io();
  370.     serial_init();
  371.     run_timer();
  372.  
  373.     check_my_address();
  374.  
  375.     LATCbits.LATC0 = 1;
  376.     LATCbits.LATC1 = 1;
  377.  
  378.     __delay_10ms(50);
  379.     LATCbits.LATC0 = 0;
  380.     LATCbits.LATC1 = 0;
  381.  
  382.     if (address == 0x01){
  383.         LATCbits.LATC0 = 1;
  384.     }
  385.     if (address == 0x02) {
  386.         LATCbits.LATC1 = 1;
  387.     }
  388.     __delay_10ms(50);
  389.     LATCbits.LATC0 = 0;
  390.     LATCbits.LATC1 = 0;
  391.  
  392.  
  393.     INTCONbits.GIE = 1;
  394.     INTCONbits.PEIE = 1;
  395.  
  396.     while (1) {
  397.  
  398.         //If read data, not me and then if no 232 traffic for >800us then any xmit over
  399.         //104 us (9600) x 8 quiet periods minus some for wiggle.
  400.  
  401.  
  402.  
  403.         if (ping) {
  404.             run_timer();
  405.         }
  406.  
  407.         if (new_rx) {       //this device was polled
  408.             //testing response
  409.             i = check_master_crc();     //check master query crc
  410.             if (i==0x00) {              //CRC Ok?
  411.                 if (ctxt[0] == 2) {       //coil status query
  412.                     respond_input_status();
  413.                 }
  414.                 //TODO other types of functions go here
  415.             }
  416.             //all done, get ready for new query
  417.             new_rx = 0;
  418.         }
  419.  
  420.         //testcrc[0] = 0xFF;
  421.         //testcrc[1] = 0x02;      //right now this is only thing processed
  422.  
  423.  
  424.         //result = modbus_crc(testcrc,0,2);
  425.         //crc_high = result >> 8;
  426.         //crc_low = result & 0x00FF;
  427.         __delay_10ms(10);
  428.         LATCbits.LATC2 = 1; //LED blinky test
  429.         __delay_10ms(10);
  430.  
  431.         LATCbits.LATC0 = 0;
  432.         LATCbits.LATC1 = 0;
  433.         LATCbits.LATC2 = 0;
  434.     }
  435. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement