NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Everything Else » Serial interface with basic C program

August 24, 2012
by scootergarrett
scootergarrett's Avatar

Very similar to the scale project I would like my micro controller to send data to my PC (windows XP) and then read it with a C program (not C++). All I have been doing is using the “Hyper terminal” then copying the data after. So are there some C commands/ libraries that allow pulling data from the serial port? I think all I will need is the necessary commands I think I could figure out the timing. I guess it would be nice to send data back to the micro controller from a C program Thanks Garrett the mechanical engineer

August 24, 2012
by scootergarrett
scootergarrett's Avatar

OK I got it http://www.robbayer.com/files/serial-win.pdf was very useful more later when I get some real results

August 27, 2012
by pcbolt
pcbolt's Avatar

Garret -

I wrote some C code to work from the Windows command prompt HERE. It has the basics for interfacing with the serial ports through windows API calls. Let me know if it is helpful or if you have questions. There is also a thread HERE that shows how to use Comm ports directly through Excel.

September 16, 2012
by scootergarrett
scootergarrett's Avatar

I got some awesome code working (needs a little bit more work to be robust) so now I can plot temperature on my screen live using Code blocks and gnuplot both free. Now I’m going to work on storing data on a micro SD card Breakout Board for microSD Transflash so I can take room temperature data all day then put in my computer and graph it. Thanks Nerd kit community. If anyone has questions about the live plotting gpt63@wildcats.unh.edu I would love to give back after getting lots of help

September 16, 2012
by Ralphxyz
Ralphxyz's Avatar

scootergarrett , that would make great article in the Nerdkit community Librry.

Ralph

September 16, 2012
by scootergarrett
scootergarrett's Avatar

Ya I would like to get the code working a little better, after running for a little while the timing works out so the C program grabs data half way through a data set at thinks the temperature is 6.78 when it was 76.78. I need to get the timing synced between the C and micro controller. gnuplot stuff always take different amount of time along with the micro controller so I’m thinking it’s time for real time interrupts and some pseudo interrupts in the C code. Should be fun, if I didn't have to do so much school work I could actually get something done -Garrett

January 07, 2013
by scootergarrett
scootergarrett's Avatar

OK I finally got my head around Serial interface with C. And in the mean time I got some plotting tools working with C, I’m worried that when I graduate and don’t have MatLab I will be useless. Anyways here is the microprocessor code

// Analog Sensor.c
// for NerdKits with ATmega328p
// scootergarrett
// Outputs an int 0000-1024 for most recient A/D conversion from port PC0

// PIN DEFINITIONS:
// PC0 -- Analog input

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/io_328p.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

volatile uint16_t s ,S;

uint16_t adc_read()
{
    while(ADCSRA&(1<<ADSC)){}
    ADCSRA|=(1<<ADSC);
    return (ADCL+(ADCH<<8));
}

int main()
{
    // start up the Analog to Digital Converter
    ADMUX = 0;
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
    ADCSRA |= (1<<ADSC);

    // start up the serial port
    uart_init();
    FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
    stdin = stdout = &uart_stream;

    //Interrupt setup stuff
    TCCR1B |= (1<<CS12) | (1<<CS10) | (1<<WGM12);
    TIMSK1 |= (1<<OCIE1A);
    OCR1A = 720;             //Interrupt timming 14400 is 1 second
    sei();

    while(1)
    {
        s = adc_read();
    }

    return 0;
}

ISR(TIMER1_COMPA_vect)
{
    FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);

    //Get the A/D data
    S = s;

    // write message to seri port (This always sends 5 characters, 4 numbers then 'A')//
    fprintf_P(&uart_stream, PSTR("%04iA"), S);

    return;
}

And the C program (running CodeBlocks)

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <Windows.h>
#include <conio.h>

#include <C:\gnuplot and C\plot.h>  // My work in progress plotting stuff

#define n 5  // amount of characters sent through the serial port each time

