Manipulera enstaka registerbitar i GCC.

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
Användarvisningsbild
manw
Inlägg: 207
Blev medlem: 16 november 2005, 11:16:17
Ort: Södermalm

Manipulera enstaka registerbitar i GCC.

Inlägg av manw »

Antar att detta har diskuterats någon annanstans på forumet, men sökfunktionen är inte den bästa...

För microkontrollers han man ofta behov av att manipulera enstaka bitar i ett register utan att påverka de andra bitarna.

Har tidigare jobbat med en C-kompilator (Cc5x) för PIC som tillåter detta med punktnotation, men nu vill jag göra samma sak i AVR/GCC som inte verkar ha ett direkt stöd för detta. Hittade tips enligt länk (http://www.moodle.tfe.umu.se/file.php/2 ... TipsTrix.c), men det kräver olika rutiner för att 1-ställa respektive 0-ställa en bit => för dåligt!

Pillade ihop ett par rutiner, som jag inte har testat på målsystemet ännu, bara i GCC. Vill kunna lägga dessa funktioner för att få en snyggare kod.

Kod: Markera allt

//Manipulerar en enstaka bit i ett register.
void writeBit(unsigned char *byte, unsigned char byteBit, unsigned char bitVal){
     bitVal &= 0x01;
     if(bitVal == 1){
               *byte |= (0x01<<byteBit);
               }else{
                     *byte &= ~(0x01<<byteBit);
                     }
     }
     
//Returnerar värdet i en enstaka bit ur ett register
unsigned char readBit(unsigned char byte, unsigned char byteBit){
         return (byte>>byteBit) & 0x01;
         }
		 
//Skriv i PORTA bit 2 till 0
writeBit(&PORTA,2,0);

//Läs ur PORTA bit 3
svar = readBit(PORTA, 3);
Identeringen försvinner när jag lägger in det här tyvärr... (Fixat! / speakman)

Kanske för klumpigt? Har någon ett bättre förslag? Eller berätta för mig om det finns något färdigt jag missat.
Användarvisningsbild
Icecap
Inlägg: 26659
Blev medlem: 10 januari 2005, 14:52:15
Ort: Starup (Haderslev), Danmark

Inlägg av Icecap »

I C som sådan finns det med att man ska kunde designera enstaka bits i en byte som variabel, kruxet är bara HUR man ska definiera att byten har en viss adress.

Kod: Markera allt

typedef struct
  {
  unsigned char B0 : 1;
  unsigned char B1 : 1;
  unsigned char B2 : 1;
  unsigned char B3 : 1;
  unsigned char B4 : 1;
  unsigned char B5 : 1;
  unsigned char B6 : 1;
  unsigned char B7 : 1;
  } T_BYTEBIT;
Detta deklarerar en byte som bits.

Sedan är det "bara" att deklarera en T_BYTEBIT på samma adress som PORTx, hur man gör det i GCC har jag inte kolla på men det borde finnas i en processorspecifik .INC-fil dör en viss PORT får ett värde och sedan kollar man hur detta värde används i deklarationen.

EDIT:
Men om det är så att du bara vill nolla eller etta bits kan man ju göra såhär:
#define TXEN 0x01 // En bit bara

PORTA &= ~TXEN; // Nolla TXEN-bitten
PORTA |= TXEN; // "Etta" TXEN-bitten
Senast redigerad av Icecap 17 oktober 2008, 12:55:41, redigerad totalt 1 gång.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Identeringen försvinner när jag lägger in det här tyvärr...


Använd code-taggarna...
Användarvisningsbild
speakman
Inlägg: 4838
Blev medlem: 18 augusti 2004, 23:03:32
Ort: Ånge

Inlägg av speakman »

Nått sånt här?

Kod: Markera allt

#define setbit(reg,bit) reg |= (1 << bit)
#define clrbit(reg,bit) reg &= ~(1 << bit)
Användarvisningsbild
manw
Inlägg: 207
Blev medlem: 16 november 2005, 11:16:17
Ort: Södermalm

Inlägg av manw »

EDIT: Dubbelpost :oops:
Senast redigerad av manw 17 oktober 2008, 14:06:14, redigerad totalt 1 gång.
Användarvisningsbild
manw
Inlägg: 207
Blev medlem: 16 november 2005, 11:16:17
Ort: Södermalm

Inlägg av manw »

Det var just den där sista grejen som inte dög som sådan, eftersom det är två olika kodsnuttar för 1:a och för 0:a. Jag hade ju i princip gjort samma sak, men lagt dem i en "wrapperfunktion" eller vad jag skall kalla det.

I Cc5x (www.bknd.com/cc5x) för PIC kan man direkt ange en enstaka bit vad man vill sätta den till.

Tänkte om det kanske finns någon sådan "odokumenterad" i portningen av gcc för AVR.
Användarvisningsbild
speakman
Inlägg: 4838
Blev medlem: 18 augusti 2004, 23:03:32
Ort: Ånge

Inlägg av speakman »

Jag tror ingen AVR har stöd i hårdvaran för att adressera bitar direkt, såsom t.ex. 8051or har. Stöds det inte av hårdvaran så är det en fullösning i kompilatorn, och såna är inte populära att implementera i GNU.

Alternativt:

Kod: Markera allt

#define setbit(reg,bit,val) reg = val ? reg | (1<<bit) : reg & ~(1<<bit)
Alltså rätt likt det du själv skrev, men i ett snabbare makro.
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

> Jag tror ingen AVR har stöd i hårdvaran för att adressera bitar direkt,

Med reservation för att jag inte är helt säker på hur "stöd i hårdvaran"
och "adressera" ska tolkas... :-)

