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.

Microcontroller Programming » Reading from uart stream

October 12, 2012
by sask55
sask55's Avatar

I have set up a serial communication link between the Nerd kit and a PC. I am using the uart functions available on the micro and the serial com functions available in C#. As a test and to allow my to get some experience handling data flow in both directions I decided I would to send a constantly changing stream of data each way. The actual data content is not important it is the general concepts that I am trying to get a handle on. I am using the date/time string because the word length and number of digest change form time to time and I wanted to set up a system on the MCU that could deal with that and still display properly.

The Nerdkit hardware is the same as the tempsensor set up in the Nerdkit guide. With the USB serial cable included.

I have the C# code running on the PC sends a custom formatted date/time string out on the serial com each second. The Nerd Kit receives that data and returns to the PC the current room temperature as read by the temp sensor. The idea was to simply display the PC’s current calendar/clock data on the LCD and display the current room temp on the PC. For the most part this setup is working well.

I am getting a temperature read out on the PC that is updated each second as expected. I am also getting most of the date/time data displayed on the LCD as expected. The time count is changing each second. If I reset the system date/time data on the PC it is immediately reflected on the LCD.

There is glitch that I have not been able to track down. The day of the week does not show correctly on the LCD screen. The date, time and temperature all show up as expected. The weekday line on the LCD does not remain constant it often show part of the word usually at least the first three letters. It changes often. It might show”Fry” for a few seconds then “Fray” for a few seconds then something else and so on. I believe I am using the same system to load and display the wkDay variable as the others yet it is displaying something totally different then the other variables. As I said the other lines on the LCD are working perfectly fine. I must be missing something I can’t see a difference with the wkDay variable.

I was thinking that the date stream may have been corrupted in the PC before it was sent out on the serial. In order to verify what was being sent out on the serial by the c# module I used a second USB connector and sent the C# output to PuTTY . That confirmed the data sent by the C# module was good. There is a full word being sent each second for the day of the week as expected.

There must be something in my code on the MCU. Why can’t I break out and display the weekday word just as I am doing with the time and the date?

