Category Archives: Microcontrollers

C Electronics LEDs PIC Tindie

@tymkrs TTL-8

TYMKRS TTL-8

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

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

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

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

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

 

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


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

#include 
#include 
#include 
#include 


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

#define _XTAL_FREQ 4000000 //defined for delay
 

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

void init_io(void) {

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

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

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

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

}

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

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

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

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

        }
        else{
            LATCbits.LATC3 = 0;
        }

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

    latch();
}

int main(void) {

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

    init_io();

    latch();

    while (1) {

        n = n+1;;

        shift_out(n);

        __delay_ms(50);

    }
    return (EXIT_SUCCESS);
}


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

This I would have done different:

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

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

 

C Microcontrollers RF

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

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

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

C Electronics i2c Microcontrollers PIC

AD5246 I2C Digital Resistor+PIC 16F1509 Test Circuit

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

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

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

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

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

Some thoughts I had..

Pros:

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

Cons:

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

 

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

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

 

 


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

#include 
#include 
#include 
#include 

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

#define _XTAL_FREQ 4000000 //defined for delay

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

/*
 * 
 */
void init_io(void) {

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

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

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

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


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

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

unsigned char RX_I2C_Data (void)
{

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

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

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

}

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



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

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

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

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

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

void set_resistor(void)
{

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

}




int main(void) {

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

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

    init_io();

    __delay_ms(100);             // let everything settle.

    set_resistor();

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


 

C Electronics GPS PIC Time

WWVB signal generated from GPS

I sat at the bench this morning waiting for the inspiration to continue on with a project I’m working on with Francesco; I gave in to procrastination.

I wrote some assembler code to generate WWVB signal from a PIC a few months ago, remember? That stirred some interest from overseas folks looking for a way for their automatically update their “atomic” watches… apparently there isn’t some NTP device or anything else easily found to be purchased to do this. I was asked to do this NTP, but I’ll leave that for someone with a ‘Pi–I gave mine away to a kid. I did this project using GPS and a PIC micro.

It’s a pretty simple setup, gobble up a GPRMC sentence, pick off the data I need and spit out time. I generate 60KHz with the PWM module within the PIC (okay it’s 62KHz, but that should be close enough).. switch it through a 4066 to generate my time code.

GPRMC: there are other sentences you can use, but my cheapo module only spits out 4 sentences and GPRMC is the only one with time and date, so it’ll do. I re-used my GPGGA parsing code.. there is probably some left-overs in there if you look at the code below. I also dump the working string (first 63 bits of GPGGA) followed by the day number of the year which is calculated on the the UART, pick it up off the TX line @ 9,600 baud.. It’s spits it out about once a minute (in between time transmissions). I ended up using some DOY code online because I found some nice code that included determining if it was a leap year for day-of-year determination; I left the URL in the code.

The code isn’t finished nor is it accurate: it doesn’t recognize the valid data bit yet, and just spits out time whenever it feels like it so the time can be off by up to 59 seconds. I’ll work on this later on since I was just looking for proof of concept so I could get the hardware going and off a breadboard. I built up the circuit in eagle.. I’ll check it a few more times and have a couple PCBs made for round one of testing. The prototype was built on a Tautic 18F26K22 dev board, I considered just having the dev board plug-in but that’s probably a waste of $$.

You’ll note the SMA jack, perhaps this is over kill since I’ll never have a good enough antenna to match 60KHz… I don’t really know what to do with that yet, wire antenna for now I suppose. (version 2?) It’s likely illegal to sell these since it’s purposefully broadcasting, fat chance I’m doing the research beyond the FCC. I’ll look it up tomorrow but I suspect Part 15 doesn’t apply to 60KHz.

Version 1 of the GPS>WWVB board
Version 1 of the GPS>WWVB board

 

The code is a total hack job.. poorly documented, perhaps even incorrectly documented. I know I have “place-holder” functions.. but like I said..  it was a start to get my time code and it was close enough for testing; you get what you pay for! 🙂

 

/*
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com / 0xEE.net / @chasxmd
 * Created on April 4th, 2014
 *
 * Target Device: 18F26K22
 *
 * Project: GPS --> WWVB simulator
 *
 * This version uses the GPRMC block
 * This is a limitation because GPRMC doesn't pass seconds
 * The time passed will always be up to 60 seconds off
 * I have to deterine DDMMYY --> Day of year, year
 * No simple leap year info in GPS :(
 *
 * TODO
 * Determine GPS lock and output to LED
 * Consider re-writing how I am writing my time code
 *
 * I'll re-write this sometime with a better (more expensive) GPS module.
 *
 * Version:
 * 0.1      First build I could prove I had GPS lock
 * 0.2      Output time/date on 232
 *
 */
#ifndef _XTAL_FREQ
#define _XTAL_FREQ 8000000 //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 8000000 //defined for delay

    char    ctxt[120], wstr[120];            //buff NMEA string, working string
    char    str1[20], str2[20], c, latstr, lonstr, setstr, doych[8];
    char    hourch[3], minch[3], secch[3], daych[3], monthch[3], yearch[3];
    char buffer[32] = "none";                        //temp dump
    volatile unsigned int     ping, isrcall, index, reading, new_rx;
    int     ready, gpgga, gprmc, mode;        //gps associated vars
    int     leap_year, dayint, monthint, yearint, year4int, secondint, minuteint, hourint;
    long    doy;
    int     min_40, min_20, min_10, min_8, min_4, min_2, min_1;
    int     hour_20, hour_10, hour_8, hour_4, hour_2, hour_1;
    int     doy_200, doy_100, doy_80, doy_40, doy_20, doy_10;
    int     doy_8, doy_4, doy_2, doy_1, leapint;
    int     year_80, year_40, year_20, year_10, year_8, year_4, year_2, year_1;

    //char    *rxdata;
    //volatile unsigned int uart_data;    // use 'volatile' qualifer as this is changed in ISR

/*
 *  Interrupt Service
 */
void interrupt ISR() {

   if (PIR1bits.RCIF){          // see if interrupt caused by incoming data

        isrcall = 0x01;
        char temp;
        temp = RCREG1;     // read the incoming data
        if(temp=='$' && new_rx==0)      //if first char of a GPS string..
        {
            index = 0;                  //reset index
            reading = 1;                //from now on go to else if
        }
        else if(reading == 1)           //in middle of GPS sentence
        {
            ctxt[index] = temp;         //load it up
            index++;                    //increment index
            ping = 1;                   //this is for debugging
            if(index > 63)              //thats more than enough data
                {
                index = 0;              //reset index
                reading = 0;            //no longer storing the string
                new_rx = 1;             //"ding"
                }
        }


   }
}


/*
 * Set up my ports
 */
void init_io(void) {
    // This code before the TRIS setup is for switching the RX2/TX2 to proper pins for the dev board
    INTCONbits.GIE = 0;         //no interruptions please

    LATA = 0x00;

    TRISAbits.TRISA0 = 0; //Onboard LED
    TRISAbits.TRISA1 = 0; //LED
    TRISAbits.TRISA2 = 0; //MCU (ON)
    TRISAbits.TRISA3 = 1; // input
    TRISAbits.TRISA4 = 1; // input
    TRISAbits.TRISA5 = 1; // input
    TRISAbits.TRISA6 = 1; // input
    TRISAbits.TRISA7 = 1; // input


    TRISBbits.TRISB0 = 0; // output
    TRISBbits.TRISB1 = 0; // output
    TRISBbits.TRISB2 = 0; // PWM1B
    TRISBbits.TRISB3 = 0; // output
    TRISBbits.TRISB4 = 0; // SCK
    TRISBbits.TRISB5 = 0; // PWM1C
    TRISBbits.TRISB6 = 0; // SCK
    TRISBbits.TRISB7 = 1; // input

    LATC = 0x00;

    TRISCbits.TRISC0 = 0; // N/W
    TRISCbits.TRISC1 = 0; // S/E
    TRISCbits.TRISC2 = 0; // PWM1A (output to 4066 control)
    TRISCbits.TRISC3 = 1; // MODE SELECT (LAT/LONG)
    TRISCbits.TRISC4 = 1; // SET INPUT
    TRISCbits.TRISC5 = 1; // input
    TRISCbits.TRISC6 = 1; // input
    TRISCbits.TRISC7 = 1; // input

    ADCON0 = 0b00000000;        //don't need any ADC
    ADCON1 = 0b00000000;        //speed Vref=AVdd, VssRef=AVss

    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
}

void uart_xmit(unsigned int mydata_byte) {  //send a character to the UART
    while(!TXSTA1bits.TRMT);    // make sure buffer full bit is high before transmitting
    TXREG1 = mydata_byte;       // transmit data
}

void uart_write(const char *txt)            //sent a multiple characters
{
    while(*txt != 0) uart_xmit(*txt++);     //this send a string to the TX buffer
                                            //one character at a time
}


void serial_init(void)
{
    //9600 8N1


    TXSTA1bits.BRGH=1;       // select low speed Baud Rate
    TXSTA1bits.TX9=0;        // select 8 data bits
    TXSTA1bits.TXEN = 1;     // enable transmit


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

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

    PIR1bits.RCIF=0;        // make sure receive interrupt flag is clear

    __delay_ms(50);        // give time for voltage levels on board to settle
    uart_write("RESET");         // transmit some data for testing

}


void pwm_init(){
//
//Take care if setting up the PWM pins (DISBALE A/D, etc)
//
//Select the 8-bit TimerX resource, (Timer2,Timer4 or Timer6) to be used for PWM generation
//by setting the CxTSEL bits in the CCPTMRSx register.(1)
//
//Load the PRx register for the selected TimerX with the PWM period value.
//
//Configure the CCP module for the PWM mode by loading the CCPxCON register with the
//appropriate values.
//
//Load the CCPRxL register and the DCxB bits of the CCPxCON register, with the PWM
//duty cycle value.
//

//    CCPR1L = 0x120;
    CCPR1Lbits.CCPR1L = 0x01;       //PWM duty cycle
    //CCPR2Lbits.CCPR2L = 0xCE;       //PWM duty cycle
    //CCPR3Lbits.CCPR3L = 0xCE;       //PWM duty cycle
    PR2 = 0x01;                     //Timer 2 Prescale
    //PR4 = 0xFF;                     //Timer 4 Prescale
    //PR6 = 0xFF;                     //Timer 6 Prescale
    CCPTMRS0bits.C1TSEL = 0x00;     //PWM1 TMR2 Selection
    //CCPTMRS0bits.C2TSEL = 0x01;     //PWM2 TMR4 Selection
    //CCPTMRS0bits.C3TSEL = 0x02;     //PWM3 TMR6 Selection
    CCP1CONbits.P1M = 0x00;         //single output mode
    CCP1CONbits.DC1B = 0x00;
    PWM1CONbits.P1RSEN = 0;
    PWM1CONbits.P1DC = 0x00;    //dead band delay
    ECCP1ASbits.CCP1AS = 0x00;
    ECCP1ASbits.CCP1ASE = 0;    //Auto-shutdown off
    CCP1CONbits.CCP1M = 0x0C;   //PWM Mode
    //CCP2CONbits.CCP2M = 0x0C;   //PWM Mode
    //CCP3CONbits.CCP3M = 0x0C;   //PWM Mode
    PSTR1CONbits.STR1A = 1;
    PSTR1CONbits.STR1B = 1;


    //T2CONbits.T2OUTPS = 0x0F;      //post scaler
    T2CONbits.T2CKPS = 2;       //16x prescaler
    //T4CONbits.T4CKPS = 2;
    //T6CONbits.T6CKPS = 2;
    T2CONbits.TMR2ON = 1;       //Turn the Timers On...
    //T4CONbits.TMR4ON = 1;
    //T6CONbits.TMR6ON = 1;



}


/*
 *  Append a string with a character
 *  append(str, c);
 */


//taken from http://stackoverflow.com/questions/19377396/c-get-day-of-year-from-date

int yisleap(int year)
{
    return (year % 4 == 0 && year % 100 != 0);
}

int get_yday(int mon, int day, int year)
//use: int day = get_yday(1, 31, 2013);
{
    static const int days[2][13] = {
        {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334},
        {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}
    };
    int leap = yisleap(year);
    leapint = leap;

    return days[leap][mon] + day;
}

void append(char* s, char c)
{
        int len = strlen(s);
        s[len] = c;
        s[len+1] = '\0';
}

void marker(void){
    //send a marker frame - 800ms off, 200 ms on
    LATBbits.LATB0 = 0;
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);

    LATBbits.LATB0 = 1;
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    LATBbits.LATB0 = 0;
}

void one(void){
    //send a one - 500 ms off, 500 ms on
    LATBbits.LATB0 = 0;
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    LATBbits.LATB0 = 1;
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
}

void zero(void){
    //send a zero - 200ms off, 800ms on
    LATBbits.LATB0 = 0;
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    LATBbits.LATB0 = 1;
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
    __delay_ms(50);
}



void sendtime(void){

    /*
    *;0
    xCALL    MARKER                      ;MARKER FRAME REFERENCE BIT
    xCALL    ONE                         ;40min
    xCALL    ZERO                        ;20min
    xCALL    ZERO                        ;10min
    xCALL    ZERO                        ;Reserved
    xCALL    ZERO                        ;8mins
    xCALL    ZERO                        ;4mins
    xCALL    ONE                         ;2mins
    xCALL    ZERO                        ;1mins
    xCALL    MARKER                      ;MARKER 1
     */

    marker();                       //marker, frame reference

    if (minuteint >= 40){           //40min
        minuteint = minuteint - 40;
        one();
    }else {
        zero();
    }
    if (minuteint >= 20){           //20min
        minuteint = minuteint - 20;
        one();
    }else {
        zero();
    }
    if (minuteint >= 10){           //10min
        minuteint = minuteint - 10;
        one();
    }else {
        zero();
    }    
    zero();                        //reserved (zero)
    if (minuteint >= 8){           //8min
        minuteint = minuteint - 8;
        one();
    }else {
        zero();
    }    
    if (minuteint >= 4){           //4min
        minuteint = minuteint - 4;
        one();
    }else {
        zero();
    }    
    if (minuteint >= 2){           //2min
        minuteint = minuteint - 2;
        one();
    }else {
        zero();
    }    
    if (minuteint >= 1){           //1min
        minuteint = minuteint - 1;
        one();
    }else {
        zero();
    }    
    marker();                       //marker 1
    
    /*
    ;10
    xCALL    ZERO                        ;RESERVED
    xCALL    ZERO                        ;RESERVED
    xCALL    ZERO                        ;20hours
    CALL    ZERO                        ;10hours
    CALL    ZERO                        ;RESERVED
    CALL    ZERO                        ;8hours
    CALL    ONE                         ;4hours
    CALL    ONE                         ;2hours
    CALL    ZERO                        ;1hour
    CALL    MARKER                      ;MARKER 2     
     */
    zero();                         //reserved
    zero();                         //reserved
    if (hourint >= 20){           //20 hours
        hourint = hourint - 20;
        one();
    }else {
        zero();
    }       
    if (hourint >= 10){           //10 hours
        hourint = hourint - 10;
        one();
    }else {
        zero();
    }  
    zero();                         //reserved    
    if (hourint >= 8){           //8 hours
        hourint = hourint - 8;
        one();
    }else {
        zero();
    }  
    if (hourint >= 4){           //4 hours
        hourint = hourint - 4;
        one();
    }else {
        zero();
    }  
    if (hourint >= 2){           //2 hours
        hourint = hourint - 2;
        one();
    }else {
        zero();
    }  
    if (hourint >= 1){           //1 hours
        hourint = hourint - 1;
        one();
    }else {
        zero();
    }  
    marker();                       //marker 2    
    /*
     * 
;20
    CALL    ZERO                        ;RESERVED
    CALL    ZERO                        ;RESERVED
    CALL    ZERO                        ;200 day of year
    CALL    ONE                         ;100 day of year
    CALL    ZERO                        ;RESERVED
    CALL    ZERO                        ;80 day of year
    CALL    ONE                         ;40 day of year
    CALL    ZERO                        ;20 day of year
    CALL    ONE                         ;10 day of year
    CALL    MARKER                      ;MARKER 3
     */
    zero();                         //reserved
    zero();                         //reserved
    if (doy >= 200){           //200th day
        doy = doy - 200;
        one();
    }else {
        zero();
    }           
    if (doy >= 100){           //100th day
        doy = doy - 100;
        one();
    }else {
        zero();
    }   
    zero();                         //reserved
    if (doy >= 80){           //80th day
        doy = doy - 80;
        one();
    }else {
        zero();
    }      
    if (doy >= 40){           //40th day
        doy = doy - 40;
        one();
    }else {
        zero();
    }   
    if (doy >= 20){           //20th day
        doy = doy - 20;
        one();
    }else {
        zero();
    } 
    if (doy >= 10){           //10th day
        doy = doy - 10;
        one();
    }else {
        zero();
    }     
    marker();                       //marker 3

/*
 ;30
    CALL    ONE                         ;8 day of year
    CALL    ZERO                        ;4 day of year
    CALL    ZERO                        ;2 day of year
    CALL    ZERO                        ;1 day of year
    CALL    ZERO                        ;RESERVED
    CALL    ZERO                        ;RESERVED
    CALL    ZERO                        ;UTI Sign +
    CALL    ZERO                        ;UTI Sign -
    CALL    ZERO                        ;UTI Sign +
    CALL    MARKER                      ;MARKER 4
 */

     if (doy >= 8){           //8th day
        doy = doy - 8;
        one();
    }else {
        zero();
    }
    if (doy >= 4){           //4th day
        doy = doy - 4;
        one();
    }else {
        zero();
    }
    if (doy >= 2){           //2nd day
        doy = doy - 2;
        one();
    }else {
        zero();
    }
    if (doy >= 1){           //1st day
        doy = doy - 1;
        one();
    }else {
        zero();
    }
    zero();                         //reserved
    zero();                         //reserved
    zero();                         //reserved
    zero();                         //reserved
    zero();                         //reserved
    marker();                       //marker 4

    /*
   ;40
    CALL    ZERO                        ;UTI Corr 0.8s
    CALL    ZERO                        ;UTI Corr 0.4s
    CALL    ZERO                        ;UTI Corr 0.2s
    CALL    ZERO                        ;UTI Corr 0.1s
    CALL    ZERO                        ;RESERVED
    CALL    ZERO                        ;80 year yearint
    CALL    ZERO                        ;40 year
    CALL    ZERO                        ;20 year
    CALL    ONE                         ;10 year
    CALL    MARKER                      ;MARKER 5
     */

    zero();                         //reserved
    zero();                         //reserved
    zero();                         //reserved
    zero();                         //reserved
    zero();                         //reserved

    if (yearint >= 80){           //80th year
        yearint = yearint - 80;
        one();
    }else {
        zero();
    }
    if (yearint >= 40){           //40th year
        yearint = yearint - 40;
        one();
    }else {
        zero();
    }
    if (yearint >= 20){           //20th year
        yearint = yearint - 20;
        one();
    }else {
        zero();
    }
    if (yearint >= 10){           //10th year
        yearint = yearint - 10;
        one();
    }else {
        zero();
    }
    marker();                       //marker 5

/*
     *
;50
    CALL    ZERO                        ;8 year
    CALL    ONE                         ;4 year
    CALL    ONE                         ;2 year
    CALL    ZERO                        ;1 year
    CALL    ZERO                        ;RESERVED
    CALL    ZERO                        ;LEAP YEAR TRUE
    CALL    ZERO                        ;LEAP SEC WARN
    CALL    ONE                        ;DST
    CALL    ONE                        ;DST
    CALL    MARKER                        ;FRAME BIT P0
    *
*/

    if (yearint >= 8){           //8th year
        yearint = yearint - 8;
        one();
    }else {
        zero();
    }
    if (yearint >= 4){           //4th day
        yearint = yearint - 4;
        one();
    }else {
        zero();
    }
    if (yearint >= 2){           //2nd day
        yearint = yearint - 2;
        one();
    }else {
        zero();
    }
    if (yearint >= 1){           //1st day
        yearint = yearint - 1;
        one();
    }else {
        zero();
    }
    zero();                         //reserved

                                     //leap year
    if (leapint){
        one();
    }else {
        zero();
    }
    zero();
    zero();                         //leap sec warn
    zero();                         //dst??
    zero();                         //dst
    marker();                       //frame bit P0
}

void gettime(void){
    
    hourch[0] = wstr[6];      //HHMMSS
    hourch[1] = wstr[7];

    minch[0] = wstr[8];
    minch[1] = wstr[9];

    secch[0] = wstr[10];
    secch[1] = wstr[11];

    daych[0] = wstr[56];     //DAY1  DDMMYY
    daych[1] = wstr[57];     //DAY2

    monthch[0] = wstr[58];     //MONTH1
    monthch[1] = wstr[59];     //MONTH2

    yearch[0] = wstr[60];     //YEAR1
    yearch[1] = wstr[61];     //YEAR2

    uart_write(wstr);

    uart_write(" ");

   hourint = atoi(hourch);
   minuteint = atoi(minch);
   secondint= atoi(secch);

   dayint = atoi(daych);
   monthint = atoi(monthch);
   yearint = atoi(yearch);

   year4int = yearint + 2000;

   doy = get_yday(monthint, dayint, yearint);

   ltoa(doych,doy,10);

   uart_write(doych);

    uart_write("\r");
}

void lon(void){

}

void determine_mode(void){  //determine lat or long mode

}


int main(void) {

    // set up oscillator control register, using internal OSC at 8MHz.
    OSCCONbits.IRCF = 0x06; //set OSCCON IRCF bits to select OSC frequency 8MHz
    OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
    __delay_ms(70);         //lets think about life a bit before proceeding..
    __delay_ms(70);
    __delay_ms(70);

    ping = 0;
    new_rx = 0;
    isrcall = 0;

    init_io();
    serial_init();

    //RCONbits.IPEN = 0;
    PIE1bits.RC1IE = 1;         //Enable RX Interrupt
    INTCONbits.PEIE = 1;        // Enable peripheral interrupt
    INTCONbits.GIE = 1;         // enable global interrupt

    pwm_init();

    ready = 0;

    while (1) {
    __delay_ms(10);
    isrcall = 0;
    ping = 0;
    if (ready){
        LATCbits.LATC2 = 1;
    }

        if (new_rx == 1)            //got our string...
        {
            if (strstr(ctxt, "GPRMC"))  // && ready
            {
                gprmc = 1;
                strncpy((char*)wstr, (char*)ctxt, sizeof(ctxt));
                gettime();
            }
            new_rx=0;              //finished with GPS string
        }


    if (gprmc){
        sendtime();
    }
    gpgga = 0;
    gprmc = 0;
    }
}
C Electronics GPS Microcontrollers PIC

GPS on the PIC 18F26K22

I was asked to create a box that notified by LED if you were too far off a N/S or E/W course. I failed, I doubted the specified GPS accuracy/drift as my Garmin seems to stay pretty steady.. perhaps that’s some integration in the processing?

I started with one of the 18F26K22 TAUTIC development boards I bought from him off Tindie; They’re nice little breadboarding microcontrollers… I’ve recommended them before. I had purchased some GPS modules I got from eBay, so check .. they all have TTL NMEA output, some with 1PPS… and finally some tidbits I picked up out of my parts collection. Wrote some code.. tested it and found my drifting issue. After pondering the number 42 a while, I decided there was no sense in continuing because every idea I had meant something in the specification of the project was out.

I’m not going to go deep into explanation of the code since the project didn’t work but like all failures I learned something. I also expanded my knowledge of XC8 which was nice..  I’ve actively been coding in XC8 for a few months now; in some ways I miss ASM but XC8 does shorten the development time (if you don’t count how long it takes me to figure out atol() should have been used instead of atoi().

This project will be converted into a piece of test equipment of sorts. I’ve been meaning to hook a GPS receiver up to my WWVB transmitter and check it out. Of course WWVB is way more accurate … but if you can’t receive WWVB .. you’re up a creek.

 

GPS Receiver and Microcontroller outside locked.
GPS Receiver and Microcontroller outside locked.

Keep in mind this code is un-optimized, and essentially abandoned while I was building the framework. I leave it to you in case you want a jump start on grabbing some data out of a NMEA string.

/*
/*
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com / 0xEE.net / @chasxmd
 * Created on April 4th, 2014
 *
 * Target Device: TAUTIC.com dev board / 18F26K22
 *
 * Project: AG GPS Indicator
 * Using a PIC to indicate if you're staying in lat/long "groove" by GPS.
 * *** This is not complete ***
 *
 *
 * Version:
 * 0.1      First build I could prove I had GPS lock
 * 0.2      Output GPS on 232 and position set, no debounce, etc.. abandonded
 */
#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    ctxt[120], wstr[120];            //buff NMEA string, working string
    char    str[60], c, latstr, lonstr, setstr;
    char buffer[32] = "none";                        //temp dump
    volatile unsigned int     ping, isrcall, index, reading, new_rx;
    int     ready, gpgga, gprmc, mode;        //gps associated vars
    long long    position_set, position_now;
    //char    *rxdata;
    //volatile unsigned int uart_data;    // use 'volatile' qualifer as this is changed in ISR

/*
 *  Interrupt Service
 */
void interrupt ISR() {

   if (PIR1bits.RCIF){          // see if interrupt caused by incoming data

        isrcall = 0x01;
        char temp;
        temp = RCREG1;     // read the incoming data
        if(temp=='$' && new_rx==0)      //if first char of a GPS string..
        {
            index = 0;                  //reset index
            reading = 1;                //from now on go to else if
        }
        else if(reading == 1)           //in middle of GPS sentence
        {
            ctxt[index] = temp;         //load it up
            index++;                    //increment index
            ping = 1;                   //this is for debugging
            if(index > 50)              //thats more than enough data
                {
                index = 0;              //reset index
                reading = 0;            //no longer storing the string
                new_rx = 1;             //"ding"
                }
        }


   }
}


/*
 * Set up my ports
 */
void init_io(void) {

    INTCONbits.GIE = 0;         //no interruptions please

    LATA = 0x00;

    TRISAbits.TRISA0 = 0; //Onboard LED
    TRISAbits.TRISA1 = 0; //LED
    TRISAbits.TRISA2 = 0; //MCU (ON)
    TRISAbits.TRISA3 = 1; // input
    TRISAbits.TRISA4 = 1; // input
    TRISAbits.TRISA5 = 1; // input
    TRISAbits.TRISA6 = 1; // input
    TRISAbits.TRISA7 = 1; // input


    TRISBbits.TRISB0 = 0; // output
    TRISBbits.TRISB1 = 0; // output
    TRISBbits.TRISB2 = 0; // output
    TRISBbits.TRISB3 = 0; // output
    TRISBbits.TRISB4 = 0; // SCK
    TRISBbits.TRISB5 = 1; // input
    TRISBbits.TRISB6 = 0; // SCK
    TRISBbits.TRISB7 = 1; // input

    LATC = 0x00;

    TRISCbits.TRISC0 = 0; // N/W
    TRISCbits.TRISC1 = 0; // S/E
    TRISCbits.TRISC2 = 0; // GPGGA DETECT
    TRISCbits.TRISC3 = 1; // MODE SELECT (LAT/LONG)
    TRISCbits.TRISC4 = 1; // SET INPUT
    TRISCbits.TRISC5 = 1; // input
    TRISCbits.TRISC6 = 1; // input
    TRISCbits.TRISC7 = 1; // input
    
    ADCON0 = 0b00000000;        //don't need any ADC
    ADCON1 = 0b00000000;        //speed Vref=AVdd, VssRef=AVss

    ANSELA = 0x00;
    ANSELB = 0x00;
    ANSELC = 0x00;
}

void uart_xmit(unsigned int mydata_byte) {  //send a character to the UART
    while(!TXSTA1bits.TRMT);    // make sure buffer full bit is high before transmitting
    TXREG1 = mydata_byte;       // transmit data
}

void uart_write(const char *txt)            //sent a multiple characters
{
    while(*txt != 0) uart_xmit(*txt++);     //this send a string to the TX buffer
                                            //one character at a time
}


void serial_init(void)
{
    //9600 8N1

    
    TXSTA1bits.BRGH=1;       // select low speed Baud Rate 
    TXSTA1bits.TX9=0;        // select 8 data bits
    TXSTA1bits.TXEN = 1;     // enable transmit


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

    SPBRG1=25;  // here is calculated value of SPBRGH and SPBRGL
    SPBRGH1=0;

    PIR1bits.RCIF=0;        // make sure receive interrupt flag is clear

    __delay_ms(50);        // give time for voltage levels on board to settle
    uart_write("RESET");         // transmit some data for testing

}


/*
 *  Append a string with a character
 *  append(str, c);
 */


void append(char* s, char c)
{
        int len = strlen(s);
        s[len] = c;
        s[len+1] = '\0';
}



/*
 *
 * convert the decminal bits of lat or long to integer
 * send over RS-232 for review
 *
 *
 */



void lat(void){
    str[0] = wstr[17];
    str[1] = wstr[18];
    str[2] = wstr[19];
    str[3] = wstr[20];   // .21 is a decimal place
    str[4] = wstr[22];
    str[5] = wstr[23];
    str[6] = wstr[24];
    str[7] = wstr[25];

    position_now = atol(str);

    uart_write(str);
    uart_write(" ");

        //check to set position
    if (PORTCbits.RC4 && ready) {
        position_set = position_now;
    }

    if (position_set){
        if (position_now > (position_set + 3)) {
            LATCbits.LATC1 = 1;
        }   else {
            LATCbits.LATC1 = 0;
        }
        
        if (position_now < (position_set - 3)) {
            LATCbits.LATC0 = 1;
        }   else {
            LATCbits.LATC0 = 0;
        }
    }
        sprintf(buffer, "%lld", position_now);
        uart_write(buffer);
        uart_write(" ");
        sprintf(buffer, "%lld", position_set);
        uart_write(buffer);
    uart_write("\r");
}

void lon(void){
 //not started
}

void determine_mode(void){  //determine lat or long mode
 //not started   
}


int main(void) {

    // 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
    __delay_ms(50);         //lets think about life a bit before proceeding..


    ping = 0;
    new_rx = 0;
    isrcall = 0;
    
    init_io();
    serial_init();

    //RCONbits.IPEN = 0;
    PIE1bits.RC1IE = 1;         //Enable RX Interrupt
    INTCONbits.PEIE = 1;        // Enable peripheral interrupt
    INTCONbits.GIE = 1;         // enable global interrupt

    
    ready = 0;

    while (1) {
    isrcall = 0;
    ping = 0;
    if (ready){
        LATCbits.LATC2 = 1;
    }

    LATAbits.LATA2 = 1;         //startup heartbeat LED
    __delay_ms(1);
    LATAbits.LATA2 = 0;

        if (new_rx == 1)            //got our string...
        {
            if (strstr(ctxt, "GPGGA"))
            {
                gpgga = 1;
                strncpy((char*)wstr, (char*)ctxt, sizeof(ctxt));  
            }
            new_rx=0;              //finished with GPS string
        }

        if (gpgga) {
            LATAbits.LA1 = 0;
            if(ctxt[42] == '1')     //this is the 43rd bit but we didn't drop the $ into the buffer
            {                       //If "$GPGGA" NMEA message has '1' sign in the 43rd
                                    //position it means that tha GPS receiver has a position fix
                                    //
            ready = 1;              //This is my "locked" variable for future code
            LATAbits.LATA1 = 1;     //LOCK LED
            }
        }

    if (ready) {

        if (mode){

        }
        if (!mode) {
            lat();
        }
        
    }
    __delay_ms(149); //delay don't really even need to update this often
    }
}

If you noticed my posts/week is way down.. well it’s summer. This will happen every summer. My daughter is home and so my time gets happily gobbled up by her 🙂 I’ve got a huge backlog of projects.. thankfully the day job has eased off a bunch. I’m working on a 0xEE project which I probably should have been doing today.. but I needed a break. Also I recently found out our annual backpacking trip is going to be a bit more intense; I’ll have to train appropriately .. so less time on the bench. It’ll be this way until September I imagine.

C Electronics GPS Microcontrollers PIC Programming

Project: GPS2 Click with the PIC Clicker

I’m working on a GPS-based WWVB simulator and I’m pretty close to finished. I wanted to share up to the point where I add on the WWVB circuits and have it stripped down to just the PIC development board and GPS module.

This is on a Mikrelektronika PIC Clicker 18F47J53 development board and a QUECTEL L30 “GPS2” Click board (Mikroe’s module boards built for MikroeBus).

The code is below, I’ll leave the rest of the explanation in the video.

NMEA stream from this module
NMEA stream from this module

 

 

The good stuff..

/*
* File: main.c
* Author: Charles M Douvier
* Contact at: http://iradan.com / 0xEE.net / @chasxmd
* Created on April 4th, 2014
*
* Target Device: PIC Click / 18F47J53
*
* Project: Mikroe.com GPS2 test
* Using a PIC Click Dev board and GPS2 click module I am reading the NMEA
* string and declaring a "lock" by turning on the LED on RA1
*
*
* Version:
* 0.1 First build I could prove I had GPS lock
*
*
*
*/
#ifndef _XTAL_FREQ //blah blah not the right way I don't care
#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
#include
#include
#include

//config bits
#pragma config OSC=INTOSC, WDTEN=OFF, CP0=OFF //Internal OSC, No WDT and No code protect
#pragma config IESO=OFF, FCMEN=OFF
#pragma config XINST = OFF
#pragma config PLLDIV = 1 //Divide by 1
#pragma config STVREN = ON //stack overflow/underflow reset enabled
#pragma config XINST = OFF //Extended instruction set disabled
#pragma config CPUDIV = OSC1 //No CPU system clock divide

#define _XTAL_FREQ 8000000 //defined for delay

char ctxt[120]; //buff NMEA string
volatile unsigned int ping, isrcall, index, reading, new_rx;
int ready, gpgga, gprmc; //gps associated vars
//char *rxdata;
//volatile unsigned int uart_data; // use ‘volatile’ qualifer as this is changed in ISR

/*
* Interrupt Service
*/
void interrupt ISR() {

if (PIR3bits.RC2IF) // see if interrupt caused by incoming data
{
isrcall = 0x01;
char temp;
temp = RCREG2; // read the incoming data
if(temp==’$’ && new_rx==0) //if first char of a GPS string..
{
index = 0; //reset index
reading = 1; //from now on go to else if
}
else if(reading == 1) //in middle of GPS sentence
{
ctxt[index] = temp; //load it up
index++; //increment index
ping = 1; //this is for debugging
if(index > 50) //thats more than enough data
{
index = 0; //reset index
reading = 0; //no longer storing the string
new_rx = 1; //”ding”
}
}
//PIR3bits.RC2IF = 0; // clear interrupt flag
}
//RCSTA2bits.FERR = 0; //Clear errors
//RCSTA2bits.OERR = 0;
}

/*
* Set up my ports
*/
void init_io(void) {
// This code before the TRIS setup is for switching the RX2/TX2 to proper pins for the dev board
INTCONbits.GIE = 0; //no interruptions please
EECON2 = 0x55;
EECON2 = 0xAA;
PPSCONbits.IOLOCK = 0; //turn off PPS write protect

//PPS Info:
//RX2DT2R: EUSART2 Synchronous/Asynchronous Receive (RX2/DT2) to the Corresponding RPn Pin bits
//RP22 000[RX2DT2R4 RX2DT2R3 RX2DT2R2 RX2DT2R1 RX2DT2R0]
//TX2/CK2 6 EUSART2 Asynchronous Transmit/Asynchronous Clock Output
//RP23 000 [RP6R4 RP6R3 RP6R2 RP6R1 RP6R0]
//RD5 RX
//RD6 TX
//sample:
// Assign RX2 To Pin RP0
//MOVLW 0x00 MOVWF RPINR16, BANKED
// Assign TX2 To Pin RP1
//MOVLW 0x06 MOVWF RPOR1, BANKED

RPINR16 = 0x16; //Pin 22 / RD5
RPOR23 = 0x06; //Pin 23 / RD6

EECON2 = 0x55;
EECON2 = 0xAA;
PPSCONbits.IOLOCK = 1; //write protect PPS

LATA = 0x00;

TRISAbits.TRISA0 = 0; //LED1
TRISAbits.TRISA1 = 0; //LED2
TRISAbits.TRISA2 = 0; //POWER ON

TRISBbits.TRISB0 = 0; // HEADER
TRISBbits.TRISB1 = 0; // HEADER
TRISBbits.TRISB2 = 0; // RST
TRISBbits.TRISB3 = 0; // CS
TRISBbits.TRISB4 = 0; // SCK
TRISBbits.TRISB5 = 1; // MISO

LATC = 0x00;

TRISCbits.TRISC0 = 0; // HEADER/DEBUGGING PIN
TRISCbits.TRISC1 = 0; // HEADER/DEBUGGING PIN
TRISCbits.TRISC2 = 0; // HEADER
TRISCbits.TRISC3 = 0; // output
TRISCbits.TRISC6 = 1; // PWM (WAKE UP)
TRISCbits.TRISC7 = 1; // MOSI

TRISDbits.TRISD0 = 1; // SCL
TRISDbits.TRISD1 = 1; // SCA
TRISDbits.TRISD4 = 1; // INT
TRISDbits.TRISD5 = 1; // RX
TRISDbits.TRISD6 = 0; // TX

TRISEbits.TRISE0 = 1; // HEADER
TRISEbits.TRISE1 = 1; // HEADER
TRISEbits.TRISE2 = 1; // HEADER
}
void uart_xmit(unsigned int mydata_byte) {
while(!TXSTA2bits.TRMT); // make sure buffer full bit is high before transmitting
TXREG2 = mydata_byte; // transmit data
}

void serial_init(void)
{
//4800 8N1
// calculate values of SPBRGL and SPBRGH based on the desired baud rate
//- SPEN bit (RCSTA2) must be set (= 1)
//- TRIS bit for RPn2/RX2/DT2 = 1
//- TRIS bit for RPn1/TX2/CK2 = 0 for
//Asynchronous and Synchronous Master modes

PIR3bits.RC2IF=0; // make sure receive interrupt flag is clear

TXSTA2bits.BRGH=1; // select low speed Baud Rate (see baud rate calcs below)
TXSTA2bits.TX9=0; // select 8 data bits
TXSTA2bits.TXEN = 1; // enable transmit
RCSTA2bits.SPEN=1; // serial port is enabled
RCSTA2bits.RX9=0; // select 8 data bits
RCSTA2bits.CREN=1; // receive enabled
SPBRG2=104; // here is calculated value of SPBRGH and SPBRGL
SPBRGH2=0;

__delay_ms(50); // give time for voltage levels on board to settle
uart_xmit(‘R’); // transmit some data for testing
}

int main(void) {
ping = 0;
new_rx = 0;
isrcall = 0;
ready = 0;
gpgga = 0;

init_io();
serial_init();

// set up oscillator control register, using internal OSC at 8MHz.
OSCCONbits.IRCF = 0x07; //set OSCCON IRCF bits to select OSC frequency 8MHz
OSCCONbits.SCS = 0x02; //set the SCS bits to select internal oscillator block
__delay_ms(50); //lets think about life a bit before proceeding..

RCONbits.IPEN = 0;
PIE3bits.RC2IE = 1; //Enable RX2 Interrupt
INTCONbits.PEIE = 1; // Enable peripheral interrupt
INTCONbits.GIE = 1; // enable global interrupt

LATBbits.LATB2 = 0; //GPS Reset
__delay_ms(74);
LATBbits.LATB2 = 1; //pull out of reset

LATAbits.LATA0 = 1; //startup heartbeat LED
__delay_ms(50);
LATAbits.LATA0 = 0;

LATCbits.LATC1 = 1; //proves my ISR RC1 output works
__delay_us(35);
LATCbits.LATC1 = 0;

LATCbits.LATC2 = 1; //proves my New Byte RC2 output works
__delay_us(35);
LATCbits.LATC2 = 0;

ADCON0 = 0b00000000; //don’t need any ADC
ADCON1 = 0b00000000; //speed Vref=AVdd, VssRef=AVss

/* Disable for the time being
* This is TIMER code, untested.
INTCONbits.TMR0IE = 0;
TMR0=0;
T0CONbits.T08BIT = 1;
T0CONbits.T0CS = 0;
T0CONbits.PSA = 0;
T0CONbits.T0PS = 0x04;
INTCONbits.TMR0IF = 0;
T0CONbits.TMR0ON = 1;
*/

while (!PORTCbits.RC6) { //in warmup?
LATAbits.LA2 = 0;
__delay_ms(3);
LATAbits.LA2 = 1; //Turn GPS On
__delay_ms(10);
}

LATAbits.LA2 = 1; //Ensure GPS is On

while (1) {
isrcall = 0;
ping = 0;
gpgga = 0;

if (RCSTA2bits.OERR)
{
RCSTA2bits.CREN=0; //DS39964B-page 347
__delay_us(2);
RCSTA2bits.CREN=1; //Overrun error (can be cleared by clearing bit, CREN)
}

if (new_rx == 1) //got our string…
{
if (strstr(ctxt, “GPGGA”))
{
gpgga = 1;
}
new_rx=0; //finished with GPS string
}
// uart_xmit(‘x’); // this was a test, it works.
if (isrcall) { //testing bits
LATAbits.LATA0 = 1; // $ Detect!
__delay_us(10); //
LATAbits.LATA0 = 0;
}

if (gpgga) {
LATCbits.LATC2 = 1; //GPGGA detect
__delay_us(1); //
LATCbits.LATC2 = 0;
if(ctxt[42] == ‘1’) //this is the 43rd bit but we didn’t drop the $ into the buffer
{ //If “$GPGGA” NMEA message has ‘1’ sign in the 43rd
//position it means that tha GPS receiver has a position fix
//
ready = 1; //This is my “locked” variable for future code
LATAbits.LATA1 = 1; //LOCK LED
LATCbits.LATC1 = 1; //DEBUGGING
__delay_us(1); //
LATCbits.LATC1 = 0;
}
else if (ctxt[42] != ‘1’) //this is the 43rd bit but we didn’t drop the $ into the buffer
{
ready = 0; //
LATAbits.LATA1 = 0; //LOCK LED
}
}
}
}

 

PIC

PIC-based Dumb Terminal

Okay before you start; I know.. I know.. Because I’m shooting for a VT-100-ish emulation my project is technically an “intelligent” terminal but not in a sense that it runs an embedded OS/IP-based, but in the sense that it decodes ANSI ESC codes.. it’s still a freakn’ dumb terminal in my book.

I’m just about finished with the PC board for this and in the photo below is my proof of concept. All the key’s don’t work yet as I don’t have enough IO without some encoding; the keyboard is an old row/column keyboard  I scored off eBay.  Because I want to stick with a smaller PIC I’m going to use 74HC series logic to accomplish this, but I was considering switching to CPLD in a version 2.

My PIC 18F14K22  Dumb Terminal
My PIC 18F14K22 Dumb Terminal

Target Features: Baud change (to terminal) via a config screen on the fly (likely limited it to 1200, 2400, 4800, 9600, 115k). Switching the “retro” color from green, amber, or if you’re boring/have been playing too much telehack, white. Half/Full duplex… no color decoding (or VT220 emulation) but perhaps in the future.

Once my PC board is finished and back from the fab I’ll finish the code and make a proper post… the code isn’t presentable besides at least being decently commented. When will you see my next post on this?….. read on.

I have collaborated with Francesco of GarageTech to create a new site. Francesco had some really good ideas, so I pushed 0xEE.net off to its own site. We’re just ironing out a few details and starting our first articles… look for some good stuff soon! 0xEE.net will be ALL PIC-based… I’m pushing all my PIC based projects over there. I’ll co-post and maintain my blog for all non-PIC based stuff .. but if I’m dropping code and it’s not snippets, I’ll post a generic tidbit and a description here and link in over to 0xEE.

If you’re curious: it’s also not lost on me that you could use RealTerm or PuTTY on your PC… I just think this is nifty if you have the bench space.

Analog C Electronics Microcontrollers PIC Programming Steppers

Analog Stepper Gauge Friday night fun

Took a while to clear off the bench tonight. I got a little happy with the ORDER button last week through weekend. I’ve filled two more parts storage containers and luckily one of my new packages with another parts storage container from Amazon; I couldn’t find any locally, I hope they’re still making them.

The last thing I got to was this “Gauge Stepper Breakout” I got off The Rengineer (The Renaissance Engineer)’s Tindie store. Adam took this very nice stepper motor, put a gauge needle on it (which I believe he 3D printed, or at least it looks like it) and some nice diodes for voltage protection. What’s great about this board is you can drive it right from your microcontroller. I’m sure I don’t need to tell you how weird that is.. I worry about big LEDs.. but here this stepper was happy as a clam being powered by my PIC. I still might consider at least buffering this if I was to place it in a permanent  circuit.

Adam’s provides links to some Arduino libraries but he was saved me a ton of time and just happened to have some PIC sample code! Not before I had about 75% of a ECCP program completed. (I’ll link some of that code if you’re looking for an example of Enhanced PWM output). Once I had Adam’s code I ported it to the TAUTIC 18F26K22 dev board because I already had one on the breadboard. That was mostly changes in the TMR0 (timer 0) code. The only thing of note is check out my IO comments for wiring. You have to cook up 5V/GND to this board then I drove RC3 –> A1 RC2 — A2, RC1 –> B1 and RC0 –> B2. When I had it flipped around the stepper moved in the opposite direction of how I wanted.

A little video of the code in action:

This code is a little sloppy but I just testing this out and it did the trick:


/*
 * File:   main.c
 * Author: Charles M Douvier  Contact at: http://iradan.com
 * Core Driver Code by Adam F. of http://www.therengineer.com/
 *
 * Created on April 4th, 2014
 *
 * Target Device:
 * TAUTIC PIC 18F26K22 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
#define MIN_STEPS_LEFT 23
#define MAX_ACCEL_INDEX 6
#define MAX_STEP 945 /* motor can move 945 steps from stop to stop*/

    int     an8_value, an9_value;          //value for a/d
    char    buf[10];            //buff for iota
    long int    fvar;           //long for format math
    long int    tens;           //left of decm
    long int    decm;           //decimal places
    int     tempi;              //to add leadign zeros..
    int     vtxdata;             //volts int for TX
    int     itxdata;

    unsigned short defaultAccelTable[][2] =
{
  {   1750, 3},
  {   1149, 3},
  {  926,   3},
  {  794,   3},
  {  709,   3},
  {  666,   4},
  {  /*629*/450,   4},
};
unsigned int currentStep;
unsigned char currentState;
unsigned char stateMap[] = {0x09, 0x01, 0x07, 0x06, 0x0E, 0x08};
unsigned char serialBuffer[10];
unsigned char serialByteCount;
 static const unsigned char stateCount = 6;

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

    if (PIR1bits.RCIF)          // see if interrupt caused by incoming data
    {
        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
    TRISAbits.TRISA6 = 0; // output
    TRISAbits.TRISA7 = 0; // output

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

    LATAbits.LATA0 = 0;
    PORTAbits.RA0 = 0;

    TRISBbits.TRISB1 = 0;   //P1C output
    TRISBbits.TRISB2 = 0;  // P1B output
    TRISBbits.TRISB3 = 1;  // AN9    speed control 0-5V
    TRISBbits.TRISB4 = 0;  // P1D output
    TRISBbits.TRISB5 = 1; // RB5 = nc
    TRISBbits.TRISB6 = 0; // RB6 = nc
    TRISBbits.TRISB7 = 0; // RB7 = nc

    ANSELB = 0b00001000;     //RB3, AN9

    TRISCbits.TRISC0 = 0; // output to B2 .. reversed to stoke the right direction
    TRISCbits.TRISC1 = 0; // output to B1
    TRISCbits.TRISC2 = 0; // output to A2
    TRISCbits.TRISC3 = 0; // output to A1
    TRISCbits.TRISC4 = 0; // output
    TRISCbits.TRISC5 = 0; // output
    TRISCbits.TRISC6 = 0; // output
    TRISCbits.TRISC7 = 0; // output
    ANSELC = 0x00; // all port B pins are digital I/O
}

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)
{
    //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)

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

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

    SPBRG1=25;  // 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

    uart_xmit('R');         // transmit some data
}

// All this motor and timer code is from Adam with very minor changes to fit the processor

void t0Delay(unsigned int usec)
{
    unsigned int t0ticks; //16 microsecond timer0 ticks
    unsigned char t0Preload;
    if(usec<16)
    {
        t0ticks=1;
    }
    else
    {
        t0ticks = usec/16;
    }
    t0Preload = 0xFF - t0ticks;
    INTCONbits.TMR0IF=0; //clear the flag
    TMR0 = t0Preload;
    while(INTCONbits.TMR0IF==0)
    {
        ;
    }
}

void zeroMotor()
{
    unsigned int i;
    for (i=0; i < MAX_STEP; i++)
    {
        LATC=stateMap[currentState];
        currentState = (currentState + 5) % stateCount;
        t0Delay(1900);  //2200 in datasheet
    }
    //now the motor is zeroed, reset our state variables.
    currentStep = 0;
    currentState = 0;
    LATC=0; //turn off coils
}

void moveMotor(unsigned int targetStep)
{
    unsigned int dir;
    unsigned int curDelay;
    unsigned char speedIndex=0;
    unsigned char stepsAtThisSpeed=0;
    unsigned int stepsLeft;
    if(currentStep<targetStep)     {         dir = 1;         stepsLeft = targetStep-currentStep;     }     else     {         dir = -1;         stepsLeft = currentStep - targetStep;     }     while(stepsLeft>0)
    {
        if(stepsLeft<=MIN_STEPS_LEFT)         {             //decellerating             if(stepsAtThisSpeed==0)             {                 if(speedIndex>0)
                    speedIndex--;
                curDelay=defaultAccelTable[speedIndex][0];
                stepsAtThisSpeed=defaultAccelTable[speedIndex][1];
            }
        }
        else
        {

            //accellerating or steady state
            if(stepsAtThisSpeed==0)
            {
                if(speedIndex<MAX_ACCEL_INDEX)                 {                     speedIndex++;                     curDelay=defaultAccelTable[speedIndex][0];                     stepsAtThisSpeed=defaultAccelTable[speedIndex][1];                 }                 //else we're at steady state - do nothing.             }         }         //write step         LATC=stateMap[currentState];         if(dir==1)         {             currentState = (currentState + 1) % stateCount;         }         else         {             currentState = (currentState + 5) % stateCount;         }         t0Delay(curDelay);         if(stepsAtThisSpeed>0)
        {
            stepsAtThisSpeed--;
        }
        stepsLeft--;
        currentStep+=dir;
    }
}

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

    ADCON0 = 0b00100101;                            //select AN9 and enable
    ADCON1 = 0b00000000;                  //speed Vref=AVdd, VssRef=AVss
    ADCON2 = 0b00111011;                //ledft justified, 20RAD, FRC

    INTCONbits.TMR0IE = 0;

    TMR0=0;

    T0CONbits.T08BIT = 1;
    T0CONbits.T0CS = 0;
    T0CONbits.PSA = 0;
    T0CONbits.T0PS = 0x04;
    INTCONbits.TMR0IF = 0;

        T0CONbits.TMR0ON = 1;

    __delay_us(5);

    currentStep = 0;
    currentState = 0;

    zeroMotor();         
    __delay_ms(149);        //this could be less messy
    __delay_ms(149);
    __delay_ms(149);
    __delay_ms(149);
    __delay_ms(149);
    __delay_ms(149);
    moveMotor(20);
    __delay_ms(149);
    __delay_ms(149);
    __delay_ms(149);
    __delay_ms(149);
    __delay_ms(149);
    __delay_ms(149);

    moveMotor(940);
    moveMotor(5);

    while (1) {

        //PORTAbits.RA0 = 1;      //heart beat
        //__delay_ms(50);
        //PORTAbits.RA0 = 0;
        //__delay_ms(50);

        ADCON0 = 0b00100101;    //select AN9 and enable
        __delay_us(5);
        GO = 1;
        while (GO) continue;    //wait for conversion
        an9_value = ADRESH;     //AN9 value

        fvar = an9_value;
        fvar = fvar * 10749;    //calibration.. change to meet your needs
        fvar = fvar / 256;
        tens = fvar / 100;
        //tens = tens % 10;
        decm = fvar % 100;
        vtxdata = fvar / 20;
        uart_xmit(vtxdata);    // -->RS232

        moveMotor(vtxdata);
        //moveMotor(5); //from sample code
    }
    return (EXIT_SUCCESS);
}

Some ECCP ( Enchanced PWM ) code written for the PIC 18F26K22 code I wrote following the screen shot of the output:

Deadband on Enhance PWM mode output on PIC
Deadband on Enhance PWM mode output on PIC

void pwm_init(){

//    CCPR1L = 0x120;
    CCPR1Lbits.CCPR1L = 0xFE;
    PR2 = 0xFE;
    CCPTMRS0bits.C1TSEL = 0;     //CCP TMR2 Selection
    CCP1CONbits.P1M = 0x02;     //half bridge
    CCP1CONbits.DC1B = 0x00;
    PWM1CONbits.P1RSEN = 0;
    PWM1CONbits.P1DC = 0x1F;    //dead band delay
    ECCP1ASbits.CCP1AS = 0x00;
    ECCP1ASbits.CCP1ASE = 0;    //Auto-shutdown off
    CCP1CONbits.CCP1M = 0x0C;
    PSTR1CONbits.STR1A = 1;
    PSTR1CONbits.STR1B = 1;

    T2CONbits.T2CKPS = 1;
    T2CONbits.TMR2ON = 1;

}

//.. now dump something smaller than 127 into CCPR1Lbits.CCPR1L
//to set the pulse width
C Electronics Microcontrollers MOSFETs Motors PIC Programming

PIC 18F26K22 PWM+A/D with MOSFET, the start of an eScooter?

I’m considering building an electric scooter; considering it probably putting it lightly.. I have almost everything I need for it.  Interested? Why type so much when you can just watch my proof of concept!

If you’re following along and want to use the same hardware (warning totally untested… ):
Controller found on the TAUTC Tindie Store
Search on eBay for “24VDC scooter motor” ..
..and pick up a MOSFET that’ll pull off a couple 20 amps and saturates at or below 5VDC.
…what am I talking about? Try google or read this.

To the important stuff, the code:


/* 
 * File:   main.c
 * Author: Charles M Douvier
 * Contact at: http://iradan.com
 *
 * Created on March 27, 2014, 4:12 PM
 *
 * Target Device:
 * 18F26K22 on TAUTIC Dev Board
 *
 * Project:
 * Electric Scooter
 *.. a real hack job, comment, delete garbage, etc.
 *
 * Version:
 * 0.1
 *
 */
#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

//clean up on isle 2.. 

    int     an9_value;          //value for a/d
    char    buf[10];            //buff for iota
    long int    fvar;           //long for format math
    long int    tens;           //left of decm
    long int    decm;           //decimal places
    int     tempi;              //to add leadign zeros..
    int     vtxdata;             //volts int for TX
    int     itxdata;

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

    if (PIR1bits.RCIF)          // see if interrupt caused by incoming data
    {
        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
    TRISAbits.TRISA6 = 0; // output
    TRISAbits.TRISA7 = 0; // output

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

    TRISBbits.TRISB3 = 1; // AN9
    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; // 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 C pins are digital I/O
}

void pwm_init(){

//         PSTR1CONbits.STR1A
//hackhackhackhack... TODO

//    CCPR1L = 0x120;
    CCPR1Lbits.CCPR1L = 0xFE;
    PR2 = 0xFE;
    CCPTMRS0bits.C1TSEL = 0;     //CCP TMR2 Selection
    CCP1CONbits.P1M = 0x00;
    CCP1CONbits.DC1B = 0x00;
    PWM1CONbits.P1RSEN = 0;
    T2CONbits.T2CKPS = 1;  //1:2 Prescale
    T2CONbits.TMR2ON = 1;  //timer 2 go

    CCP1CON = 0x0C;       //PWM (CCP)1 ON

}

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)
{
    //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)

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

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

    SPBRG1=25;  // 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

    uart_xmit('R');         // transmit some data "restart" notification
}