SBR/CBR. Även om det är en bit-mask man anger, inte ett bit-nummer,
men resultatet blir detsamma. Endast mot R16-31 dock.

Dock endast då biten är bestämd vid build-time (det är en konstant
i instruktionen) men jag tolkade att det var det som manw var ute efter.
Men det kanske var val av bit vid run-time som avsågs med "adressera".

PIC har inte heller någon instruktion där man kan ange aktuell bit dynamiskt
vid run-time.
Användarvisningsbild
ErikJ
Inlägg: 36
Blev medlem: 17 september 2004, 16:06:53
Ort: Malung

Inlägg av ErikJ »

Typdefiniera ett bitfält som det som Icecap föreslog längre upp.
(Jag tog bort onödiga "char" i koden)

Kod: Markera allt

typedef struct
{
  unsigned B0 : 1;
  unsigned B1 : 1;
  unsigned B2 : 1;
  unsigned B3 : 1;
  unsigned B4 : 1;
  unsigned B5 : 1;
  unsigned B6 : 1;
  unsigned B7 : 1;
} T_BYTEBIT;
Sedan kan du bara casta om 8-bitars-variabler till T_BYTEBIT och både skriva till och läsa från enskilda bitar.

Kod: Markera allt

((T_BYTEBIT)PORTA).B3 = 1; // ettställ bit3
((T_BYTEBIT)PORTA).B3 = 0; // nollställ bit3

((T_BYTEBIT)PORTA).B5 = ((T_BYTEBIT)PORTA).B2; // kopiera bit2 till bit5
EDIT: Nej det fungerar inte alls för det går ju inte att casta till vänster om "=". :humm:
Senast redigerad av ErikJ 17 oktober 2008, 19:50:38, redigerad totalt 1 gång.
Användarvisningsbild
AndLi
Inlägg: 18312
Blev medlem: 11 februari 2004, 18:17:59
Ort: Knivsta
Kontakt:

Inlägg av AndLi »

ErikJ: Vad vinner du på att ta bort "char"?
Användarvisningsbild
ErikJ
Inlägg: 36
Blev medlem: 17 september 2004, 16:06:53
Ort: Malung

Inlägg av ErikJ »

AndLi: Inget. :D Vad vinner man på att ha "char" där?
Användarvisningsbild
AndLi
Inlägg: 18312
Blev medlem: 11 februari 2004, 18:17:59
Ort: Knivsta
Kontakt:

Inlägg av AndLi »

Tydlighet. Jag skulle aldrig utgå från att något blir det man tror det blir.
(Det är ju inte för inte som char,short,int osv brukar definas över till int8,int16,uint8 osv)

Om man har ett 16 bitars register? Ska man ha en uint16 där då? Det borde väll inte behövas...
Användarvisningsbild
manw
Inlägg: 207
Blev medlem: 16 november 2005, 11:16:17
Ort: Södermalm

Inlägg av manw »

Ja just det, det där med att använda en struct var nog det bästa förslaget idag, C är underbart :)

Näe, jag kanske inte är tillräckligt säker på hur kompilatorn allokerar minne här. Tanken med "char" var att vara säker på att det inte allokeras mer än en byte för variabeln, men helt enkelt, jag vet inte hur det är med den saken, men någon annan kanske vet?
Användarvisningsbild
speakman
Inlägg: 4838
Blev medlem: 18 augusti 2004, 23:03:32
Ort: Ånge

Inlägg av speakman »

"char" ska defintivt vara där. Ta snarare bort "unsigned" eftersom du ändå inte använder byten i sin helhet.

Kan du verifiera att det funkar att göra ett bitfält? Jag utgick ifrån att det inte gick, men det kanske inte var svårare än så?

@sodjan:
På 8051:an har vissa bitar egna adresser som du kan skriva direkt till. EA=1; kräver t.ex. bara en instruktion; "SETB EA"
sodjan
EF Sponsor
Inlägg: 43251
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Inlägg av sodjan »

Är inte SBR eller CBR "bara en instruktion" ?

Jag kan se att det kan finnas arktiekturskillnader. D.v.s om
en instruktion ändrar biten "in-place" eller om registret där biten
finns finns först läses, ändras och sedan skrivs tillbaka (så som
BCF/BSF på PIC gör, vilket kan sina sidoeffekter, och som jag tokade det
att AVR också gör). Men det är väl hur som helst fortfarande en instruktion !?
Skriv svar