C 16 Bit Variable in 2x 8 Bit Variable aufteilen und dann noch Bits verschieben

BayerWaldWichtel

Neues Mitglied
10. Mai 2011
15
0
0
Sprachen
Hallo zusammen, ich heiße Christian, bin 32, und komme aus Niederbayern aus der Nähe von Passau. Seit etwa einem Jahr beschäftige ich mich mit Mikrocontrollern, hauptsächlich mit dem ATMega8. Auch Platinen habe ich schon selbst hergestellt.

Nun kommt auch schon meine erste Frage.

Ich denke der Titel sagt es schon, was ich machen möchte.

Ich habe eine Platine mit einem ATMega8, einem MCP2515 und einem
MCP2551. Programmiert wird über AVR Studio und AVR-GCC in "C".

Ich würd gerne eine 16-Bit Variable in zwei 8-Bit Register klopfen, so
wie auf der Grafik... Und dann müssen noch beim rechten Register 3 Bits
verschoben werden, der Rest der bestehenden Daten (Fragezeichen) darf
nicht überschrieben werden... Nur ich bekomms nicht hin.

Hat jemand einen Tip wo ich da anfangen soll?

Danke

Christian

16und8bit.GIF
 
Hallo Christian!

Willkommen im AVR-PRAXiS Forum. :ciao:

Die Lösung könnte zum Beispiel wie folgt aussehen:
Code:
uint16_t d;   // das ist die 16bit Variable
uint8_t TXB0SIDH, TXB0SIDL;

// ...

TXB0SIDH = d>>3;
TXB0SIDL = (d<<5) | (0b00011111 & TXB0SIDL);
Grüße,
Dirk
 
Hallo! Vielen Dank für die fixe Antwort.

Ich versuche grad mal, den Code für meinen Fall umzusetzen und zu verstehen.

TXB0SIDH = identifier >> 3
heißt, dass der Inhalt der 16-Bit-Variable um 3 Stellen nach rechts verschoben wird, und somit das 11. Bit an die 8. Stelle wandert. Richtig?

TXB0SIDL = identifier << 5
heißt, dass der Inhalt der 16-Bit-Variable um 5 Stellen nach links wandert, und somit die Bits 3 2 1 an den Stellen 8 7 6 stehen. Auch Richtig?

0b00011111 müsste die Bitmaske sein, damit die Stellen an denen eine 1 steht, nicht überschrieben werden.

Hab ich da richtig gedacht?

Grüße, Christian
 
Hallo Christian,

Ich versuche grad mal, den Code für meinen Fall umzusetzen und zu verstehen.

TXB0SIDH = identifier >> 3
heißt, dass der Inhalt der 16-Bit-Variable um 3 Stellen nach rechts verschoben wird, und somit das 11. Bit an die 8. Stelle wandert. Richtig?

TXB0SIDL = identifier << 5
heißt, dass der Inhalt der 16-Bit-Variable um 5 Stellen nach links wandert, und somit die Bits 3 2 1 an den Stellen 8 7 6 stehen. Auch Richtig?

ja beides ist richtig. Im zweiten Fall willst du ja die Bits 2 1 0 an den Stellen 7 6 5 haben.

0b00011111 müsste die Bitmaske sein, damit die Stellen an denen eine 1 steht, nicht überschrieben werden.

Hab ich da richtig gedacht?

Jain. Du möchtest ja die niederwertigsten 5 Bits von TXB0SIDL beibehalten. Wir maskieren hier also mit & 0b00011111, damit die höherwertigen 3 Bits "0" werden, also die Bits 7 6 5, die unteren 5 bleiben erhalten. Danach "verodern" wir mit um 5 Bit nach links geshifteten 16bit Wert.

Beim shift um n Bitstellen, werden immer n Bitstellen mit "0" nachgeführt.

Grüße,
Dirk
 