int main()
{
    HANDLE hSerial;
    DCB dcbSerialParams = {0};
    COMMTIMEOUTS timeouts = {0};
    char szBuff[n+1] = {0};
    DWORD dwBytesRead = 1;

    int k=0, Num = 0;

{   // Serial Communication stuff including timming numbers //
    hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if(hSerial==INVALID_HANDLE_VALUE)
    {
        if(GetLastError()==ERROR_FILE_NOT_FOUND)
            printf("ERROR 'File Not found'\n");
        printf("ERROR Other\n");
    }

    dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
    if (!GetCommState(hSerial, &dcbSerialParams))
        printf("error getting state\n");

    dcbSerialParams.BaudRate=CBR_115200;
    dcbSerialParams.ByteSize=8;
    dcbSerialParams.StopBits=ONESTOPBIT;
    dcbSerialParams.Parity=NOPARITY;

    if(!SetCommState(hSerial, &dcbSerialParams))
        printf("error setting serial port state\n");

    timeouts.ReadIntervalTimeout=MAXWORD;
    timeouts.ReadTotalTimeoutConstant=10000;
    timeouts.ReadTotalTimeoutMultiplier=0;
    timeouts.WriteTotalTimeoutConstant=500;
    timeouts.WriteTotalTimeoutMultiplier=100;
    if(!SetCommTimeouts(hSerial, &timeouts))
        printf("error occureed setting timeouts\n");

}

    // Reads one character at a time untill a 'A' is read which is my end of transmision //
    do
    {
        ReadFile(hSerial, szBuff, 1, &dwBytesRead, NULL);
    }while(szBuff[0] != 'A');

    // This will run through grabbing the serial data //
    for(k=0;k<200;k++)
    {
        ReadFile(hSerial, szBuff, n, &dwBytesRead, NULL);   // Read serial data
        szBuff[4] = '\0';                                   // Replace 'A' with NULL
        Num = atoi(szBuff);                                 // Convert string number into int

        // From here Num is the A/D conversion so do what ever you want. I choose to live plot it but that's a whole other story//

        PlotCon((double)k, (double)Num, "mp*10000FF", "Plot", "Time", "A/D conv");
    }

    // Plotting stuff //
    PlotConEnd();
    ClosePlots();

    CloseHandle(hSerial);
    return 0;
}

screen shot of what was a 'live' graph of me turning a potentiometer (sorry for the wide post)

January 08, 2013
by Ralphxyz
Ralphxyz's Avatar

scootergarrett that is great. You'll find there are other IDE's you will come to "love"!

This is one project I really want to test, thank you so much!

Ralph

January 08, 2013
by scootergarrett
scootergarrett's Avatar

I have it also working reading in 3 A/D values from a 3 axis accelerometer and plotting them, not live YET. gnuplot can get bogged down with too many numbers to fast.

March 13, 2013
by scootergarrett
scootergarrett's Avatar

My serial communication code has gotten even better. In the C code above I was taking A/D 10 bit values converting them to ASCII, sending a string then receiving and parsing out the data. I realized that is a big waste of time. So here is a sending and receiving just 16 bit numbers. The most useful part of this would probably be the uart_write16(uint16_t x) function to send a raw 16 bit number MC code:

// Analog Sensor.c
// for NerdKits with ATmega328p
// scootergarrett
// Outputs a 'short' for most recient A/D conversion from port PC0-PC2
    // with all numbers one after another with 0xFF at the end because that
    // is a number that that the AD converter will never output

// PIN DEFINITIONS:
// PC0-PC2 -- Analog inputs

#define F_CPU 14745600

#include <stdio.h>
#include <math.h>

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <inttypes.h>

#include "../libnerdkits/io_328p.h"
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

volatile uint8_t OverFlowFlag = 0;

uint16_t adc_read(int port)
{
    ADMUX = port;
    ADCSRA |= (1<<ADSC);
    while(ADCSRA&(1<<ADSC));
    return (ADCL+(ADCH<<8));
}

void uart_write16(uint16_t x)
{
    // wait for empty receive buffer //
    while ((UCSR0A & (1<<UDRE0))==0);
    // send LSBF //
    UDR0 = x;

    // wait for empty receive buffer //
    while ((UCSR0A & (1<<UDRE0))==0);
    // send MSBL //
    UDR0 = *((char*)&x + 1);

    return;
}

int main()
{
    uint16_t ax, ay, az;            // Each axes acceleration

    // start up the Analog to Digital Converter
    ADMUX = 0;
    ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);
    ADCSRA |= (1<<ADSC);

    // start up the serial port
    uart_init();
    FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
    stdin = stdout = &uart_stream;

    //Interrupt setup stuff
    TCCR1B |= (1<<CS12) | (1<<CS10) | (1<<WGM12);
    TIMSK1 |= (1<<OCIE1A);
    OCR1A = 18;             //Interrupt timming 14400 is 1 second   36 is 400Hz
    sei();

    while(1)
    {
        az = adc_read(0);
        ay = adc_read(1);
        ax = adc_read(2);

        if(OverFlowFlag)        // Send that most recient A/D values
        {
            uart_write16(ax);
            uart_write16(ay);
            uart_write16(az);

            uart_write(0xf);
            uart_write(0xf);

            OverFlowFlag = 0;
        }
    }

    return 0;
}

