printing function question

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

hi all
im wondering if this is a legal thing to do.in the winavr documentation i read that using the printf and scanf functions eats code space and ram so i wrote my own function as follows:

void sendString(char data[])
{
unsigned char i;
for(i = 0 ; i == 34;i++)
{
while(!(UCSRA & (1<<UDRE)));//wait for data register to be empty
UDR = data[i];
if(data[i] == '\0')
return;
}
return;
}

no my question is that assuming the above code is happy, can i call the function like this:

sendString("Enter number of samples ");

or do i first have to stick that string into an array and then feed that to the
function?also will the compiler put a '\0 ' at the end of the string or do i have to figure out how to do that myself? is there anything else im missing?
thanks

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

Generally speaking, what you're doing is fine; passing a literal "string" will do
what you think, and the compiler will insert the '\0' for you. (If you build your
own string, of course, you'll need to put the '\0' there yourself.) There are some
things here that will get you, though:

for(i = 0 ; i == 34;i++) 

The "i==34" here will always be false, so the loop won't run. You probably want "i < 34",
though that will artificially restrict you to 34-or-less byte strings.

if(data[i] == '\0')

You're testing for '\0' after sending it, so you'll get extraneous '\0's in your data stream.
An alternative coding (I'll stick with the array[] notation) :

void sendString(char data[])
{
   unsigned char i;
   for(i = 0 ; data[i] != '\0' ; i++)
   {
      while(!(UCSRA & (1<<UDRE)));//wait for data register to be empty
      UDR = data[i];
   }
   return;
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The C standard will automatically terminate strings encased in double quotes with a 0x00 null character.

You can indeed pass the string as in your example; the compiler will copy the string out of RAM and into SRAM at startup, and then just pass the pointer to the (unnamed) string at runtime. If the string never changes you might want to check out my PROGMEM tutorial in the tutorial section which will save SRAM space.

The C language will treat your character string as just an array of (unsigned) chars, so passing a string as a pointer and accessing it as an array is perfectly valid.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Hello!

To add to the comments already provided, may I suggest just a couple of changes?

void sendString(char *data)
{
     unsigned int i = 0;

     // While the indexed character is not the null terminator,
     // send the character to the USART.
     while (data[i] != '\0') {
          // Wait for data register to be empty
          while (!(UCSRA & (1<<UDRE)));
          // Transmit the character, bump the index
          UDR = data[i];
          ++i;
     }
}

The artificial limit on the maximum number (34) of characters transmitted has been eliminated. Instead, if the current character is not '\0' then transmit it when the TX register is empty.

When the function return value is declared as void, the return statement is optional.

Do you know about pointers in 'C' yet?
Do you understand how strings are stored in physical memory?
Ask questions if you wish.

I could go on and on, but I hope you have a 'C' programming book instead!

Tom

Last Edited: Tue. Jul 18, 2006 - 03:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
The "i==34" here will always be false, so the loop won't run. You probably want "i < 34",
though that will artificially restrict you to 34-or-less byte strings.

how is i==34 different fro i < 35 ? if i remember my c correctly the "==" operator tests for equality. so why does it always evaluate as false if 'i' is incremented from zero to whatever?

abcminiuser: thanks for answering the question i didnt ask but meant to. i was hoping the data just lived in flash.

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

The second argument causes the for statement to run only whilst true. So when I starts off as 1 the conditional is tested and returns false, so the for statement never runs. Using I < 34 means that the for loop runs until I == 34.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

Quote:
how is i==34 different fro i < 35 ? if i remember my c correctly the "==" operator tests for equality. so why does it always evaluate as false if 'i' is incremented from zero to whatever?

Right! Here are the rules:

for (expr1 ; expr2 ; expr3 )
    statement;

expr1 is usually an initializer and will always execute if included in the for() statement. Note, you could have a for ( ; i < 10; ++i ). This is valid. As is for ( ; ; ) and other combinations.

So, first, expr1 is executed.
Then, expr2 is evaluated. If it is non-zero, statement and then expr3 are executed.
This continues until expr2 evaluates to 0.

BTW, the same sequence but with a while loop looks like this

expr1;
while (expr2) {
    statement;
    expr3;
}

Cool, eh?

This is not the full description for the for loop. Refer to the K&R C Programming Language book.

TOm

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

:oops: ok i feel silly. for some reason i remembered it as
for( expr 1 : until expr 2 is true ; expr 3)
{
stuff;
}
sorry about that
as a side note to anyone who cares if you can find a k&r or for that matter any other c book in pdf format it makes life so much easier cause of the search function in acrobat.....

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

No problem!

Ah, one other comment!

When posting/replying, please take the time to use as close to proper punctuation and correct spelling as possible. Use capitalization in your sentences. You'll then present the image of someone who really cares about, and is precise in their work. Anyone here answering one of your questions may be a potential employer at some point in your future. BTW, the new Firefox 2.0 (now in beta) has a built-in spell checker. As you type, words mis-spelled are underlined in red. Right-click on the word to add it to its dictionary or select another spelling of the word from a list.

I'm still learning proper punctuation.

Enjoy!

Tom

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

If I understand the PROGMEM tutorial I have to modify the sendString() code like this:

void sendString_P(const char *data)
{
 // While the indexed character is not the null terminator,
 // send the character to the USART.
while (pgm_read_byte(*data) != '\0') 
{
	while (!(UCSRA & (1<<UDRE))); // Wait for data register to be empty
          
	UDR = pgm_read_byte(*data);// Transmit the character, bump the index
	data++;
}
return;
} 

and call it like this:

sendString_P(PSTR("FLASH STRING"));

in order to get it to work properly. Am iI correct in this? Also can this function handle RAM strings as well or do I have to write a separate function for that?

zoomcityzoom wrote:

BTW, the new Firefox 2.0 (now in beta) has a built-in spell checker. As you type, words mis-spelled are underlined in red. Right-click on the word to add it to its dictionary or select another spelling of the word from a list.

Tom


Neat I'll have to upgrade.

Last Edited: Tue. Jul 18, 2006 - 08:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to change the prototype to:

void sendString_P(PGM_P data)

and also remove the '*' to get:

UDR = pgm_read_byte(data);//

I simulated this and it worked.

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

Are you sure? abcminiuser's tutorial
https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=38003&sid=9dadf223f4e76204609ca02554ff0
uses pointers and i dont think that it uses the PGM_P datatype at all. is this just a different way of doing things?

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

Quote:
I have to modify the sendString() code like this:

Why not use the function I provided in one of my previous replies?

BTW, Thanks! Your replies are much easier to read now.

Tom

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

zoomcityzoom wrote:
Quote:
I have to modify the sendString() code like this:

Why not use the function I provided in one of my previous replies?

BTW, Thanks! Your replies are much easier to read now.

Tom

It actualy is your code I just tried to modify it so that the data is read directly from flash to the USART so that i don't eat up all my precious, precious SRAM.I dont think lack of SRAM is realy going to be a problem for this app but given that I tend to cut and paste my code from older apps I figure i might as well do it the best way I can find the first time so tthat it doesnt bite me later when I have no idea what might be wrong.

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

Quote:
Are you sure? abcminiuser's tutorial
https://www.avrfreaks.net/index.php?n...
uses pointers and i dont think that it uses the PGM_P datatype at all. is this just a different way of doing things?

When I simulated your code I was getting incompatible pointer warnings. My code above gives no warnings or errors. It does make sense that if you're going to send a string of type PROGMEM you would need to pass a pointer of type PROGMEM (PGM_P) as a parameter.

For simulation I commented out the While loop that checks UDRE and changed UDR to a volatile character and observed it in the simulator. I did see the characters I was expecting.

Hope this helps.

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

My code doesnt give me any warnings or errors either...well other than the usual me being rusty at C kind. :wink:
found a link to a plugin that does let you simulate the usart:
http://www.helmix.at/hapsim/index.htm#hapsimfeatures
cant get it to hook in to AVR Studio with service pack 3 installed. if i get realy desperate I'll try removing the service pack. anyone else know of anything that lets you simulate the usart?
::edit:: altered my code like this:

void sendString_P(const char *data)
{
char udrdata;
 // While the indexed character is not the null terminator,
 // send the character to the USART.
while (pgm_read_byte(*data) != '\0') 
{
	while (!(UCSRA & (1<<UDRE))); // Wait for data register to be empty
          
	udrdata = pgm_read_byte(*data);// Transmit the character, bump the index
	UDR = udrdata;
	data++;
}
return;
} 

and watched the udrdata variable.when I step into the sendString_P function i initialy get a warning that says invalid location. If i step further it displays as a box in single quotes.Is this just the debugger not liking me grabbing things out of flash or is this a genuine bug in my code?

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

I tried your code again and, your right, it didn't give any errors or warnings. However, the only way I can get it to work is by declaring the variabe 'data' as type PGM_P. Here is your code with my mod. that works for me:

void sendString_P(PGM_P data)  // MOD
{ 
	char udrdata; 
	 // While the indexed character is not the null terminator, 
	 // send the character to the USART. 
	while (pgm_read_byte(data) != '\0')  //MOD
	{ 
	   while (!(UCSRA & (1<<UDRE))); // Wait for data register to be empty 
          
	   udrdata = pgm_read_byte(data);// MOD
	   UDR = udrdata; 
	   data++; 
	} 
	return; 
}

The pgm_read_byte() function requires a pointer of type character. When the parameter is *data what is being passed to the function is the value 'data' is pointing to. In my simulation my test string "Program String" resides in code space at address 0x002A and, in the end, it is the address 0x002A that should be sent to the pgm_read_byte() function the first time it executes to fetch 'P'. But, when *data is the parameter, in my simulation 'data' has the value of 0x61 and what is sent to the function is the value at RAM location 0x61, which is in my case 0x0C.

Maybe someone else can better explain what is going on. Give my mods a shot. It "should" work.

Good luck.

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

It lives!!!!::maniacal laughter:: thank you so much for the help. now back to writing betterMouseTrap.h....but first; Caffeine !!
From looking at the code it appears that I'll have to write a second function if i want to transmit data from RAM locations right?

Last Edited: Wed. Jul 19, 2006 - 04:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not convinced the PGM_P is nessesary. While it should work of course, I'm pretty certain my code uses regular pointers with no ill-effects. When I get home tonight I'll go over my code and see what's what.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!