Danke für die ausführliche Antwort... Ich glaube in ein paar Stunden kommt gleich meine nächste Frage, da muss ich dann nämlich was basteln, um aus den 2 verschiedenen 8 Bit Registern wieder einen 11-Bit Identifier erzeugen zu können, also praktisch die ganze Aktion rückwärts... Falls ich da irgendwo hängenbleibe, melde ich mich ;-)

Vielen Dank

Grüße

Christian
 
Hallo,

ich hatte es schon vermutet dass ich nicht weiterkomme mit der Umkehrfunktion.

Also: Ich würde gerne von zwei anderen Registern 2 mal 8 Bit in eine 16 Bit Variable schaufeln, so wie in der Grafik angedeutet. Habs zwar schon versucht den Code auf die Anforderung umzubasteln, aber es will mir nicht gelingen :(

8und16bit.GIF

Danke,

Christian
 
Hallo,
Habs zwar schon versucht den Code auf die Anforderung umzubasteln, aber es will mir nicht gelingen

es wäre interessant gewesen, wie du es versucht hast.

Hier mal eine mögliche Lösung:

Code:
uint16_t d;
uint8_t RXB0SIDH, RXB0SIDL;

// ...

d = ((uint16_t)RXB0SIDH<<3) | (RXB0SIDL>>5);
"Casten" von uint16_t bei RXB0SIDH ist notwendig, da die Variable 8Bit umfasst und beim Linksshift um drei Bits die obersten Bits rausgeschoben werden, also verloren gehen. Die Bits benötigst du aber. Wenn du 16bit (WORD) verwendest, passiert es nicht.

(Alle Angaben wie immer ohne Gewähr ;))

Grüße,
Dirk
 
Naja, versucht, am Ende blieb bei mir folgender Code, bis ich nicht mehr weiterkam... Hab zwar auch noch was aufgezeichnet und Bits hin und hergeschoben aber ich bin mit 1 Jahr Programmiererfahrung noch nicht soweit das Gedankengut im Hirn auf den PC zu übertragen :) Hab zwar schon alles mögliche programmiert, aber noch nie was, was bis auf die Bitebene ging...

Mein Versuch:

Code:
		// Startadresse festlegen, ab der ausgelesen wird
		rx_adress = RXB0D0;

		// Länge der angekommenen Nutzdaten
		rx_datalength = MCP2515_SPI_READ(RXB0DLC);
	
		// Identifier auslesen
		temp_RXB0SIDL = MCP2515_SPI_READ(RXB0SIDL);
		temp_RXB0SIDH = MCP2515_SPI_READ(RXB0SIDH);
	
		// Identifier Low um 5 nach rechts schieben zu Stellen 1 2 3
		temp_RXB0SIDL = temp_RXB0SIDL >> 5;	

		// Identifier High

Danke :)

Christian
 
Hallo zusammen,

hab jetzt meinen Code nochmal umgebastelt, weil die Controller im CAN Bus auf sämtliche Nachrichten reagiert haben, auf die sie gar nicht reagieren sollen...

Hier mal mein Entwurf, um aus zwei 8-Bit Variablen einen 11-Bit-Identifier zu erstellen.

rx_identifier ist uint16_t
temp_RXB1SIDL ist uint8_t
temp_RXB1SIDH ist uint8_t

Code:
		// Identifier auslesen
		temp_RXB1SIDL = MCP2515_SPI_READ(RXB1SIDL);
		temp_RXB1SIDH = MCP2515_SPI_READ(RXB1SIDH);
	
		// 16-Bit Identifiervariable zusammensetzen aus den zwei 8 Bit Variablen
		
			// Highbyte in Identifiervariable schreiben
			rx_identifier = temp_RXB1SIDH;

			// Highbyte um 8 Stellen nach links shiften
			rx_identifier = rx_identifier << 8;

			// Lowbyte in Idetifiervariable schreiben, Highbyte nicht überschreiben
			rx_identifier = 0000000011111111 & temp_RXB1SIDL;

			// 11-Bit Identifier aus 16-Bit Variable machen, Rechtsshift um 5
			rx_identifier = rx_identifier >> 5;

