Thread: unexpected sprintf results

  1. #1
    Registered User
    Join Date
    Jan 2012
    Posts
    8

    unexpected sprintf results

    I'm writing some code on a PIC16F874A and I'm having an issue trying to build a string to display on an LCD. I want to display the temperature and pressure on the LCD, but the code that I expect to work doesn't, but I can get what I'm looking for using a workaround. I have no idea what the difference is. I'm totally new to C so it's probably pretty basic.

    I thought should work but doesn't:
    Code:
     long temperature;
     long pressure;
     long *temperature_ptr = &temperature;
     long *pressure_ptr = &pressure;
     while(1)
     {
      Take_Measurement(temperature_ptr, pressure_ptr);
      sprintf(LCD_string, "T:%lu, P:%lu", temperature, pressure);
      LCDWriteString(LCD_string);
     }
    Workaround that does work
    Code:
     long temperature;
     long pressure;
     long *temperature_ptr = &temperature;
     long *pressure_ptr = &pressure;
     while(1)
     {
      Take_Measurement(temperature_ptr, pressure_ptr);
      sprintf(temp_str, "%lu", temperature);
      sprintf(pres_str, "%lu", pressure);
      strcpy(LCD_string, "T:");
      strcat(LCD_string, temp_str);
      strcat(LCD_string, ", P:");
      strcat(LCD_string, pres_str);
      LCDWriteString(LCD_string);
     }
    For a little more background, for the code I expected to work, the LCD doesn't even display anything. If I use "%u" without the l modifyer then it displays something, but isn't correct.

    I tried simple things like
    Code:
    sprintf(LCD_string, "%u %u %u, temperature, temperature, pressure);
    And this would give me the temperature then a zero then the temperature again instead of the pressure.

    It's got to be something with how I'm handling the string or conversion, but I have no idea.

  2. #2
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    What compiler are you using?

  3. #3
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    Also, "%lu" is "unsigned long", you have declared them as "long"

    It might be a good idea to post your function "Take_Measurement()"

    Can you print the strings using USART?

  4. #4
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    The only thing I see wrong is that you're using an "unsigned" format specifier for a signed value. (I.e., either the format should be "%ld" or the values should be unsigned long.) Other than that, I can't see anything wrong with it. How is LCD_string defined? What is the return value of sprintf?

    Also, this is not necessary:
    Code:
    long *temperature_ptr = &temperature;
    long *pressure_ptr = &pressure;
    //...
    Take_Measurement(temperature_ptr, pressure_ptr);
    Try this instead:
    Code:
    Take_Measurement(&temperature, &pressure);
    Code:
    sprintf(LCD_string, "%u %u %u, temperature, temperature, pressure);
    And this would give me the temperature then a zero then the temperature again instead of the pressure.
    That makes perfect sense if longs are 8 bytes and regular ints are 4 bytes and the machine is little endian.

    temperature low 4 bytes <-- 4-byte format prints this
    temperature high 4 bytes <-- then this (which will be zero if temperature < 2 to power 32
    temperature low 4 bytes <-- then this (which is the same as the first one)
    temperature high 4 bytes <-- all the rest is ignored
    pressure low 4 bytes
    pressure high 4 bytes


    Anyway, it sounds like you might have some undefined behavior occuring as a result of code you haven't posted. Post all your code if you can.
    Last edited by oogabooga; 08-22-2012 at 07:08 PM.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  5. #5
    Registered User
    Join Date
    Jan 2012
    Posts
    8
    I'm using PICC lite 9.83 with MPLAB v8.86.

    I had %ld originally and it doesn't work either (they are supposed to be longs not unsigned so I'll switch back to the d's). I just tried the %ld again to confirm and it still doesn't work.

    This is how I defined the strings
    Code:
    char LCD_string[25];
    char temp_str[10], pres_str[10];
    I'm not familiar with USART, that might show how green I am!

    Code:
    void Take_Measurement(long *temperature_ptr, long *pressure_ptr)
    {
     long raw;
     long up;
     bmp085_start_convert_temp();
     DelayMs(5);
     raw = bmp085_read_temp();
     bmp085_start_convert_pressure();
     DelayMs(26);
     up = bmp085_read_pressure();
     bmp085_convert_temp(raw, temperature_ptr);
     bmp085_convert_pressure(up, pressure_ptr);
    }

  6. #6
    Registered User
    Join Date
    Jan 2012
    Posts
    8
    Here is the main routine and the sensor routine. I looked through the datasheet and didn't find any information on big/little endian. I see how you were dissecting the bytes, but why would it do lower byte for one %u and the higher byte for the next %u. I would have thought it would have just given you the lower byte each time. It seems like the question is what is going on with the l modifyer between the 2 methods. They both compile fine, but the first way doesn't show anything on the screen.

    Code:
    #include "pic.h"
    #include "string.h"
    #include "87x_i2c_master.h"
    #include "lcd_wintek_wdc2401p_rev1.h"
    #include "BMP085_rev2.h"
    #include "delay_jrb.h"
    #include "math.h"
    #include "stdio.h"
    
    
    #define _XTAL_FREQ 8000000
    
    
    __CONFIG(FOSC_HS & WDTE_OFF & PWRTE_OFF & BOREN_ON & LVP_OFF & CPD_OFF & WRT_OFF & CP_OFF);
    
    
    char LCD_string[25];
    char temp_str[10], pres_str[10];
    
    
    int main(void)
    {
    
    
     ADCON1 = 0b00000110; // Set PORTA to all digital I/O
     TRISA  = 0b000000;   // All RAs are outputs for the LCD
     TRISB  = 0b00000000; // All RBs are outputs for the LCD
     TRISD  = 0b00000111; // Set RD0, RD1, and RD2 to inupt and the rest to output
     PSPMODE = 0;         // Parallel Slave Port Mode Select bit, 0 = PORTD functions in general purpose I/O mode
    
    
     long temperature;
     long pressure;
     long *temperature_ptr = &temperature;
     long *pressure_ptr = &pressure;
    
    
     LCDInitialize();       // Initialize the LCD
     i2c_init();            // Initialize the I2C protocol
    
    
     while(1)
     {
    
    
      Take_Measurement(temperature_ptr, pressure_ptr);
    
    
      // For whatever stupid reason, I can't get this to work
      //sprintf(LCD_string, "T:%ld, P:%ld", temperature, pressure);
    
    
      // So I gave up and connected the string together bit by bit
      sprintf(temp_str, "%ld", temperature);
      sprintf(pres_str, "%ld", pressure);
      strcpy(LCD_string, "T:");
      strcat(LCD_string, temp_str);
      strcat(LCD_string, ", P:");
      strcat(LCD_string, pres_str);
    
    
      LCDWriteString(LCD_string);
    
    
     }
     return 0;
    }

    Code:
    /*
    BMP085_rev2 removes the BMP085 initialization call that captures
    the calibration coefficients.  These values have already been
    captured and are now being hard coded to reduce code space.
    */
    
    
    #include "delay_jrb.h"
    #include "BMP085_rev1.h"
    #include "87x_i2c_master.h"
    #include "pic.h"
    
    
    #define BMP085_ADDRESS 0xEE  // I2C address of BMP085
    #define OSS 3
    
    
    /*
    // Old calibration constant variable declarations
    int ac1;
    int ac2; 
    int ac3; 
    unsigned int ac4;
    unsigned int ac5;
    unsigned int ac6;
    int b1; 
    int b2;
    int mb;
    int mc;
    int md;
    */
    
    
    // Calibration constants initialized with known values.
    int ac1 = 0x2117;
    int ac2 = 0xFB5C; 
    int ac3 = 0xC79E; 
    unsigned int ac4 = 0x8803;
    unsigned int ac5 = 0x617E;
    unsigned int ac6 = 0x5B34;
    int b1 = 0x157A; 
    int b2 = 0x0040;
    int mb = 0x8000;
    int mc = 0xD4BD;
    int md = 0x0980;
    
    
    static long x1, x2, b5, b6, x3, b3, p;
    static unsigned long b4, b7;
    
    
    // Read 2 bytes from the BMP085
    // First byte will be from 'address'
    // Second byte will be from 'address'+1
    int bmp085ReadInt(unsigned char address)
    {
     unsigned char msb, lsb;
     i2c_start();
     i2c_write(BMP085_ADDRESS);
     i2c_write(address);
     DelayUs(10);
     i2c_repStart();
     i2c_write(BMP085_ADDRESS | 0x01);
     msb = i2c_read(1);
     lsb = i2c_read(0);
     i2c_stop();
     return (int) ((int) msb<<8 | lsb);
    }
    
    
    unsigned long bmp085ReadLong3(unsigned char address)
    {
     unsigned char msb, lsb, xlsb; 
     i2c_start();
     i2c_write(BMP085_ADDRESS);
     i2c_write(address);
     DelayUs(10);
     i2c_repStart();
     i2c_write(BMP085_ADDRESS | 0x01);
     msb = i2c_read(1);
     lsb = i2c_read(1);
     xlsb = i2c_read(0);
     i2c_stop();
      
     unsigned long result = msb;
     result <<= 8;
     result += lsb;
     result <<= 8;
     result += xlsb;
     result >>= 8-OSS;
     return result;
    //return (long) ((long) msb<<16 | (long)lsb << 8 | (long)xlsb) >> (8-OSS);
    }
    
    
    /*
    // Do not need initialize since calibration coefficients are already known
    void bmp085_init(void)
    {
     ac1 = bmp085ReadInt(0xAA);
     ac2 = bmp085ReadInt(0xAC);
     ac3 = bmp085ReadInt(0xAE);
     ac4 = bmp085ReadInt(0xB0);
     ac5 = bmp085ReadInt(0xB2);
     ac6 = bmp085ReadInt(0xB4);
     b1 = bmp085ReadInt(0xB6);
     b2 = bmp085ReadInt(0xB8);
     mb = bmp085ReadInt(0xBA);
     mc = bmp085ReadInt(0xBC);
     md = bmp085ReadInt(0xBE);
    }
    */
    
    
    void bmp085_start_convert_pressure(void)
    {
     i2c_start();
     i2c_write(BMP085_ADDRESS);
     i2c_write(0xF4);
     i2c_write(0x34 + (OSS<<6));
     DelayUs(10);
     i2c_stop();
    }
    
    
    void bmp085_start_convert_temp(void)
    {
     i2c_start();
     i2c_write(BMP085_ADDRESS);
     i2c_write(0xF4);
     i2c_write(0x2E);
     DelayUs(10);
     i2c_stop();
    }
    
    
    long bmp085_read_temp(void)
    {
     long raw;
     raw = bmp085ReadInt(0xF6);
     return raw;
    }
    
    
    unsigned long bmp085_read_pressure(void)
    {
     unsigned long up;
     up = bmp085ReadLong3(0xF6);
     return up;
    }
    
    
    void bmp085_convert_temp(long raw, long *temperature_ptr)
    {
     x1 = ((long)raw - (long)ac6) * (long)ac5 >> 15;
     x2 = ((long) mc << 11) / (x1 + (long)md);
     b5 = x1 + x2;
     *temperature_ptr = (b5 + 8L) >> 4;
    }
    
    
    // bmp085_convert_temp required before this call!!
    void bmp085_convert_pressure(unsigned long up, long* pressure_ptr)
    {
     b6 = b5 - 4000L;
     x1 = ((signed long)b2 * (b6 * b6 >> 12)) >> 11;
     x2 = (signed long)ac2 * b6 >> 11;
     x3 = x1 + x2;
     b3 = ((((signed long)ac1 * 4L + x3) << OSS) + 2) >> 2;
     x1 = (signed long)ac3 * b6 >> 13;
     x2 = ((signed long)b1 * (b6 * b6 >> 12)) >> 16;
     x3 = ((x1 + x2) + 2) >> 2;
     b4 = ((unsigned long)ac4 * (unsigned long)(x3 + 32768L)) >> 15;
     b7 = (up - (unsigned long)b3) * (50000L >> OSS);
     p  = (signed long)((b7 < 0x80000000) ? (b7 * 2L) / b4 : (b7 / b4) * 2L);
     x1 = (p >> 8) * (p >> 8);
     x1 = (x1 * 3038L) >> 16;
     x2 = (-7357 * p) >> 16;
     *pressure_ptr = p + ((x1 + x2 + 3791L) >> 4);
    }
    
    
    void Take_Measurement(long *temperature_ptr, long *pressure_ptr)
    {
     long raw;
     long up;
     bmp085_start_convert_temp();
     DelayMs(5);
     raw = bmp085_read_temp();
     bmp085_start_convert_pressure();
     DelayMs(26);
     up = bmp085_read_pressure();
     bmp085_convert_temp(raw, temperature_ptr);
     bmp085_convert_pressure(up, pressure_ptr);
    }

  7. #7
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    I'm using PICC lite 9.83 with MPLAB v8.86.
    This is a quote from the printf manual entry:

    Note
    Certain features of printf are only available when linking in alternative libraries. Printing floating point numbers requires that the float to be printed be no larger than the largest possible long integer. In order to use long or float formats, the appropriate supplemental library must be included. See the description on the PICC18 -L library scan option for more details.
    Your best bet when you have a problem like this is to start with a compiler specific forum - HI-TECH has there own forum and microchip has a section for HI-TECH compilers.

  8. #8
    - - - - - - - - oogabooga's Avatar
    Join Date
    Jan 2008
    Posts
    2,808
    I see how you were dissecting the bytes, but why would it do lower byte for one %u and the higher byte[s] for the next %u. I would have thought it would have just given you the lower byte[s] each time.
    The sprintf routine can't know where the beginning of the "next" value is on the stack. It has to trust that you've given it the proper formats so it can find them. On your machine, with %d (or %u) it reads 4 bytes, then the next %d reads another 4 bytes starting right after the first 4 bytes, and so on. (And to be complete, it could also be the case that ints are 2 bytes and longs are 4.)

    And you should make the change to your call to Take_Measurement that I mentioned earlier:
    Code:
        Take_Measurement(&temperature, &pressure);
    and get rid of the extraneous ..._ptr variables. That's just not how C programmers do it!

    click_here seems to have found the solution. You could try this, although it will limit the range of values you can print:
    Code:
    sprintf(LCD_string, "T:%d, P:%d", (int)temperature, (int)pressure);
    You could print sizeof(int) and sizeof(long) to see how big they are. If ints are only 2 bytes, then the max value is 32767. If 4 bytes, then max is 2,147,483,647.
    The cost of software maintenance increases with the square of the programmer's creativity. - Robert D. Bliss

  9. #9
    Registered User
    Join Date
    Jan 2012
    Posts
    8
    Thanks for the help.

    I didn't see any mention of the l length specifier in the compiler user guide, which I thought would be http://ww1.microchip.com/downloads/e...l_PICC_983.pdf. I did find that statement at some other website, but don't know it's source http://www.ro.feri.uni-mb.si/predmet...si/piclite.pdf. either way, I guess I don't know what click_here's solution is, I need an alternate library? The pressure term is pretty much always more than 2 bytes, but I don't think the temperature ever will be.

    I wonder if
    Code:
     sprintf(LCD_string, "T:%s P:%s", ltoa(dum, temperature, 16), ltoa(dum, pressure, 16))
    would work. Never used ltoa before so I'll have to research the syntax.

  10. #10
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    If you didn't want to learn how to link the correct library, then I'd suggest you do what oogabooga suggested -> In fact, I would go one further and tell you to make a union with at least two smaller datatype and print them out one at a time.

    However, I strongly advise that you that the best way is to add the functionality to your project - It's good for you to learn more about your compiler.

    Go to the HI-TECH forum and explain your problem and the fact that you want to add unsigned int32 numbers to the printf family - Someone there will be able to answer you, or put you on the right path.

  11. #11
    TEIAM - problem solved
    Join Date
    Apr 2012
    Location
    Melbourne Australia
    Posts
    1,907
    Research "doprnt.c"

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. call to library gives unexpected results
    By django in forum C Programming
    Replies: 14
    Last Post: 08-25-2011, 03:18 PM
  2. Unexpected Results
    By Bailz in forum C Programming
    Replies: 5
    Last Post: 11-08-2006, 07:10 AM
  3. Unexpected results from function
    By ajdspud in forum C++ Programming
    Replies: 2
    Last Post: 11-27-2005, 04:19 PM
  4. Unexpected results
    By Syked4 in forum C Programming
    Replies: 2
    Last Post: 06-07-2005, 09:41 PM
  5. Unexpected results using argc / *argv[]
    By Morgan in forum C Programming
    Replies: 1
    Last Post: 09-11-2003, 01:38 AM

Tags for this Thread