Ik krijg altijd een maximale waarde van 255 als ik de tc77 zo aanstuur. Hoe kan dit?

int tc77(void)
{
 int x,y;

 	PORTCbits.RC6=0;    		//  /cs active
 	TRISCbits.TRISC7=1;         //  MOSI high impedance
 	Delay1KTCYx(1);
 	x= ReadSPI();               // high byte from temperature
	y= ReadSPI();               //low byte from temperature  	Delay1KTCYx(1);
	PORTCbits.RC6=1;  	     	//  /cs =1 
        return x;
}

omdat je SPI hardware bytes uitleest. Een byte, 8 bit, heeft een maximale waarde van 8 bits "1", wat 255 is. Als je sensor meer dan 8 bit uitstuurd (wat het geval is denk ik, gezien je 2 bytes leest), zul je beide getallen moeten retourneren:

int tc77(void)
{
 int x,y;
 
 	PORTCbits.RC6=0;    		//  /cs active
 	TRISCbits.TRISC7=1;         //  MOSI high impedance
 	Delay1KTCYx(1);
 	x= ReadSPI();               // high byte from temperature
	y= ReadSPI();               //low byte from temperature  	Delay1KTCYx(1);
	PORTCbits.RC6=1;  	     	//  /cs =1 
        return x * 256 + y;
}

Klopt wat roadrunner zegt, de TC77 geeft een digital WORD (13-bit). Een TC74 trouwens een 8-bit Digital WORD. Dus zul je dit moeten inlezen als een WORD variable.

Dim WordVar as Word (16-bits)

Dag Huub,

1) De temperatuurwaarde van de TC77 heeft een 13-bits resolutie welke 3 bits naar links verschoven zit in een 16-bits register.
(LSB temperatuur = b3). Je SW moet de data dus 3 bits naar rechts verschuiven.

2) De TS77 chip kan negatieve temperaturen weergeven (b13 = teken bit, two-complement). Als je die bit meeschuift loop je het risico om het teken te vernietigen. Pas dus op.

3)Het prototype is:

unsigned char ReadSPI( void );

Alhoewel het niet noodzakelijk verkeerd is om de return value aan een integer toe te wijzen is dit veelal een "slechte gewoonte" omdat je jezelf op het verkeerde spoor zet wanneer je bewerkingen op die variabele gaat toepassen. Bytes (unsigned char) optellen is niet noodzakelijk hetzelfde als een 16/32-bits integers optellen.

