Problem med funktion i CCS C

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
omega
Inlägg: 43
Blev medlem: 6 november 2005, 14:01:19
Ort: Äholm

Problem med funktion i CCS C

Inlägg av omega »

Hej!

Jag försöker få rätt på en liten funktion som ska läsa av temperaturen på en smt160 temp sensor.

Jag har försökt att göra så lite flyttalsberäkningar som möjligt och det är
kanske där det blir fel.

(Jag använder en 16F628 (inte A) på 4MHz XT.)

Rutinen ska returnera ett heltal t.ex. 224 = 22.4 grader.
Vad gör jag för fel?!? :?

Kod: Markera allt

signed int smtsensor_read()
{
   int k1,k2;
   long high,n;
   int temperatur,inValue,dutycycle;

   k1=320;
   k2=47;
   high=0;
   n=100000;

   while (n > 0 )
   {
      inValue = input_state(PIN_B4);
      if (inValue == 1 )
         high++;
      n--;
   }

   n=100000;

   dutycycle=(high/n)*1000;
   temperatur=(dutycycle-k1)/(k2/100);

return temperatur;
}
sodjan
EF Sponsor
Inlägg: 43247
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Vad gör jag för fel?!?

Först, vad *får* du för fel ?

EDIT:
Dessutom, sensorn ger en PWM signal.
Hur mäter du dutycycle igentligen ?
Beskriv med ord, gärna med riktiga kommentarer i koden, så får vi se...
Användarvisningsbild
omega
Inlägg: 43
Blev medlem: 6 november 2005, 14:01:19
Ort: Äholm

Inlägg av omega »

När jag kör koden så returnerar funktionen bara 31 dec hela tiden oavsett insignal... jag vet inte om jag gör något omvandlingsfel eller om jag bara har en tankevurpa någonstans men jag får bara inte rätt på det. :(
Användarvisningsbild
omega
Inlägg: 43
Blev medlem: 6 november 2005, 14:01:19
Ort: Äholm

Inlägg av omega »

Jag gör x antal samples och sparar hur många av dem som är höga.

dutycycle = antal höga / totala antal samples.
Användarvisningsbild
DuckHead
Inlägg: 146
Blev medlem: 12 februari 2005, 23:48:39
Ort: Malmö (org: Gbg)
Kontakt:

Inlägg av DuckHead »

Testa att ändra input_state(B4) till input(B4). Inte helt 100 på vad din kod gör (lite sent för att sätta sig in i den), men input_state() returnerar väl I/O-riktningen? Du vill väl läsa av pinnenens logiska nivå? input(B4) ändrar riktningen på B4 till input och läser därefter av nivån och returnerar den.
Användarvisningsbild
Andax
Inlägg: 4379
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Inlägg av Andax »

Ett fel är beräkningen av dutycykeln. "high" är alltid samma eller lägre än "n" och divisionen genomförs med heltal som då blir 0 (eller alt 1) och sedan multipliceras med 1000.
Samma problem har du när du beräknar temperaturen. T.ex. (k2/100) blir troligtvis 0 som du sedan dividerar med. Division med 0 är odefinierat.

Först kan du ju ändra så att du dividerar med 100 istället för att dividera med 100000 och multiplicera med 1000. Det blir mer rätt.

I temperaturberäkningen kan du ju flytta upp 100 så att det blir en multiplikation istället...
Användarvisningsbild
JimmyAndersson
Inlägg: 26548
Blev medlem: 6 augusti 2005, 21:23:33
Ort: Oskarshamn (En bit utanför)
Kontakt:

Inlägg av JimmyAndersson »

Jag är också inne på Andax idé.

omega:
Har du kollat så att inValue verkligen blir 1 (åtminstone en gång) ?

Om inte hela koden är kilometerlång så får du gärna visa den. Jag programmerar egentligen inte C, men jag förstår vad som händer iallafall. :)
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Jag är inne på att du mätar på helt fel sätt!

Om du har pulsar varför mätar du då inte med CCP? Då kan du få exakta data och bra upplösning och låg belastning (interruptstyrd t.ex.).
Användarvisningsbild
DuckHead
Inlägg: 146
Blev medlem: 12 februari 2005, 23:48:39
Ort: Malmö (org: Gbg)
Kontakt:

Inlägg av DuckHead »

Tycker också metoden är lite knepig, jag använde följande lilla programkod (CCS C), när jag skulle sampla värden från en accelerometer (pwm). Utnyttjar en räknare... ett steg åt rätt håll.

Kod: Markera allt

#include <16F874A.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP,DEBUG
#use delay(clock=16000000)
#use rs232(baud=19600, BITS=8, xmit=PIN_C6, rcv=PIN_C7)

long pulses,flank2,flank3;
float hertz,pulsew;
int indata;

#int_rda
void serial_isr() {
	indata=getc();
}

void main()
{
	//INITSIERING
	delay_ms(300);
	setup_timer_1 ( T1_INTERNAL | T1_DIV_BY_1 ); // setup time scale
	enable_interrupts(global);
	enable_interrupts(int_rda);

	do {
		while( input(PIN_D2)); // if now already up, wait...
		while(!input(PIN_D2)); // if now already down, wait...
		while( input(PIN_D2)); // if going from down to up.

		set_timer1(0); // set timer to zero
		while(!input(PIN_D2)); // if low now.. wait
		flank2=get_timer1();
		while( input(PIN_D2)); // if high a second time, get time elapsed from timer.
		flank3 = get_timer1();
		pulsew = (float) flank2/ (float) flank3;
		printf("%f\n", pulsew);
	} while(true);
Användarvisningsbild
Icecap
Inlägg: 26632
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Kod: Markera allt

      pulsew = (float) flank2/ (float) flank3;
      printf("%f\n", pulsew);
Ush.... float! .... och polling...

Men OK, när du har de rätta värden mätta och du ska räkna Temp = X*faktor/Y ska du FÖRST räkna X * faktor och SEDAN dela med Y, då klarar du dig galant utan float.

Jag orkar inte leta upp databladet för sensorn så vad minste pulslängd är har jag ingen aning om men jag har ett produkt (som jag säljer kommerciellt) som mätar just pulser med CCP och fångar båda stigande och fallande flank.

Kod: Markera allt

typedef union
  {
  unsigned int  Word;
  unsigned char Byte[2];
  } W_AND_B;

typedef union
  {
  dword DW;
  word  WO[2];
  byte  BY[4];
  } T_ALL_ACCESS;
typedef unsigned char byte;
typedef unsigned int  word;

//---------- Variables ----------
W_AND_B Pulse_Start;
W_AND_B Pulse_Stop;

#define Overflow Biggie.BY[2]

struct
  {
  byte Running : 1;
  byte Update  : 1;
  byte Timeout : 1;
  } Flag;
T_ALL_ACCESS Biggie;

//------------------------------ Here we go ------------------------------
void interrupt(void)
  {
  if(PIR1.TMR1IF)
    {
    PIR1.TMR1IF = false; // Acknowledge interrupt
    if(Flag.Running) Overflow++;
    if(Overflow > 7) Flag.Timeout = true;
    }
  if(PIR1.CCP1IF)
    {
    if(CCP1CON.CCP1M0) // If capturing rising edges
      { // Rising edge, save data and make it capture falling edge
      if(!Flag.Running && !Flag.Update)
        {
        Pulse_Start.Byte[0] = CCPR1L; // Transfer the value, low byte
        Pulse_Start.Byte[1] = CCPR1H; // Transfer the value, high byte
        Overflow            =      0; // Start from known state
        Flag.Running        =   true;
        Flag.Timeout        =  false;
        }
      CCP1CON = 0x00; // To avoid false interrupts
      CCP1CON = 0x04; // Make it capture falling edge
      }
    else
      { // It was a falling edge
      if(Flag.Running)
        { // Somethin's cooking...
        Pulse_Stop.Byte[0] = CCPR1L; // Transfer the value, low byte
        Pulse_Stop.Byte[1] = CCPR1H; // Transfer the value, high byte
        Flag.Update = true;
        }
      CCP1CON = 0x00; // To avoid false interrupts
      CCP1CON = 0x05; // Make it capture rising edge
      }
    PIR1.CCP1IF = false; // Acknowledge interrupt
    }
  PIR1 = 0x00; // Clear all interrupts
  }

void main(void)
  {
  Initialize();
  while(true)
    {
    if(Flag.Update)
      {
      // Now calculate and send out data
      Biggie.BY[3] = 0;
      if(Pulse_Stop.Word <= Pulse_Start.Word) Biggie.WO[1]--;
      Biggie.WO[0]  = Pulse_Stop.Word - Pulse_Start.Word;
      Flag.Update = false; // Now it's calculated, allow the next measurement to begin
      // Här kan man då räkna om pulserna till temperatur
      }
Detta är ett urval av den kod jag använder, jag har saxat en del men du fattar nog grundgrejen.
Användarvisningsbild
TomasL
EF Sponsor
Inlägg: 46929
Blev medlem: 23 september 2006, 23:54:55
Ort: Borås
Kontakt:

Inlägg av TomasL »

dutycycle=(high/n)*1000;
Blir alltid 0 eller 1 eftersom du använder "int" (en "int" kan aldrig bli mindre än 1)
high=5000, 5000/10000=0,5->=0*1000=0

Bättre så här:
dutycycle= high/10; // du behöver inte använda "n" här, tar onödig plats och tid att återinitiera "n"

dutycycle=5000/10=500
temperatur=(dutycycle-k1)/(k2/100);
Återigen k2/100=0,47 dvs 0 eftersom du anväder "int"
även om du ändrar till (dutycycle-k1)/k2*100 blir det fel, då du först dividerar ner dig, och eftersom det är "int" blir decimaldelen då bortplockad.
ex.vis dutycycle=500, 500-320=180, 180/47=3,82.... ->=3*100=300

Bättre så här:
temperatur=(dutycycle-k1)*100/k2; // på så sätt "skalar" du inte bort decimalerna

Med samma exempel: 500-320=180*100=18000/47=382

Lycka till :)
Användarvisningsbild
omega
Inlägg: 43
Blev medlem: 6 november 2005, 14:01:19
Ort: Äholm

Inlägg av omega »

Tack för alla tips! Nu fattar jag vilka räknefel jag har gjort.. tror jag.

Orsaken till att jag inte använder CCP är att jag tyckte det verkade kul att göra hela koden själv. Om jag får det att fungera någon gång så blir det flera sensorer fördelade på ett antal uC ben. :)

Jag har gjort ett testprogram för att se om det fungerar men det fungerar fortfarande inte.. jag kan inte begripa varför :(

Här kommer hela testkoden.. jag har gått igenom koden ett par gånger och testkört med sensor ingång på PORTB:4 och LED på PORTA:2

Hjälp!! :shock:

Kod: Markera allt

#include <16F628.h>

#FUSES NOWDT             //No Watch Dog Timer
#FUSES XT                    //Crystal osc <= 4mhz
#FUSES NOPUT              //No Power Up Timer
#FUSES NOPROTECT       //Code not protected from reading
#FUSES BROWNOUT        //Reset when brownout detected
#FUSES NOMCLR             //Master Clear pin used for I/O
#FUSES NOLVP                //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NOCPD               //No EE protection

#use delay(clock=4000000)



signed int smtsensor_read()
{
   int k1,k2,inValue,dutycycle;
   long high,n;
   signed int temperatur;

   k1=3200;
   k2=47;
   high=0;

   n=100000;                           //antal samples

   while (n > 0 )
   {
      inValue = input(PIN_B4);
      if (inValue == 1 )               //Räkna antalet 1'or på ingången
         high++;
      n--;
   }

   n=100000;                           //Återställ inför beräkning

   dutycycle=high/10;                  //4375 samples = 43,75% = 25 grader C

   temperatur=((dutycycle-k1) * 10)/k2;   //t = (dc-0,32)/0,0047

return temperatur;                     //returnera temp, 25.5gr = 255
}


void main()
{
   signed int readtemp;
   setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   SET_TRIS_A( 0b00000000 );
   // A7-A0 utgångar
   SET_TRIS_B( 0b11111111 );
   // A7-A0 ingångar

   while (1)
   {
      readtemp=smtsensor_read();   //läs temp, spara i readtemp

      if ( readtemp >= 260 )       //om 26 grader eller högre
      {
         output_high(PIN_A2);      //tänd LED på PORTA :2
      }
      if ( readtemp < 260 )        //om lägre än 26 grader
      {
         output_low(PIN_A2);       //släck LED på PORTA :2
      }
   }

}
Användarvisningsbild
Andax
Inlägg: 4379
Blev medlem: 4 juli 2005, 23:27:38
Ort: Jönköping

Inlägg av Andax »

Testa att sätt "high" hårdkodat till lite olika värden precis innan beräkningen av dutycycle och temperatur. Då kan du ju se vad som beräknas och kan jämföra med vad det borde bli om high är si eller så...
Användarvisningsbild
omega
Inlägg: 43
Blev medlem: 6 november 2005, 14:01:19
Ort: Äholm

Inlägg av omega »

Jag har testat att hårdkoda in ett värde på high .. tror jag provade med ca 43750 samples (43,8% dutyc.). I simulering får jag ut ca 250 dvs 25 grader.. och det stämmer med formel.
Kan det vara så att jag samplar för kort stund eller för länge?

Om jag inte minns fel så har smt160 en utsignal på mellan 1-4 kHz.

Jag kör min pic 16f628 på 4Mhz vilket motsvarar ca 1us per instruktion.

Kan det vara här jag gör något fel? Vad säger ni? Någon som har lite experthjälp?
Användarvisningsbild
bengt-re
EF Sponsor
Inlägg: 4829
Blev medlem: 4 april 2005, 16:18:59
Skype: bengt-re
Ort: Söder om söder
Kontakt:

Inlägg av bengt-re »

När det gäller SMT160 är inte frekvensen styrande så den typen av genomsnittande blir inte så bra. Man skall ta EN hög låg och räkna duty på den. Det gäller också att ta den låg/hög som "hör ihop". Har för mig att det är låg med efterföljande hög som hör ihop. Vill man medelvärdesbilda så gör det på uträknade temperaturen och INTE på cyckel tiderna för bästa noggranhet.

EDIT:
Det är bra att köra på så hög klockfrekens sommöjligt för bästa noggranhet, men det går med 4MHz också - jag har dock använt 20MHz til de kort jag använt SMT160 bara för att få upp noggranheten.
Skriv svar