Überschreib ich mir da wieder die Hälfte, oder kann man das so gebrauchen, vom Funktionsprinzip her?

Danke,

Christian
 
Hallo Christian,

das funktioniert so nicht. Schau dir mal diese Zeile an:
Code:
// Lowbyte in Idetifiervariable schreiben, Highbyte nicht überschreiben
rx_identifier = 0000000011111111 & temp_RXB1SIDL;
Alles was zuvor in rx_identifier war, wird überschrieben. Die Konstante ist zudem dezimal, nicht binär. Selbst wenn sie binär wäre, hat die UND Funktion keine Wirkung auf das LowByte. Die richtige Lösung müsste in dem Thread aber schon stehen.

Gruß,
Dirk
 
Ok, Danke.

Ich werd mal weiter rumprobieren.

Ich frag mich grade warum die bei MCP die Register so derartig kompliziert gestaltet haben. :-(

Christian
 
Hallo Christian,

versuche mal folgendes:
Code:
rx_identifier = ((uint16_t)temp_RXB1SIDH<<3) | (temp_RXB1SIDL>>5);

Gruß,
Dirk
 
Hallo,

danke für den Tipp.

Kann man das eigentlich in möglichst viele Einzelschritte zerlegen?

Mag sein dass das für meinen Zweck tatsächlich funktioniert, aber ich versteh das leider nicht, darum geht es mir eigentlich.

Danke ;)

Christian
 
Hallo,
Kann man das eigentlich in möglichst viele Einzelschritte zerlegen?

Code:
rx_identifier = ((uint16_t)temp_RXB1SIDH<<3) | (temp_RXB1SIDL>>5);
Nun in Einzelschritten:

Code:
// Zuerst kopiere ich temp_RXB1SIDH in die Variable rx_identifier,
// undzwar um 3 Bit nach links verschoben. Die untersten 3 Bit werden
// ja für das Lowbyte benötigt.
// Damit die oberen 3 Bits von temp_RXB1SIDH nicht aus der 8Bit Variablen
// "herausgeschoben" werden, muss die Variable temp_RXB1SIDH mit 16Bit
// "gecastet" werden. Die Variable wird in der folgenden Zeile als 16 Bit Variable behandelt.

rx_identifier = (uint16_t)temp_RXB1SIDH<<3;

// Nun schiebe ich den Inhalt von temp_RXB1SIDL um 5 Bit nach rechts,
// von links werden dann "0" nachgeschoben.

 
temp_RXB1SIDL = temp_RXB1SIDL>>5;

// Nun verwende ich ODER "|", dies kopiert mir die oberen drei Bits von
// temp_RXB1SIDL in die unteren drei Bits von rx_identifier. Die ODER-
// Funktion kann man hier anwenden, da ja die untersten 3 Bits von 
// rx_identifier zuvor "0" waren.

rx_identifier = rx_identifier | temp_RXB1SIDL;
Ich hoffe, es ist so verständlicher ... und ich hoffe, dass ich nun keinen Fehler drin habe ;)

Grüße,
Dirk
 
Aha jetzt wendet sich das Blatt.

Langsam versteh ich die Materie.

Das einzige was mir hier noch nicht in den Kopf will, warum die unteren 3 Bits "verodert", und nicht "verundet" werden ;)

Grüße

Christian
 
Das einzige was mir hier noch nicht in den Kopf will, warum die unteren 3 Bits "verodert", und nicht "verundet" werden ;)

Die untersten drei Bits im Ergebnisregister sind 0, dann kann man "verodern". Vielleicht helfen dir folgende Beispiele:

0b00000000 OR "|"
0b00000101
---------------
0b00000101


0b00000000 AND "&"
0b00000101
---------------
0b00000000 ( geht nicht )

Schau dir vielleicht auch mal das Thema GPIOs mit C von Nomis an. (Wir haben erst vor kurzem das Forum aktualisiert, leider habe ich das Codehighlighting noch nicht wieder integrieren können, so dass sich das Thema vielleicht nicht so gut lesen läßt)