De code (antwoord #8) in deze link laat zien hoe dit netjes en correct gebeurt, wel met een struct, waarvan het gebruik in dit geval overigens ook een goede gewoonte is: http://www.microchip.com/forums/m284713.aspx

Succes ermee.

Mag ik deze reactie even finaal de grond in rammen?
Als het even kan, vermijd dan endian afhankelijkheid. Dat is namelijk wat er in dat
Dat betekent dus dat je er niet vanuit gaat dat de lsb voor de msb komt of andersom. Als je dat wel doet koppel je je code expliciet aan 1 type processor. Het venijnige is dat je het veel minder makkelijk in code herkent dan bijvoorbeeld een specifiek register.
En bovendien, een beetje fatsoenlijke compiler zal een constructie als w=a*256+b herkennen en optimaliseren. Omdat 256 sowieso al niet in en byte past zal dit altijd goed gaan, ook als a en b chars zijn.
In het voorbeeld wordt vanalles met bits, shiften en signed en unsigned gedaan, dat maakt het nodeloos ingewikkeld en alles behalve intuïtief. Vergelijk die code

     if ((RawTemp.uVal == 0x0000) || (RawTemp.uVal == 0xFFFF))
     {
         strcpypgm2ram( buffer, (rom char *)"Problem reading TC77." );
     }
     else
     {
         // Right align the 13 bit temperature reading
         RawTemp.uVal >>= 3;
 
         // Sign extend the value from 13 bits to 16 bits
         if(((BYTE_VAL*)&RawTemp.byte.MSB)->bits.b4)
         {
             RawTemp.byte.MSB |= 0xE0;
         }
 
         // Convert the TC77 integer to a scaled one with units 0.0001 degrees C
         ScaledTemp = (long)RawTemp.Val * (long)625;
 
         // Convert the integer to a string and find its length
         pTempString = ltoa( ScaledTemp, tempString );
         strLen = strlen( tempString );

eens hiermee

     // Convert the TC77 integer to a scaled one with units 0.0001 degrees C
     ScaledTemp = (((int) RawTempMSB*256 + RawTempLSB) / 8) * (long)625;

     if ((ScaledTemp == 0x0000) || (ScaledTemp == 0xFFFF))
     {
         strcpypgm2ram( buffer, (rom char *)"Problem reading TC77." );
     }
     else
     {
         // Convert the integer to a string and find its length
         pTempString = ltoa( ScaledTemp, tempString );
         strLen = strlen( tempString );

Dit doet hetzelfde, maar is veel leesbaarder en waarschijnlijk zelfs efficiënter!

[Bericht gewijzigd door roadrunner84 op (14%)]

Dag roadrunner,

Behalve dan hoe hij "finaal moet leren rammen", denk ik niet dat je Huub echt uitlegt waar het probleem met zijn code zit :D.

Op 18 februari 2013 19:50:03 schreef roadrunner84: Dat betekent dus dat je er niet vanuit gaat dat de lsb voor de msb komt of andersom.

Gezien dit door de CPU, bussen en andere HW bepaald wordt, hoe doe je dat als ik dat vragen mag?

Op 18 februari 2013 19:50:03 schreef roadrunner84: In het voorbeeld wordt vanalles met bits, shiften en signed en unsigned gedaan, dat maakt het nodeloos ingewikkeld en alles behalve intuïtief.

Op 18 februari 2013 19:50:03 schreef roadrunner84: Dit doet hetzelfde, maar is veel leesbaarder en waarschijnlijk zelfs efficiënter!.

Dit is een persoonlijke opinie, zeker geen absolute technische waarheid. Zeker voor een beginner, kan je in de voorbeeldcode duidelijk zien wat het dataformaat in de TC77/SPI bus eigenlijk is en hoe je dit stap voor stap terug in een "leesbaar" formaat krijgt. Bovendien leert hij nog iets bij over structs en hoe je code netjes documenteert, wat ook mooi meegenomen is.

ScaledTemp = (((int) RawTempMSB*256 + RawTempLSB) / 8) * (long)625;

Geen idee wat de bedoeling van (long)625 is. Misschien kan je dat even uitleggen.

Groetjes

De originele code laat misschien beter zien hoe het intern werkt, maar een programmeertaal is juist een tool die je de mogelijkheid biedt om dingen op een abstractere manier op te lossen.
Hoe ik het "endianness" probleem oplos? Nou, door de binnengekomen MSB met 256 te vermenigvuldigen. Helaas is dat een stukje aanname waar je niet onderuit komt; je moet weten in welke volgorde de sensor de data aanlevert.

In de originele code staat deze regel

ScaledTemp = (long)RawTemp.Val * (long)625;

In plaats van RawTemp.Val te vermenigvuldigen doe ik dat in één adem door.

ScaledTemp = (((int) RawTempMSB*256 + RawTempLSB) / 8) * (long)625;

Eventjes in stukjes dan:
Ik heb twee bytes (RawTempMSB en RawTempLSB) die samen een integer vormen. De eerste stap is dus om één van de termen expliciet op te waarderen naar een integer, dat gebeurd met (int).
Vervolgens vermenigvuldig ik de MSB met 256, dit klopt altijd, op iedere processor in iedere omstandigheid. Een compiler kan dit eventueel weer vertalen naar het schuiven van bits of het maskeren van bytes over elkaar, maar daar wil ik als programmeur niet naar kijken als het niet hoeft.
Goed, het stukje ((int) RawTempMSB*256 + RawTempLSB) levert dus een integer op. Een integer is van zichzelf een signed type, dat betekent dus dat i.p.v. 0 tot max, de waarden lopen van -max/2 tot max/2-1. Die -1 komt ervan dat er altijd een even aantal waarden zijn, terwijl er één waarde voor 0 is, dus is er nog een oneven aantal waarden voor de positieve en negatieve getallen over. Bijvoorbeeld, stel een getal kan van 0 tot 9 lopen (10 waarden), dan kan je dat hernummeren van -5 tot 4 of van -4 tot 5, maar niet van -5 tot 5. Een signed nummer kenmerkt zich doordat de hoogste bit 1 is, dat wordt via een if(...) en een x |= y opgelost in de eerste code. Maar dat is geheel niet nodig, immers was de originele RawTemp.Val ook al een signed integer met de hoogste bit 1 als het getal negatief is.
Ter vergelijking: als ik -20 door 4 wil delen(op een rekenmachine) ga ik niet 20 door 4 delen en er een min voor plakken, ik deel de -20 direct door 4. En een >>= 3 is niet meer dan een /23, en 23 is 8, vandaar dus (((int) RawTempMSB*256 + RawTempLSB) / 8).
Dan is het schijnbaar nodig dit getal met 625 te vermenigvuldigen, nou is het zo dat 625 in 10 bits past, de sensor waarde past in 13 bits. Deze beide getallen passen dus in een 16 bits getal. Echter als ik ga vermenigvuldigen moet ik het aantal bits bij elkaar optellen (worst case), dus 10 + 13 bits is 23 bits. 23 bits passen niet in een 16 bits getal. Om dit op te lossen beschrijven we met (long) expliciet dat we geen 16 bits berekening, maar een 32 bits berekening willen uitvoeren. Zouden we twee 16 bits getallen vermenigvuldigen en dit toewijzen aan een 32 bits variabele, dan lost dat het probleem niet op, want de "tussenuitkomst" wordt in een 16 bits getal opgeslagen.
Dit is redelijk theoretisch, maar niet minder overzichtelijk dan het origineel, waar dezelfde opwaardering van het type naar long ook gebeurt.

Dag roadrunner,

Op 19 februari 2013 09:11:56 schreef roadrunner84: Vervolgens vermenigvuldig ik de MSB met 256, dit klopt altijd, op iedere processor in iedere omstandigheid.

Omdat ik er nogal verbaasd van stond dat jouw code ook effectief kan werken heb ik dit getest op verschillende platformen.

Omdat ik geen 16-bit PIC bord heb, heb ik mijn VC1.3 compiler van mijn over-over-grootmoeder dan maar bovengehaald :). Ik moet er eerlijkheidshalve wel bijvertellen dat ik haar AT/XT i8088/8086 16-bit PC van weleer niet meer kon terugvinden :) en heb dus mijn 16-bit exe op een moderne PC laten draaien. Op geen van de 3 platformen werkt deze code correct.

Compiler, Platform, CPU
Visual C++ 2008, Win7, Intel Core i3 (64 bit code)
VC1.3, WinXp, Pentium 3 (16 bit code)
gnu, OSE, Power PC (32 bit code)

.... en op geen enkel platform loopt deze code correct met uitzondering van positieve temperaturen op sommige platformen. Ik heb dan nog steeds sterke twijfels of dit op een 16-bit PIC echt werkt. Het is precies de tekenbit die de mist ingaat.

Met wat aanpassingen hier en daar wel.


void temp (void)
{  
 int iScaledTemp; 
                              /*+25,0	-25,0	-55,0*/       
 unsigned char ucRawTempLSB = /*0x87;*/	0x87;  	0x87; 
 unsigned char ucRawTempMSB = /*0x0C;*/	0xF3;  	0xE4;
 float fFloatTemp;

 iScaledTemp = (int)((char)ucRawTempMSB * 256 + ucRawTempLSB)>>3;
 fFloatTemp = iScaledTemp * 0.0625;
 
 return;
}

De deling door 8 heb ik vervangen door >>3 omdat delen en schuiven NIET hetzelfde zijn, een wijdverbreid misverstand overigens. Delen door 8 leidt onherroepelijk tot (kleine) afrondingsfouten. In dit geval kan je dit wel oplossen door maskering van de 3 LSBs.

Groetjes

Je cast een unsigned char naar een char, het is niet vastgelegd of char signed of unsigned is.
Je maakt de aanname (dat klopt opzich wel) dat 256 een integer is, en dat MSB*256 dus een integer wordt, ook al is MSB een char.
Uhm... >>3 en /8 doet (bij een beetje compiler) exact hetzelfde! Je verwart het met /8.0 wat een float deling is! Een integer op een integer delen geeft geen afrondingsfouten, je krijgt altijd en exact het deel voor de komma terug van je deling. Het verschil is dat >>3 de processor opdraagt om iets met bits te doen, terwijl /8 geen expliciete manier oplegt; hetgeen het meest efficient is wordt gedaan, en dat is meestal een shift operatie.

Dag roadrunner,

Op 20 februari 2013 09:28:48 schreef roadrunner84:
Je cast een unsigned char naar een char, het is niet vastgelegd of char signed of unsigned is.

char = signed char.

Op 20 februari 2013 09:28:48 schreef roadrunner84:Uhm... >>3 en /8 doet (bij een beetje compiler) exact hetzelfde! Je verwart het met /8.0 wat een float deling is! Een integer op een integer delen geeft geen afrondingsfouten, je krijgt altijd en exact het deel voor de komma terug van je deling. Het verschil is dat >>3 de processor opdraagt om iets met bits te doen, terwijl /8 geen expliciete manier oplegt; hetgeen het meest efficient is wordt gedaan, en dat is meestal een shift operatie.

Daar ben ik niet echt van overtuigd.

Stel dat het formaat is zoals bij de TC77 chip (13 bits links gealigneerd in een 16-bit woord). Gezien de 3 LSB allen 1 kunnen zijn komt +0,0625°C dus overeen met 0000 0000 0000 1111 (15 decimaal)(Zie datasheet).

1) >>3: Dit wordt door de compiler altijd omgezet naar een shift operatie in machinecode. Het resultaat is dus altijd 0000 0000 0000 0001 wat exact overeenkomt met +0,0625°C.

2) /8: De compiler beslist uiteindelijk wat hij met deze deling gaat doen (Hangt trouwens af van het processortype). 15/8 = 1 of 2 al naargelang het gedrag van het delingsalgorithme: +0,125 °C ipv +0,0625°C. Voor kleine temperaturen is dit al een aanzienlijke afwijking.

Groetjes

Je zit twee keer fout. Een char is expliciet niet gedefinieerd als signed of unsigned. Als je dus een signed char wilt hebben moet je (signed char) gebruiken, tenzij je je code voor één specifieke compiler maakt, al zou ik dan nog steeds signed ervoor zetten.

Een integer deling is volledig vastgelegd hoe deze werkt. 15/8 is dan ook altijd 1. Dat is dus ook iets heel anders dan (int)(15/8.0), wat weer wel afhenkelijk is van je implementatie. Bovendien weten de meeste compilers dat een deling door de integer 8 hetzelfde doet als >>3, er zal dan vaak een optimalisatie plaatsvinden. Maar dat is weer compiler en processor afhankelijk.

De standaard zegt dacht ik niet of een "char" signed of unsigned moet zijn. De compiler mag kiezen wat hem het beste uitkomt. Maar dat betekent dat als je met een specifieke compiler werkt, je wel kan weten dat bijvoorbeeld voor jou geldt: "char == signed char".

Hoe had je gedacht dat er uit (int) (15/8.0) iets anders dan "1" komt? De deling 15/8.0 is een floating point berekening. Daar komt 1.875 uit. En vervolgens cast je die naar een "int" hetgeen gedefinieerd is als: "laat het achter-de-comma deel weg". Daar komt dus altijd "1" uit.

Of deze (15/8.0) volgens de standaard met float of met double moet weet ik even niet. Maar dat ligt aan mij. Het zal er vast in staan.

Ik heb nu onderstaande code, maar er blijft een maximale waarde uitkomen (8191 een maximale waarde van 13 bits) wat doe ik nog fout?

int tc77()
{
    int x,y,i,j;


    PORTCbits.RC6=0;    //  /cs active cs on RC0!!!
    x = ReadSPI();
    y = ReadSPI();
    PORTCbits.RC6=1;  	//  /cs =1 chip nactive

	
	x = x*256;
	x = y + x;
	x = x>>3;

    return x;
}

Dag Huub,

Probeer dit eens en laat iets van je horen of het werkt.


main ()
{  
    int iScaledTemp; 

    unsigned char ucRawTempLSB = 0x87;
    unsigned char ucRawTempMSB = 0xF3;
    float fFloatTemp;

    PORTCbits.RC6=0;    //  /cs active cs on RC0!!!
    ucRawTempMSB = ReadSPI();
    ucRawTempLSB = ReadSPI();
    PORTCbits.RC6=1;  	//  /cs =1 chip nactive

    iScaledTemp = (int)((char)ucRawTempMSB * 256 + ucRawTempLSB) >>3;
    fFloatTemp = iScaledTemp * 0.0625;

    return;
}

En voor Rew en Roadrunner, probeer ook dit eens en laat ook weten of er een verschil is tusssen fFloatTemp en fFloatTempW.


main ()
{  
	int iScaledTemp; 
	int iScaledTempW; 

	/*+25,0	-25,0	-55,0*/       
	unsigned char ucRawTempLSB = 0x87;
	unsigned char ucRawTempMSB = 0xF3;
	float fFloatTemp;
	float fFloatTempW;

	iScaledTemp = (int)((char)ucRawTempMSB * 256 + ucRawTempLSB) >>3;
	iScaledTempW = (int)((char)ucRawTempMSB * 256 + ucRawTempLSB) /8;

	fFloatTemp = iScaledTemp * 0.0625;
	fFloatTempW = iScaledTempW * 0.0625;

	return;
}

Veel succes

Ja, je hebt gelijk. >>3 doet inderdaad iets anders dan /8. >>3 "rond af" naar -oneindig, terwijl /8 afrond naar 0. Dat betekent dus dat >>3 de bits die overblijven niet aanpast en /8 wel.
Zaak blijft overigens wel, dat (char) ambigu is. Ik heb de standaard er op nagezocht; char is ten minste zo groot dat alle karakters erin gerepresenteerd kunnen worden. Dat betekent dus dat niet vast staat of hij nou signed of unsigned is. Daarom zou ik nog altijd (signed char) gebruiken, al is het maar voor de zekerheid. Je zou trouwens (int) achterwege kunnen laten, het getal 256 kan namelijk niet in een char, en een int is signed tenzij anders aangegeven (als je 256U gebruikt zou het niet werken).

Hij blijft een maximale waarde geven. Maar als ik mijn sensor eraf haal dan doet hij dit ook.

Op 23 februari 2013 12:29:52 schreef Huub12345:
Hij blijft een maximale waarde geven. Maar als ik mijn sensor eraf haal dan doet hij dit ook.

Dit zou ook een HW pobleem kunnen zijn. Wat zijn de hexadecimale waarden van x en y direct na ReadSPI() en liggen deze binnen het geldige bereik in de datasheet?

Groetjes

Dit is mijn complete code. Voor x en y apart krijg ik ook 2x 255. Mijn miso en de klok zitten via een weerstand aan de VDD. Kan dit kloppen en zie ik wat over het hoofd bij het instellen van de spi ?

*********************** pic18f2550 **************************\
|    |
+-------------------------------------------------------------+
|                                                             |
+-------------------------------------------------------------+
|                                                             |
Connections:                                                  |
            Bootloader    Serial  SPI 		I2C 	USB   |
RC0          led/button				              |
RC4							      |
RC5						      D+      |
RC6         TX now as /CS for TC77                            |
RC7	    RX	  MOSI				              |
RB0		  MISO		  SDA                         |
RB1                               SCK       SCLK              |
RA5                               /CS                         |
 programm explenation:                                        |
|            |
|                                                             |
| De Xtal frequentie is 20MHz                                 |
| led's of SPI board counting  gives high value from          |
| temperature sensor TC77                                     |
| 							      |
\*************************************************************/
 
#include <p18cxxx.h>            
#include <p18F2550.h>
#include <spi.h>
 
 
///// VECTOR REMAPPING for use with Bootloader /////
extern void _startup(void);
#pragma code reset_vector_section = 0x800   // in plaats van 0x00
void _reset(void) { _asm goto _startup _endasm }
#pragma code
 
void Delay1KTCYx( unsigned char);
void mcp23s08(unsigned char data_out);
int tc77(void);
 
 
void init(void)
{
  TRISA=0; 		         //RA5 als /cs mcp23s08
  TRISC=0b00000000;		//RC7 als MOSI en RB6 als /cs tc77
  TRISB=0b00000001;	     //RB0 als MISO en RB1 als clock
  INTCON2bits.RBPU=0;         // pull-ups portB enabled 
} 
 
void SPIInit(void)
{
 
 OpenSPI(SPI_FOSC_64,MODE_00,SMPMID);   //init SPI
 }
 
void mcp23s08(unsigned char data_out)
{
 PORTAbits.RA5=0;  				//cs =0 
 WriteSPI(0x40);				 // write device opcode for writinf (R/W =0)
 WriteSPI(0x00); 				// select IODIR (address 00)
 WriteSPI(0x00);				 //all output
 PORTAbits.RA5=1; 				 //cs =1   deselect device
 
 PORTAbits.RA5=0; 				 //cs =0    select device for next write
 WriteSPI(0x40); 				// write device opcode for writinf (R/W =0)
 WriteSPI(0x09); 				// select GPIO (address 9 in mcp23s08)
 WriteSPI(data_out);			 // 
 PORTAbits.RA5=1;  				//cs =1
}
 
 int tc77(void)
{
 int x,y;
 
 	PORTCbits.RC6=0;    		//  /cs active
 
 
 	x= ReadSPI();               // high byte from temperature
	y= ReadSPI();               //low byte from temperature  not used here
 
 
	PORTCbits.RC6=1;  	     	//  /cs =1 chip nactive
 
        x=x*256;
        x=x+y;
        x=x>>3 //shiften om de 13 waardes van het temperatuur register te krijgen        
 
 return x;
}
 
#pragma code
void main (void)
{
    unsigned char x;
    init();
    SPIInit();
 
   	while(1)
   	{
           x=tc77();
           mcp23s08(x);
            // mcp23s08(0xaa);  // good test value
   	}
}