Användning av struct

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Användning av struct

Inlägg av ankan »

Definierar en struct enligt följande:

Kod: Markera allt

typedef struct {
               byte SenderID[8];
               byte ReciverID[8];
               byte Datatype;
               byte Data[16];
               byte Length;
               } RF_Data_Struct;
               
RF_Data_Struct RX_Data;
Sedan tilldelar jag den värden med denna interrupt varje gång det finns datat på RX-porten.

Kod: Markera allt

  if (PIR1.RCIF) {

     TMR0=0;
     inner_state=RECIVING;

     if (!RX_Data.Length) {
       OPTION_REG = 0x07;
       INTCON.TMR0IF=0;
       INTCON.TMR0IE=1;
     }

     if (RX_Data.Length<9)
       RX_Data.SenderID[RX_Data.Length]=Usart_Read();
     else {
       if (RX_Data.Length>8 && RX_Data.Length<17)
         RX_Data.ReciverID[RX_Data.Length]=Usart_Read();
       else {
         if (RX_Data.Length==17)
           RX_Data.Datatype=Usart_Read();
         else {
           if (RX_Data.Length>17 && RX_Data.Length<26)
             RX_Data.Data[RX_Data.Length]=Usart_Read();
         }
       }
     }

    RX_Data.Length++;

  }
När jag sedan ska skriva ut innehållet så stämmer byten i RX_Data.SenderID men inte i RX_Data.ReciverID som hamnar i nästa 8 byte där datat ska finnas.

Provat att lägga allt i en array i stället och då kom all data på rätt plats så det måste vara uppdelningen i interruptet eller något med upplägget av structen.

Några tips?
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Kod: Markera allt


typedef struct
  {
  byte SenderID[8];
  byte ReciverID[8];
  byte Datatype;
  byte Data[16];
  byte Length;
  } T_RF_Data_Struct;
               
