C Electronics Microcontrollers PIC RS-232 RS-485

PIC Micro MODbus : [2] Calculating CRC

Work continued this morning on my MODbus project. It was interrupted by sunshine which prompted yard work… While having a house/yard is worth the work and loss of “fun time”, there are certainly aspects of renting an apartment I miss :D

The code I’m sharing this round could be a nice starting point for anything following along. I haven’t thoughly debugged it yet, but it is a good start. The modpoll program I am using sends a 0x01 0x03 0x00 0x00 0x00 0x00 0x0A CRC CRC.

A review of the modbus protocol reference guide shows us this is a query from the master to slave address 0x01 (Address 1), 0x03 Read Holding Register request starting at register 0x00 0x00 (4 0001, usually seen as 40001) and the request includes  10 registers, 0x00 0x0A, which would be 4 00001 to 4 00011. The code I’m sharing will  have the micro properly respond to the Modpoll software with a predefined response. (0x01, 0x03, <number of bytes>, 20 bytes of data, CRC, CRC). Before sending the canned response the software determines if the poll is for the address of the micro (0x01), it will also check to ensure the CRC is correct. If the CRC is not correct, or the address is wrong the micro will ignore the poll. I’m running straight serial at 9,600 baud but once I port the micro I will bump it up to 19200, 8N1 and throw a hardware RS485 interface on it.

The micro also determines its address now from two inputs during start-up. It can be address 0x01, 0x02, 0x03, and 0x04 depending on the switch settings. I will expand this to address up to d32 in the future. RS-485 specification only allows for 32 devices on the bus so there is no reason for me to address any device over 32.

Of note: The code will drop the master packet if it times out on the interrupt timer (Timer1) but my implementation is far more fault-friendly than specifications allow for. I am currently experiencing the occasional timeout at the master which I have not looked into yet.

Please note this code is “under construction” and is unoptimized. I’ve done many thing the long way because it was easy to debug spots and dump values to the uart for troubleshooting. This is what happens when you give someone a micro with way more flash than they need! :)


/* 
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com
 *
 * Created on November 8, 2014, 2:37 PM
 *
 * Target Device:
 * 16F1509 on Tautic 20 pin dev board
 *
 * Project:
 *
 *
 * Version:
 * 1.0
 *
 */
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 8000000 //8Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
#endif

#include 

//config bits
#pragma config FOSC=INTOSC, WDTE=OFF, PWRTE=OFF, MCLRE=ON, CP=OFF, BOREN=ON, CLKOUTEN=OFF, IESO=OFF, FCMEN=OFF
#pragma config WRT=OFF, STVREN=OFF, LVP=OFF

#define _XTAL_FREQ 8000000 //defined for delay

    char    ctxt[10], buf1, testcrc[4];    //buff serial string
    volatile unsigned int     ping, isrcall, index, reading, new_rx;
    int address, crc_high, crc_low, i;
    unsigned int result;
    int AV1, AV2, AV3, AV4, BV1, BI1;               //fake inputs for testing

/*
 *  Interrupt Service
 */
void interrupt ISR() {
/*    *    if (PIR1bits.RCIF)          // see if interrupt caused by incoming data
    *{
    *    parity_rx = RCSTA1bits.RX9D;
    *    uart_data = RCREG;     // read the incoming data
    *    PIR1bits.RCIF = 0;      // clear interrupt flag
    *}
        */
    if (PIR1bits.RCIF)          // see if interrupt caused by incoming data
    {
        isrcall = 0x01;
        char temp;
        temp = RCREG;     // read the incoming data
        buf1 = temp;
        if(temp==address && reading==0)      //if my address..
        {
            index = 0;                  //reset index
            reading = 1;                //from now on go to else if
            LATCbits.LATC1 = 1;
            new_rx = 0;
            ping = 1;
        }
        else if(reading == 1)           //in middle of GPS sentence
        {
            //TODO reset timeout timer
            ctxt[index] = temp;         //load it up
            index++;                    //increment index
            ping = 1;                   //this is for debugging
            if(index > 6)              //1+7 = master frame.
                {
                index = 0;              //reset index
                reading = 0;            //no longer storing the string
                new_rx = 1;             //"ding"
                T1CONbits.TMR1ON = 0;
                }
        }

        //PIR3bits.RC2IF = 0;      // clear interrupt flag
    }
    //RCSTA2bits.FERR = 0;    //Clear errors
    //RCSTA2bits.OERR = 0;

    //time out timer, if tripped new_rx=0;
    if (PIR1bits.TMR1IF)
    {
        new_rx=0;
        ping = 0;
        T1CONbits.TMR1ON = 0;
        PIR1bits.TMR1IF = 0;
        if (reading) {
        reading = 0;
        LATCbits.LATC0 = 1;
        }
    }
}

void init_io(void) {

    ANSELA = 0x00; // all port A pins are digital I/O
    ANSELB = 0x00; // all port B pins are digital I/O
    ANSELC = 0x00; // all port B pins are digital I/O

    LATC = 0x00;

    TRISAbits.TRISA0 = 0; // output
    TRISAbits.TRISA1 = 0; // output
    TRISAbits.TRISA2 = 0; // output
    TRISAbits.TRISA3 = 0; // output
    TRISAbits.TRISA4 = 0; // output
    TRISAbits.TRISA5 = 0; // output

    TRISBbits.TRISB4 = 0; // RB4 = nc
    TRISBbits.TRISB5 = 1; // RB5 = nc
    TRISBbits.TRISB6 = 0; // RB6 = nc
    TRISBbits.TRISB7 = 0; // RB7 = nc
    
    TRISCbits.TRISC0 = 0; // output
    TRISCbits.TRISC1 = 0; // output
    TRISCbits.TRISC2 = 0; // output
    TRISCbits.TRISC3 = 0; // output
    TRISCbits.TRISC4 = 1; // address input
    TRISCbits.TRISC5 = 1; // address input
    TRISCbits.TRISC6 = 1; // input
    TRISCbits.TRISC7 = 1; // input    
}

