Home



Stand 07.07.2014
07.2020 mal reingesehen
21 auch
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 )