union
  {
  T_RF_Data_Struct Data;
  byte Buffer[1];
  } Rx_Data;

  if(PIR1.RCIF)
    {
    TMR0=0;
    inner_state = RECIVING;
    if(!RX_Data.Data.Length)
      {
      OPTION_REG = 0x07;
      INTCON.TMR0IF=0;
      INTCON.TMR0IE=1;
      }
    if(Rx_Data.Data.Length < (sizeof(T_RF_Data_Struct) - sizeof(Rx_Data.Data.Length))
      {
      Rx_Data.Buffer[RX_Data.Length] = Usart_Read();
      RX_Data.Length++;
      }
    } 
På detta sätt fylls data in i strukturen ganska som de ska, sedan kan du komma åt dom såhär:

Rx_Data.Data.SenderID[]
Rx_Data.Data.RecieverID[]
osv....
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Vad är det som är felet med att använda en struct som jag hade gjort?

Vad är det man gör när man kör union?

Ser ut som att man lossas om att structen är en array när man stoppar in datat och sedan när man ska läsa ut data så har varje del fått sitt eget namn skulle man kunna säga.

Tar "structen/arrayen" RX_Data någon mer plats och hur mycket i så fall när man gör så där?
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Vad du gör fel är att du indexerar 0-7 i SenderID och det är helt OK.

Därefter indexerar du 8-16 i RecieverID och det är ju helt fel eller hur? Du måste ju dra av storleken av SenderID i indexeringen och det är samma fel som finns genomgående.

'union' anger att de 2 variabler ligger "över varandra", alltså är Buffer[0-7] den samma adress som SenderID[0-7], Buffer[8-15] det samma som RecieverID[0-7] osv.

Edit: Förtydligande:
Med 'union' anger man att alla grundelementen börjar på samma adress. Om man skrivar:

Kod: Markera allt

typedef unsigned char byte;
typedef unsigned int  word;
typedef unsigned long dword;
union
  {
  byte[1] Byte;
  word[1] Word;
  dword  Long;
  } The_One;
då kommer den största variablen att bestämma den totala storleken på 'The_One' och de övriga variabler kan sedan indexeras inom detta område och plocka ut/ändra bytes eller words efter önska. Kan vara extremt praktisk om man behöver att flippa bitar, kolla specialgrejer eller spara värden som t.ex. bytes när det i själva verket är en float eller liknande.
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Då tar jag alltså upp dubbelt så mycket minne, eller?
Millox
Inlägg: 559
Blev medlem: 10 december 2005, 22:10:43
Ort: Östhammar

Inlägg av Millox »

Nope. Den upptar bara så mycket plats som det största elementet. Du kan se det som att man kan titta på en variabel bit för bit. För vanliga datorer:

union {
int integer;
char[4] character;
} un;

Där kan du tilldela enkelt unionen genom att använda un.integer = 1234; men du kan också sätta varje individuell char genom un.character[x] = 255;

När du läser tillbaka återspeglas värdet i den andra variabeln. Mycket praktiskt ibland.

Dock har du gjort lite bus i din kod ändå, som jag dock inte har nån susning om om det funkar för just den kompilatorn, men i ansi-C kommer det bli dål.

Nämligen den här raden:
RX_Data.SenderID[RX_Data.Length]=Usart_Read();

Problemet är att du bara kan tilldela grundläggande variabler, dvs upp till din registerstorlek (osäker om det funkar med 16 bitar, eller bara 8). Kompilatorn kanske fixar dehär felen, men man kan inte vara säker på att det funkar överallt och om man går vidare och kodar för vanliga datorer så kommer man absolut att stöta på patrull. Gör såhär istället:

Usart_Read(buf); // Om det är du som gjort funktionen, ändra till dethär
bcopy(RX_Data.SenderID,buf,sizeof(RX_Data.SenderID);


Med reservation för att det kanske funkar på specialkompliatorer.
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

???? Har du inte förstått något av vad jag skrev?

OK, jag ger ett annan exempel:

Kod: Markera allt

typedef struct
  {
  byte SenderID[8];
  byte ReciverID[8];
  byte Datatype;
  byte Data[16];
  byte Length;
  } T_RF_Data_Struct; // Totalt 34 bytes

union
  {
  T_RF_Data_Struct Data;
  byte                   Buffer[34];
  } Alla_Bytes_I_Samma_Klump;
Nu är det 2 stycken minnesarea som är definierat som
1: Alla_Bytes_I_Samma_Klump.Data; (som är en T_RF_Data_Struct på 34 bytes)
2: Alla_Bytes_I_Samma_Klump.Buffer[34];
Dessa två minnesarea BÖRJAR PÅ SAMMA ADRESS och är därför med samma innehåll och FYLLER SOM DEN STÖRSTA av de två.

Alltså är minnesåtgången den samma, det är ju samma bytes det gäller.

Du kan även göra en fuling och enbart indexera i SenderID, resultatet blir det samma då den variabel ligger först i blocken och blocken per definition är en sammanhängande minnesblock. Detta betyder att SenderID[8] är samma byte som RecieverID[0] osv.
ankan
Inlägg: 1091
Blev medlem: 12 november 2004, 01:50:35

Inlägg av ankan »

Det där var bra förklarat. Det var just det där om att det är samma minnesarea som man definierar med två olika "namn". Den ena accessas som en array och den andra som en struct. Antar att ordningen i uppbyggnaden av structen är väldigt viktig.

Tack för hjälpen!
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Jag hade själv "en del förståelsesproblem" med union i början men när man sedan har fattat grejen kan det vara en stor hjälp.

Det finns en del sätt att fuska på men dom sparar vi till senare. ;-)

Såklart är uppbyggnade av struct'en viktig, om man definierar den som man använder den, byte för byte blir livet mycket lättare.

Jag har en del projekt som "pratar med varandra" via serieport (PC<->enhet) och då gör jag en gemensam .H-fil där jag har alla kommunikationsdefinitioner i inklusive typedef's som anger kommunikationsblock.

Ändrar jag då ett ställe är det bara att kompilera om alla ingående delar i projekten och då fungerar det!

Jag har som definition att med datorer ska en viss variabel/definition enbart anges 1 gg och allt annat ska referera till detta ställe.
JJ
Inlägg: 366
Blev medlem: 16 maj 2005, 21:33:02

Inlägg av JJ »

Och glöm inte 'volatile' på det som accessas från både interrupt och main! (Eller från flera interrupt.)
Användarvisningsbild
$tiff
Inlägg: 4941
Blev medlem: 31 maj 2003, 19:47:52
Ort: Göteborg
Kontakt:

Inlägg av $tiff »

Jag tackar för denna ingående förklaring, Icecap (och Millox). Även om jag är passiv deltagare i diskussionen så blev begreppet Union mycket klarare nu :D
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Bra! Man ska lära sig lite varje dag, om man inte känner för det är det lika bra att reservera en ett-rummare utan brevinkast!
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> ...så blev begreppet Union mycket klarare nu...

He he, ni har kört för lite Fortran och Cobol !! :-)
thepirateboy
EF Sponsor
Inlägg: 2109
Blev medlem: 27 augusti 2005, 20:57:58
Ort: Borlänge

Inlägg av thepirateboy »

Bra exempel! Kan man initiera vissa element i structen i samband med att man deklarerar den och hur gör man isåfall?
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

Det beror faktisk på kompilern.

Generellt är svaret "nej" men vissa kompilers är så att de kan göra det tack vara en kod som körs innan main.

Jag har löst det på det sätt att jag deklarerar en typedef med den önskade struktur, här kallat T_TEST:

typedef struct
{
char Char_1;
char Char_2;
int Int_1;
} T_TEST;

Först deklarerar man dom i RAM:
T_TEST Testvar;

Sedan är det standardinställningen:
const T_TEST Default = {0, 0, 2};

Sedan kopierar man från Default till Testvar på lämpligt sätt, memcpy(&Testvar, &Default, sizeof(Testvar)); kan vara ett av dessa.

Medger kompilern att man kan definiera och initiera RAM-delen utförs dessa steg automatisk innan main aktiveras men jag gillar att ha koll på vad som händer så jag föredrar att göra det själv.
Skriv svar