SHARE
TWEET

18F C update

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