Gruß,
Dirk
 
Jetzt hab ich das alles verstanden, hab auch noch ein bisschen herumprobiert, eigentlich logisch, dass da UND nicht funktionieren kann. :)

Muss ich bei dieser Zeile eigentlich casten, wenn eine 8-Bit Variable in eine 16-Bit Variable hineinkopiert wird?

Code:
// Lowbyte-Registerinhalt an die ersten 3 Stellen von rx_identifier setzen
rx_identifier = rx_identifier | temp_RXB1SIDL;

Nämlich so?

Code:
// Lowbyte-Registerinhalt an die ersten 3 Stellen von rx_identifier setzen
rx_identifier = rx_identifier | (uint16_t)temp_RXB1SIDL;

Grüße, Christian
 
Hallo Christian,
Muss ich bei dieser Zeile eigentlich casten, wenn eine 8-Bit Variable in eine 16-Bit Variable hineinkopiert wird?
Nämlich so?

Code:
// Lowbyte-Registerinhalt an die ersten 3 Stellen von rx_identifier setzen
rx_identifier = rx_identifier | (uint16_t)temp_RXB1SIDL;

musst du nicht. Der Compiler "verodert" hier nur das Lowbyte, das Highbyte von rx_identifier bleibt unberührt.

Gruß,
Dirk
 
Hallo,

hätte da noch ne Frage:

Ich schicke das da in die MCP-Register und in den CAN Bus:

Code:
		temp_TXB0SIDL = 0b00001000;
		temp_TXB0SIDH = 0b00000000;

		// Registerinhalt in TXB0SIDL zurückschreiben
		MCP2515_SPI_WRITE(TXB0SIDL, temp_TXB0SIDL);

		// Registerinhalt in TXB0SIDH zurückschreiben
		MCP2515_SPI_WRITE(TXB0SIDH, temp_TXB0SIDH);


So gestaltet sich der Empfang:

Code:
	// Buffer 1 befragen
	if( bit_is_set(rx_status, RX1IF) )
	{

		// Startadresse festlegen, ab der ausgelesen wird
		rx_adress = RXB1D0;

		// Länge der angekommenen Nutzdaten
		rx_datalength = MCP2515_SPI_READ(RXB1DLC);	

		// 4 Bits rausschieben und mit Nullen wieder auffüllen 
		// (obere 4 Bits haben noch weitere, hier nicht benötigte Bedeutung)
		rx_datalength = rx_datalength << 4;
		rx_datalength = rx_datalength >> 4;

		// Identifier auslesen
		temp_RXB1SIDL = MCP2515_SPI_READ(RXB1SIDL);
		temp_RXB1SIDH = MCP2515_SPI_READ(RXB1SIDH);
	
		// 16-Bit Identifiervariable zusammensetzen aus den zwei 8 Bit Variablen
		
			// Highbyte-Registerinhalt um 3 Bit linksverschoben in rx_identifier hineinkopieren
			// (casten) ist notwendig da sonst die 3 Bits rausfallen
			rx_identifier = (uint16_t)temp_RXB1SIDH << 3;

			// Lowbyte-Registerinhalt um 5 Bit rechtsverschieben, wichtig sind Bytes 6 7 8
			temp_RXB1SIDL = temp_RXB1SIDL >> 5;

			// Lowbyte-Registerinhalt an die ersten 3 Stellen von rx_identifier setzen
			// Die anderen Bits bleiben unberührt, da die "0" sind. (wegen Rechtsshift vorhin)
			rx_identifier = rx_identifier | temp_RXB1SIDL;

	}


Das doofe ist, wenn ich den Identifier mit LEDs "ausblinke", steht da immer ne "0" in der Variable, obwohl ja eigentlich ne "8" drin stehen sollte...

Liegts am obigen Code, oder dann doch an was anderem? (grübel)

Christian
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)