int main(void) {

    init_io();
    serial_init();
    LATCbits.LATC2 = 0;
    pwm_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

    ADCON0 = 0b00100101;                            //select AN9 and enable
    ADCON1 = 0b00000000;                  //speed Vref=AVdd, VssRef=AVss
    ADCON2 = 0b00111011;                //ledft justified, 20RAD, FRC
    __delay_us(5);

//loop

    while (1) {

        PORTAbits.RA0 = 1; //blinky i'm alive.
        __delay_ms(140);
        PORTAbits.RA0 = 0;
        __delay_ms(140);

            GO = 1;
    while (GO) continue;              //wait for conversion
    an9_value = ADRESH;               //AN9 value

        fvar = an9_value; //this is hacked off another project but works
        fvar = fvar * 10749;        //calibration
        fvar = fvar / 256;
        tens = fvar / 100;
        //tens = tens % 10;
        decm = fvar % 100;
        vtxdata = fvar / 43; //because I'm lazy... I'll change this later.
        uart_xmit(vtxdata);
        CCPR1Lbits.CCPR1L = vtxdata;

    }
    return (EXIT_SUCCESS);
}

Electronics Microcontrollers PIC Weather

A tiny Si4707 WX Radio Project Update

I pulled out the weather radio project today to see what I could get done in a few hours. I was pretty close on finishing off the hardware but I fell a little short right at the end. I found I had forgotten to buy something to convert the regulated 5V to 3.3V for the radio (and PIC since they’re tied together on I2C). I ran into a few issues I totally spaced:1.  The Si4707 requires a reset after power up .. it ignores I2C if you don’t.

2. Pull-ups.. duh, not only on I2C which I had, but don’t forget the Si4707 reset (oops).

I ended up buying a couple random Digikey parts at the ham radio convention and guess what? 10@ MCP1802T-3002I/OT (300mA 3.0V LDO) ..they are SOT23-5 and I had JUST gotten 10 break-out boards in the mail so I ended up having a 5V3.3V converter (close enough anyways, as the PIC and Si4707 work down to 2.7V).. soldered it up but I didn’t have time to pop it in. I used the Weller but I think I’m going to solder another one with the new hot air gun I got this week. I ordered one of those cheap 858D rework stations; I don’t plan on using it too much so hopefully it’ll do the trick. I also got a bunch of SMD protoboards.. so I get to practice reflow this week.

Tomorrow I should be able to finish up the radio and then it’s all software.

Si4707 WX Radio Build - 11MAR14

The photo isn’t the most exciting workbench shot but you can see that 858D in the back corner. The WX radio is the black box right up close to the left.