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;
}
roadrunner84
Meep! Meep!
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;
}plano
Webshop voor Electronic Prototype | http://eProto.nl/
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.
roadrunner84
Meep! Meep!
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
.
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
roadrunner84
Meep! Meep!
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
roadrunner84
Meep! Meep!
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
roadrunner84
Meep! Meep!
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.
rew
four NANDS do make a NOR . Kijk ook eens in onze shop: http://www.bitwizard.nl/shop/
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
roadrunner84
Meep! Meep!
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).
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
}
}