void uart_xmit(unsigned int mydata_byte) {

    while(!TXSTAbits.TRMT);    // make sure buffer full bit is high before transmitting
    TXREG = mydata_byte;       // transmit data
}

void write_uart(const char *txt)
{
                                //this send a string to the TX buffer
                                //one character at a time
       while(*txt)
       uart_xmit(*txt++);

}

void serial_init(void)
{
    //9600 8N1
    // calculate values of SPBRGL and SPBRGH based on the desired baud rate
    //
    // For 8 bit Async mode with BRGH=0: Desired Baud rate = Fosc/64([SPBRGH:SPBRGL]+1)
    // For 8 bit Async mode with BRGH=1: Desired Baud rate = Fosc/16([SPBRGH:SPBRGL]+1)

    TXSTAbits.BRGH=1;       // select low speed Baud Rate (see baud rate calcs below)
    TXSTAbits.TX9=0;        // select 8 data bits
    TXSTAbits.TXEN = 1;     // enable transmit


    RCSTAbits.SPEN=1;       // serial port is enabled
    RCSTAbits.RX9=0;        // select 8 data bits
    RCSTAbits.CREN=1;       // receive enabled

    SPBRGL=51;  // here is calculated value of SPBRGH and SPBRGL
    SPBRGH=0;

    PIR1bits.RCIF=0;        // make sure receive interrupt flag is clear
    PIE1bits.RCIE=1;        // enable UART Receive interrupt
    INTCONbits.PEIE = 1;    // Enable peripheral interrupt
    INTCONbits.GIE = 1;     // enable global interrupt

         __delay_ms(50);        // give time for voltage levels on board to settle

}

void run_timer (void) {
        T1CONbits.TMR1ON = 0;
        TMR1=0;
        TMR1L=0x00;
        TMR1H=0xAA;
        T1CONbits.TMR1CS = 0x01;
        T1CONbits.T1CKPS = 0x01;
        PIE1bits.TMR1IE = 1;
        T1CONbits.TMR1ON = 1;


//        PIR1bits.TMR1IF=0;
}



void check_my_address(void) {
    /*
     *  Determine what address the unit is based on DIP switches
     *  PORTCbits.RC4   Switch 1
     *  PORTCbits.RC5   Switch 2
    */

    if (PORTCbits.RC4 & PORTCbits.RC5) address = 0x04;
    if (!PORTCbits.RC4 & PORTCbits.RC5) address = 0x03;
    if (PORTCbits.RC4 & !PORTCbits.RC5) address = 0x02;
    if (!PORTCbits.RC4 & !PORTCbits.RC5) address = 0x01;

}

void send_mpoll (void) {
               //testing response
            uart_xmit (address);    //address
            uart_xmit (ctxt[0]);    //function
            uart_xmit (0x14);       //20 bytes back
            uart_xmit (0x01);
            uart_xmit (0x02);
            uart_xmit (0x03);
            uart_xmit (0x04);
            uart_xmit (0x05);
            uart_xmit (0x04);
            uart_xmit (0x03);
            uart_xmit (0x02);
            uart_xmit (0x01);
            uart_xmit (0x00);
            uart_xmit (0x01);
            uart_xmit (0x02);
            uart_xmit (0x03);
            uart_xmit (0x04);
            uart_xmit (0x05);
            uart_xmit (0x04);
            uart_xmit (0x03);
            uart_xmit (0x02);
            uart_xmit (0x01);
            uart_xmit (0x00);
            uart_xmit (0x77);       //2 byte CRC
            uart_xmit (0xA8);
}

//did the chicken come before the egg?
//this MODbus CRC bit was gently borrowed from the internet..
//I can't determine who wrote it.. it shows up in an Ardiuno Sketch for MODbus
// Modbus over serial line - RTU Slave Arduino Sketch
//with referenced of Juan Pablo Zometa, Samuel Marco, Andras Tucsni, Philip Costigan
//It also shows up on a number of websites and forums uncredited... many PIC or C based ..
//so who knows, but I didn't write it
//The MODbus CRC-16 function is outlined on the Modicon site for reference

unsigned int modbus_crc(unsigned char buf[], int start, int cnt)
{
	int 	i,j;
   unsigned temp, temp2, flag;

   temp=0xFFFF;

   for (i=start; i> 1;
         if (flag) temp=temp ^ 0xA001;
         }
      }
   /*** Reverse byte order. ***/
   temp2=temp >> 8;
   temp= (temp << 8) | temp2;
   return(temp);
}

int check_master_crc (void) {

    int diff, diff1, diff2;       //what we send back
    char data[6];
    result = 0x0000;
    crc_high = 0x00;
    crc_low = 0x00;

    data[0] = address;  //this is ugly but all master queries are 6bytes+CRC so it'll do
    data[1] = ctxt[0];  //function
    data[2] = ctxt[1];  //start_addressH
    data[3] = ctxt[2];  //start_addressL
    data[4] = ctxt[3];  //# of regH
    data[5] = ctxt[4];  //# of regL

    result = modbus_crc(data,0,6);
    crc_high = result >> 8;
    crc_low = result & 0x00FF;

    diff1 = ctxt[5] ^ crc_high;
    diff2 = ctxt[6] ^ crc_low;
    diff = diff1 + diff2;
    //this spits back an XORed value between calculated and received CRC
    return(diff);
}

