string problem with C

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

first of all I'm new with C programming.
I'm a Delphi programmer so strings usually are not managed as "char pointers" like with C.
I'm writing a program for an ATMega16P to drive an LCD display HD44780 compatible.
Strings to display on the LCD are received from a "pc-side" program through a FTDI232RL connected to the ATMega.
When the ATMega16P is turned on it displays a welcome message on the LCD and it is displayed correctly.
The function to display a string on the LCD is the following:

void SendCommand(uint8_t cmd)
{
	// sends a command to the HD44780

	PORTA= cmd;
    _delay_us(100);

    // pulse Enable and RS

    PORTC= RS_0 | E_1;
    _delay_us(100);
    PORTC= E_0;
    _delay_us(100);

}

void WriteCharAt(char txt, uint8_t X, uint8_t Y)
{
     // write a character at the position X,Y

     // row selection

     if (Y==0) SendCommand(0x80+X);
     if (Y==1) SendCommand(0xC0+X);
     if (Y==2) SendCommand(0x94+X);
     if (Y==3) SendCommand(0xD4+X);

     PORTA= txt;
     _delay_us(500);
     // pulse Enable and RS
     PORTC=RS_1 | E_1;
     _delay_us(500);
     PORTC= E_0;

}

void WriteXY(char *txt, uint8_t X, uint8_t Y)
{
   	// write the contents of "txt" at the position X,Y.
	// Text to display starts from the array element [3]

	uint8_t ciclo;
	
	ciclo=3;

	// row selection

    if (Y==0) SendCommand(0x80+X);
    if (Y==1) SendCommand(0xC0+X);
    if (Y==2) SendCommand(0x94+X);
    if (Y==3) SendCommand(0xD4+X);

    while (ciclo<44)
    {
      if (txt[ciclo]=='\0') break;
	  if (txt[ciclo]==42) break;
      WriteCharAt(txt[ciclo], X+ciclo-3, Y);
	  ciclo++;
    }
}

void main(void)
{
     char txt0[]= "        CBS System     ";
	char txt1[]= "      LCD Board v1.0   ";
	char txt3[]= "                by R.G.";

	InitCPU();
	
	InitLCD();
	ClearLCD();
	
	// welcome message
	
	WriteXY(txt0, 0,0);
	WriteXY(txt1, 0,1);
	WriteXY(txt3, 0,3);
	
	_delay_ms(1000);
	_delay_ms(1000);
	_delay_ms(1000);
	_delay_ms(1000);
	_delay_ms(1000);

	ClearLCD();

        while (1>0)
	{
		
		WaitForNewData();

		//read new data, data are stored into recData
		
		ReadRecData();
		
		// interprets data
		
		if (recData[0]==7)
		{ 	// writes a text
			WriteXY(recData, recData[1], recData[2]);
			
		}

}

}

Data received from the FTDI are stored into an array (declared as uint8_t recData[64]) and then passed as parameter to the WriteXY.
The format of the command sent from the "pc-side" program is the following:
recData[0]--> 7 (it is the command for the ATMega to write a message to the display)
recData[1]--> X coord. of the message to display
recData[2]--> Y coord. of the message to display
recData[3] to recData[43]--> message to display

where a "*" (ASCII 42) means "end message" and WriteXY exits.
When new data are received, the array "recData" is passed to WriteXY in the following way:

WriteXY(recData, recData[1], recData[2]);

but garbage it is always shown on the display.

I think the problem is probably my mistake, I'm not passing received data to WriteXY correctly.
So I wonder what is the correct way to do this.
The ATMega is running at 1 MHz (internal oscillator) and the connection speed between ATMega and FTDI is 4800 bps.

Thanks you , regards.

Roberto

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
WriteXY(recData, recData[1], recData[2]); 

Try passing as:

WriteXY(&recData[3], recData[1], recData[2]); 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

CirMicro has given you the solution.

As a brief explanation... when you pass recData that is the address of the [0] element of recData array. So (as it says in the comment in your code!) you need the address of the [3] element, hence the use of the "address of" operator on recData[3].

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you guys, I will try and I will let you know the result.

Regards,

Roberto

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tried the solution but another problem came out: the procedure WriteXY exits after the first "cycle", but only if I pass recData as parameter, if I pass one of the strings that contains the welcome message, everything works fine.
Since it was not writing anything on the display, then I assigned recData some fixed values and went into debug.
Then I noticed that when it enters into WriteXY, when "ciclo" goes from 0 to 1 the procedure exits (it should exit only when it encounters "*" or "\0" or ciclo>=44)!
I didn't get why this thing happens, it seems that some error occurs with recData but the compiler doesn't show any error or warning.
I added two watches: "txt" and "ciclo".
TXT always shows "location non valid" and "ciclo" is displayed correctly but when its values is "1", the program exits from WriteXY and returns to the "main" procedure.
Why that happens?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you post the full code? When ciclo is at 0 can you view the string that txt points to in the debugger? Does it it still show what you expect?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is the full code:

#include 
#include 
#include 
#include 
#include 
#include "LCD.h"

#define F_CPU 1000000UL  // 1 MHz

const uint8_t E_0= 0;
const uint8_t E_1= 2;
const uint8_t RS_0= 0;
const uint8_t RS_1= 1;

void InitCPU(void)
{
	// Chip initialization
	
	uint8_t ciclo;

	// set PORT A,B and C as output
    
	DDRA= 0xFF;
    DDRB= 0xFF;
    DDRC= 0xFF;

     
    // reset outputs
    
	PORTA= 0x00;
	PORTB= 0x00;
    PORTC= 0x00;
	
	// set USART at 4800 bps 

	UBRRL= 12;
    UBRRH= 0;
     
     // enables the USART receiver and transmitter,8 bit, 1 bit stop, parity none
     
    UCSRB= 0x18;
    UCSRC= 0x86;

	for (ciclo=0; ciclo>64; ciclo++)
		recData[ciclo]=0;

	_delay_ms(50);
}

void SendCommand(uint8_t cmd)
{
	// sends a command to the HD44780

	PORTA= cmd;
    _delay_us(100);

    // pulse Enable and RS

    PORTC= RS_0 | E_1;
    _delay_us(100);
    PORTC= E_0;
    _delay_us(100);

}

void ClearLCD(void)
{
	// clears LCD

	SendCommand(0x01);

}

void InitLCD(void)
{
	// HD44780 initialization

     SendCommand(0x30);
     SendCommand(0x30);
     SendCommand(0x30);

     // 8-bit mode, 2 or more rows, 5x8 font

     SendCommand(0x38);

     // display on, cursor on

     SendCommand(0x0C);

     // entry mode set

     SendCommand(0x06);

}

void WriteCharAt(char txt, uint8_t X, uint8_t Y)
{
     // write a character at the position X,Y

     // row selection

     if (Y==0) SendCommand(0x80+X);
     if (Y==1) SendCommand(0xC0+X);
     if (Y==2) SendCommand(0x94+X);
     if (Y==3) SendCommand(0xD4+X);

     PORTA= txt;
     _delay_us(500);
     // pulse Enable and RS
     PORTC=RS_1 | E_1;
     _delay_us(500);
     PORTC= E_0;

}

void WriteXY(char *txt, uint8_t X, uint8_t Y)
{
   	// write the contents of recData (passed as parameter) at the position X,Y.
	// Text to display starts from the array element [3]

	uint8_t ciclo;
	uint8_t xx;
	uint8_t yy;

	ciclo=0;
	xx= X;
	yy= Y;

	// row selection

    if (Y==0) SendCommand(0x80+X);
    if (Y==1) SendCommand(0xC0+X);
    if (Y==2) SendCommand(0x94+X);
    if (Y==3) SendCommand(0xD4+X);

    while (ciclo<44)
    {
      if (txt[ciclo]=='\0') break;
	  if (txt[ciclo]=='*') break;
	  WriteCharAt(txt[ciclo], xx, yy);
	  ciclo=ciclo+1;
	  xx=xx+1;
    };

}

void WaitForNewData(void)
{
	// wait for new data

	while ((UCSRA & (1 << RXC)) == 0) 
	{
	};

}

void ReadRecData(void)
{
	uint8_t ciclo;
	uint8_t iByte;
	uint8_t X;

	ciclo=0;
	iByte=0;

	while (ciclo<44)
	{
		iByte= UDR;
		recData[ciclo]= iByte;
		if (iByte=='*') break;
		ciclo++;
		_delay_ms(1);		
	};

}

void main(void)
{
	// main program
	
	char txt0[]= "     CBS System     ";
	char txt1[]= "   LCD Board v1.0   ";
	char txt3[]= "             by R.G.";

	InitCPU();
	
	InitLCD();
	ClearLCD();
	
	// welcome message
	
	WriteXY(txt0, 0,0);
	WriteXY(txt1, 0,1);
	WriteXY(txt3, 0,3);
	
	_delay_ms(1000);
	_delay_ms(1000);
	_delay_ms(1000);
	_delay_ms(1000);
	_delay_ms(1000);

	ClearLCD();
		
	// infinite loop 

	while (1>0)
	{
		
		WaitForNewData();

		//read new data
		
		ReadRecData();
		
		// interprets data

		if (recData[0]==7)
		{ 	// writes a text
			WriteXY(&recData[3], 0, 0);
		}

		if (recData[0]==8)
		{	// clear display
			ClearLCD();
		}
			
		if (recData[0]==9)
		{	// init display
			InitLCD();
		}
			
		recData[0]=0;
			
	}

}

Quote:
When ciclo is at 0 can you view the string that txt points to in the debugger?

No, a "location not valid" appears

It seems that the problem is in the line "if (txt[ciclo]=='*') break;", if I remark it with "//" the procedure writes but the text appears with garbage.
The strange thing is that if I modify the "ReadRecData" in this way:

void ReadRecData(void)
{
	uint8_t ciclo;
	uint8_t iByte;
	uint8_t X;

	ciclo=0;
	iByte=0;
	X=0;

	while (ciclo<44)
	{
		iByte= UDR;
		recData[ciclo]= iByte;
		//shows data as they are received
                WriteCharAt(iByte, X,0);
		if (iByte=='*') break;
		ciclo++;
		X++;
		_delay_ms(1);		
	};

}

a next call to WriteXY (after ClearLCD()), the message is displayed correctly !
I must admit, I'm getting really confused...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Further update: the only way that the program works is to have "ReadRecData" as the one modified above.
Someone could explane me why?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, I see the problem. The above change is probably working because the LCD output is creating enough delay for the next character to be received.

You need to check the TXC bit before every iByte= UDR;
Otherwise you're trying to read the data before it all gets there. At 10Mhz you probably reach 44 reads before the second character has arrived :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Maybe you meant "RXC" instead of "TXC" eheh
I modified the routine and it worked thanks !!

Regards,

Roberto

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Maybe you meant "RXC" instead of "TXC" eheh
I modified the routine and it worked thanks !!

Yes, RXC is what I meant. It's early here, not enough coffee yet :)

Glad to hear it's working for you.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have the last question even if maybe it's a bit OT...making some tests to check the max transfer speed of a text string to the display I noticed that I have always to delay a transmission from the next by 15 msec...I mean if I transmit to the display a message, the next message I want to display must be delayed with an interval of 15 msec.
I admit to be a "novice" with serial communication and microcontroller but the "pc-side" program transmits data through the USB to the FTDI that is USB 2.0 full speed compliant, so 12 Mbit/sec, and the FTDI transmists data to the ATMega at 230400 bps.
The max lenght of the strings transmitted is 20 bytes, so 160 bit...at 230400 bps (not considering the time needed to transmit through USB at 12 Mbit/sec) the transmission time should be about 650 microsec., every command sent to the display is delayed by 100 microsec, and the ATMega is running at 18.432 MHz (I know it's overclocked but I've finished 16 MHz crystal).
Maybe I'm not calculating this time correctly but previously I was driving the display through the parallel port without any problem of this kind.
Where is my mistake?

Thank you, regards.

Roberto

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
20 bytes, so 160 bit...at 230400 bps
not that it matters a whole lot, 20 bytes would be 200 bits at 8n1, 1 start bit, 8 data bits, 1 stop bit.

Calculate all those delays you have sprinkled in your code when writing to the lcd. Add them up. Maybe that comes up close to 15ms when writing a full text message. You may be 'getting away' with sending commands with the short delay, as you have a 3 byte hardware buffer (2+shift register).