Link zu : Bitmanipulation
Bitte die vielen kleinen Rechtschreibfehler verzeihen..
Merkzettel:
Alternative zur ARDUINO IDE frü dei ESP Programmierung;
Visual Studio Code + PlatformIO IDE, oder
Sloeber
PlatformIO plus VScode
TeamPlatformIO
https://byte-style.de/.../visual-studio-code-und.../
https://platformio.org/install
https://platformio.org/install/ide?install=vscode
https://marketplace.visualstudio.com/items?itemName=platformio.platformio-ide
Häufig gestellte Fragen zum Visual Studio-Code: https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting
https://byte-style.de/2018/01/visual-studio-code-und-mikrocontroller/?fbclid=IwAR3zaEGFwbApDsYVipvgd1yd_gpiNo4SzqG1xCoRtf9sK2KqmcTWA8i3NZQ
// ******************
int Round(float fIn)
// ******************
{
return (int)floor(fIn+0.5);
}
Externe
LINKS
C Kurs - wenn der Pointer in den Wald zeigt: https://www.peacesoftware.de/ckurs12.html
int
a = 123;
int
*intptr = NULL; //
die Variable intptr kann eine Adresse eines integer Wertes
aufnehmen.
Mit
dem Kaufmanns-und
'&' Operator weist
man dem Pointer die Adresse einer Variablen zu,
intptr
= &a; //
weise jetzt dem Integerpointer intptr die Adresse der Integervariablen
a zu.
printf("%d\n",
a); // a wird angezeigt es sollte '123' dort stehen
Der
Pointer intptr enthält jetzt die Adresse der Variablen a, intptr
referenziert a oder intptr zeigt auf a.
Mit Sternchen
'*' Operator erhält
man das, was in der Speicheradresse steht, die der Pointer enthält, man
dereferenziert den Pointer:
integervar
= *pointer_auf_int_var;
*intptr
= 321; //
und schreibe jetzt in die Adresse, auf die intptr zeigt, einen neuen
Wert, 321
Pointer Arithmetik
*(Array + Abstand) IST GLEICH
Array[Abstand];
*(Array
+ 0) = 'A';
*(Array
+ 1) = 'B';
*(Array
+ 2) = 'C';
*(Array
+ 3) = 0; --> Text
ist "ABC"
Pointer auf Pointer:
int
x = 42;
int
*myptr, **masterptr;
myptr
= &x; // myptr zeigt auf x
masterptr
= &myptr; // masterptr zeigt auf myptr
printf("**masterptr:
%d\n", **masterptr);
// masterptr wird zweimal dereferenziert
char*
pChar;
char** ppChar;
char cText[] = "\r\nDies ist eine PointerText";
pChar = cText;
printf("\r\nText=%s", pChar); oder
std::cout << pChar;
ppChar = &pChar;
printf("\r\nPointerPointer Text=%s", *ppChar);
NICHT GEHT !!
//geht auch Direkt auf Array?
ppChar = aText;
printf("\r\nArry PointerPointer Text=%s", *ppChar);
NEIN
NEIN
NEIN
NEIN
NEIN
NEIN
NEIN
NEIN
NEIN
NEIN
Man
kann das natürlich noch weitertreiben und Pointer auf Pointer zeigen
lassen, die wiederum auf Pointer auf Pointer zeigen:
int
x = 42, *ptr1, **ptr2, ***ptr3, ****ptr4; // usw.
ptr1 = &x;
ptr2 = &ptr1;
ptr3 = &ptr2;
ptr4 = &ptr3;
printf("****ptr4: %d\n", ****ptr4); // ergibt: ****ptr4: 42
Programmierung
Stile und
Konzepte:
WARUM ?
Bei der Programmierung in C kann man ohne großen
Aufwand allein durch die Schreibweise für professionelle Strukturen
sorgen.
Es gibt einen anfänglich, auch von
Microsoft Windows Entwicklern, eingeführten Programmierstil.
Ich selbst bin vor vielen Jahren beim studieren der Windows 3.1
Programmierung auf dieses Konzept gestoßen.
Und schon bald, als ich die ersten größeren Programme geschrieben habe,
wurde mir klar, wie klasse das Konzept ist.
WIE ?
Ein simpler Buchstaben-Code in der Benennung von Variablen
oder
bewussteGroß-Keinschreibung
unterscheiden Variablen von Konstanten
oder #defines
(in einer Headerdatei).
Das sorgt somit für einen übersichtlichen,
lesbaren
Code und einem einheitlichen Programmierstil.
Dieser
Stil hilft sehr dabei Fehler und passende Zusammenhänge
unmittelbar/sofort, allein an der evtl. unpassenden Schreibweise, zu
erkennen.
* es sorgt für Übersicht.
* gute verständliche Lesbarkeit der Bedeutung von Variablen und
Konstanten.
* gibt dabei Auskunft über wichtige Eigenschaften von
Variablen.
Datentyp: Ganzzahl, Zeichen, oder Pointer
Datenbreite:
char, short, Byte, Word, etc....
unsigned oder
signed oder float
* gibt Auskunft über Lokaler oder Globaler
Geltungsbereich....
das ist doch schon mal recht hilfreich, und es kostet eben
keine Mühe.
Die Regeln sind leicht zu
merken und passen auf einen Bierdeckel.
(Copy - Paste)...Schützt auch vor der allseits beliebten Tippfehler
Falle.
Zuerst
ein Vergleich:
Ich kann nur hoffen, dass ich euch mit folgender Darstellung
überzeugen kann "niemals wieder" die beliebten "Ein-Buchstaben
Variablen" zu benutzen!
for( i = 0; i
< 20.......) = Bäääääh
= Anfänger-max. Basic Programmierstil.
Beispiel:
Zwei Programme, die das gleiche tun,
nur, das ein ist unverständlich, -->das andere lesbar:
for( i = 0, i<20, i++ )
{ v = s *
z[i] : }:
// Was tut das Programm
warum ? Keine Ahnung
keiner weis,
istVariable "i" evtl int oder float ??
, loakal oder global verwendet ?? ´..
Nichts ist so klar !!
BESSER:
for(
u8NN =
0, u8NN<20, u8NN++ )
{
fSensor_Volt = fSensor *
u16Kalibrierung[ u8NN ]:
}:
Man erkennt nun :
a) Wir haben eine
"u8=unsigned Byte (
8=8Bit)" als Zählvariable.
b) fSensor ist ein Float Sensorwert (erkennbar
am kleinen f
)
c) fSensor wird
mit einer "unsigned
integer"-Kalibrier-Tabelle
(erkennbar
am u16) verrechnet.
d)
Das
Ergebnis ist mit der Tabelle korrigiert, und heißt
auch lesbar
"fSensor_Volt"
und ist damit eine floatVariable ( kleines f am Anfang), die
das Ergebnis in Volt anzeigt.
Also alles gut versteh und lesbar ! Und das alles nur durch die
Schreibweise.
Es
ist nur eine Handvoll kleiner Regeln zu beachten, an die man sich
schnell gewöhnt.
Der Sinn!
* Lesbarkeit des Codes erhöhen,
* Jederzeit kann der Datentyp einer Variable in seiner
Verwendung erkannt werden ( und damit auch evtl. fehlerhafte
Anwendung!)
* Wildes Verteilen von festen Zahlenwerten im Code ist unprofessionell.
Viel vernünftiger ist die Verwendung von "Lesbaren" Platzhaltern, auch
zur Programmpflege.
Das ist der Unterschied zwischen einem Anfänger-hauruck-Programm und
gutem pflegbaren Code.
.
Konventionen:
Mit Großbuchstaben geschrieben
sind: kurz gesagt, alles was in Header Dateien
gehört.
In gutem C-Code
sollten keine Konstanten als feste Zahlen geschrieben werden, auch wenn
man das vom Compiler aus darf.
#define Platzhalter
erhöhen die Lese und Pflegbarkeit des Codes enorm.
Großbuchstaben:
Beispiele:
Konstanten |
#define
VOLTAGE_REFERENCE 2.56
#define SOLLWERT 1234.56 |
Makros |
#define MIN ((A)
<(B)) |
Selbstdefinierte
Daten-Typen |
typedef struct
NMEA_STRUCT
{
..
}NMEA_STRUCT_TYPE; |
Alle
anderen "#define"
wie z.B. FlagBit-Definitionen´
( die in einer Flagbyte Varibale gesetzt,gelöscht werden, typisch für
µController Programme ) |
#define
KEY_PRESSED_EVENT 0x01
#define KEY_PRESSED
0x02
#define KEY_RELEASE_EVENT 0x04
#define KEY_RELEASED
0x08
#define KEY_VALID
0x10 |
Anwendung:
im Headerfile "main.h" wird
zuerst z.B. eine Arraygröße definiert:
#define STRMAX 80
eine Variable aus Char.Array wird
so geschrieben
char caVariablenName[
STRMAX + 1 ];
Regeln zur Scheibweise
von Variablennamen.
Schreibweise mit vorangestelltem Kleinbuchstaben, die den Datentyp
anzeigen.
Allein an der
trickreichen Schreibweise kann
viel Information über eine
Variable verpackt werden:
1. ob die Variable global oder lokal definiert ist...
2. welchen Datentyp die Variable hat.
a) ob die Variable ein Array
ist,
b) ob die Variable ein Pointer
ist,
3. welche Bedeutung/Funktion die Variable hat.
oder noch mehr..
Wie das geht?
ganz einfach
1.
JEDE“ Variable hat einen sehr minimalen "Präfix-Code"
aus kleinen Buchstaben
an Variablen-Namensanfang stehen
stehen.
2. Variablen
in C-Code sollten ausführlich und lesbar ausgeschrieben sein.
3. Variablen erkennt man an gemischter
Gross-Klein Schreibung. also daran, daß
„JEDE“ Silbe einen
Großbuchstaben hat Bsp: u16DiesIstEineVariableVomTypUnsignedInteger_mit16Bit
|
Eine Handvoll Bezeichner sind
sinnvoll, die fast selbsterklärend sind:
g
= Global
p = Pointer
c = char
b = BYTE = unsigned char
ca = charArrray
i = Integer
u = unsigned
w = WORD = unsigned int
(das ganze vtl ergänzt
durch die Zahl der Bitbreite der Variable ) :
"u16VariablenName" )
l = long
f = float
d = double
z = ist Zeroed Array = STRING
(wird so gern vom Microsoft
Programmierern verwendet) |
Geschrieben wird in der Reihenfolge nach Priorität...
gpcVariablenName;
Lese: Global Pointer auf Char
VariablenName
Beispiele
czMessage = „Borschaft\0“
.z.B. caDiesIstEinArrayAusChar
pcaStrField
//Pointer auf Char-array
Beispiele:
eine Variable aus char mit 8 Bit wird so
Geschrieben eine Variable aus Char.Array wird
so Geschrieben
char caVariablenName
// Lese char
array
<Name> Bsp: caLCDText;
eine Variable aus unsigned char mit 8 Bit wird so
Geschrieben:
uint8_t u8VariablenName;
// Lese char
array unsigend 8 Bit
<Name> Bsp: u8KeyFlag;
eine Variable aus unsigned char mit 16 Bit wird so
Geschrieben
uint16_t u16VariablenName;
// Lese:
unsigned integer,16Bit...<VariablenName>
eine Variable aus pointer auf char wird so
Geschrieben
char* pcVariablenName;
//Lese: Pointer auf char <VariablenName>
eine Variable aus Char Array wird
so Geschrieben
char caVariablenName[
STRMAX ]; //Lese: char array
<VariablenName>
eine Pointer auf ein CharArray wird
so Geschrieben
char * pcaVariablenName;
// Lese: pointer auf char array <VariablenName>
UND NOCH ETWAS... wie schon erwähnt,
Es gibt KEINE EIN-Buchstaben
Variablen , auch wenn man das immer wieder sieht
zumindestnicht im professionellen Code.
Funktionen werden wie Variable geschrieben, nur ohne
Typkenzeicher!
void CatLastCharOfString(char* pczInStr)
Block und Klammerebenen
mit Einrückungen kennzeichnen
Gut ist es jeden Block mit einem Bereichsoperator {}
einzuleiten.
WIE ?
Jeder
Block/Bereichsoperator { } rückt eine
Tabulatorstelle ein
Richtige Verwendung: Klammern mit TAB einrücken!
Jede öffnende
Klammer {
steht immer über einer
schließenden Klammer
};
auch wenn man das woanders anders sieht !... man muss nicht nachmachen
was Andere machen.
Vorteil:
So sieht man
gleich, ob man die Klammerebennen stimmen und richtig im Zusammenhang
stehen.
Bsp:
void Funktion(void)
{
char cXX =0;
for(cXX=0; cXX<10; cXX++)
{
for(cYY=0; cYY<10; cYY++)
{
code.......
};
code..........
};
};
// ende der Funktion
Programmierkonzepte
für bitweise Methoden:
Es gibt immer mehrere Wege.. davon
doch nur einige 'Gute'.
Thema: wie definiere ich Bit-Positionen ?
Gezählt werden Bits von NULL bis SIEBEN,
--> 0..7,
nicht von 1
bis 8
!
( Binäre Zahlen werden von
rechts nach links, aufwärts zählend, geschrieben
)also:
BIT 7 |
BIT 6 |
BIT 5 |
BIT 4 |
BIT
3 |
Bit 2 |
Bit 1 |
Bit 0 |
Bin
Hex
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 A-10
1011 B-11
1100 C-12
1101 D-13
1110 E-14
1111 F-15
Wer aufmerksam ist, erkennt einen 01 Wechselrythmus,der je Stelle nach
links gesehen, sich halbiert.
C-konforme definitionen.... // = Komentar
#define BIT_0
0x01 // = 0000
0001 0
#define BIT_1
0x02 // = 0000
0010 1 #define BIT_2
0x04 // = 0000
0100 2
#define BIT_3
0x08 // = 0000
1000 3
#define BIT_4
0x10 // = 0001
0000 4
#define BIT_5
0x20 // = 0010
0000 5
#define BIT_6
0x40 // = 0100
0000 6
#define
BIT_7 0x80
// = 1000 0000
7
So "wandert" die EINS
schrittweise nach links......
Methoden zum gezielten SETZEN
und LÖSCHEN von Bits...
Bits setzen:
Dazu wird das Zielbyte
verodert mit den zu setzenden Bitpositionen:
ZIEL |=
Binaerposition ; Bsp:
u8KeyFlag |= 0x08;
Bits Löschen:
zum Löschen wird mit der Bitumkehrung ( Komplement = ~ ) verundet
ZIEL &= ~(Binaerposition);
u8KeyFlag &= ~0x08;
alternatives Verfahren zum Löschen von Bits mit EXOR ( dafür ohne
Komplementbildung )
Bits Loeschen:
ZIEL ^=
(Binaerposition);
Bsp: 1101 ^= 0001
ergibt 1100
Was bedeutet das !!
Syntaktische Erklärung und Theorie
( In Bytes, in Registern, in Status Bytes etc..
Sehr sehr wichtig... Schreibweise in Ansi-C: 0bnn
= Binär 0xnn= hexadezimal.
Ein Byte = 8 Bit , wird mit 2 Hexzahlen dargestellt: BSP: 0b00101010
ist
das selbe wir 0x2A
Berechnen der Bitposition
mit einer Aufzählung:
Wenn man für die Position des Bites das man setzen möchte nur
als Aufzählung kennt ( z.B. 0..7 ) dann kann man die BitPosition mit
dem
Trick: "Schieben eine EINS" genau ermitteln
Position = ( 1 << BitNummer )
So sind die Bits oft im Headerfile zum µC definiert:
Bsp: unter PORTA ist das 3. Bit so
definiert:
#define PA3 3
Um nun dieses Bit zu setzen geht man so vor, wen man die #define
verwenden will:
setzen: PORTA
|= (1<<
PA3 );
löschen:
PORTA
&= ~(1<<
PA3 );
TIP: In WINAVR
gibt es eine fertige Hilfe: _BV(
BitNummer ) = (
1<< BitNummer )