Problem med intToStr i C

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
SeniorLemuren
Inlägg: 8432
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Problem med intToStr i C

Inlägg av SeniorLemuren »

Jag använder PIC16F887 och microC Pro for PIC. Nu har jag ett problem som man kanske borde kunnat om man varit lite mer flitig med assembler.
Jag gör om ett tal till en sträng med

Kod: Markera allt

char meas_temp[7];
.
.
.
IntToStr(spi_out, meas_temp);
som sedan skrivs ut på LCD.
"IntToStr beskrivs enligt följande i microC PRO: Converts input signed integer number to a string. The output string has fixed width of 7 characters including null character at the end (string termination). The output string is right justified and the remaining positions on the left (if any) are filled with blanks.

När jag gör build får jag följande meddelande från kompilatorn:
IRP bit must be set manually for indirect access to "meas_temp" variable.
Om man tittar på hur mycket ram-minne som används så kommer detta fel upp när 177 bytes av RAM används. Tar jag bort en av tecknen i den textsträng som jag vill skriva ut på LCD så visar compilatorn 175 bytes RAM och allt funkar och inget felmedelande från compilatorn. Det betyder vad jag kan förstå att rutinen "IntToStr()" inte hanterar bankselect korrekt? Hur löser jag detta?. Ett fall för sodjan :) antar jag. Jag trodde man skulle slippa den här typen av problem i C.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Problem med intToStr i C

Inlägg av sodjan »

IRP har inte med de traditionella (4) minnesbankerna att göra, den är för att
välja minneshalva för indexerad adressering. Se "2.4 Indirect Addressing,
INDF and FSR Registers" och bilden på nästa sida.

Manualen till MicroC har ett kapitel om just detta, "PIC16 specifics".
Där finns även tips om en "work around".
Användarvisningsbild
SeniorLemuren
Inlägg: 8432
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Problem med intToStr i C. Läst men ej förstått.

Inlägg av SeniorLemuren »

Ja det där fattade jag nada av. Visst förstår jag vad det står, men hur jag skall coda rent praktiskt i programmet kan jag inte få ihop. Kan man hitta programexempel på hur det kan se ut? :humm:

Edit: Har lagt in en fråga i http://www.mikroe.com/forum också. Men det dröjer med svar.
Användarvisningsbild
Icecap
Inlägg: 26651
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Problem med intToStr i C

Inlägg av Icecap »

Om den resulterande strängen är (upp till) 7 tecken lång som värst måste du ha plats för den avslutande EOL (0x00) varför du måste reservera 8 minnesplatser!

Sedan är det frågan om just IntToStr är rätt lösning...

Kod: Markera allt

char Result[7]; // Could at most show "-xxxxx" which should be 6 places + EOL = 7 places

void Convert_Value(int Value)
  {
  unsigned char Index, Sign;
  if(Value < 0)
    {
    Sign = 1; // Remember that it's negative
    Value = -Value; // Make it positive for further work
    Index = 2;
    }
  else
    {
    Sign = 0;  // Nope, normal positive
    Index = 1;
    }
  if(Value >= 10000) Index++;
  if(Value >= 1000) Index++;
  if(Value >= 100) Index++;
  if(Value >= 10) Index++;
  Result[Index] = 0x00; // Inject the EOL
  Index--;
  do
    {
    Result[Index] = (Value % 10) + '0';
    Index--;
    Value /= 10;
    }
  while(Value);
  if(Sign) Result[0] = '-';
  }
Användarvisningsbild
SeniorLemuren
Inlägg: 8432
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Problem med intToStr i C

Inlägg av SeniorLemuren »

Om IntToStr är rätt eller fel att använda kan man ju fråga sig, men eftersom den finns så vilie jag använda den. Det vore dessutom trevligt om man kunde lära sig hur man hanterar problem av den här typen med RAM. På sid 33 i denna manual kan man läsa om problemet. Tyvärr så gick det inte att kopiera endast den aktuella sidan i manualen, så jag fick länka till hela.

Där förklaras ju bra hur det ligger till, jag förstår bara inte hur man skall använda det som står där, i koden rent praktiskt. Eftersom RAM bank 0 och 1 är använd av variabler, så gäller det tydligen att använda sig av bank 2 eller 3 till "meas_temp" variable rutin. Något svar från microeforumet har inte dykt upp så jag får förlita mig på gamla kära EF. :)

Skickar med programkoden här. Vissa delar av programmet där stegmotor slås av och på är än så länge bara testrutiner och skall inte tas på allvar.

Nu skall jag åka ut och laga en storköks-diskmaskin på ett café. Hoppas någon hittar en bra lösning på detta problem medan jag kryper omkring på ett blött golv under en bautadiskmaskin. :)

Kod: Markera allt

/**************************************************************
* Programet Skrivet av Urban Hahne
* Filnamn: MaxDS18B20.c
* Funktion: Läser in temperaturinfo från MAX6675 kopplad till 
* termogivare typ k. Samt temperaturinfo från DS10B20-givare.
* Microkontroller: PIC16F887
* Programmer skrivet i microC for PIC och använder färdiga
* rutiner för LCD, One-wire och SPI-communication.* Delar 
* av koden är plankad och modifierad från microC´s exempelkoder.
**************************************************************/

// LCD module connections
sbit LCD_RS at RB4_bit;
sbit LCD_EN at RB7_bit;
sbit LCD_D4 at RD0_bit;
sbit LCD_D5 at RD1_bit;
sbit LCD_D6 at RD2_bit;
sbit LCD_D7 at RD3_bit;

sbit LCD_RS_Direction at TRISB4_bit;
sbit LCD_EN_Direction at TRISB7_bit;
sbit LCD_D4_Direction at TRISD0_bit;
sbit LCD_D5_Direction at TRISD1_bit;
sbit LCD_D6_Direction at TRISD2_bit;
sbit LCD_D7_Direction at TRISD3_bit;
// End LCD module connections

//******************* output bits ***************************
sbit circ_pump at RA4_bit ;
sbit blower at RA6_bit ;
//******************* Stegmotor anslutningar ****************
sbit C_E at RA0_bit ;          //0 = count
sbit MON at RA1_bit ;          //M0 M2 till 0 för att slå av motor
sbit RST at RA2_bit ;          //Räknar upp vid 0
sbit DIR at RA3_bit ;         //aktiv låg. Sätt till 1 vid ränkning
sbit STP at RA5_bit;          //Stega motor
long int step = 0;
//************************************************************

//**************** limitations ******************************
unsigned  int water_min_temp = 75 ;// no fire stop the blower
unsigned  int water_max_temp = 85 ;// to hoot give less speed to the blower
unsigned  int aire_max_temp = 30 ;// close aire_gate
unsigned  int aire_min_temp = 25 ;// open aire_gate
unsigned  int water_cold_temp = 24 ;// stop circ.pump
unsigned  int water_start_temp = 27 ;// start circ.pump
//*******************indicator****************************

//******************* definitioner ************************
#define stegtid   delay_us(500);
#define paustid   delay_ms(200);


// **************** MAX6675 Stuff ****************************
unsigned char spi_out_H;
unsigned char spi_out_L;
unsigned int spi_out;
unsigned char buffer;
char meas_temp[7];
//*******************texter****************************
char sPstop[] = "Pump Stoppad" ;
char sPstart[] = "Pump Startad" ;
char sMoreAire[]  ="Mer Luft";
char sLessAire[]  ="Mindre Luft" ;

// **************** DS18B20 Stuff ****************************
const unsigned short TEMP_RESOLUTION = 12;  //18B20: = 12bit
unsigned char temp_out[7];
unsigned int temp;
unsigned short stemp ;
//************************************************************
void step_right(){
     DIR = 1 ;
     MON = 1 ;
     step = 1000;
       do 
  {
       STP = 1 ;
       stegtid;
    STP = 0 ;
    stegtid;
    step--;
  } while(step);
}
void step_left()
{
     DIR = 0 ;
     MON = 1 ;
     step = 1000;
       do 
  {
       STP = 1 ;
       stegtid;
    STP = 0 ;
    stegtid;
    step--;
  } while(step);
}


void init_cpu() 
{
  //***************** Config and init-stuff *******************
  ANSEL  = 0;                    // Configure AN pins as digital I/O
  ANSELH = 0;
  C1ON_bit = 0;                  // Disable comparators
  C2ON_bit = 0;

  TRISA = 0b00000000 ;
  TRISB = 0b00000000 ;
  TRISC = 0b00010000 ;
  TRISD = 0b00000000 ;
  TRISE = 0b00001001 ;
  PORTC = 0b00000001 ;
}
  void init_step_motor() 
{
  //****************** init step *******************************
  C_E = 0 ; // Counter = 0 for counting
  DIR = 1 ; //depend on motor connection
  RST = 1 ; // must be 1 for counting
  MON = 0 ; // motor on/off
  //***********************************************************
}
void init_spi()
{
  //******************** init SPI *****************************
  SPI1_Init_Advanced(_SPI_MASTER_OSC_DIV4,    // Initialize PIC as master
  _SPI_DATA_SAMPLE_END,        // Data sample at end
  _SPI_CLK_IDLE_LOW,
  _SPI_HIGH_2_LOW);
}
  //***********************************************************
void init_lcd()
{
  Lcd_Init();                                // Initialize LCD
  Lcd_Cmd(_LCD_CLEAR);                       // Clear LCD
  Lcd_Cmd(_LCD_CURSOR_OFF);                  // Turn cursor off
}

  
  //************** for test (take away later ) *****************
void motor_test()
{
  LCD_OUT(1,1, "Motor test");
  LCD_OUT(2,1, "One rev. Right  ");
  step_right();
  Delay_ms(500);
  LCD_OUT(2,1, "One rev. Left   ");
  step_left();
  MON = 0 ;
  LCD_OUT(2,1, "Motor Ok.       ");
  Delay_ms(1000);
  Lcd_Cmd(_LCD_CLEAR);
  //***********************************************************
}
void read_DS18B20()
{
    //******* Perform DS18B20 temperature reading *************
    Ow_Reset(&PORTE, 2);                     // Onewire reset signal
    Ow_Write(&PORTE, 2, 0xCC);               // Issue command SKIP_ROM
    Ow_Write(&PORTE, 2, 0x44);               // Issue command CONVERT_T
    Delay_us(120);
    Ow_Reset(&PORTE, 2);
    Ow_Write(&PORTE, 2, 0xCC);                 // Issue command SKIP_ROM
    Ow_Write(&PORTE, 2, 0xBE);               // Issue command READ_SCRATCHPAD
    // this is the 16 bit from DS18B20
    temp =  Ow_Read(&PORTE, 2);
    temp = (Ow_Read(&PORTE, 2) << 8) + temp;
    temp = temp >> 4 ; //Use only integer part of temp.
    IntToStr(temp,temp_out);
    LCD_OUT(2,1, "Vattentemp ");
    LCD_Chr_Cp (temp_out[3]);                   // hundreds
    LCD_Chr_Cp (temp_out[4]);                   // ten
    LCD_Chr_Cp (temp_out[5]);                   // one
    LCD_Chr_Cp (223);                           // degree symbol
    LCD_Out_Cp ("C");
    Delay_ms(300);
}                                            // at least 220ms, see
    //**********END DS18B20 temperature reading ****************
    
    //******* Perform MAX6675 temperature reading *************
void read_MAX6675()
{
        RC0_bit = 0;                                // CS=0
        spi_out_H = Spi1_Read(buffer);          // upper 8 bit from MAX6675
        spi_out_L = Spi1_Read(buffer);          // lower 8 bit
        RC0_bit = 1;                            // CS=1
        Delay_ms(1);
        spi_out = spi_out_H*256+spi_out_L;
        spi_out = spi_out >> 4;             // formatting result
        spi_out = spi_out* 26/100;
        IntToStr(spi_out, meas_temp);
        LCD_OUT(1,1, "Hetluft    ");
        //LCD_Chr_Cp (meas_temp[2]);        // thousands
        LCD_Chr_Cp (meas_temp[3]);          // hundreds
        LCD_Chr_Cp (meas_temp[4]);          // tens
        LCD_Chr_Cp (meas_temp[5]);          // ones
        LCD_Chr_Cp (223);                   // degree symbol
        LCD_Out_Cp ("C");
        Delay_ms(300);                    // at least 220ms, see datasheet}
}
//************  check_water_temp  *****************
void check_water_temp(){

        if (temp <= water_cold_temp)
        {
           circ_pump = 0;
           LCD_CMD(144);
           Lcd_Out_CP(sPstop);

        }
        else
        if (temp >= water_start_temp)
        {
           circ_pump = 1;
           LCD_CMD(144);
           Lcd_Out_CP(sPstart);
        }
}
//************  check_aire_temp  *****************
void check_aire_temp(){

        if (spi_out <= aire_min_temp)
        {
           //step_right();
           LCD_CMD(208);
           Lcd_Out_CP(sMoreAire);
        }
        else
        if (spi_out >= aire_max_temp)
        {
          // step_left();
          LCD_CMD(208);
          Lcd_Out_CP(sLessAire);
        }
}
void main(){
     init_cpu();
     init_step_motor();
     init_spi();
     init_lcd();
     motor_test();
//************* main loop ****************
do {
     read_DS18B20();
     check_water_temp();
     read_MAX6675();
     check_aire_temp();
   } while (1);

}
Användarvisningsbild
stekern
Inlägg: 453
Blev medlem: 2 november 2008, 08:24:18
Ort: Esbo, Finland

Re: Problem med intToStr i C

Inlägg av stekern »

Icecap skrev: Sedan är det frågan om just IntToStr är rätt lösning...

Kod: Markera allt

char Result[7]; // Could at most show "-xxxxx" which should be 6 places + EOL = 7 places

void Convert_Value(int Value)
  {
...
  }
Sedan är det frågan om just Convert_Value är rätt lösning...

Kod: Markera allt

snprintf(Result, 7, "%d", Value);
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Problem med intToStr i C

Inlägg av sodjan »

> Där förklaras ju bra hur det ligger till, jag förstår bara inte hur man skall använda det som står där, i koden rent praktiskt.

Problemet är alltså att alla variabler som accessas indexerat (bl.a alla pekare i C) måste ligga
i bank 0 eller 1 (d.v.s första halvan av RAM) eftersom kompilatorn inte hanterar IRP biten.

Deras förslag är att flytta alla ej-indexerade variabler till bank 2-3 (andra halvan av minnet),
jag antar att länkaren då kommer att "flytta ner" pekarna till bank 0-1.

På sidan 33 hänvisar de till "linker directive absolute". En snabb sökning ger svarat på sidan 37,
"linker directives". Speciellt första stycket "Directive absolute".

Du får/kan alltså lägga till detta till de variabler som inte är (accessas via) pekare.
Användarvisningsbild
SeniorLemuren
Inlägg: 8432
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Problem med intToStr i C

Inlägg av SeniorLemuren »

Ok. Jag fick nu en annan lösning från microE-forumet som var trevlig. Definiera alla fasta texter som const.
och kopiera dom till RAM när dom skall skrivas ut.Det frigör en hel massa utrymme i bank 0 och bank 1

Kod: Markera allt

//**************** RAM Saver LCD Text ************************
const char txt1[] = "Motor test";
const char txt2[] = "One rev. Right  ";
const char txt3[] = "One rev. Left   ";
const char txt4[] = "Motor Ok.       ";
.
.
.
.
char msg[17]; //declare array set 16 bytes of RAM aside for copying into

// copy const to ram string
char * CopyConst2Ram(char * dest, const char * src){
char * d ;
d = dest;
for(;*dest++ = *src++;)
;
return d;
}
.
.
.Lcd_Out_CP(CopyConst2Ram(msg,txt2));
}
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: Problem med intToStr i C

Inlägg av sodjan »

Ja, det är ju generellt sett en bättre metod.
Bara dumt att ta upp "dyrbart" RAM för fasta texter...
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Problem med intToStr i C

Inlägg av bearing »

Istället för att kopiera från ROM till RAM och använda en funktion som skickar från RAM till LCD, skulle du kunna skriva en funktion som skickar texter ur ROM till LCD direkt.
Användarvisningsbild
Icecap
Inlägg: 26651
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Problem med intToStr i C

Inlägg av Icecap »

stekern: visst är printf en bra grej till detta - men då måste kompilern importera hela biblioteket och då kan det bli trångt... Jag har en "snikversion" av printf liggande och den sparar en hel del minne. Den kan inte skriva ut floats och vissa mer avancerade saker men till vardagsbruk fungerar den mycket bra, just för att den inte tar så mycket plats.
Användarvisningsbild
SeniorLemuren
Inlägg: 8432
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Problem med intToStr i C

Inlägg av SeniorLemuren »

bearing skrev:Istället för att kopiera från ROM till RAM och använda en funktion som skickar från RAM till LCD, skulle du kunna skriva en funktion som skickar texter ur ROM till LCD direkt.
Kan du förklara det lite närmare och vilka fördelar skulle det ha jämfört med ROM2RAM

Med ROM2RAM behövs endast "char msg[17]; //declare array set 16 bytes of RAM aside for copying into" 17 bytes, i stället för alla olika meddelandetexter, det är ju en droppe i havet.

Dessutom så kan man använda den färdiga LCD-rutinen som finns i microC biblioteket, det går inte om inte texten ligger i RAM.
Användarvisningsbild
Icecap
Inlägg: 26651
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Re: Problem med intToStr i C

Inlägg av Icecap »

Om det finns en LCD-rutin som skriver ut ett tecken kan du fint klara dig utan att behöva denna ROM2RAM-grej!

Självklart ska man ange alla konstanter som just konstanter, att skriva ut en sträng från minnet är inte svårare än att skicka en minnesadress till den rutin, den läser sedan från ROM till den hittar en EOL (0x00) och skriver ut alla tecken den hittar på vägen.

Kod: Markera allt

const char txt1[] = "Motor test";
const char txt2[] = "One rev. Right  ";
const char txt3[] = "One rev. Left   ";
const char txt4[] = "Motor Ok.       ";

void Print_Const(char* Data)
  {
  while(*Data)
    {
    LCD_Out_Char(*Data); // Vad den rutin nu annars heter som skriver ut ett tecken på LCD'n. Finns den inte är det dags att skriva själv, det är plätt-lätt.
    Data++;
    }
  }
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Problem med intToStr i C

Inlägg av bearing »

Förslaget kom mest ur en allmän strävan att göra saker så enkla som möjligt. Tänkte att programmet borde bli enklare, snabbare, och ta mindre minne. Men om detta inte är något problem för dig så går det väl bra att göra som du gör nu. Och eftersom att du använder en biblioteksfunktion för att skriva till LCDn, är säkert nuvarande lösning enklare än mitt förslag.
Användarvisningsbild
SeniorLemuren
Inlägg: 8432
Blev medlem: 26 maj 2009, 12:20:37
Ort: Kristinehamn

Re: Problem med intToStr i C

Inlägg av SeniorLemuren »

Icecap skrev:Om det finns en LCD-rutin som skriver ut ett tecken kan du fint klara dig utan att behöva denna ROM2RAM-grej!

Självklart ska man ange alla konstanter som just konstanter, att skriva ut en sträng från minnet är inte svårare än att skicka en minnesadress till den rutin, den läser sedan från ROM till den hittar en EOL (0x00) och skriver ut alla tecken den hittar på vägen.

Kod: Markera allt

const char txt1[] = "Motor test";
const char txt2[] = "One rev. Right  ";
const char txt3[] = "One rev. Left   ";
const char txt4[] = "Motor Ok.       ";

void Print_Const(char* Data)
  {
  while(*Data)
    {
    LCD_Out_Char(*Data); // Vad den rutin nu annars heter som skriver ut ett tecken på LCD'n. Finns den inte är det dags att skriva själv, det är plätt-lätt.
    Data++;
    }
  }
Microelektronika meddelade detta
If you take a look of implementation of Lcd_Out_Cp function
Help file:Lcd Library wrote:

Kod: Markera allt

void Lcd_Out_Cp(char *text);
you can see that function require pointer to char, not pointer to const char.

If you wish to use Lcd_Out_Cp() function you must place text in RAM.
Som det görs nu, vilket var en enkel lösning så funkar det med microC LCD-rutin. Jag hade förut skrivit en LCD-rutin i HiTech men den funkade inte här så jag tyckte det var enklare att använda den befintliga än att göra om min rutin.

Allt funkar perfekt nu så det är bara att gå vidare. Skall börja slipa på hur motorn skall styras för att få en bra reglering. Åtekommer säkert med massor av frågor om det. :)
Skriv svar