1

I have a lab assignment which needs me to use an Atmega328P to do an ADC, and with USART, transmit the digital value to a MILFORD-4X20-BKP LCD display. The LCD needs to display the value in a 2 byte format (decimal, 0-255) on the first line, and a word format (4 bytes, 0-1023) on the third line.

I was successful in doing this, but because I was unsure of the array sizes, I initially had them all big enough to not have an issue. When I changed it to what I believe was necessary, I had a weird bug. It's the weird symbol shown below (or I guess at the bottom). The symbol in that position would depend on the potentiometer value.

So here is my thinking. I allocated 36 (+1 for pos 0) positions to the buff, which was sent to the LCD. I allocated 3 to buff2 for the word value (4 n positions) and finally 4 for buff1 for the 2 bytes value (5 n positions)

buff[36]; buff1[4]; buff2[3];

3n positions for the word value works, but when I put 4n for the 2byte value, the bug appears. See the first picture.

The bug also appears in the form of a portion of the 0-255 value appearing at the end of line 3, depending on different array values of buff and buff1. The second photo has buff[37], buff1[2], buff2[3]

Last note, if I change the value to buff1[5], the bug disappears..but why? The array size for 2 bytes should be less than that for 4 bytes.

The weird bug

The LCD

I'm doing my best at explaining my issue, but don't know if I'm clear enough. I know I am having my arrays cross over into one another's memory address, but I don't see how and where.

/*
 * Serial Lcd.c
 * 
 * Use's a 4x20 serial LCD display.
 *
 * Adapted by Phil J to suit Atmega328P: 15/2/2015 (corrected Usart_Rx Int Vector address ref. for 328)
 *
 * Editted by Tomi Fodor
 *
 */ 

#define F_CPU 16000000UL
#define BAUDRATE 9600 - change to External 16MHz crystal on MCU

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include "stdlib.h"
#include "USART.h"

// Global Variables
// Note the use of the volatile keyword to ensure that the compiler knows that these variables can be changed at 
// any time, including by the ISR

volatile int i=0; 
volatile uint16_t buffer[]; // 20 place array
volatile char buff[36];     // var sent out value
volatile char buff1[4];     // var for the pot value / 4 ***** HAS TO BE AT LEAST 4 FOR SOME REASON (5 w/o bug), SHOULD BE FINE AT 2
volatile char buff2[3];     // var for the actual pot value
volatile uint16_t StrRxFlag=0;
volatile int Ana, Bell;     // pot value

int main(void)
{
   buff[4]=' ';buff[5]='P';buff[6]='o';buff[7]='t';buff[8]=' ';buff[9]='V';buff[10]='a';buff[11]='l';buff[12]='(';buff[13]='D';buff[14]=')'; // constants to be displayed
   _delay_ms(500);

   ADCSRA = (1<<ADEN)|(1<<ADPS2)|(1<<ADPS1);    // Enables the ADC, sets the ADC to use the division factor 64 for the ADC clock 
   USART_interrupt_init();
   USART_putstring("Ready ");           // Send String to the LCD
// USART_putstring(buff3);                  
   USART_send('\r');                // Send carriage return
// USART_send('\n');                // Send linefeed
   _delay_ms(500);              // Allows for the LCD module to initialize

   while(1)
      {
     USART_send(254);       // LCD control mode
     USART_send(0);         // LCD HOME command
     USART_send(254);
     USART_send(1);         // LCD CLEAR SCREEN
     buff[0] = ' ';         // Required for offset of display
     buff[4] = ' ';         // Signifies terminator of pot
     ADCSRA |= (1<<ADSC);       // Starts A-D conversion
     while (ADCSRA & (1<<ADSC));    // Wait till A-D conversion is complete
     Ana = ADCW/4;          // Get A-D result
     Bell = ADCW;           // Get actual A-D result
     itoa(Ana,buff1,10);        // Creats the dec value of the Analogue value [stdlib.h]
     itoa(Bell,buff2,10);       // actual

     if (buff1[1] == '\0')      // If only 1 digit
        {
           buff[1] = ' ';       // Not hundreds
           buff[2] = ' ';       // Not tens
           buff[3] = buff1[0];  // Place in single digit
        }
     else if(buff1[2] == '\0')  // If only 2 digits
        {
           buff[1] = ' ';       // Not hundreds
           buff[2] = buff1[0];  // Shift
           buff[3] = buff1[1];  // Shift
        }
     else
        {
           buff[1] = buff1[0];  // Shift
           buff[2] = buff1[1];  // Shift
           buff[3] = buff1[2];  // Shift
        }

     for(i=0;i<25;i++)
        {
           buff[i+15] = ' ';
        }
buff[25]=' ';buff[26]='P';buff[27]='o';buff[28]='t';buff[29]=' ';buff[31]='V';buff[32]='a';buff[33]='l';buff[34]='(';buff[35]='D';buff[36]=')'; // constants to be displayed

     if (buff2[1] == '\0')      // If only 1 digit
        {
           buff[21] = ' ';      // Not thousands
           buff[22] = ' ';      // Not hundreds
           buff[23] = ' ';      // Not tens
           buff[24] = buff2[0]; // Place in single digit
        }
     else if(buff2[2] == '\0')  // If only 2 digits
        {
           buff[21] = ' ';      // Not thousands
           buff[22] = ' ';      // Not hundreds
           buff[23] = buff2[0]; // Shift
           buff[24] = buff2[1]; // Shift
        }
     else if(buff2[3] == '\0')  // If only 3 digits
        {
           buff[21] = ' ';      // Not thousands
           buff[22] = buff2[0]; // Shift
           buff[23] = buff2[1]; // Shift
           buff[24] = buff2[2]; // Shift
        }
     else
        {
           buff[21] = buff2[0]; // Shift
           buff[22] = buff2[1]; // Shift
           buff[23] = buff2[2]; // Shift
           buff[24] = buff2[3]; // Shift
        }

          USART_putstring(buff);
          USART_send('\r'); 
         _delay_ms(500);
    }
}

//ISR(USART0_RX_vect) - not for 328
ISR(USART_RX_vect)              //this is the right vector ref, not above
{   
   buffer[i]=UDR0;              //Read USART data register
   if(buffer[i++]=='\r')            //check for carriage return terminator and increment buffer index
      { 
     // if terminator detected
     StrRxFlag=1;           //Set String received flag 
     buffer[i-1]=0x00;      //Set string terminator to 0x00
     i=0;               //Reset buffer index
      }
}
Tomi Fodor
  • 15
  • 6
  • Is the code shown in your post == to the code you compiled,ran and observed your bug? – ryyker Mar 19 '16 at 00:45
  • The main code I posted is the code I ran yes. It should be the same code that resulted in the issues first described, and shown in the first picture. I did repeat a part of the same code in my question which may have resulted in some confusion. (buff[36], buff1[4], buff2[3]). – Tomi Fodor Mar 19 '16 at 02:33

1 Answers1

1

Your cited problem is likely due to an non-null terminated string when using calling USART_putstring(buff);. C strings by definition require the last character to be \0 ( NULL ). Example:

Given char string[5];

|h|e|r|e|\0| is legal
|h|e|r|e|s| |a| |b|u|g| is a populated buffer, but not a string

There are places in your example where you are writing a non NULL character to the last element of a buffer. For example buff is created as an array with 36 elements:

volatile char buff[36];     // var sent out value

In the line:

buff[25]=' ';buff[26]='P';buff[27]='o';buff[28]='t';buff[29]=' ';buff[31]='V';buff[32]='a';buff[33]='l';buff[34]='(';buff[35]='D';buff[36]=')'; // constants to be displayed

index 35 (the last legal index) is populated with the D character. Index 36 (not legal) is populated with ), and should result in a run-time error at the very least. By definition then, because it is not NULL terminated, it is not a string, and using it as one will lead to Undefined Behavior.

Here also, buf2 is created with 3 elements:

volatile char buff2[3];     // var for the actual pot value 

But on this line, the index 3 is used: (only 0-2 are valid)

else
        {
           buff[21] = buff2[0]; // Shift
           buff[22] = buff2[1]; // Shift
           buff[23] = buff2[2]; // Shift
           buff[24] = buff2[3]; // Shift <<< only buff2[0] - buff2[2] are legal
        }

These errors will compile, but will result in an out-of-bound pointer at run-time.

This variable has an undefined size, and should have flagged an error:

volatile uint16_t buffer[];  // 20 place array

The assumption is that you intended it to be:

volatile uint16_t buffer[20];  // 20 place array

Later, you use it here:

ISR(USART_RX_vect)              //this is the right vector ref, not above
{   
   buffer[i]=UDR0;   

Because I am not sure what C standard you are working to (i.e. if its something other than ANSI C) I do not know if your environment would flag an undefined size for an int array at compile time. But since the rest of your array sizes are defined, this one seems suspicious.

Also, I see that you have i defined as:

volatile int i=0;

Are the bounds of i well known? Is it possible for i to ever go beyond the value 19? (assuming the array was at some point initialized)

Community
  • 1
  • 1
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • Sorry, I believe I included the tags, but didn't mention it in the question. It's WinAVR, on Proteus. TBH I am not sure the rules it follows. I thought that an undefined array would adopt the size it needs, but perhaps that's a foolish mistake. I learned programming from Arduino first. The Null character makes a lot of sense. I can't remember if the compiler will automatically add those to the string if I leave it blank, but I'll fix these tomorrow morning. The weird errors make more sense since all 3 arrays have this shortcoming, so I'll update asap. Thanks. – Tomi Fodor Mar 19 '16 at 02:44
  • @TomiFodor - No, an undefined array does not automatically adjust for size. However, this initialization method will work: char array[]={"string"}; Initialized like this, the string array will be terminated with a NULL automagically, and of course the size of the array will be 7, 6 for the visible characters and 1 for the NULL. Adding space for (and applying) the NULL in your strings will probably fix your stated issues. – ryyker Mar 19 '16 at 14:11
  • Ok so I did it and it gets rid of the bug, but here's the annoying thing. buff2, a value which seems to need an array of 5 elements, works perfectly fine with only 3. This doesn't seem to make any sense. – Tomi Fodor Mar 19 '16 at 15:10
  • volatile char buff2[3]; // var for the actual pot value and later on I do this: buff[24] = buff2[3]; // Shift ...but it works!? Even though buff2 only have 0,1 and 2 for its positions.. element 3 does not even exist.. let alone the '\0' – Tomi Fodor Mar 19 '16 at 15:12
  • Ok I think I get it. It still works because it's using memory somewhere after buff2. And it's still pointing to it properly. It's just that if I had another array, I'd have crossovers. Which was my issue before. Correct? I'll assume I'm correct. Thanks. – Tomi Fodor Mar 19 '16 at 15:20
  • Be careful with this. referencing a memory element that is not owned by the process (or a variable) may work at some times when the hijacked space is not needed at the moment by its owner. But at any time the owner of that space can write to it, changing the definition of the array. – ryyker Mar 19 '16 at 15:21
  • Yes, you beat me to the punch. I was writing the last comment when you posted yours. – ryyker Mar 19 '16 at 15:21
  • Yes, actually I just realized this is exactly what you said in the answer but I got my brain thinking about things all over the place. Thanks for making my first experience on stackoverflow so awesome! – Tomi Fodor Mar 19 '16 at 15:56