int main(void) {

    // set up oscillator control register, using internal OSC at 4MHz.
    OSCCONbits.IRCF = 0x0E; //set OSCCON IRCF bits to select OSC frequency 8MHz
    OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
    OPTION_REGbits.nWPUEN = 0; // enable weak pullups (each pin must be enabled individually)
    new_rx=0;

    init_io();
    serial_init();
    run_timer();

    check_my_address();

    LATCbits.LATC0 = 1;
    LATCbits.LATC1 = 1;

    __delay_ms(500);
    LATCbits.LATC0 = 0;
    LATCbits.LATC1 = 0;

    if (address == 0x01){
        LATCbits.LATC0 = 1;
    }
    if (address == 0x02) {
        LATCbits.LATC1 = 1;
    }
    __delay_ms(500);
    __delay_ms(500);
    LATCbits.LATC0 = 0;
    LATCbits.LATC1 = 0;


    INTCONbits.GIE = 1;
    INTCONbits.PEIE = 1;

    while (1) {

        //If read data, not me and then if no 232 traffic for >800us then any xmit over
        //104 us (9600) x 8 quiet periods minus some for wiggle.

        if (ping) {
            run_timer();
        }

        if (new_rx) {       //this device was polled
            //testing response
            i = check_master_crc();     //check master query crc
            if (i==0x00) {              //CRC Ok?
              send_mpoll();             //send test response to known test query
            }

            new_rx = 0;
        }

        //testcrc[0] = 0xFF;
        //testcrc[1] = 0x02;      //right now this is only thing processed


        //result = modbus_crc(testcrc,0,2);
        //crc_high = result >> 8;
        //crc_low = result & 0x00FF;


        __delay_ms(50);
        LATCbits.LATC0 = 0;
        LATCbits.LATC1 = 0;
    }
}

Some screenshots I took of my success!

Finally, good data!
Finally, good data!
A good MODbus query and response.
A good MODbus query and response.

 

C PIC RS-485

PIC Micro MODbus : [1] Breadboard mock-up

I was sitting in my workshop looking for something other than painting/staining… I have project overload for sure, so I was looking for something that was 180 degrees out from what I had been working on. On my whiteboard I have one of those “been-on-the-TODO-list-for-at-least two-years” projects aptly named “MODbus”.. After determining that a BACnet controller wasn’t going to run on a 8-bit micro (many moons ago) I figured I’d try it’s much lighter weight cousin MODbus. It was time.

MODbus is a older-than-dirt communications protocol for industrial process/power controls. It’s generally seen in the wild as MODbus/RTU which is a RS-485 protocol ran at 9600 or 19200 baud. Of course there are a ton of variations and it wouldn’t be a comm protocol these days unless someone decided to make a MODbus/IP.. but that’s beyond the scope. Google, as always, is your friend. Super basic protocol the master polls the loop with the address, a basic command, and what data points it’s affecting. The slave responds with it’s address, the command the resulting data as required. The error correction is CRC 16bits.. never used CRC before.. seen it a million times, so this will be fun. In the beginning I’ll just ignore it and send it some pre-determined correct data as needed.

 

Step 1.. pick a micro, because I don’t feel like making process flow charts just yet.

I might try Jason’s new dev-board that’s on the way now, hopefully it’ll pop in tomorrow or next week. For now I’m working on the 16F1509. I don’t think it’ll stay on this just because I feel like mixing it up a bit. I’ll switch it to the ’14K22 at the very least, the ‘1509 was in the board already and wanted to get going on this project. Note: I didn’t even successfully complete Step 1. :D

 

Step 2. Determine what’s a good target for a successful test?

Well I like to break dev up into little pieces.. so I have as simple of goals as possible:

2a. I just wanted to prove to myself my 8x DIP switch was read to choose a slave device address. Check, no brainer.. that was easy. CPU timing seemed off through… oops:! 0x0D is 4MHZ.. I want 0x0E! (8).

2b. I got the serial going, okay… sending 0x2E (“.”).

2c. I found a MODbus master polling simulator that will work for the next 29 days (it’s 198x tech people, why are we charging for the basic tools?) .. I’ll build one of those later… and now..

2d. Have the PIC recognize that it’s being polled. FAIL!

 

That’s where I ended off for the evening. Scratching my head why my received address isn’t recognized (when it does it should output a pulse on a known good output).. the LA didn’t pick up anything but it is going through interrupt for the RX. I guess it’ll be a nice Saturday morning project with some tea.

 

The logic analyzer ascreenshot. I am getting good packets.. just not recognizing them yet.
The logic analyzer ascreenshot. I am getting good packets.. just not recognizing them yet.

In the screenshot I didn’t rename the channels, so Channel 1 is RX (from the PC running the sim), TX is channel 3. I’m dumping the RX buffer into a temp variable and transmitting it when it makes it through my main loop.

No sense in sharing the code just yet.. it’s a hacked up pile of festering dog crap. The breadboard even looks a little chaotic!

 

Mock-up of my MODbus controller breadboard style version 0.00001.
Mock-up of my MODbus controller breadboard style version 0.00001.

So why MODbus? Why not! I have four reasons for taking up this project, now more than ever.

1. I would like to implement a small comm system in our new home.. MODbus is light weight enough to do it on 8bit micros, an I can use something more powerful for control if I want to later.

2. At work our BACnet supervisors (master units) are getting native MODbus drivers in the next sub-version (instead of using integrators); that’s cool, I want to test it out.

3. I want to turn the whole thing into an article of 0xEE.net after I’m done… bonus.

4. why not? I’d like to experience.. plus I already told co-workers I would so no way I can fail now!

Workbench

New Workshop Tour

A tour of my new workshop.. it runs a little long but I didn’t feel like re-shooting. Enjoy! Recommendations and comments welcome.

C Electronics Hardware Hacking i2c PIC

XC8 + PIC 16F1509 with the Wii Nunchuk (yet another…)

A quick and dirty PIC to Wii Nunchuk project
A quick and dirty PIC to Wii Nunchuk project

I needed a “quick fix” of bench time.. I’ve been working on hunny-dos replacing light switches with dimmers, replacing our HVAC control with a remote network interfaced controller, etc… I needed a little bench time! I took the first quick-looking project off my “shelf of unfinished or unstarted projects”. Wii Nunchuk + a little eBay interface board that cost me a dollar or two. I spent the evening getting some C code set up using the data sheet at robotshop and some 80s music.

The TAUTIC 20 Pin PIC dev board was an obvious, I already had done some recent I2C code on it; reuse, reduce, recycle!

I wrote the code to look for the “Z” button push. The C & Z buttons threw me off a bit because I think the datasheet was a little off in some of the text. The last two bits (LSB) of the 5th status response bytes indicate the button positions of BOTH buttons. It incorrectly states one bit is for one button and the other for the other. This is the table I figured out using the Saleae logic analyzer:

0Bxxxx xx11 no buttons pushed

0Bxxxx xx01 C pushed

0Bxxxx xx00 Z pushed

0Bxxxx xx10 both buttons pushed

This is a white original Wii remote…. I didn’t do any OR decoding, etc.. I didn’t really pay much attention to the other data other than verifying it actual works.

The code doesn’t do much; we initialize the *white* nunchuk and then turn on LATA1 port when the Z button is pressed (but not the C and Z simultaneously). From here everything is popped into a variable for your happy hardware hacking needs!

Well anyways.. what you’re here for! The code:


/*
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com
 *
 * Created on January 26, 2014, 12:00 PM
 *
 * Target Device:
 * 16F1509 on Tautic 20 pin dev board
 *
 * Project:
 *  Wii Nunchuck
 *
 * Version:
 * 0.1  Start Bit, and Control Byte ... check
 * 0.2  Read_State Success

 *
 */
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 //4Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
#endif

#include 
#include 
#include 
#include 

//config bits
#pragma config FOSC=INTOSC, WDTE=OFF, PWRTE=ON, MCLRE=ON, CP=OFF, BOREN=OFF, CLKOUTEN=OFF, FCMEN=OFF
#pragma config WRT=OFF, STVREN=OFF, LVP=OFF


#define _XTAL_FREQ 4000000 //defined for delay

    unsigned int ACK_bit;
    int i, x;   //garbage flags
    long int tempi, tempn, tempx, tempy, temp10, temp25;
    unsigned char byte, tempbyte1, tempbyte2;
    unsigned char StatusByte;
    unsigned char RESP1Byte, RESP2Byte, RESP3Byte, RESP4Byte, RESP5Byte;
    unsigned char RESP6Byte, RESP7Byte, RESP8Byte, RESP9Byte, RESP10Byte;
    char buf[10];

void init_io(void) {

    ANSELA = 0x00; // all port A pins are digital I/O
    ANSELB = 0x00; // all port A pins are digital I/O
    ANSELC = 0x00; // all port B pins are digital I/O

    TRISAbits.TRISA0 = 0; // 
    TRISAbits.TRISA1 = 0; // 
    TRISAbits.TRISA2 = 0; // 
    TRISAbits.TRISA3 = 1; // /MCLR
    TRISAbits.TRISA4 = 0; // 
    TRISAbits.TRISA5 = 0; // 

    TRISBbits.TRISB4 = 1; // RB4 I2C SDA, has to be set as an input
    TRISBbits.TRISB5 = 1; // RB5 NC (RESERVED RS232)
    TRISBbits.TRISB6 = 1; // RB6 I2C SCLK, has to be set as an input
    TRISBbits.TRISB7 = 0; // RB7 NC (RESERVED RS232)

    TRISCbits.TRISC0 = 0; // 
    TRISCbits.TRISC1 = 0; // 
    TRISCbits.TRISC2 = 0; // 
    TRISCbits.TRISC3 = 0; // 
    TRISCbits.TRISC4 = 1; // 
    TRISCbits.TRISC5 = 1; // 
    TRISCbits.TRISC6 = 1; // 
    TRISCbits.TRISC7 = 1; // 
}

/*
 *  I2C Functions
 *
 */

void I2C_ACK(void)
{
   PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
   SSP1CON2bits.ACKDT=0;        // clear the Acknowledge Data Bit - this means we are sending an Acknowledge or 'ACK'
   SSP1CON2bits.ACKEN=1;        // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
   while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete

}

void Send_I2C_Data(unsigned int databyte)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPBUF = databyte;              // send databyte
    while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
}

unsigned char RX_I2C_Data (void)
{

    RCEN = 1;               //
    while( RCEN ) continue;
    while( !BF ) continue;
    byte = SSPBUF;
   return byte;
}

void I2C_Control_Write(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSP1BUF = 0xA4;             // send the control byte 
    while(!PIR1bits.SSP1IF)     // Wait for interrupt flag to go high indicating transmission is complete
        {
        i = 1;
          // place to add a breakpoint if needed
        }
    PIR1bits.SSP1IF=0;

}

void I2C_Control_Read(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSP1BUF = 0xA5;             // send the control byte
    while(!PIR1bits.SSP1IF)     // Wait for interrupt flag to go high indicating transmission is complete
        {
        i = 1;
          // place to add a breakpoint if needed
        }
    PIR1bits.SSP1IF=0;
   }

void I2C_Start_Bit(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPCON2bits.SEN=1;          // send start bit
    while(!PIR1bits.SSP1IF)    // Wait for the SSPIF bit to go back high before we load the data buffer
        {
        i = 1;
        }
    PIR1bits.SSP1IF=0;
}

void I2C_check_idle()
{
    unsigned char byte1; // R/W status: Is a transfer in progress?
    unsigned char byte2; // Lower 5 bits: Acknowledge Sequence, Receive, STOP, Repeated START, START

    do
    {
        byte1 = SSPSTAT & 0x04;
        byte2 = SSPCON2 & 0x1F;
    } while( byte1 | byte2 );
}
/*
 * Send the repeated start message and wait repeated start to finish.
 */
void I2C_restart()
{
    I2C_check_idle();
    RSEN = 1; // Reinitiate start
    while( RSEN ) continue;
}

void I2C_Stop_Bit(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPCON2bits.PEN=1;          // send stop bit
    while(!PIR1bits.SSP1IF)
    {
        i = 1;
        // Wait for interrupt flag to go high indicating transmission is complete
    }
}

void I2C_NAK(void)
{
    PIR1bits.SSP1IF=0;           // clear SSP interrupt bit
    SSP1CON2bits.ACKDT=1;        // set the Acknowledge Data Bit- this means we are sending a No-Ack or 'NAK'
    SSP1CON2bits.ACKEN=1;        // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom

    while(!PIR1bits.SSP1IF)     // Wait for interrupt flag to go high indicating transmission is complete
    {
        i = 1;
    }
}

/*
 *  nunchuk commands
 *
 */

void initialize_nunchuk(void)
{
// 0xA4 0x40 0x00

    I2C_Start_Bit();                     // send start bit
    I2C_Control_Write();                 // send control byte

    Send_I2C_Data(0x40);
    Send_I2C_Data(0x00);                 //

    I2C_Stop_Bit();
    __delay_ms(100);
}

void start_conversion_with_nunchuck(void)
{
// 0xA4 0x00

    I2C_Start_Bit();                     // send start bit
    I2C_Control_Write();                 // send control byte

    Send_I2C_Data(0x00);                 //0x00

    I2C_Stop_Bit();
			
}



void read_state (void)
{
/*
 *   STATUS, 6 Bytes
 *  Joy X
 *  Joy Y
 *  Accel X
 *  Accel Y
 *  Accel Z
 *  Z, C bits and LSB Accel

*/
//0x52 RX_STATUS
//STATUS, RESP1, RESP2, RESP3, RESP4, RESP5, RESP6

    I2C_Start_Bit();                     // send start bit
    I2C_Control_Read();

    RX_I2C_Data();                      //Joy X
	StatusByte = byte;
    I2C_ACK();

	RX_I2C_Data();                      //Joy Y
	RESP1Byte = byte;
    I2C_ACK();

	RX_I2C_Data();                      //Acc X
	RESP2Byte = byte;
    I2C_ACK();

	RX_I2C_Data();                      //Acc Y
	RESP3Byte = byte;
    I2C_ACK();

	RX_I2C_Data();                      //Acc Z
	RESP4Byte = byte;
    I2C_ACK();

    	RX_I2C_Data();                      //CZ Status LSB
	RESP5Byte = byte;
    I2C_ACK();

    RX_I2C_Data();                      //
	RESP6Byte = byte;
    I2C_NAK();                          //NAK

	I2C_Stop_Bit();                     // Send Stop Bit

}



int main(void) {

    OSCCONbits.IRCF = 0x0d;     //set OSCCON IRCF bits to select OSC frequency 4MHz
    OSCCONbits.SCS = 0x02;
    OPTION_REGbits.nWPUEN = 0;  //enable weak pullups (each pin must be enabled individually)

    init_io();

    __delay_ms(50);            //let the power settle


    TRISBbits.TRISB6 = 1;

    SSPSTATbits.SMP = 1;
    SSPCONbits.SSPM=0x08;       // I2C Master mode, clock = Fosc/(4 * (SSPADD+1))
    SSPCONbits.SSPEN=1;         // enable MSSP port
    SSPADD = 0x27;              //figure out which one you can ditch sometime (probably either)
    SSP1ADD = 0x27;             // 100KHz
                                //0x09 = 100KHz
    // **************************************************************************************


    initialize_nunchuk();
    x = 0;
    LATAbits.LATA1 = 0;
    __delay_ms(50);


    while (1) {
        x = 0;
        LATAbits.LATA1 = 0;

        start_conversion_with_nunchuck();
        __delay_ms(5);
        read_state();
        //RESP6Byte
        if (!(RESP5Byte & 0x02) && !(RESP5Byte & 0x01)){
            x = 1;
        }
        if (x) {
            LATAbits.LATA1 = 1;
        }
        __delay_ms(100);
    }
    return;
}

The only thing that might not work for your application right out of the box is the return through the loop turns off the output for just a bit while the button stays pressed… you would want to change this obviously.

Workbench

Workshop Moving Update

We’ve been in our new house a week now. I’ve now got a honey-do list that will last me a solid decade.. in-between switching all our outlets and switches to Decora-style (including some timer and occupancy sensing switching), the endless painting and unpacking I’ve managed to sneak off a couple of hours in my new shop. It was a dark dungeon before but it was dry and had a little 30 amp service sub-panel.. I had enough vision to know this shop is going to work out just fine. The building is 16’x10′, by no means a huge place, however it’s twice as much room as I had before .. it has some additional attic storage and I don’t have to share this space.

The seller had cleaned up the shop somewhat from its previous state during inspection, but there was still a lot of garbage, old paint, and stuff I needed to get rid of.

Closing Day, east side of the shop.. a dark dungeon.
Closing Day, east side of the shop.. a dark dungeon.
Closing Day -- West side of my new shop.
Closing Day — West side of my new shop.

I filled the shop up on the first day of moving. It’s amazing how much stuff I’ve ratted away here and there. I have some more stuff to put in here yet, but it’s not too much. Some batteries, a little more test equipment, my tool box and some odds and ends.

We've been in the house a week now and the little time I have in the shop I've gotten it to a nearly useable condition.
We’ve been in the house a week now and the little time I have in the shop I’ve gotten it to a nearly useable condition.

So in another week or so I’ll be able to get back to hardware hacking! I just have to finish up a little kitchen project, put some storage management into a closet and finish some switches & outlets and then I can slow down on the long-long list of little projects.

C Electronics LEDs PIC Tindie

@tymkrs TTL-8

TYMKRS TTL-8

I’ve been watching for the release of the tymkrs ttl-8 ( no -me ?) for some time now. @whixr had been showing it off in a MIDI project on one of their YouTube videos a while back and I thought it was a great little breadboard-hackers tool. Is it magic? No it’s a shift register.. but a worthy bench-top tool for sure. For $6 this board showed up in the mail box a couple of days after purchase… Atdiy is pretty prompt about shipping.

I also have plans on putting this to use while troubleshooting a MIDI project I’m working on for my brother but I’m sure this little board will come in handy for all sorts of purposes.

So like all items I buy off Tindie* I had to check it out right away! My workshop is about half packed for the move but I suspect my bench top items will wait for last so I still had the tools to check this item out; In full tradition of moving, my bench top items will also be first to move into the new house ;)

* with exception for the Minishift and CPLD dev board; someday..
I wrote up some basic code to shift out a counter in my main loop to test this board out; nothing special but it got the job done. There isn’t much to go wrong.. and in fact the task was pretty vanilla; nothing went wrong.

The tymkrs TTL-8 up close...
The tymkrs TTL-8 up close…

 

The test code.. [Edit: my code works with no delays built in.. at 4MHz (I tested this), whixr runs these faster and has ganged many of these together but adds capacitors for filtering on power mentioned adding a ceramic cap between the clock and ground after about 5 chained boards]


/*
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com
 *
 * Created on September 26, 2014, 2:47 PM
 *
 * Target Device:
 * 16F1509 on Tautic 20 pin dev board
 *
 * Project: ttl-8 test
 *
 *
 * Version:
 * 1.0
 *
 */
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 //4Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
#endif

#include 
#include 
#include 
#include 


//config bits
#pragma config FOSC=INTOSC, WDTE=OFF, PWRTE=OFF, MCLRE=ON, CP=OFF, BOREN=ON, CLKOUTEN=OFF, IESO=OFF, FCMEN=OFF
#pragma config WRT=OFF, STVREN=OFF, LVP=OFF

#define _XTAL_FREQ 4000000 //defined for delay
 

/*
 *
 */
int r;
unsigned char n;

void init_io(void) {

    ANSELA = 0x00; // all port A pins are digital I/O
    ANSELB = 0x00; // all port B pins are digital I/O
    ANSELC = 0x00; // all port B pins are digital I/O

    TRISAbits.TRISA0 = 0; // output
    TRISAbits.TRISA1 = 0; // output
    TRISAbits.TRISA2 = 0; // output
    TRISAbits.TRISA3 = 0; // output
    TRISAbits.TRISA4 = 0; // output
    TRISAbits.TRISA5 = 0; // output

    TRISBbits.TRISB4 = 0; // output
    TRISBbits.TRISB5 = 1; // input
    TRISBbits.TRISB6 = 0; // output
    TRISBbits.TRISB7 = 0; // output

    TRISCbits.TRISC0 = 0; // output
    TRISCbits.TRISC1 = 0; // output
    TRISCbits.TRISC2 = 0; // output
    TRISCbits.TRISC3 = 0; // DATA OUT
    TRISCbits.TRISC4 = 0; // CLOCK
    TRISCbits.TRISC5 = 0; // LATCH
    TRISCbits.TRISC6 = 0; // output
    TRISCbits.TRISC7 = 0; // output

}

void latch(void) {
    PORTCbits.RC5 = 1;  //latch bump
    __delay_us(10);      //this is slow.. that's okay for me
    PORTCbits.RC5 = 0;
}

void clk(void){
    PORTCbits.RC4 = 1;  //set clock
    __delay_us(5);      //this is slow.. that's okay for me
    PORTCbits.RC4 = 0;
}

void shift_out (unsigned int x){
    r = 8;

    while(r){
        if (x & 0b10000000){
            LATCbits.LATC3 = 1;

        }
        else{
            LATCbits.LATC3 = 0;
        }

        clk();
        x = x << 1;
        --r;
        LATCbits.LATC3 = 0;
    }

    latch();
}

int main(void) {

    // set up oscillator control register, using internal OSC at 4MHz.
    OSCCONbits.IRCF = 0x0d; //set OSCCON IRCF bits to select OSC frequency 4MHz
    OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
    //OPTION_REGbits.nWPUEN = 0; // enable weak pullups (each pin must be enabled individually)

    init_io();

    latch();

    while (1) {

        n = n+1;;

        shift_out(n);

        __delay_ms(50);

    }
    return (EXIT_SUCCESS);
}


TTL-8 what comes in the bag..
TTL-8 what comes in the bag..

This I would have done different:

We all have different needs for our tools; I believe this board layout worked for their projects and made sense, it just wouldn’t have been how mine would have been laid out. As you can tell from the first photo I chose to use my own.

I would have opted for side mounted port in/out connections with right angle connectors. I would have also added a pair of mounting holes. My layout would have increased the cost of the board by about 30%. That’s fine and dandy for *me*….the tymkrs are obviously targeting breadboard-hackers with this board which makes more sense; the outputs on the board could have a right angle connector (not included, pennies on eBay) soldered on and plugged into a breadboard while you had jumpers come in from your micro to plug into a right angle female connector of choice. The LEDs would then be the correct orientation for normal viewing (reading 0 on the left).  The current board size is a compact 1.15 in (29mm) by 0.65 in (11.5mm) or about .75 in²; my alterations would have pushed it up to just over 1 in².

 

C Microcontrollers RF

PIC talks UART on RF – 434 MHz | 0xEE.net

PIC talks UART on RF – 434 MHz | 0xEE.net.

A cross post of the latest 0xEE.net article on using cheap RF modules with PIC Microcontrollers streaming serial using parity.

Electronics Test Equipment

How is my Saleae Logic 8 Logic Analyzer working out?

IMG_5241
I received my Saleae Logic 8 analyzer in the mail a few days ago. I pre-ordered it maybe a day or two after they opened up pre-orders for their new logic analyzers earlier this year. Last I checked they haven’t started shipping the Logic Pro 8/16 yet (USB 3.0/faster). I ordered the Logic 8 as I typically use the LA for 100KHz I2c and *kbaud serial analysis. Today I used it to check some singles on a microcontroller (16F1509). I flashed the micro with some sample code to confirm it worked; it did. The LA worked just fine but I noticed something and I’m not sure if it’s an over zealous algorithm, inductance, a ghost or what? I get a lot more wiggle in the analog display in the LA app than I do on my scope. Full disclosure: my scope has not been calibrated for over two years I still trust it’s input and I use it in the 10MHz+ range all the time, seems pretty solid. This is a 100ms transition off and on. I’ll be keeping my eye on this.. naturally I checked this at max sample rate (25MHz) after I noticed this.. same wiggle.

 

The digital and analog signal shown in Saleae Logic 1.1.24 beta using a Logic 8
The digital and analog signal shown in Saleae Logic 1.1.24 beta using a Logic 8

 

The same signal (channel 0) on a 60MHz Tektronix TDS1002B scope screenshot; I trust the scopes input.
The same signal (channel 0) on a 60MHz Tektronix TDS1002B scope screenshot; I trust the scopes input.

The same signal but with the analog view in 8x mode:

saleae-4
I read through the online manual but I suspect the beta software documentation is lagging behind a bit. Version 1.1.21 said there was a “New voltage measurement tool for analog”.. I see there is a .25 just out so I’ll try that. Still, regardless I have zero regrets and love this thing. I like the bigger form.. I’m also glad they have these black ground clips but wish they would make two changes.

1. Channel 0 is a black leaded wire.. like all the grounds. Maybe white or any other color?

2. In the older application the channels were color coded in the application to match their wire leads.. that was nice :)

.. ask and though shall receive.. I downloaded 1.1.25 and there was my voltages. Didn’t even get to draft!

saleae-5

 

This is a great tool and a must-have if you’re doing any kind of microcontroller work. Maybe I’ll try the Logic Pro’s out once they get caught up with all the pre-orders.

Uncategorized

Big changes for the workbench coming soon, a new address!

We are looking for a new home and our offer for a house we liked has been accepted. Pending some post-inspection requests and VA appraisal we should be closing on the new home in ~30 days? Who knows the real date.. sooner the better for us of course.

Of course a workshop area was on my “must-have” list while looking. Luck would have it my wife’s favorite house came with ample workshop area. It’s apart from the house but has full power and is quite nice. It’s loaded with stuff they’re using to fix up a few items that need attention with the home. So it’s a bit messy in the photo taken here.

photo from inspection
photo from inspection

The new workshop will give me ~120 ft2 which is roughly half the space I have now. It’s also has some additional store space and is laid out well.

Unfortunately this move means I’ll be a little lacking on the content on the blog as I have been for a few weeks now (vacation followed by a couple weeks of house buying). I’ll be packing up much of my shop shortly, my blogging activities will be strictly writing.

The home will have some free space in which I might be able to host some hackerspace activities for the a while if there is ever interest beyond one other person. We’re located close to downtown Everett, WA. I’d like to get 4 or 5 people together as a start–as always let me know if you’re interested. Thanks for stopping by and I look forward to getting back to some quality bench time in the near future.

C Electronics i2c Microcontrollers PIC

AD5246 I2C Digital Resistor+PIC 16F1509 Test Circuit

As part of a larger MIDI project I was looking for some digital resistors. With any project I try to tackle the parts I think I’m going to have the most problem with first, of course it rarely works out I choose the difficult bits… this was the case with these resistors, they’re super simple.

A simple circuit with the PIC dev board, the SC70-6 i2C resistor and some pull up resistors (don't forget those!) I'm running 5V using 10k here..
A simple circuit with the PIC dev board, the SC70-6 i2C resistor and some pull up resistors (don’t forget those!) I’m running 5V using 10k here..

My last adventure with I2C took me a while to beat into submission so I thought I would start with this. I have ordered a enough SOIC/SC70/TSSOP, etc. breakout boards to be considered borderline hoarding. All the fun stuff comes in small packages which makes sense for placing it in a product but it’s no fun when you’re just trying to check something out. I recommend grabbing some if you haven’t already. I’m not brave enough to shoot from the hip and have boards fabed without at least testing the stuff I’m questionable about… granted in this case I2C hardware is pretty simple.  I popped my first Analog Devices AD5246BKSZ10 (Digi-Key Part number: AD5246BKSZ10-RL7CT-ND )  on to a little SIP package breakout board.. I eventually gave up on the SIP package. I hand-soldered it and it must have had some little bits under the package. I grabbed a DIP package breakout and threw some chipquick down then used my 858D to reflow the tiny SC70-6 package.

A little breakout board, I bought a ton of these on eBay for SC70-6 and others..
A little breakout board, I bought a ton of these on eBay for SC70-6 and others..

On power up the resistor defaults to mid-span (with no command).. that’s kind of nice but I will have to consider that when integrating this to a project; In my case that is 5K (of 10k). I hacked up some recent I2C code for quick dev … it was a no-brainer really. The 10K resistor I have centers out at 5.00K but its top side is 9.90K .. not a big deal for me. The resistance data command is 7bit, 0x00 to 0x7F takes you from 0 Ohms to 10K. If you did the math that’s 78 Ohm steps… There is a 5K, 10K, 50K and a 100K version in this series and they can only handle 5mA of current. I will consider putting in a typical passive resistor in series to limit the possible current through the resistor if that will be an issue.

Some thoughts I had..

Pros:

  • Easily adaptable to an all analog circuit
  • tiny!
  • great for firmware calibration! no tiny pots in a case you have to open
  • Doesn’t power up at 0 Ohm!  (this might be  a con for you..)

Cons:

  • Not addressable (it’d be nice if it was a 8 pin package to select between 4 different addresses)
  • Tiny package if you’re trying to hand-solder a project
  • No so good if you need very fine resolution

 

No surprises really.. So here’s the goodies: a screen shot of the I2C write command for max resistance followed by the code.

The i2c command was super simple, start, address,n a 7-bit value 0x00 - 0x7F, stop!
The i2c command was super simple, start, address,n a 7-bit value 0x00 – 0x7F, stop!

 

 


/* 
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com
 *
 * Created on September 14, 2014, 8:06 AM
 *
 * Target Device:
 * 16F1509 on Tautic 20 pin dev board
 *
 * Project: Digital Resistor Test
 *
 *
 * Version:
 * 1.0  Initial Code to Test Wiper
 *
 *
 *  AD5246BKSZ10-RL7
 *  I2C Address 0x5C (Write)
 *  Resistor I2C
 *  Write S 0 1 0 1 1 1 0 W A X D6 D5 D4 D3 D2 D1 D0 A P
 *  Read  S 0 1 0 1 1 1 0 R A 0 D6 D5 D4 D3 D2 D1 D0 A P
 */
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 //4Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/4000.0)))
#endif

#include 
#include 
#include 
#include 

//config bits
#pragma config FOSC=INTOSC, WDTE=OFF, PWRTE=ON, MCLRE=ON, CP=OFF, BOREN=OFF, CLKOUTEN=OFF, FCMEN=OFF
#pragma config WRT=OFF, STVREN=OFF, LVP=OFF

#define _XTAL_FREQ 4000000 //defined for delay

    unsigned int ACK_bit;
    int i;
    unsigned char byte, tempbyte1, tempbyte2;

/*
 * 
 */
void init_io(void) {

    ANSELA = 0x00; // all port A pins are digital I/O
    ANSELB = 0x00; // all port A pins are digital I/O
    ANSELC = 0x00; // all port B pins are digital I/O

    TRISAbits.TRISA0 = 0; // output
    TRISAbits.TRISA1 = 0; // output
    TRISAbits.TRISA2 = 0; // output
    TRISAbits.TRISA3 = 0; // output
    TRISAbits.TRISA4 = 0; // output
    TRISAbits.TRISA5 = 0; // output

    TRISBbits.TRISB4 = 1; // RB4 I2C SDA, has to be set as an input
    TRISBbits.TRISB5 = 1; // RB5 = nc
    TRISBbits.TRISB6 = 1; // RB6 I2C SCLK, has to be set as an input
    TRISBbits.TRISB7 = 0; // RB7 = nc

    TRISCbits.TRISC0 = 0; // output
    TRISCbits.TRISC1 = 0; // output
    TRISCbits.TRISC2 = 0; // output
    TRISCbits.TRISC3 = 0; // output
    TRISCbits.TRISC4 = 0; // output
    TRISCbits.TRISC5 = 0; // output
    TRISCbits.TRISC6 = 1; // input
    TRISCbits.TRISC7 = 1; // input
}


void I2C_ACK(void)
{
   PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
   SSP1CON2bits.ACKDT=0;        // clear the Acknowledge Data Bit - this means we are sending an Acknowledge or 'ACK'
   SSP1CON2bits.ACKEN=1;        // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
   while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
}

void Send_I2C_Data(unsigned int databyte)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPBUF = databyte;              // send databyte
    while(!PIR1bits.SSP1IF);    // Wait for interrupt flag to go high indicating transmission is complete
}

unsigned char RX_I2C_Data (void)
{

    RCEN = 1;               //
    while( RCEN ) continue;
    while( !BF ) continue;
    byte = SSPBUF;
   return byte;
}

void I2C_Control_Write0(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSP1BUF = 0x5C;             // send the control byte (90 TCN75, EF BMP085)
    while(!PIR1bits.SSP1IF)     // Wait for interrupt flag to go high indicating transmission is complete
        {
        i = 1;
          // place to add a breakpoint if needed
        }
    PIR1bits.SSP1IF=0;

}
void I2C_Control_Write1(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSP1BUF = 0x92;             // send the control byte (90 TCN75, EF BMP085, change this)
    while(!PIR1bits.SSP1IF)     // Wait for interrupt flag to go high indicating transmission is complete
        {
        i = 1;
          // place to add a breakpoint if needed
        }
    PIR1bits.SSP1IF=0;

}

void I2C_Control_Read0(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSP1BUF = 0x91;             // send the control byte (90 TCN75, EF BMP085, change this)
    while(!PIR1bits.SSP1IF)     // Wait for interrupt flag to go high indicating transmission is complete
        {
        i = 1;
          // place to add a breakpoint if needed
        }
    PIR1bits.SSP1IF=0;
   }



void I2C_Start_Bit(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPCON2bits.SEN=1;          // send start bit
    while(!PIR1bits.SSP1IF)    // Wait for the SSPIF bit to go back high before we load the data buffer
        {
        i = 1;
        }
    PIR1bits.SSP1IF=0;
}

void I2C_check_idle()
{
    unsigned char byte1; // R/W status: Is a transfer in progress?
    unsigned char byte2; // Lower 5 bits: Acknowledge Sequence, Receive, STOP, Repeated START, START

    do
    {
        byte1 = SSPSTAT & 0x04;
        byte2 = SSPCON2 & 0x1F;
    } while( byte1 | byte2 );
}
/*
 * Send the repeated start message and wait repeated start to finish.
 */
void I2C_restart()
{
    I2C_check_idle();
    RSEN = 1; // Reinitiate start
    while( RSEN ) continue;
}

void I2C_Stop_Bit(void)
{
    PIR1bits.SSP1IF=0;          // clear SSP interrupt bit
    SSPCON2bits.PEN=1;          // send stop bit
    while(!PIR1bits.SSP1IF)
    {
        i = 1;
        // Wait for interrupt flag to go high indicating transmission is complete
    }
}

void I2C_NAK(void)
{
    PIR1bits.SSP1IF=0;           // clear SSP interrupt bit
    SSP1CON2bits.ACKDT=1;        // set the Acknowledge Data Bit- this means we are sending a No-Ack or 'NAK'
    SSP1CON2bits.ACKEN=1;        // set the ACK enable bit to initiate transmission of the ACK bit to the serial eeprom
    while(!PIR1bits.SSP1IF)     // Wait for interrupt flag to go high indicating transmission is complete
    {
        i = 1;
    }
}

void set_resistor(void)
{

    I2C_Start_Bit();                    // send start bit
    I2C_Control_Write0();               // send control byte with read set
    Send_I2C_Data(0x7F);                // Send Resistance Value Data
                                        // 0x00 - 0x7F
                                        // 0 - 10KOhm
    I2C_Stop_Bit();                     // Stop

}




int main(void) {

    // set up oscillator control register, using internal OSC at 4MHz.
    OSCCONbits.IRCF = 0x0d; //set OSCCON IRCF bits to select OSC frequency 4MHz
    OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
    OPTION_REGbits.nWPUEN = 0; // enable weak pullups (each pin must be enabled individually)

    SSPCONbits.SSPM=0x08;       // I2C Master mode, clock = Fosc/(4 * (SSPADD+1))
    SSPCONbits.SSPEN=1;         // enable MSSP port
    SSPADD = 0x09;              // 100KHz
    // **************************************************************************************

    init_io();

    __delay_ms(100);             // let everything settle.

    set_resistor();

    while (1) {
        //do something
        PORTAbits.RA0 = 0;
        __delay_ms(100);
        PORTAbits.RA0 = 1;
        __delay_ms(100);
    }
    return (EXIT_SUCCESS);
}


 

Follow

Get every new post delivered to your Inbox

Join other followers: