Category Archives: RS-232

1-wire 18F14K22 Accelerometer C i2c LEDs Microcontrollers PIC RS-232

WS2812 RGB and BNO055 IMU PIC Projects

This weekend I spent my Friday afternoon and Saturday at the NW Hobby Expo; talk about an intense desire to get another hobby.

RC Plane NW Hobby Expo

Oh wait… oops; don’t tell me wife ๐Ÿ˜‰ Should be here tomorrow. A little small guy to get me started.

hk_order201602

I was out helping a friend sell 3D printers and interest people into joining our Makerspace.. mission success? I don’t know we’ll see at the next open house.

The makerspace has kept me busy every moment I haven’t been spending with my little man.. thankfully my makerspace activities have been more to my desires.. I developed a little code to run WS2812 LED drivers for a PIC… why we used a PIC (my fav) I’m unsure.. these maker-folks are all Arduino… I borrow a board and took a look. It’s cute.. micros with training wheels. Here is some basic code to generate colors.. maybe it’ll cut your dev time down on a real project.
RGB WS2812 PIC


/*
 * File:   main.c
 * Author: Charles M Ihler
 * Contact at: http://iradan.com
 *
 * Created on Janurary 1, 2015
 *
 * Target Device:
 * 18F14K22 
 *
 * Project: RGB Resistor Clock
 *
 
 * Version:
 * 0.1  Configuration, with reset test
 * 0.2  Send a single color to 1 neopixel (WS2811/WS2812)
 *
 */
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 4000000 //16Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/16000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/16000.0)))
#endif

#define CHECK_BIT(var,pos) ((var) & (1<<(pos)))

#include 
#include 
#include 
#include 


//config bits
#pragma config FOSC=IRC, WDTEN=OFF, PWRTEN=OFF, MCLRE=ON, CP0=OFF, CP1=OFF, BOREN=ON
#pragma config STVREN=ON, LVP=OFF, HFOFST=OFF, IESO=OFF, FCMEN=OFF

#define _XTAL_FREQ 64000000 //defined for delay

/*
 * Variables
 */

    int     device_present;             // 1 = 1-wire device on 1-wire bus
    int     i, x, y, temp, an4_value;               //
    long int    decm;
    int     itxdata, txdata;            //int RS232 tx data
    char    rxbuff[10], z[1], buf[4];                 //buffer for T-sense 1-wire device
    float    temperature, f, d;
    volatile unsigned int uart_data;    // use 'volatile' qualifer as this is changed in ISR

/*
 *  Functions
 */

    void interrupt ISR() {

    if (PIR1bits.RCIF)          // see if interrupt caused by incoming data .. unused currently
    {
        uart_data = RCREG;     // read the incoming data
        PIR1bits.RCIF = 0;      // clear interrupt flag
                                //
    }
    // I left this timer interrupt if I needed it later. This is unused.
    if (PIR1bits.TMR1IF)
    {
        //T1CONbits.TMR1ON = 0;
        
        PIR1bits.TMR1IF = 0;
        //T1CONbits.TMR1ON = 1;

    }
}


     void __delay_10ms(unsigned char n)     //__delay functions built-in can't be used for much at this speed... so!
 {
     while (n-- != 0) {
         __delay_ms(10);
     }
 }


void uart_send (unsigned int mydata_byte) {      //bytes

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

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

//This code if from Microchip but is unused currently.
void uart_send_hex_ascii(unsigned char display_data)
{

	//unsigned char temp;
	//temp = ((display_data & 0xF0)>>4);
	//if (temp <= 0x09)
	//	Putchar(temp+'0');
	//else
	//	Putchar(temp+'0'+0x07);
        //
	//temp = display_data & 0x0F;
	//if (temp <= 0x09)
	//	Putchar(temp+'0');
	//else
	//	Putchar(temp+'0'+0x07);

	//Putchar('\r');
	//Putchar('\n');
}

void serial_init(void)
{

    // 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
    BAUDCONbits.BRG16=0;

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


    SPBRG=25;               //38,400bps-ish
                            //BRG16=0, 7=31.25k, 25=9.615k

    PIR1bits.RCIF=0;        // make sure receive interrupt flag is clear
    PIE1bits.RCIE=1;        // enable UART Receive interrupt


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

}

void send_low(void){
    LATCbits.LATC0 = 1;
    _delay(5);
    LATCbits.LATC0 = 0;
    _delay(11);
}

void send_high(void){
    LATCbits.LATC0 = 1;
    _delay(11);
    LATCbits.LATC0 = 0;
    _delay(8);
}


void init_io(void) {
    ANSEL = 0x00;         
    ANSELH = 0x00;

    TRISAbits.TRISA0 = 1; // PGD
    TRISAbits.TRISA1 = 1; // PGC
    TRISAbits.TRISA2 = 0; // output
    TRISAbits.TRISA4 = 1; // OSC
    TRISAbits.TRISA5 = 1; // OSC



    TRISBbits.TRISB4 = 0; // output
    TRISBbits.TRISB5 = 1; // input (RX UART)
    TRISBbits.TRISB6 = 0; // output
    TRISBbits.TRISB7 = 0; // output (TX UART)

    LATC = 0x00;

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

}

void send_black (void) {
        send_low(); //G1
        send_low(); //G2
        send_low(); //G3
        send_low(); //G4
        send_low(); //G5
        send_low(); //G6
        send_low(); //G7
        send_low(); //G8
        
        send_low(); //R1
        send_low(); //R2
        send_low(); //R3
        send_low(); //R4
        send_low(); //R5
        send_low(); //R6
        send_low(); //R7
        send_low(); //R8
        
        send_low(); //B1 00000 0000
        send_low(); //B2
        send_low(); //B3
        send_low(); //B4
        send_low(); //B5
        send_low(); //B6
        send_low(); //B7
        send_low(); //B8
}

void send_brown (void) {
        send_low(); //G1 64
        send_low(); //G2 
        send_high(); //G3 
        send_low(); //G4 
        send_low(); //G5 
        send_low(); //G6 
        send_low(); //G7 
        send_low(); //G8 
        
        send_low(); //R1 128
        send_high(); //R2 0
        send_low(); //R3 1
        send_low(); //R4 0
        send_low(); //R5 0
        send_low(); //R6 1
        send_low(); //R7 0
        send_low(); //R8 1      
  
        send_low(); //B1 0
        send_low(); //B2
        send_low(); //B3
        send_low(); //B4
        send_low(); //B5
        send_low(); //B6
        send_low(); //B7
        send_low(); //B8
}

void send_orange (void) {
        send_high(); //G1 165 / 1010 0101
        send_low(); //G2 
        send_high(); //G3 
        send_low(); //G4 
        send_low(); //G5 
        send_high(); //G6 
        send_low(); //G7 
        send_high(); //G8 
        
        send_high(); //R1 1111 1111
        send_high(); //R2 
        send_high(); //R3 
        send_high(); //R4 
        send_high(); //R5 
        send_high(); //R6 
        send_high(); //R7 
        send_high(); //R8       
  
        send_low(); //B1 00000 0000
        send_low(); //B2
        send_low(); //B3
        send_low(); //B4
        send_low(); //B5
        send_low(); //B6
        send_low(); //B7
        send_low(); //B8
}


void send_yellow (void) {
        send_high(); //G1 255 / 1111 1111
        send_high(); //G2 
        send_high(); //G3 
        send_high(); //G4 
        send_high(); //G5 
        send_high(); //G6 
        send_high(); //G7 
        send_high(); //G8 
        
        send_high(); //R1 1111 1111
        send_high(); //R2 
        send_high(); //R3 
        send_high(); //R4 
        send_high(); //R5 
        send_high(); //R6 
        send_high(); //R7 
        send_high(); //R8       
  
        send_low(); //B1 00000 0000
        send_low(); //B2
        send_low(); //B3
        send_low(); //B4
        send_low(); //B5
        send_low(); //B6
        send_low(); //B7
        send_low(); //B8
}

void send_blue (void) {
        send_low(); //G1
        send_low(); //G2
        send_low(); //G3
        send_low(); //G4
        send_low(); //G5
        send_low(); //G6
        send_low(); //G7
        send_low(); //G8
        
        send_low(); //R1
        send_low(); //R2
        send_low(); //R3
        send_low(); //R4
        send_low(); //R5
        send_low(); //R6
        send_low(); //R7
        send_low(); //R8        
  
        send_high(); //B1
        send_high(); //B2
        send_high(); //B3
        send_high(); //B4
        send_high(); //B5
        send_high(); //B6
        send_high(); //B7
        send_high(); //B8
}

void send_green (void) {
        send_high(); //G1
        send_high(); //G2
        send_high(); //G3
        send_high(); //G4
        send_high(); //G5
        send_high(); //G6
        send_high(); //G7
        send_high(); //G8
        
        send_low(); //R1
        send_low(); //R2
        send_low(); //R3
        send_low(); //R4
        send_low(); //R5
        send_low(); //R6
        send_low(); //R7
        send_low(); //R8        
  
        send_low(); //B1
        send_low(); //B2
        send_low(); //B3
        send_low(); //B4
        send_low(); //B5
        send_low(); //B6
        send_low(); //B7
        send_low(); //B8
}

void send_grey (void) {
        send_low(); //G1
        send_low(); //G2
        send_high(); //G3
        send_low(); //G4
        send_low(); //G5
        send_low(); //G6
        send_low(); //G7
        send_low(); //G8
        
        send_low(); //R1
        send_low(); //R2
        send_high(); //R3
        send_low(); //R4
        send_low(); //R5
        send_low(); //R6
        send_low(); //R7
        send_low(); //R8        
  
        send_low(); //B1
        send_low(); //B2
        send_high(); //B3
        send_low(); //B4
        send_low(); //B5
        send_low(); //B6
        send_low(); //B7
        send_low(); //B8
}

void send_violet (void) {
        send_low(); //G1
        send_low(); //G2
        send_low(); //G3
        send_low(); //G4
        send_low(); //G5
        send_low(); //G6
        send_low(); //G7
        send_low(); //G8
        
        send_high(); //R1
        send_high(); //R2
        send_high(); //R3
        send_high(); //R4
        send_high(); //R5
        send_high(); //R6
        send_high(); //R7
        send_high(); //R8        
  
        send_high(); //B1
        send_high(); //B2
        send_high(); //B3
        send_high(); //B4
        send_high(); //B5
        send_high(); //B6
        send_high(); //B7
        send_high(); //B8
}

void send_red (void) {
        send_low(); //G1
        send_low(); //G2
        send_low(); //G3
        send_low(); //G4
        send_low(); //G5
        send_low(); //G6
        send_low(); //G7
        send_low(); //G8
        
        send_high(); //R1
        send_high(); //R2
        send_high(); //R3
        send_high(); //R4
        send_high(); //R5
        send_high(); //R6
        send_high(); //R7
        send_high(); //R8        
  
        send_low(); //B1
        send_low(); //B2
        send_low(); //B3
        send_low(); //B4
        send_low(); //B5
        send_low(); //B6
        send_low(); //B7
        send_low(); //B8
}

void send_white (void) {
        send_high(); //G1 255 / 1111 1111
        send_high(); //G2 
        send_high(); //G3 
        send_high(); //G4 
        send_high(); //G5 
        send_high(); //G6 
        send_high(); //G7 
        send_high(); //G8 
        
        send_high(); //R1 1111 1111
        send_high(); //R2 
        send_high(); //R3 
        send_high(); //R4 
        send_high(); //R5 
        send_high(); //R6 
        send_high(); //R7 
        send_high(); //R8       
  
        send_high(); //B1
        send_high(); //B2
        send_high(); //B3
        send_high(); //B4
        send_high(); //B5
        send_high(); //B6
        send_high(); //B7
        send_high(); //B8
}


int main(void) {

    init_io();

    // set up oscillator control register, using internal OSC at 16MHz.
    OSCCONbits.IRCF = 0x07; //set OSCCON IRCF bits to select OSC frequency 16MHz
    OSCCONbits.SCS = 0x00; //set the SCS bits to select internal oscillator block
    OSCTUNEbits.PLLEN = 0x01;   //x4 PLL
    //RCONbits.IPEN = 0;          //disable priority levels

    INTCONbits.PEIE = 1;        // Enable peripheral interrupt
    INTCONbits.GIE = 1;         // enable global interrupt

    temp = 0x10000000;

    while (1) {
        //RGB high bit first
        //if (CHECK_BIT(temp, 0)) {
        //send_high(); //G1    
        //} else {
        //send_low(); //G1    
        //}
        
        send_brown();
        send_orange();
        send_yellow();
        
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);        
        
        send_black();    //1st LED
        send_grey();   //2nd LED
        send_white();     //3rd LED

        
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);    
        
        //send_green();
        //send_violet();
        //send_grey();
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10);
        __delay_ms(10); 
        __delay_us(100);

    }
    return (EXIT_SUCCESS);
}

It’s very very dirty and there is some un-used stuff… but it was a quick hack based on old code and I didn’t optimize the delays at all.
… This is work in progress for a resistor color code clock. I’ll obviously need to switch to a crystal, etc.

I also threw together some basic test code for the 9DOF BNO055 IMU. I bought one just for kicks in case I want to add it to my SRS Pop Can Challenge competition robot. I volunteered for the job left over on our makerspace robot group project… because power systems are a bit of a snooze, and I’m pretty much done, I decided to make my own. It won’t win (because I’m not going for all the points) but it’ll be another robot on the field. Here is some cBode to get you started if you’re interested… I used the Adafruit BOB.. pretty cool and they broke out just enough stuff it seems. I tried it with the UART interface first for kicks.. but then switched to I2C.


/*
 * File:   main.c
 * Author: Charles M Ihler
 * Contact at: http://iradan.com
 *
 * Created on February 8, 2014, 11:39 AM
 *
 * Target Device:
 * 18F14K22 on Tautic 20 pin dev board
 *
 * Project: IMU for PCC
 *
 *
 * Version:
 * 0.1  IO Confoiguration RS232/TX/I2C
 * 0.2  Test BNO055 by UART
 * 0.3  Dump UART interface and get cal status via I2C and send to UART 
 *
 */
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 16000000 //4Mhz FRC internal osc
#define __delay_us(x) _delay((unsigned long)((x)*(_XTAL_FREQ/16000000.0)))
#define __delay_ms(x) _delay((unsigned long)((x)*(_XTAL_FREQ/16000.0)))
#endif

#include 
#include 
#include 
#include 


//config bits
#pragma config FOSC=IRC, WDTEN=OFF, PWRTEN=OFF, MCLRE=ON, CP0=OFF, CP1=OFF, BOREN=ON
#pragma config STVREN=ON, LVP=OFF, HFOFST=OFF, IESO=OFF, FCMEN=OFF

/*
 * Variables
 */

#define r_device_address  0x51 // BNO055 Default Address
#define w_device_address  0x50 // BNO055 Default Address

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

    int     w0,w1,w2,w3,w4;     //confirguration words
    int     present;              //device present
    //int     itxdata;            //int RS232 tx data
    //char    buf[10];            //buff for iota
    volatile unsigned int uart_data;    // use 'volatile' qualifer as this is changed in ISR

/*
 *  Functions
 */

    void interrupt ISR() {

    if (PIR1bits.RCIF)          // see if interrupt caused by incoming data
    {
        uart_data = RCREG;     // read the incoming data
        if (uart_data == 0xBB)
        {
            LATAbits.LA0 = 1;
            present = 1;
        }
        PIR1bits.RCIF = 0;      // clear interrupt flag
                                //
    }
    if (INTCONbits.T0IF)
    {
        //LATAbits.LATA0 = 1;
        INTCONbits.T0IF = 0;
    }

}

void __delay_10ms(unsigned char n)     //__delay functions built-in can't be used for much at this speed... so!
 {
     while (n-- != 0) {
         __delay_ms(10);
     }
 }
     
void uart_tx(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){
    while(*txt != 0) uart_tx(*txt++);     //this send a string to the TX buffer
                                            //one character at a time
}

void serial_init(void)
{

    // 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;       // 
    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

    SPBRG=103;               //

    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
    INTCONbits.T0IE = 0;

         __delay_10ms(5);        // give time for voltage levels on board to settle
}


void init_io(void) {
    TRISAbits.TRISA0 = 0; // output
    TRISAbits.TRISA1 = 1; // input
    TRISAbits.TRISA2 = 1; // 
    TRISAbits.TRISA4 = 1; // 
    TRISAbits.TRISA5 = 1; // 
    
    ANSEL = 0x00;         // no A/D
    ANSELH = 0x00;

    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; // RS232 TX

    WPUBbits.WPUB4 = 0x01;        //PORT B WEAK PULL UPS
    WPUBbits.WPUB6 = 0x01;
    
    TRISCbits.TRISC0 = 1; // 
    TRISCbits.TRISC1 = 1; // input
    TRISCbits.TRISC2 = 1; // 
    TRISCbits.TRISC3 = 0; // 
    TRISCbits.TRISC4 = 0; // 
    TRISCbits.TRISC5 = 0; // 
    TRISCbits.TRISC6 = 1; // input
    TRISCbits.TRISC7 = 1; // input

    __delay_10ms(10);   //let voltage settle
    
    //write_uart("RESET\n");         // transmit some data

}

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

void Send_I2C_Data(unsigned int databyte)
{
    PIR1bits.SSPIF=0;          // clear SSP interrupt bit
    SSPBUF = databyte;              // send databyte
    while(!PIR1bits.SSPIF);    // 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.SSPIF=0;          // clear SSP interrupt bit
    SSPBUF = w_device_address;             // send the control byte (90 TCN75, EF BMP085)
    while(!PIR1bits.SSPIF)     // Wait for interrupt flag to go high indicating transmission is complete
        {
        i = 1;
          // place to add a breakpoint if needed
        }
    PIR1bits.SSPIF=0;

}

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


void I2C_Start_Bit(void)
{
    PIR1bits.SSPIF=0;          // clear SSP interrupt bit
    SSPCON2bits.SEN=1;          // send start bit
    while(!PIR1bits.SSPIF)    // Wait for the SSPIF bit to go back high before we load the data buffer
        {
        i = 1;
        }
    PIR1bits.SSPIF=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.SSPIF=0;          // clear SSP interrupt bit
    SSPCON2bits.PEN=1;          // send stop bit
    while(!PIR1bits.SSPIF)
    {
        i = 1;
        // Wait for interrupt flag to go high indicating transmission is complete
    }
}

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

void read_imu_status(void){
    
    I2C_Start_Bit();                    // send start bit
    I2C_Control_Write();                // send control byte with read set
    Send_I2C_Data(0x00);                // register

    I2C_restart();                      // restart

    I2C_Control_Read();
    RX_I2C_Data();                      // read high
    tempbyte1=byte;
//    I2C_ACK();                          // ACK
//    RX_I2C_Data();                      // read low
//    tempbyte2=byte;
    I2C_NAK();                          // NAK
    //I2C_restart();
    I2C_Stop_Bit();                     // Send Stop Bit

    uart_tx(tempbyte1);               //send data off raw by UART

}

void read_imu_cal_status(void){
    
    I2C_Start_Bit();                    // send start bit
    I2C_Control_Write();                // send control byte with read set
    Send_I2C_Data(0x35);                // register

    I2C_restart();                      // restart

    I2C_Control_Read();
    RX_I2C_Data();                      // read high
    tempbyte1=byte;
//    I2C_ACK();                          // ACK
//    RX_I2C_Data();                      // read low
//    tempbyte2=byte;
    I2C_NAK();                          // NAK
    //I2C_restart();
    I2C_Stop_Bit();                     // Send Stop Bit

    uart_tx(tempbyte1);               //send data off raw by UART

}

void write_imu_9DOF(void)
{

    I2C_Start_Bit();                    // send start bit
    I2C_Control_Write();                // send control byte
    Send_I2C_Data(0x3D);                // address
    Send_I2C_Data(0x1C);                // enable 9dof
    I2C_Stop_Bit();

}

void i2c_muck(void)
{

    I2C_Start_Bit();                     // send start bit
    I2C_Control_Write();                  // send control byte with read set

    //if (!SSP1CON2bits.ACKSTAT)
    //LATCbits.LATC1 = 0;                   //device /ACked?

    Send_I2C_Data(0x01);                //pointer
    Send_I2C_Data(0xE1);                //1 shot, 12bit res
    I2C_Stop_Bit();

    __delay_10ms(50);                     //wait for conversion

    I2C_Start_Bit();                     // send start bit
    I2C_Control_Write();                  // send control byte with read set

    //    if (!SSP1CON2bits.ACKSTAT)
//    LATCbits.LATC1 = 0;

    Send_I2C_Data(0x00);                //pointer

    I2C_restart();                      //restart
    I2C_Control_Read();
    RX_I2C_Data();                      //read high
    tempbyte1=byte;
    I2C_ACK();                          //ACK
    RX_I2C_Data();                      //read low
    tempbyte2=byte;
    I2C_NAK();                          //NAK
    //I2C_restart();
    I2C_Stop_Bit();                     // Send Stop Bit

    uart_tx(tempbyte1);               //send data off raw by UART
    uart_tx(tempbyte2);
}


int main(void) {

    // set up oscillator control register, using internal OSC at 16MHz.
    OSCCONbits.IRCF = 0x07; //set OSCCON IRCF bits to select OSC frequency 16MHz
    OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
    //OSCTUNEbits.PLLEN = 0x01;   // PLL ENABLE FOSC -> 64MHz
    //OSCTUNEbits.TUN = 0x63;     ///trim up that frequency to get closed to 115200

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

    // ***********************************************************************************
    __delay_10ms(100);

    
    //init -- check and wait for IMU
    
    // test
    tempbyte1 = 0xFF;
    
    uart_tx(tempbyte1);               //send data off raw by UART
    uart_tx(0xAA);
    
    __delay_10ms(100);
    
    read_imu_status();    
    //set IMU for 9DOF
    __delay_10ms(10);
    
    
    write_imu_9DOF();
    
    //     UART method
    //     uart_tx(0xAA);                   //Start
    //     uart_tx(0x00);                   //read
    //     uart_tx(0x3D);                   //address 
    //     uart_tx(0x01);                   //length 
    //     uart_tx(0x1C);                   //data 9DOF

    __delay_10ms(50);

    while (1) {
         __delay_10ms(10);                   //delay for debugging

        LATAbits.LA0 = 0;           //good rx flag off
        //LATCbits.LATC3 = 0;
        //__delay_ms(149);
        //LATCbits.LATC3 = 1;     //debugging
        read_imu_cal_status();
  
        LATAbits.LA0 = 1;           //good rx flag on
         __delay_10ms(150);                   //delay for debugging
        
     }                                      //... wash, rinse, repeat
    return;
}



So yeah, again also sloppy but maybe you can use some of it. Price is right anyways.

Okay well maybe you might have notices a TINY gap in posting… it seems I greatly underestimated the amount of time a baby consumes in your life. I have a 9 month old son now.. he is running around trying to chew on USB cables (his favorite)… he’s just now realizing a bit of independence and with a recent purchase of a nice Lenovo Yoga 900 to replace my couple month old dead Sony Vaio I now can develop from the couch.. though I have a porta-crib in the workshop ๐Ÿ™‚

Analog to Digital Converter C Electronics Microcontrollers PIC RS-232

PID Control 1: Set up and thoughts

A while ago I acquired some electronics that had plenty of good stuff to “recycle”… An influx of some interesting mechanical bits I hope to add to a future project; It’s also held me up on new project posting after my last Modbus blog post.

Today I entered my shop and realized I was all done for the most part… What to do?! Well next on my projects list is “something PID” … It should really read “something PI”. PID is an algorithm for proportional, integral, derivative control. Wikipedia is your friend on the painful math bits. I don’t really use the D in PID much but it’s something to be aware of if you’re every looking into it yourself.ย My expectation in this experiment/project is to keep some item level with a stepper motor and hopefully tune my PI loop so it’s not hunting.

I work with PID nearly daily… So I’m going to just shoot from the hip and see what I come up with. No fancy math the first time around, let see where I go?

IMG_5668.JPG

I set up a 18F14K22 on the 20pin breadboard.. All the important stuff, and I’m outputting an accelerometer to an A/D and dumping it to the UART… I’m now at the point of writing the PID code. No code for now, I’ll link in some code when this project isn’t an over-glorified LED blinker and has some purpose.

My notes say I’ll take my process variable which will be accelerometer data on the X-axis. I’ll feed my PID process a setpoint (the value read at level) and the multiplier, a “gain” figure, and then finally process a error correcting value at some set time constant (timer?) I believe this legitimate proportional control.

The integration seems to be much more straightforward but maybe that’s wishful thinking… TBC!

 

C Electronics LEDs Microcontrollers PIC RS-232 RS-485

PIC Micro Modbus Update 3 : The 18F27J53 Port & tinyLED

We got our first snow yesterday; it’s a little early in the year for this and even more odd was it stuck around. The Puget Sound usually keeps us over freezing point (mostly) but Mother Nature had other plans; It was a chilly 22 deg F outside. I haven’t fully insulated my workshop yet, so despite my heater doing all it could it was 55 deg F in the workshop this morning. I can only put in ~2 hours of comfortable work at temperature so work on the my Modbus project was slow. I’ve mostly gotten the project ported to the new micro with a few exceptions.

On the breadboard is the PIC 18F27J53 development board from @atomsoft - I put some of my other favorite development boards next to it for size comparison... I believe that 18F26K22 board is no longer available.. I think I still have one or two unused boards.. better save them for a rainy day or sell them off as collector items :)
On the breadboard is the PIC 18F27J53 development board from @atomsoft – I put some of my other favorite development boards next to it for size comparison… I believe that 18F26K22 board is no longer available.. I think I still have one or two unused boards.. better save them for a rainy day or sell them off as collector items ๐Ÿ™‚

Whenever I start a microcontroller project I try to get the basics out of the way:

1. Port I/O set up correctly? Inputs, Ouputs, Disable A/D converters, etc.

  • I think I have a problem with this or perhaps it’s my OSCCON; I did some additional reading of the specification sheet and wrote down some notes to check yet. My UART is not working.. I’m using the EUSART1 which is the “easy” one.. no peripheral pin select to worry about

2. Oscillator running? Sure, probably but is it running at the right speed?

  • Well, yes.. it took some tweaking as the device I’m using has the PLL that takes the crystal oscillator from 12MHz to 96MHz /2 .. then an additional CPU divider from there. I missed the bit about the CPUDIV register at first.. I was running my CPU at 48MHz ..  that’s okay though because I caught it fast. I always through in a little heartbeat LED blinker in my main code to test to ensure my clock is running at the right speed. I was way off.. did my reading and found I hadn’t set the CPUDIV configuration correctly (for my assumed frequency)
#pragma config CPUDIV = OSC3_PLL3// CPU System Clock Postscaler (CPU system clock divide by 3 from 48MHz)
Okay, clock is good.. 100ms test: check!
Okay, clock is good.. 100ms test: check!
  • However, now with reading the Errata I’ve found there is a problem with the ESUART receive vs. transmitter baud rate generator and it’s recommended that I run my CPUDIV at the OSC1 (1:1) frequency.. so I’ll change that. It’s still not my problem though, On boot my micro transmits some test but that’s not happening according to my logic analyzer.

3. Make sure your IO actually works!

  • Once I think I have everything set up I usually set up some test LEDs and/or the UART.. I’ll send data to the console.. or if it’s basic.. just some LED blinking. I am doing both in this project..

So as of now I have my code to the point my oscillator is running at the frequency I want it to, of course that will have to change which is annoying because the built-in __delay functions with XC8 don’t like running so fast so I have to go and build my own. Or use timers…