ISR(TIMER1_COMPA_vect)
{
    OverFlowFlag = 1;
    return;
}

Computer C code:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <Windows.h>
#include <conio.h>

#define n 8             // Number of characters sent per communication 3 16 bit = 6 then error check 0xFF
#define L 1000          // Size of data arrays

int main()
{
    HANDLE hSerial;
    DCB dcbSerialParams = {0};
    COMMTIMEOUTS timeouts = {0};

    char szBuff[n] = {0};
    DWORD dwBytesRead = 1;

    int k=0;
    short t[L] = {0}, AX[L] = {0}, AY[L] = {0}, AZ[L] = {0};
    short *PT;

    PT = (short*)&szBuff;

    /// Serial Communication stuff including timming numbers ///
{
    hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
    if(hSerial==INVALID_HANDLE_VALUE)
    {
        if(GetLastError()==ERROR_FILE_NOT_FOUND)
            printf("ERROR creating file\n");
        printf("ERROR creating file Other\n");
        return 1;
    }

    dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
    if (!GetCommState(hSerial, &dcbSerialParams))
        printf("error getting state\n");

    dcbSerialParams.BaudRate=CBR_115200;
    dcbSerialParams.ByteSize=8;
    dcbSerialParams.StopBits=ONESTOPBIT;
    dcbSerialParams.Parity=NOPARITY;

    if(!SetCommState(hSerial, &dcbSerialParams))
        printf("error setting serial port state\n");

    timeouts.ReadIntervalTimeout=MAXWORD;
    timeouts.ReadTotalTimeoutConstant=10000;
    timeouts.ReadTotalTimeoutMultiplier=0;
    timeouts.WriteTotalTimeoutConstant=500;
    timeouts.WriteTotalTimeoutMultiplier=100;
    if(!SetCommTimeouts(hSerial, &timeouts))
        printf("error occureed setting timeouts\n");

}

    // reads one char at a time untill there is an 0xFF (end of string) then moves on //
    printf("\nSynchronizing\n");
    do
    {
        ReadFile(hSerial, szBuff, 2, &dwBytesRead, NULL);
    }while(szBuff[0] != 0xF && szBuff[1] != 0xF);

    // Loop of receving data //
    while(1)
    {
        // read the serial data and puts in string 'szBuff' //
        ReadFile(hSerial, szBuff, n, &dwBytesRead, NULL);

        // Error checking has to be an 0xFF at end of string or something is wrong //
        if(szBuff[6] != 0xF && szBuff[7] != 0xF)
        {
            printf("\n\nCommunication breakdown ERRER :%s \tk: %i\tBR: %i\n\n", szBuff, k, dwBytesRead);
            break;
        }

        // Parce the data //
        AX[k] = *PT;
        AY[k] = *(PT + 1);
        AZ[k] = *(PT + 2);

        // This is where I do my plotting but you can do what ever you want
        // with AX, AY, and AZ filling up with the A/D values from the MC //

        k++;

        // continuously loop around //
        if(k == L)
            k=0;

        // check if a key has been hit and exit if so //
        if(kbhit())
        {
            getchar();
            break;
        }
    }

    CloseHandle(hSerial);
    return 0;
}

I even have some code to live show me the discrete Fourier transform plot of my A/D signal. But I need some FFT code, currently I’m using my own slow Fourier transform. 3 axes accelerometer live plot example. if anyone wants the plotting stuff I have been working on I I'm trying to make the syntax as close to Matlab as possible but that is imposable but you can certainly try. I guess where I would like to go from here would be to live plot data from my car using the OBD project but I'm having problems finding what OBD protocol my car uses (2001 kia sephia). But I do have some opto isolators.

March 16, 2013
by esoderberg
esoderberg's Avatar

ScooterG,

UDR0 = *((char*)&x + 1);

That's a nice little snippet of code. I'll have to incorporate that into my UART and SPI utilities for 16 bit transfers. It only saves me one line of code and a bit shift, but it's pretty slick for someone like me who's still warming up to pointers.

Once you get your real time plotting worked to your satisfaction, I'd appreciate it if you'd post your final product, I'm sure many of us could put it to use.

Post a Reply

Please log in to post a reply.

Did you know that the printf format string "%.3f" will show three digits after the decimal point? Learn more...