Note-The screen shot where not taken at the same time therefore the times do not match up. This is the section of my code that deals with filling and displaying the time/date variables after the ( char was read by the uart.

 //reset wkDay
        i_next = 0;
        memset(wkDay,0x00,sizeof(wkDay));

        incoming = uart_read();
        //read char and fill WkDay untill # is read on serial 
        while  (incoming != 35) {

        wkDay[i_next++] = incoming;

        incoming = uart_read();
        }

        incoming = uart_read();
        // reset Date
        i_next = 0;
        memset(date,0x00,sizeof(date));
     //read char fill Date untill  $ is read 
        while  (incoming != 36)  {

        date[i_next++] = incoming;

        incoming = uart_read();
        }

        //reset time
        incoming = uart_read();
        i_next = 0;
        memset(time,0x00,sizeof(time));
        // read char fill time untill ) char is read
        while  (incoming != 41)  {

        time[i_next++] = incoming;

        incoming = uart_read();

        }

// print date and time to lcd

  lcd_line_one();
    fprintf_P(&lcd_stream, PSTR("%s  "),time);

  lcd_line_two();
    fprintf_P(&lcd_stream, PSTR("%s  "),wkDay);
  lcd_line_three();
    fprintf_P(&lcd_stream, PSTR("%s  "),date);

LCD display with currupted weekday in line two
LCD screen

Screen shot of Putty rading C# output Putty screen shot

Screen shot of PC desktop GUI for C# module show time and temp data. C# on PC

October 13, 2012
by pcbolt
pcbolt's Avatar

sask55 -

I had something similar happen to me using "uart_read()". I tried a bunch of things to get it to work, but since I didn't like the way "uart_read()" blocks execution I went on to use UART_RX interrupt instead. Bang...problem solved! Here is the interrupt code I used...

ISR(USART_RX_vect){ // this is the symbol from AVR compiler docs (interrupt.h)

//  UDR0 is data register for uart - must read it during interrupt
//  See page 185 of datasheet for chip
//  Simply read uart rx reg and copy to buffer, update pointer

    rx_buf[rx_in_idx] = UDR0;
    rx_in_idx++;
    if (rx_in_idx >= MAX_RX) rx_in_idx = 0; // make buffer "circular"
    return;
}

Both the buffer array and the pointer are global and I have a similar pointer in the main section of the code called "rx_out_idx". In the main code, I just test to see if the pointers are not equal and if not, I know there is data to read. So all I have to do is read the buffer, update "rx_out_idx" and process the character. Works great.

PS - You can combine lines 7 and 8 into;

rx_buf[rx_in_buf++] = UDR0;

since it increments the index variable after the operation. Also, I've never needed a MAX_RX size greater than 32, you're results may differ.

October 14, 2012
by sask55
sask55's Avatar

Thanks pcbolt

That sounds like great solution for my project. I like the idea of not tying up the processor wait or testing for UART RX to arrive. The buffer is perfect for what I am ultimately attempting to do. I have not had any luck getting the uart interrupt system to work. I think I have tracked down the root of my problem. I believe the uart interrupt is triggering. I do receive data that is echoed back to the PC from the interrupt handler function.

The problem seems to be that none of the changes made to the variables in the interrupt ever show up in the same variables in the main. I have globally declared two volatile variables, outside of any functions, at the top of the code. Everything complies ok no warnings or errors. Yet, when I print (to the LCD) the values of the global variables from the main they never change even when I am receiving echoed data back to the PC on the serial from the interrupt event function. It seams like the Volatile designation is not working

int max_rx = 40;
volatile uint8_t rx_in_idx = 0 ;
volatile char rx_buf[40];

ISR(USART_RX_vect){ // this is the symbol from AVR compiler docs (interrupt.h)

//  UDR0 is data register for uart - must read it during interrupt
//  See page 185 of datasheet for chip
//  Simply read uart rx reg and copy to buffer, update pointer

    rx_in_idx++;
    rx_buf[rx_in_idx] = UDR0;
    UDR0= rx_buf[rx_in_idx];

    if (rx_in_idx >= max_rx) rx_in_idx = 0; // make buffer "circular"
    return;
}

The loop count integer i increments as expected. The value displayed for all the other variables remain constant even when I am getting echoed data back on the PC.

while  (rx_buf[rx_out_idx] != 40){ // loop untill ( is read
                lcd_line_one();
            fprintf_P(&lcd_stream, PSTR("char %c loop %d"),rx_buf[rx_in_idx],i);
            lcd_line_two();
            fprintf_P(&lcd_stream, PSTR("out idx %d  in %d"),rx_out_idx,rx_in_idx);

I am lost any help would be greatly appreciated.

October 14, 2012
by pcbolt
pcbolt's Avatar

sask55 -

You have to be careful accessing the value of "rx_in_idx" inside your main portion of code since this could change at any moment. What I usually do is take a "snapshot" of it before using it. Here's an example:

in_idx = rx_in_idx;        // take "snapshot" of rx_in_idx

while (rx_out_idx != in_idx){  // chase the input pointer
  ch = rx_buf[rx_out_idx];      
  lcd_write_data(ch);
  rx_out_idx++;
  if (rx_out_idx >= MAX_RX) rx_out_idx = 0;
}

When the program first starts, both of the index variables are set to 0. Then data comes across and the buffer starts filling up and "rx_in_idx" gets incremented. So let say 8 characters come across then "rx_in_idx" is 8 and "rx_out_idx" is 0. The test inside the "while loop" will see that the values are different and start reading the buffer from 0 until "rx_out_idx" equals 8. This will keep happening and eventually "rx_in_idx" will wrap around to 0 after it reaches MAX_RX and start incrementing again. As long as the main code has read the buffer at 0 and a little beyond, everything is fine. The variable "rx_out_idx" just chases "rx_in_idx" around from 0 to MAX_RX and back to 0 again over and over. The only problem occurs when the main code isn't fast enough to keep up with the input and the contents of the buffer are overwritten before the main code can read it. In that case just make MAX_RX larger, but like I said earlier, I usually don't need anything higher than 32. Some of the tests I ran showed the two index variables rarely differed by more than 4 or 5.

October 14, 2012
by Noter
Noter's Avatar

sask55 -

Increment rx_in_idx after you store the character otherwise you will never use the 1st character in the buffer which is what your main is testing via rx_out_idx.

ISR(USART_RX_vect){ 
    rx_buf[rx_in_idx] = UDR0;
    UDR0= rx_buf[rx_in_idx];
    rx_in_idx++; 
    if (rx_in_idx >= max_rx) rx_in_idx = 0; // make buffer "circular"
    return;
}
October 14, 2012
by sask55
sask55's Avatar

Pcbolt

I see what you are saying. I will have to make a small change to my approach to averaging the temp sensor readings. I was just adding up the readings as they come back from the adc_read function (tempsensor guild code). Then dividing that sum by the number of loop passes that where carried out in the fraction of each second between subsequent uart read date/time strings. I was thinking since there was most of a second between RX read strings the MCU may as well just do as many temperature reads as possible and average them. In order to accomplish something like that I will have to be able to verify the value of any incoming RX characters inside the while loop as it is running. The arrival of the first char of date/time string the ( ,control character, is what I am using to end the while loop recording temperature readings. At that point the code can break out and display the time/date data to the various lines on the LCD according to the control characters within the string arriving on the uart.

I am hoping to have a code that can be doing something else and still monitoring the uart RX. When a specified control character is detected on the uart the program would change its task and deal with the uart data stream or carry out something different. My hope is to be able to recognize and handle intermittent packets of uart data periodically, as they arrive or shortly after. The buffer would be perfect as it could hold the incoming string data while the other operation finishes what it may be doing. The code could then check and deal with this data when it is logical to do so.

Noter

I do realize what you are saying. It is true that the very first time thru the buffer, the 0 position will not be filled except by the declaration statement. I changed the position of increment because it seamed more direct to have the variable rx_in_idx reference the last charter that was read rather then the next charter to read by the uart. After the initial “start” control character is detected by the main, there are numerous references made, in the code, to the positions of the last character in and the last character out on the circular buffer. I think it could be done ether way, It just make more sense to my to reference the buffer positions this way.

Thanks

October 14, 2012
by sask55
sask55's Avatar

noter

I noticed now that I had removed the rx_buf[0] =0; statement from the code I posted here It was at the top of the code with the declaration of the global variables.

October 14, 2012
by sask55
sask55's Avatar

pcbolt

I tried taking a snap shot of the two global variables that are subject to change in the interrupt function. I was hoping that moving the references to these variables out of the fprint f statements it may make a difference. The “snap shot” references still remain within the temp sensor read loop.

It did not change. I really don’t understand why I can’t reference these variables inside that loop and read the current values as set in the interrupt. I know what you are saying about they may change at any time I think I could deal with that. The problem is not that they are changing unexpectedly it is that they are never changing in the main. The values of rx_buf(n) must be changing in the interrupt function as I am reading good data, echoed back, to the PC from that variable.

while  (rx_buf[rx_out_idx] != 40){ // loop untill ( is read
            rx_in =rx_in_idx;
            incoming=rx_buf[rx_in_idx];
                lcd_line_one();
            fprintf_P(&lcd_stream, PSTR("char %c loop %d"),incoming,i);
            lcd_line_two();
            fprintf_P(&lcd_stream, PSTR("out idx %d  in %d"),rx_out_idx,rx_in);

It would seam that I am missing something with regard to the volatile variables working correctly between the interrupt function and the main. Could it be related somehow to the tempsensor read functions and using the ADC within the while loop?

October 14, 2012
by sask55
sask55's Avatar

Could the fact that the MCU may be looping a good part of the time while waiting for the next ADC result be the source of the problem?

uint16_t adc_read() {
  // read from ADC, waiting for conversion to finish
  // (assumes someone else asked for a conversion.)
  // wait for it to be cleared
  while(ADCSRA & (1<<ADSC)) {
    // do nothing... just hold your breath.
  }
  // bit is cleared, so we have a result.

  // read from the ADCL/ADCH registers, and combine the result
  // Note: ADCL must be read first (datasheet pp. 259)
  uint16_t result = ADCL;
  uint16_t temp = ADCH;
  result = result + (temp<<8);

  // set ADSC bit to get the *next* conversion started
  ADCSRA |= (1<<ADSC);

  return result;
}

Is the do noting loop or the ADC conversions themselves blocking the function of the volatile variables? Any comments on when this may happen? When can I expect not to get volatile variables updating between functions? Specifically is this likely to be an issue when using SPI functions outside the main and the uart interrupt is triggered by rx on the serial?

October 14, 2012
by Noter
Noter's Avatar

The compiler tries to be smart about executing your code and if it can load a value once into a register for the duration of a function or subroutine that's what it will do. However that doesn't work out so well if there is an interrupt function that is changing the value in memory so the keyword 'volatile' tells the compiler to always load the variable value from memory right before using it to be sure changes by interrupts are seen or visa versa. So if you're using the volatile keyword in your variable declarations then it is working and if you don't see the values you expect there is some other problem. I have never done any kind of snap shot thing with interrupt variables and have never had a problem because of it. Only the interrupt routine is changing the 'in' index and only the main is changing the 'out' index so there is no conflict. If the 'in' changes while you are in main and processing a character it doesn't matter because it is still not equal to the 'out' value which is all that needs to be tested anyway. You can make all your variables volatile and the program will work fine although a little less efficient than it would otherwise.

It's hard to say what the issue might be just looking at small code snippets. If you will post the whole thing we might see something that is causing the problem.

October 14, 2012
by sask55
sask55's Avatar

So that I can get a better understanding of what I am doing wrong I have made a very basic code using the uart interrupt. My hope is to get an understanding of why this code is not working as I think it should. Once I have learned the basic ideas from here I will carry on with my other ideas.

I have stripped down the code to the bare minimum of what I am trying to do. I hope to fill the string array once with the first 39 characters that the uart receives. After that any additional rx characters will be repeatedly stored into the same array position over and over again. All the incoming Rx is just echoed back to the PC. Without line 29 UDR0= rx_buf[rx_in_idx]; I see no text returned on the PC module, with that line in the code I see the text string back on the PC’s rx line. It seams to me that this must be an indication that the uart interrupt is working.

At this point all I wish to do in the main, is display the character contents of the string array one character at a time. My expectation was that before any serial rx is received I would see the # characters one in each of the array elements. Then after the uart interrupt fills the array with incoming characters I would see them as the loop displays the array content. I was also expecting the rx_in_idx value to go to 40

But none of the displayed characters ever change; they remain as they where even when the PC is indicating characters returned from the nerd kit. The rx_in_idx value remains at 0. It should be incremented up to 40 inside the uart event function.

Here is the code I am working with now.

// for NerdKits with ATmega168

#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 <string.h>
#include "../libnerdkits/delay.h"
#include "../libnerdkits/lcd.h"
#include "../libnerdkits/uart.h"

int max_rx = 40;
 volatile uint8_t  rx_in_idx = 0 ;
 volatile char rx_buf[40];

ISR(USART_RX_vect){ // this is the symbol from AVR compiler docs (interrupt.h)

    rx_buf[rx_in_idx] = UDR0;  
    UDR0= rx_buf[rx_in_idx]; //send echo back to PC to check for serial conection and interupt . 
    if(rx_in_idx < max_rx-1) rx_in_idx++;

    return;
}

int main() {
  // start up the LCD
  lcd_init();
  FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
  lcd_home();

  uart_init();

  UCSR0B |= (1<<RXCIE0); //set RX interrupt enable bit on USART Control and Status Register 0B
    sei();   //enable interrupts

  int tc =0;
  memset(rx_buf,0x23,sizeof(rx_buf));// fill string array with ####
  while(1) 
  {
            lcd_line_one();
            fprintf_P(&lcd_stream, PSTR("char %c index %d  "),rx_buf[tc],tc);
            lcd_line_two();
            fprintf_P(&lcd_stream, PSTR("index in val %d  "),rx_in_idx);
            delay_ms(1000);
        tc++;
        if (tc >= 40) tc=0;
  }

  return 0;
}
October 14, 2012
by sask55
sask55's Avatar

One more test I have done. If I replace line 23

   UDR0= rx_buf[rx_in_idx];

With

   UDR0= rx_in_idx+48;

ASCII code 48 is 0

I then see a continues string of 0 zeros on the PC rx input line.

The uart interrupt is sending back characters on the serial to the PC. The variable rx_in_idx is not being incremented in the function so that explains why it does not show up that way in the main and why the array is never filled. I have now noticed that rx_buf[0] is changed in the array. It always begins as # char as expected it will remains that way until the PC start sending serial data. It then changes. It is always changed to the ASCII code character for the number that is in the variable declare statement.

     volatile char rx_buf[40];.

So if array size is delared to 40 I see (, 41 I see ) , 42 I see * and so on

Sorry I did not notice that the array 0 element was updating earlier. Strangely it is not updating to anything that is received on the uart it is directly related to the value in the variable declaration statement no matter what char is recieved.

October 15, 2012
by sask55
sask55's Avatar

I have the simple version of the code working now.

Even though the issue was not the same I tried the solution suggested by hevans in this post. I simply changed the number in the declaration statement to a defined value.

#define max_rx 40
volatile uint8_t  rx_in_idx =0 ;
volatile char rx_buf[max_rx];

I was not even using a variable in the definition,I was using a number. I have no idea why changing the number 40 to a #define that is defined as 40 works But I quess I should not argue with success. I am not very comfortable with solutions that I don’t understand.

The somewhat related thread and solution Array definition issue

I am back to my original plan

Thanks

October 15, 2012
by sask55
sask55's Avatar

NerdKits » Forums » Microcontroller Programming » Multidimentional string arrays - stuck

This is the thread I was refering to

October 15, 2012
by sask55
sask55's Avatar

thread link

October 15, 2012
by sask55
sask55's Avatar

I am not sure if anyone cares at this point. It is not the dimensioning of the array that was the issue. It is the int variable max_rx. I was very uncomfortable with my last conclusion. I used the number 40 back in the array definition it works fine. It appears the problem was I cannot assign a value to a global variable the way I was.

  int8_t max_rx = 40;

and

  int max_rx =40;

do not work to assign a initial value to max_rx global variable.

Soooooooo simple yet it took me forever to figure this out.

October 15, 2012
by Noter
Noter's Avatar

Glad you figured it out. That same initialization thing has bitten me a couple of times too. Not too long ago PCBOLT pointed out that this statement in the make file will copy initialized values into ram which is likely the cause of your problem. Add the "-j .data" to the objcopy line in your makefile to have the initial variable values included.

avr-objcopy -j .text -j .data -O ihex
October 15, 2012
by sask55
sask55's Avatar

After resolving the issue that was related to the variable initialization everything has gone very smoothly.

I now have the uart rx interrupt working. Using uart interrupt to handle the uart rx has completely eliminated any issues I was having separating and displaying Date/time data as is is coming from the PC. I now have established a clear and reliable two way serial confection between the NerdKit and the PC using C#.

Both the timer event and the serial communications where very easy to drop onto a windows form using MS visual C# 2010 express which is free. The coding required in the c# was very basic. Using visual C# or visual C++ there appears to a lot of functionality built into the toolbox. By using the methods and solutions available with the visual MS .net applications it would seam that even a relative novice can produce interesting results without having to have an intricate understanding of the underling code.

C# has complied my serial connection test and produced an 11KB exe file that should run on any windows based computer with .net4 on it. COM 5 also has to be available as I never made any provision to change the COM port from the desk top window on this version.

Post a Reply

Please log in to post a reply.

Did you know that you can adjust most wire strippers to make it easier to strip insulation faster? Learn more...