Analog Electronics RF Tindie Tools and Test Equipment

Just in! and the start on a VSWR bridge

I’ve been working on little bits over the last week or two, nothing notable but filling up my notebook with plenty of “lessons-learned”. For instance I decided I’m no good as estimating how many uH my home-made inductors are. I purchased a BK Precision 879B LCR meter to confirm that fact. A side-bonus was I went through all my caps and could test my home-brew ESR meter vs. the ESR meter built into the 879B and I now have a fair amount of capacitors that are going to the garbage. No more saving scrapped caps for me. I was working on an oscillator the other day.. well I winged the inductor.. not a wiggle out of the oscillator when finished, that’s what finally made me break down and buy the meter..

A crystal oscillator.. that doesn't oscillate
A crystal oscillator.. that doesn’t oscillate

Today I sat down at the bench and I started working on a project I have had on the board for a while (it’s a surprise); for this piece I made a 50 Ohm VSWR interface; then I threw it into a test jig and checked it out. It actually worked pretty solid. I got some 1 watt SMD resistors off an eBay purchased of some 49.9 ohm 0805 SMD resistors I ordered. The guy just randomly threw in 10 1-watt 49.9 ohm resistors… nice guy. I tested it up to 500MHz but after that I had a bit of noise measuring reverse power.. that’s okay though for me. Interestingly I found with my SA I have a pretty solid local noise source at 90.9, some local station I’m sure.  If you’re interested in making your own check out this guy’s website. He has a basic schematic of a bridge that is functionally equivalent to mine it seems. (a bridge with 3 resistors.. not a brain buster).

 

A little home made VSWR bridge for an upcoming project..
A little home made VSWR bridge for an upcoming project..

So after some success I decided it was time to get to mail. Yesterday I got a packages from the @tymkrs and Jason at AtomSoftTech.

Jason stuffed a box with some goodies I ordered and extras (thanks Jason!) The two most notable items of the bunch is the breadboard PIC buddy and the ESP8266 breakout board. The ESP board was flawless, but after use of BB PIC buddy I have ideas for v2. Now let me make it clear I saw this design before it went to the fab and I missed stuff that seems now obvious.. totally my bad.

Is it functional? Yep.. What would I recommend for changes?

Make the power side a little longer and have the USB jack coming in on the side.. if the PICKit2 is plugged in you can’t use the USB connector. Also while the PIC Kit 2 works okay I think pushing it a 1/2 inch away from the RJ12 jack for the ICD would make the insertion a little more solid. Nice product though.. this is certainly a new breadboard-fav. Jason is selling these for too cheap on Tindie. Get it before he gets smart and raises his price :)

The BB PIC Buddy and ESP8266 breakout board
The BB PIC Buddy and ESP8266 breakout board

There are some other items from AST as well but I’ll mention them later on. I had purchased two items from the @tymkrs ; the new Analog Shift Kit and the new SMD LM386 audio amplifier “Amplify Me”. The Amplify Me board works as expected, I checked out the PDIP version of this some time back ago, I’m not going to hunt for the post though, it’s a LM386 audio amp, kind of a no-brainer. I though of one change for future versions of this product that might have been a little more user friendly. It’d be nice if they made the product just a few more tenths of inches wide and brought that PCB 3.5mm jack out away and further back (back in reference to the photo below). I would make the jack so that it was level with a panel if you mounted the potentiometer into a panel. I don’t think it would add too much to board coast but it wold make this much easier to integrate into projects. As it is though, the potentiometer doesn’t have to be soldered into the board (it doesn’t come soldered) so it would be easier enough to work around. I still find it shocking to get a battery included in an order! It’s the little things @tymkrs!

Updated @tymkrs SMD LM386 Amplify Me
Updated @tymkrs SMD LM386 Amplify Me

No time to get to the Analog Shift kit today, I’ll save that for the future.

I have a lot more Analog bits I’m working on in the near future; I hope to get some of them mentioned here.

1-wire 18F14K22 C Electronics Interface Microcontrollers PIC Weather

iButtonLink T-Sense 1-wire sensor (Maxim DS18B20) + PIC 18F14K22

I came across a stash of iButton T-sense 1-wire sensors.. so I grabbed a couple and decided to check out 1-wire.

Maxim makes a 1-wire device called the DS18B20. It’s a 9-12 bit temperature sensor with the possibility of being powered by parasitic power from the data line. This cuts the signal path down to a single DQ line and a return. A company called iButtonLink produces a nice little wrapper around this device called a T-Sense. There are a couple pieces of software out there that will allow you to hook these up to monitoring systems, I don’t have any though. These devices come with a 64-bit address code and can be daisy-chained which makes having many of these devices monitored very nice.

At first I thought, ugh.. lame I have to send, and parse 64-bit codes in a little 8 bit micro.. doesn’t sound like a ton of fun for just fooling around on a day off.. thank fully they have a “Skip ROM” feature/command which works similar to a broadcast but can only be used when you have one device on the bus. If there is one thing left in this project I might consider finishing it’d be to add the addressing in and daisy-chain a few of these.

Most of my research came from Microchip’s Application Note AN1199 though the T-Sense Users Manual also helped out including determining the wiring diagram.

For my circuit I hooked up 5VDC (but later ran it on 3.3V just fine) and the 5VDC return on pins 1 & 2. Then the DQ link and return on pins 4&5. The signaling is interesting as the 1-wire bus needs a weak pull and works with an open collector circuits. The master starts all signaling, writes and reads. The 1’s and 0’s are based on how long the master or slave sinks the DQ line. To accomplish this in the PIC microcontroller I switched the port from an output to a three state input when I needed the port to be in weak-pull up mode (which is also hand when I need to sample the port for a response from a slave). The pull up on the resistor in my circuit is 10Kohm but I’ve seen 4.7KOhm and I’m sure anywhere in the neighborhood is fine. Finally if you do some digging you’ll notice I run this in low speed mode, if I remember correctly the “high speed” mode is 4x faster. I don’t think speed of data transfer is really relevant when you’re waiting for 750ms for a temperature conversation though.

T-sense sensor and 18F14k22 PIC Microcontroller on the breadboard
T-sense sensor and 18F14k22 PIC Microcontroller on the breadboard

I initially started with just determining if there was a 1-wire device on the bus. If you perform a “reset” (master sinks the bus low for 480us then releases to hi-z for 70us and then performed a sample.. any (all) device(s) will sink the line slow to prove their presence…then another 410us of delay before continuing. I got this one first try.. better luck than my first time with I2C! I then wrote the code (including sampling tidbits of Microchip AN1199 code to optimize) to do an actual temperature conversion and request it (by commanding a “read scratch pad”). The device dumps all 9 bytes of it’s registers. On that note I just remembered I should mention I did NOTHING with the CRC byte.. that’s all you if you care.

My temperature conversion code looks like this: (bus control m = master or PIC, s=slave or sensor)

m RESET

s PRESENCE flag

m SKIP ROM (0xCC)

m CONV TEMP (0x44)

m HOLD DQ (Hold line high 750ms for 12bit conversion .. I am guessing we hold it high for the parasitic power supply)

m RESET

s PRESENCE flag

m SKIP ROM (0xCC)

m READ SCRATCHPAD (0xBE)

s 9 Bytes of Data

and that looks like…

1-wire data capture on the logic analyzer
1-wire data capture on the logic analyzer

 

The 12-bit conversion is basically 0.0625 deg C for every bit from 0C, The LSB holds a temperature sign.

The output of my program looks like..

Final Results!
Final Results!

There are tons of details on what a “1” is an what a “0” is, the ROM code (READ ROM, MATCH ROM), changing the configuration to 9bit for 94 ms conversions over 12bit 750ms conversions. This is all stuff you can grab out of the DS18B20 specification sheet or AN1199.

I hope you get some use out of this.. I still have enough of these T-Sense modules.. maybe if someone really wants one I’ll drop it in the mail… or perhaps we can set up a big box of electronics to ship around and I can dump some in there.

Items I used to make this happen:

TAUTIC 20-pin Development Board

Microchip PIC 18F14K22

AST Breadboard Buddy Pro

AST tinyLEDx4

iButtonLink T-Sense

CAT5 breakout board (eBay?)

Microchip PICKit 3

Then miscellaneous tools, test equipment, jumpers and a breadboard.

 

The good stuff (my code):

.. also found here on pastebin: http://pastebin.com/HrLg1GqL

 


/*
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com
 *
 * Created on Janurary 1, 2015
 *
 * Target Device:
 * 18F14K22 on Tautic 20 pin dev board
 *
 * Project: Maxim 1-Wire Testing
 *
 * Details of 1-wire protocol using Microchip AN1199
 * Field device http://datasheets.maximintegrated.com/en/ds/DS18B20.pdf
 * The 1-Wire Protocol is registered trade mark of Dallas/Maxim semiconductor.
 *
 * Some code was use by the AN1199 App Note Source code; I got stuck looking for a fast way of txing by bit-bang (yes never did this before)
 * The agreement below mentions a license agreement accompaning this software; There was none. I'll note where this software was used if you
 * want to re-write without the Microchip bits.
 * The Microchip licensing as follows:
 *
 *  * FileName:        1wire.c
 * Dependencies:
 * Processor:       PIC18
 * Complier:        MCC18 v3.13
 * Company:         Microchip Technology, Inc.
 *
 * Software License Agreement
 *
 * Copyright © 2004-2007 Microchip Technology Inc.  All rights reserved.
 *
 * Microchip licenses to you the right to use, copy and distribute Software
 * only when embedded on a Microchip microcontroller or digital signal
 * controller and used with a Microchip radio frequency transceiver, which
 * are integrated into your product or third party product (pursuant to the
 * sublicense terms in the accompanying license agreement).  You may NOT
 * modify or create derivative works of the Software.
 *
 *
 * You should refer to the license agreement accompanying this Software for
 * additional information regarding your rights and obligations.
 *
 * SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS” WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY
 * OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR
 * PURPOSE. IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR OBLIGATED
 * UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION, BREACH OF
 * WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT DAMAGES OR
 * EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL, INDIRECT,
 * PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, COST OF
 * PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY CLAIMS BY
 * THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF), OR OTHER
 * SIMILAR COSTS.
 *
 *
 *
 * Version:
 * 0.1  Configuration, with reset test
 * 0.2
 *
 */
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 16000000 //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=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 16000000 //defined for delay

/*
 * Variables
 */

    int     device_present;             // 1 = 1-wire device on 1-wire bus
    int     i, x, y, int_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 init_io(void) {
    ANSEL = 0x00;         
    ANSELH = 0x00;

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



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

    LATC = 0x00;

    TRISCbits.TRISC0 = 1; // AN4
    TRISCbits.TRISC1 = 1; // 1-wire data
    TRISCbits.TRISC2 = 0; // 
    TRISCbits.TRISC3 = 0; // 
    TRISCbits.TRISC4 = 0; // 
    TRISCbits.TRISC5 = 0; // output
    TRISCbits.TRISC6 = 1; // input
    TRISCbits.TRISC7 = 1; // input

}


void init_adc (void)
{
    ANSELbits.ANSEL4=1;         //PORTC.0
    ADCON2bits.ADCS = 0x02;     //Fosc/32
    ADCON2bits.ADFM=0;          //left oriented
    ADCON1=0x00;
}

void read_adc (void)
{
    ADCON0bits.CHS0 = 0;        // AD4
    ADCON0bits.CHS1 = 0;
    ADCON0bits.CHS2 = 1;
    ADCON0bits.CHS3 = 0;
    ADCON0bits.ADON = 1;        // A/D ON
    __delay_us(5);

    ADCON0bits.GO   = 1;        // ..GO!

    __delay_us(5);

        while (ADCON0bits.GO) continue;              //wait for conversion
        an4_value = ADRESH;                          //AN4 value
}

void one_wire_reset(void) {
    device_present = 0x00;
    TRISCbits.TRISC1 = 0;
    LATCbits.LATC1 = 0;
    __delay_us(240);        //delay 480 us
    __delay_us(240);
    TRISCbits.TRISC1 = 1;
    __delay_us(70);
    if (!PORTCbits.RC1) {
            device_present = 0x01;
    }
    __delay_us(205);        //delay 410 us
    __delay_us(205);
}

//this looks a lot like the Microchip code, it was not I just happened to be on the right track.
void one_wire_tx_bit(unsigned char txbit) {         // write a bit
    if (txbit) {
    TRISCbits.TRISC1 = 0;
    LATCbits.LATC1 = 0;
    __delay_us(6);    
    TRISCbits.TRISC1 = 1;
    __delay_us(64);      
    }
    else {      
    TRISCbits.TRISC1 = 0;
    LATCbits.LATC1 = 0;
    __delay_us(60);    
    TRISCbits.TRISC1 = 1;
    __delay_us(10);    
    }
}

//from Microchip AN1199 code, renamed and slightly modified to match my software
/**********************************************************************
* Function:        void OW_write_byte (unsigned char write_data)
* PreCondition:    None
* Input:		   Send byte to 1-wire slave device
* Output:		   None
* Overview:		   This function used to transmit a complete byte to slave device.
*				   
***********************************************************************/
void one_wire_tx_byte (unsigned char write_data)
{
	unsigned char loop;
	
	for (loop = 0; loop < 8; loop++) 	{ 		one_wire_tx_bit(write_data & 0x01); 	//Sending LS-bit first 		write_data >>= 1;					// shift the data byte for the next bit to send
	}	
}	


//from Microchip AN1199 code: I gathered the essence of this but seeing as I am not using most of the AN1199 code
//and this would not work with XC8 I had to re-write this.
/**********************************************************************
* Function:        unsigned char OW_read_bit (void)
* PreCondition:    None
* Input:		   None
* Output:		   Return the status of the OW PIN
* Overview:		   This function used to read a single bit from the slave device.
*				   
***********************************************************************/

unsigned char one_wire_rx_bit (void)
{
	unsigned char read_data; 
        read_data = 0x00;
	//reading a bit 
	TRISCbits.TRISC1 = 0;
        LATCbits.LATC1 = 0; 						// Drive the bus low
	__delay_us(6);						// delay 6 microsecond (us)
	TRISCbits.TRISC1 = 1;  						// Release the bus
	__delay_us(9);						// delay 9 microsecond (us)

        if (PORTCbits.RC1) {                                    //read 1 or 0
            read_data = 0x01;
        }

	__delay_us(55);						// delay 55 microsecond (us)	
	return read_data;
}


/**********************************************************************
* Function:        unsigned char OW_read_byte (void)
* PreCondition:    None
* Input:		   None
* Output:		   Return the read byte from slave device
* Overview:		   This function used to read a complete byte from the slave device.
*				   
***********************************************************************/

unsigned char one_wire_rx_byte (void)
{
	unsigned char loop, result=0;
	
	for (loop = 0; loop < 8; loop++)                // here we are reading 8 bits (1 byte) 	{ 		 		result >>= 1; 				// shift the result to get it ready for the next bit to receive
		if (one_wire_rx_bit())
		result |= 0x80;				// if result is one, then set MS-bit
	}
	return result;					
}	

void one_wire_conversion_pulse(void) {
    	TRISCbits.TRISC1 = 0;
        LATCbits.LATC1 = 1; 		 //For T conv we drive the DQ line high for 750ms (12bit)
	__delay_us(250);                 // delay 
	__delay_us(250);                  
        __delay_us(250);                  
	TRISCbits.TRISC1 = 1; 
        LATCbits.LATC1 = 0;             //just in case this causes problems elsewhere                              
}

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 = 0x02; //set the SCS bits to select internal oscillator block

    //RCONbits.IPEN = 0;          //dsiable priority levels

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


    init_adc();                 //unused but AN4 is there if I need it
    serial_init();

    uart_send ('x');

        LATAbits.LATA0 = 0; //this is just for debugging with an LA..
        __delay_us(1);
        LATAbits.LATA0 = 1; //also confirms oscillator setup is correct.. 1us width
        __delay_us(1);
        LATAbits.LATA0 = 0;

    while (1) {

        one_wire_reset();

        if (device_present) {
            LATCbits.LATC2 = 1;             //this is a 1-wire device out there for debugging

            one_wire_tx_byte(0xCC);         //skip-rom (similar to a broadcast)

            one_wire_tx_byte(0x44);         //do a temp conversion

            one_wire_conversion_pulse();    // hold DQ line high for 750ms

            one_wire_reset();

            //add additional check here later

            one_wire_tx_byte(0xCC);         //skip-rom (similar to a broadcast)

            one_wire_tx_byte(0xBE);         //read scratch pad

            for(i = 0; i<9; i++)            //reading all 9 bytes on the T-Sense
   		rxbuff[i] = one_wire_rx_byte();
            // T-Sense
            //  Byte 0 LSB of Temp
            //  Byte 1 MSB of Temp and sign

            // LSB
            //  2^3 2^2 2^1 2^0 2^-1 2^-2 2^-3 s^-4
            // MSB
            // S S S S S 2^6 2^5 2 ^ 4



            temperature = 0;
            f = 0.0625;

            //z[0] = rxbuff[1];
            //x = atoi(z);
            x = rxbuff[1];

            if (x & 0b10000000) {
                uart_send('-');
            } else {
                uart_send('+');
            }
            
            x = x & 0b00000111;

            int_temp = 0;

            int_temp = rxbuff[0];

            if (x & 0b00000001)
                int_temp = int_temp + 0x100;
            if (x & 0b00000010)
                int_temp = int_temp + 0x200;
            if (x & 0b00000100)
                int_temp = int_temp + 0x400;

            temperature = int_temp * f;
            
            int_temp = temperature;

            itoa(z, int_temp, 10);
            write_uart(z);
            uart_send('.');

            d = temperature - int_temp;

            decm = d * 1000;

            //page 374 of XC8 user guide
            ltoa(buf,decm,10);  //long conversion to buffer
            y=strlen(buf);  //uh, adding leading zeros..
            y=3-y;      //probably a better way of doing thing
            while (y)       //first figure out how many zeros
            {
                uart_send('0');  //missed 3-string length
                y=y-1;  //then send them until done
            }   
            write_uart (buf);
        
            uart_send(0x0A);        //LF
            uart_send(0x0D);        //CR

            //temperature   float temperature
            //int_temp      interger value of temperature

            __delay_10ms(254);
        }

    }
    return (EXIT_SUCCESS);
}

 

 

 

 

 

 

Electronics LiPo Tools and Test Equipment

Breadboard Buddy Pro on the bench

I got a great gift in the mail yesterday! I sneak preview of the up coming Breadboard Buddy Pro. This one is a development board but it looks good and worked even better.

New breadboard buddy with a LiPo battery interface for the hardware hacker on the go!
New breadboard buddy with a LiPo battery interface for the hardware hacker on the go!

I am working on a little project that reads data from Maxim 1-wire temperature sensors (coming soon!) so I put the new BBB into action. The LiPo charger seemed to work just fine.. power was all good. The only thing that I noticed, and honestly I haven’t read the documentation on AdamSoftTech’s Tindie store site, when running on the battery the power supply will only provide 3.3V, not 5.0; the 5.0V rail goes dark .. so no boost on board but in reality that’s not a surprise as there isn’t any big inductors sitting on the PCB.

This product is definitely going into my electronics hobbyist go-bag! My favorite feature on this new product is the handy LiPo battery connector making development on the road easy. Whenever I’m on a trip for training, or working out-of-town I enjoy bringing a few basics to work on some project I’ve had back-burnered but can be worked on by breadboard. I like to pack light and this tool helps out greatly.

Besides the LiPo battery option, battery is not included, I really like the fact he crammed a USB-serial bridge on a power supply board. The RX/TX lights are nice and I imagine for people putting a chip in board the reset button is handy. I use plug-in dev boards and almost everyone puts a reset button on those. I used both 5V then switched to 3.3V .. more than enough current at 500mA of course.

The BBB Pro is also pretty robust: In a silly mistake I accidentally put my logic analyzer ground on the 5V plane.. well the BBB Pro folded back and protected itself.. before it did that though it delivered enough current to melt down my micrograbber…  I’m very thankful I caught it quickly and my LA, USB ports and the BBB Pro are all just fine. The clip was replaced and made its way into my Bag Of Shame. The micrograbber will fit in well within the BOS, which includes such things as an 18F26K22 I melted down.. a handful of MOSFETs (a bag favorite), and assorted other parts I’ve cooked making dumb mistakes.

On the photo above you also notice two new breadboard favorites:

The tinyLEDx4 from AtomSoftTech and a board I made recently for attaching jumpers for my scope and logic analyzer. I sent in a sample and of course Jason (with AST) came up with a revised version of the clip holder which was better designed than mine in a few minutes.. thankfully I don’t do this for a living! :D

18F14K22 Accelerometer Analog to Digital Converter C Electronics Microcontrollers Motors PIC Steppers

18F14K22 Self-Balancing Platform

Well, it’s not finished but this is what I’m working on:

Balancing platform on a breadboard
Balancing platform on a breadboard

Ta-da.. yet another balancing something-or-rather.. except this one isn’t so great at it’s balance. This was a big old fail. I went wrong with steppers… I should have just used DC motors and maybe the next version I will. I used Pololu A4988 stepper motor drivers (A4988-based)  to control my steppers while attempting to use variable speed step resolution control. This worked okay but really while trying to drop holding current while balanced was a nightmare. Also the current usage was above what I consider useful… either backing the current limiting off to save some power (and your driver) then loosing control or you’re just cooking your motor driver and the current isn’t useful other than for a cute demonstration or a non-battery powered application.

I’m not dropping code, you can request it directly if you want it but it’s nothing magical. It has my own hacked up version of a PID loop. I didn’t use derivative. I think far too many cycles are wasted on it when PI will probably work for you. I haven’t fine tuned it because I’m unhappy with my results.

I was a little bummed out on past-Chas’ selection of an MMA7361 accelerometer;  I had gotten it from Sparkfun who-knows-how-long-ago. The link leads to the retired product. It’s just plain voltage output per axis. I must have still been shying away from I2C at the point? The board shown below:

Accelerometer break-out board
Accelerometer break-out board

For a micro I used a PIC 18F14K22 on the TAUTIC dev board with some AtomSoft breadboard goodies for troubleshooting, power and the serial interface (Breadboard Buddy).  <– that link will probably die soon. Tindie updates a lot and products are replaced with newer versions.. if you sniff around you’ll find it.

I’ll move along to another project now, perhaps order some parts and revisit this one again. If you run along this and you’ve done this with steppers I’d be interested in knowing how it worked out for you.

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 :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<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 PIC RS-485

PIC Micro Modbus Update 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.

Follow

Get every new post delivered to your Inbox

Join other followers: