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.

Support Forum » A little help with printf_P(PSTR

July 15, 2011
by Ralphxyz
Ralphxyz's Avatar

I am trying to print some debug info about my "for" statements to my PC.

 int8_t *ptr;
 while (1)
{
    for (i=0; i < 3; i++) 
    {
        ptr = _array[i];
        printf_P(PSTR("\n\r Hello!\n\r"));
        printf_P(PSTR("_array[i] is %S \r\n"), *ptr);
        arrayName = ptr;
    //*
        if (arrayName = (slantRight)) 
        {
            PORTB =32;
            delay_ms(1000);
        }
        if (arrayName = (slantLeft)) 
        {
            PORTB =4;
            delay_ms(1000);
        }
        for (i=0; i < 8; i++) 
        {
            PORTB = arrayName[i];
            delay_ms(ON);
            PORTB = 0;                          // turn'em off
            delay_ms(OFF);

        }
    //*/        
    }
}

This is what I get from the printf:

  Hello!
  _array[i] is ���

ptr is a pointer to elements of an previously established/populated array.

I would like to see the string contained in ptr.

The for statements need some work but they do sorta work which is why I need to see what is being passed as _array[i] .

Ralph

July 15, 2011
by Ralphxyz
Ralphxyz's Avatar

Here is a simpler example:

#define F_CPU 14745600

 #include <stdio.h>
 #include <stdlib.h>
 #include <inttypes.h>
 #include <avr/io.h>
 #include <avr/pgmspace.h>

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

 //char *menu[] =
 //int8_t *menu[] PROGMEM =
 int8_t *menu[] =
 {
"slantLeft",
"slantRight",
"slantUp",
"slantDown"};

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

int8_t i;

for (i = 0; i < 4; i++) 
    {
        printf_P(PSTR("\r\n %s \r\n"), menu[i]);

    }
    }

I guess the question is how does one print out an array's elements?

I get this:

  ba
  �

  �

  �

  �

Thanks,

Ralph

July 15, 2011
by Ralphxyz
Ralphxyz's Avatar

Here is wayward's great explanation of initializing string arrays.

Essentially one needs to add .data to avr-objcopy in the Makefile.

    avr-objcopy -j .text -j .data -O ihex $(ProjectName).o $(ProjectName).hex

So now I get this:

  ba
  slantLeft

  slantRight

  slantUp

  slantDown

I sure would be lost without the forum archive.

Ralph

July 15, 2011
by Noter
Noter's Avatar

Hey Ralph,

The .data section is RAM so your strings are in RAM. The array of pointers named menu is in ROM but not the character strings. If you really intend to have the strings in ROM then an extra step is required to define them in ROM before populating the array. Then you must also use %S in your printf_P format instead of %s. You can remove the .data from your makefile and the following will still work because everything is in ROM.

//#define F_CPU 14745600

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

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

const char slantLeft[]  PROGMEM = "slantLeft";
const char slantRight[] PROGMEM = "slantRight";
const char slantUp[]    PROGMEM = "slantUp";
const char slantDown[]  PROGMEM = "slantDown";

PGM_P menu[] PROGMEM =  {
    slantLeft,
    slantRight,
    slantUp,
    slantDown
};

int main(void){
    // start up the serial port
    uart_init();
    UCSR0A = 0;
    UBRR0L = (F_CPU/(16*BAUD))-1;
    FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
    stdin = stdout = &uart_stream;

    int8_t i;

    for (i = 0; i < 4; i++) {
        printf_P(PSTR(">> %S \r\n"), menu[i]);
    }

    while(1);
}
July 16, 2011
by Noter
Noter's Avatar

Using a structure may be more to your liking because the extra step of setting the strings into ROM before populating the array is not necessary. Less typing on the upside but the drawback is the max length of the strings has to be specified and storage is reserved for that amount so it will not be as efficient in memory utilization. But since they are in ROM there is probably plenty of space.

//#define F_CPU 14745600

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

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

#define MAX_LEN 11
struct {
    char data[MAX_LEN];
} menu[] PROGMEM = {
                        {"slantLeft"}, 
                        {"slantRight"}, 
                        {"slantUp"}, 
                        {"slantDown"}
                    };

int main(void){
    // start up the serial port
    uart_init();
    UCSR0A = 0;
    UBRR0L = (F_CPU/(16*BAUD))-1;
    FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
    stdin = stdout = &uart_stream;

    int8_t i;

    for (i = 0; i < 4; i++){
        printf_P(PSTR(">> %S \r\n"), menu[i].data);
    }

    while(1);
}
July 16, 2011
by Ralphxyz
Ralphxyz's Avatar

Thanks Paul, I was hoping you would jump in (and all of the rest of you I depend on so much).

I was/am just so pleased that it finally worked.

I had/have a whole lot of questions about "Memory" but figured I had best re-read the links that wayward gave in his thread.

Then I might be able to ask a intelligent question.

I like the struct, I am starting to understand them.

I was going to ask you about putting the definitions of the slant objects into EEPROM but was going to try once I at least got something working using device memory. So look for my question in your I2C EEPROM thread.

Of course I am having problems restoring the I2C code I had working once so it might take me a bit before I am ready to ask about using EEPROM.

Right now I am just trying to get something working so that I can test my water curtain .

I think now I can revise my code from the top of this thread and be able to generate patterns then I can confirm that my water curtain will actually work.

Once I can determine that I can actually see a pattern in the water then I'll be better off to see what is next. Plus I can look at expanding to 32 solenoids instead of 8.

Not only do I need to work out mcu memory but I also have to figure out lighting which I had not had high on my list but now see that it is going to be essential probable critical.

I really want to get my strobe light working but at least I have manual control over that so I do not yet have to work out the programming.

Thanks again for all of your help, we are gonna make it yet.

Ralph

July 16, 2011
by Ralphxyz
Ralphxyz's Avatar

Hey Paul your struct code doesn't quite work :-(

�X������1��с
>> slantRight 
>> slantUp 
>> slantDown

I am working on it.

Ralph

July 16, 2011
by Ralphxyz
Ralphxyz's Avatar

Well this "fixes it", the problem I reported above with Paul's struct code, which I really like.

It probable is not the best solution or method but hey it works!

    for (i = 0; i < 4; i++)
    {
        printf_P(PSTR("\r\n"));
        printf_P(PSTR(">> %S \r\n"), menu[i].data);
    }

ba
>> slantLeft

>> slantRight

>> slantUp

>> slantDown

Ralph

July 16, 2011
by Noter
Noter's Avatar

That's strange, I tested it before posting and it works fine. Something different is going on with your setup. Probably in your terminal emulator or whatever you are using to display the data from the serial line on your PC. Where do you suppose that "ba" comes from at the begining of your output?

July 16, 2011
by Ralphxyz
Ralphxyz's Avatar

Darn I've seen references to that ba but can not remember the context or how to remove it. I do not know but it might be something to do with OS X Terminal.

Now if I hit the reset button I randomly get garbage on the first line:

>> slantRight

>> slantUp

>> slantDown 
ba
>> slantLeft

>> slantRight

>> slantUp

>> slantDown 
�XC!������1��с

>> slantRight

>> slantUp

>> slantDown 
�XC!������1��с

>> slantRight

>> slantUp

>> slantDown 
ba
>> slantLeft

>> slantRight

>> slantUp

>> slantDown 
ba
>> slantLeft

>> slantRight

>> slantUp

>> slantDown 
ba
>> slantLeft

>> slantRight

>> slantUp

>> slantDown 
ba
>> slantLe

I'll fire up my windows pc and see what I get in PUTTY.

Ralph

July 16, 2011
by Ralphxyz
Ralphxyz's Avatar

I am getting the same garbage on line one on my Windows 7 x64 computer.

This is just a minor step in the process so I do not think there is a big problem with the random garbage.

It would be nice to know why and what the fix is but I have to move on.

If it inhibits my testing then I'll have to worry about it.

Ralph

July 16, 2011
by Noter
Noter's Avatar

Did you try HyperTerminal on the windows box?

July 16, 2011
by Ralphxyz
Ralphxyz's Avatar

I used PUTTY, Hyper-Terminal is not connecting for me.

Ralph

July 17, 2011
by Rick_S
Rick_S's Avatar

Doesn't the bootloader send a ba at startup?

If not, chalk it up to it being early and I'm just thinking out loud Tongue in Cheek

Rick

July 17, 2011
by Noter
Noter's Avatar

I just checked and yes it does. The ba comes from the bootloader before it jumps to the application. Good memory there Rick! Even this early!

July 17, 2011
by Ralphxyz
Ralphxyz's Avatar

I knew I had seen that ba somewhere, thanks Rick.

Now here is a interesting twist:

I tried adding a while(1) loop thinking I would just repeat the serial output.

Instead all I get is the � and it freezes until I do a reset.

What am I missing now?

int main(void)
{
    // start up the serial port
    uart_init();
    UCSR0A = 0;
    UBRR0L = (F_CPU/(16*BAUD))-1;       // BAUD not defined
    FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
    stdin = stdout = &uart_stream;

    while(1);
    {
        int8_t i;

        for (i = 0; i < 4; i++)
            {
                printf_P(PSTR("\r\n"));
                printf_P(PSTR(">> %S \r\n"), menu[i].data);     
            }
   }    
}

Ralph

July 17, 2011
by Ralphxyz
Ralphxyz's Avatar

Why can't I do this?

arrayName = menu[i].data;
printf_P(PSTR("arrayName is %s\r\n"), arrayName);

Ralph

July 17, 2011
by Noter
Noter's Avatar

That should work ... guess you need to figure out and correct your serial communications problem. I doubt it's the chip so that only leaves the usb conversion cable, although you surely would have trouble downloading if that was the problem, and the PC port config or software. I use hyperTerminal and config for 15200, N, 8, 1, and no flow control. Then I start the session before I powerup or reset my nerdkit. These days I'm using a real serial port (COM1) since my usb cable died last week and it works just as good as the usb cable did.

July 17, 2011
by Ralphxyz
Ralphxyz's Avatar

Is [quote] That should work [/quote] referencing the while(1) statement? It doesn't work.

What serial communications problem?

Using my code above with the .data in the Makefile it all works as expected with no occasional garbage.

Plus I can pass menu[i] to a variable and the while(1) works.

Ralph

July 17, 2011
by Noter
Noter's Avatar

On second look you need to remove the ";" at the end of the while statement line, then it will work.

I thought you were still having serial problem but if not that's good.

July 17, 2011
by Noter
Noter's Avatar

I thought you had data display problems without the extra cr/lf print statement? Maybe that's the serial issue I was thinking about. Then the reset thing is strange too. I don't see any of that with exactly the same code.

Post a Reply

Please log in to post a reply.

Did you know that you can build a circuit to convert the "dit" and "dah" of Morse code back into letters automatically? Learn more...