My ESUART is not working.. but I have about a dozen odd items to check. I found in the errata that the RX side of the UART gets it baud rate from somewhere other than where the TX side gets. The workaround is keeping your CPUDIV at 0x03,  1:1. I can tell by my logic analyzer the TX isn’t happening.. my other guesses are the TX is at 3.3V because that’s what the micro runs at.. my UART to USB interface runs at 5V. I can set the micro up to run open collector and use a pull-up resistor. I also need to double-check my OSCCON and finally there was some other information that under some circumstances if the interrupts are enabled after the UART is, somehow, the UART gets disabled.. so I can shuffle some code around to figure that out. There are also a few other tidbits to double-check and test.

I’m not going to drop the code in because it doesn’t work all the way.. but you might be interested in just checking out the code itself. You can find it on my new pastebin at: http://pastebin.com/7z8dBMW4 . I recently created this account to shuffle code between my laptop inside and my workbench computer.

Some other tidbits:

I recently picked up the Breadboard Buddy 6.2 and some tinyLED leds from the AtomSoft store at Tindie. The breadboard buddy serial interface worked fine when testing it. I did have to download a driver for Windows 7 but the price is nice; it’s been loopback tested.. and the power supplies are right on (3.3, 5V). The tinyLED despite being cheap was surprisingly exciting. I am in love with these things because they save so much space on my breadboard. I have two green and four white.. rumor is there might be some 4x and 8x LEDs coming out as well! It’s probably obvious I love to develop on a breadboard.. everything off dev board usually runs at 100KHz or less (I2C)… so the capacitance isn’t an issue.

 

An up close shot with the AtomSoftTech tinyLEDs in the background. I think my LEDs were my favorite purchase of the month.. they are small and save me a ton of breadboard room. Note: this breadboard was a free "reject" because the alignment was wrong.. so the LEDs don't line up correctly. In standard boards they're straight. I ordered a new breadboard just to confirm this.. it'll be a bit; it's coming from China of course.
An up close shot with the AtomSoftTech tinyLEDs in the background. I think my LEDs were my favorite purchase of the month.. they are small and save me a ton of breadboard room. Note: this breadboard was a free “reject” because the alignment was wrong.. so the LEDs don’t line up correctly. In standard boards they’re straight. I ordered a new breadboard just to confirm this.. it’ll be a bit; it’s coming from China of course.

The weather looks better later into this week so hopefully I can get in a bit more shop time and wrap this up. I’ve determined I am not going to use this device other than testing integration so I’ll use some preset registers being faux digital inputs. It will be a slam dunk for anyone to add in some hardware polling and adding on to this to make a full blow controller. If I do decide to take this further I’ll consider making the USB interface some kind of firmware update to configure the I/O without downloading code from MPLABX.

C Electronics Microcontrollers PIC RS-232 RS-485

PIC Micro Modbus Update 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 ๐Ÿ˜€

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<cnt; i++){
   	temp=temp ^ buf[i];

      for (j=1; j<=8; j++){
      	flag=temp & 0x0001;
         temp=temp >> 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 Electronics Interface RS-232

Using Parity on the PIC 18F26K22 UART

I have been working on the next article for 0xEE.net this weekend while I recover from my week long backpacking trip. My muscles are feeling great but I took a couple good hits from sharp boulders while doing some Class 2-3 scrambling. It was well worth it of course.

A view of the lake I stayed on after a bit of a scramble up the hill.
A view of the lake I stayed on after a bit of a scramble up the hill.

In the article I’m working on I used parity on a UART. I wanted to share this snippet with you in case you ran into the same thing. I’ve never used parity with the PICs and was a little surprised to find no built in parity checking function; it’s not that big of a deal of course.. I haven’t even ever really looked. At least they allow for a 9-bit mode so you can perform your own software parity checking. No bit-banging for me this weekend. In the code below I am transmitting at a crawl.. 2,400 baud 8E1 (You’ll find out why in a while if you follow 0xEE.net). I’ll drop the receiving code here but the transmitting side uses the same code.. except I did it on a 16F1509 but the parity function is exactly the same. I feel a little embarrassed I pulled this parity function itself from the internet and forgot where I sourced it; everything else is mine. I had kept the FireFox window open to grab the URL for reference but forgot to grab it before a Windows Update restart. Regardless it was just snippet on a forum somewhere; there are a couple different versions of the same thing or lookup tables.. I felt this was the most elegant method.

 

The biggest change to normal UART use was  enabling 9 bit mode. Both on the RX and the TX side… In this case for the RX micro I didn’t need to set the TXSTA1bits.TX9 because I’m only doing one way communication but I felt it was good for consistency.

TXSTA1bits.TX9=1; // select 9 data bits for TX

RCSTA1bits.RX9=1; // select 9 data bits for RX

One of the benefits to this particular parity code is it’s none destructive. I’ve seen more code that decimates the original variable than doesn’t. Not a big deal if you’re like me and your micros are always loafing around.

You can take this code and drop it on a TAUTIC 18F26K22 development board.. RA0 is the onboard LED.

Finally, the complete RX code:



/*
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com
 *
 * Created on Sept 1 2014, 12:12 PM
 *
 * Target Device:
 * 18F26K22 TAUTIC dev board
 *
 * Project:
 *
 *
 * 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=INTIO67, WDTEN=OFF, PWRTEN=OFF, CP0=OFF, CP1=OFF, BOREN=ON
#pragma config STVREN=ON, LVP=OFF, HFOFST=OFF, IESO=OFF, FCMEN=OFF

//WRT=OFF, FOSC=INTOSC, MCLRE=ON

#define _XTAL_FREQ 4000000 //defined for delay


    char    buf[10];            //buff for iota
    char    parity_rx, rx_data;


    volatile unsigned int uart_data, parity;    // use 'volatile' qualifer as this is changed in ISR
/*
 *
 */
void interrupt ISR() {

    if (PIR1bits.RCIF)          // see if interrupt caused by incoming data
    {
        parity_rx = RCSTA1bits.RX9D;  //grab parity bit first
        uart_data = RCREG;     // read the incoming data
        PIR1bits.RCIF = 0;      // clear interrupt flag
    }

}

void init_io(void) {
    TRISAbits.TRISA0 = 0; // output
    TRISAbits.TRISA1 = 0; // output
    TRISAbits.TRISA2 = 0; // output
    TRISAbits.TRISA3 = 0; // output
    TRISAbits.TRISA4 = 0; // output
    TRISAbits.TRISA5 = 0; // output


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


    TRISBbits.TRISB4 = 0; // RB4 = nc
    TRISBbits.TRISB5 = 1; // RB5 = nc
    TRISBbits.TRISB6 = 0; // RB6 = nc
    TRISBbits.TRISB7 = 0; // RB7 = nc

    ANSELB = 0b00001000;     //RB3, AN9

    TRISCbits.TRISC0 = 0; // output
    TRISCbits.TRISC1 = 0; // output
    TRISCbits.TRISC2 = 0; // P1A PWM output
    TRISCbits.TRISC3 = 0; // output
    TRISCbits.TRISC4 = 0; // output
    TRISCbits.TRISC5 = 0; // output
    TRISCbits.TRISC6 = 1; // input
    TRISCbits.TRISC7 = 1; // input
    ANSELC = 0x00; // all port B pins are digital I/O
}

//not of my code.. 
unsigned char calculateparity(unsigned char scancode)
{
	unsigned char parity = 0;

	while(scancode > 0)          // if it is 0 there are no more 1's to count
	{
		if(scancode & 0x01)    //see if LSB is 1
		{
			parity++;                // why yes it is
		}
		scancode = scancode >> 1; //shift to next bit
	}

	return (parity & 0x01);  // only need the low bit to determine odd / even }
}

void uart_xmit(unsigned int mydata_byte) {

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

void serial_init(void)
{
    //2400 8E1
    // 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)



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


    RCSTA1bits.SPEN=1;       // serial port is enabled
    RCSTA1bits.RX9=1;        // select 9 data bits
    RCSTA1bits.CREN=1;       // receive enabled

    /*
     *bit 2 FERR: Framing Error bit
     *1 = Framing error (can be updated by reading RCREGx register and receive next valid byte)
     *0 = No framing error
     *bit 1 OERR: Overrun Error bit
     *1 = Overrun error (can be cleared by clearing bit CREN)
     *0 = No overrun error
     *bit 0 RX9D: Ninth bit of Received Data
     *This can be address/data bit or a parity bit and must be calculated by user firmware.
     */

    SPBRG1=103;  // here is calculated value of SPBRGH and SPBRGL
    SPBRGH1=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

}

int main(void) {

    init_io();
    serial_init();


    // set up oscillator control register, using internal OSC at 4MHz.
    OSCCONbits.IRCF = 0x05; //set OSCCON IRCF bits to select OSC frequency 4MHz
    OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block



    while (1) {
        PORTAbits.RA0 = 0;
        if (uart_data) {

            if (RCSTA1bits.FERR) {  // Frame Error, next read will reset
                uart_data = NULL;
            }

            if (RCSTA1bits.OERR) {  // Overrun Error
                RCSTA1bits.CREN=0;       // receive disabled
                __delay_us(1);
                RCSTA1bits.CREN=1;       // receive enabled
                uart_data = NULL;
            }

            parity = calculateparity(uart_data);
            if (parity_rx == parity) {

                rx_data = uart_data;
                if (rx_data == 'a') {
                    PORTAbits.RA0 = 1;
                    __delay_ms(1);
                    PORTAbits.RA0 = 0;
                }
            }


            uart_data = NULL;   //Done
        }


 //       PORTAbits.RA0 = 1;
 //       __delay_ms(40);
 //       PORTAbits.RA0 = 0;
 //       __delay_ms(40);


    }
    return (EXIT_SUCCESS);
}