+++ title = "µC firmware: interacting with bare-systems" date = 2023-07-18 +++ --- ## Address an hardware register ### Raw ```C int main(void) { unsigned char addr = 0xff; // reg addr volatile unsigned char *reg = (volatile unisgned char *)addr; // reg ptr unsigned char val; // reg value // change the integer type if needed read: val = *(volatile unsigned char *)reg; write: *(volatile unsigned char *)reg = val; return 0; } ``` ### Macro ```C #define REG 0xff #define READ_H(reg) (*(volatile unsigned char *)(reg)) #define WRITE_H(reg, val) (*(volatile unsigned char *)(reg)) = (val) ``` ## Bit manipulation ### Raw ```C int main(void) { int val; // signed or unsigned unsigned char position; // max at bit-length of val unsigned char bit; // 0 or 1 unsigned char x; // unknown value, 0 or 1 setBit: val |= (1 << position); unsetBit: val &= ~(1 << position); toggleBit: val ^= (1 << position); verifyBit: bit = (val >> position) & 0x1; bit_to_x: // works with both x = 1 or 0 val = (val & ~(1 << position)) | (x << position); return 0; } ``` ### Macro ```C #define SET_BIT(val, bitIndex) (val) |= (1 << (bitIndex)) #define CLEAR_BIT(val, bitIndex) (val) &= ~(1 << (bitIndex)) #define TOGGLE_BIT(val, bitIndex) (val) ^= (1 << (bitIndex)) #define BIT_VALUE(val, bitIndex) (((val) >> (bitIndex)) & 1) #define BIT_TO_X(val, x, bitIndex) (val) = ((val) & ~(1 << (bitIndex)) | ((x) << (bitIndex)) ``` ## Declaration and use of hardware register Following the last two sections: ```C #define REGD (*(volatile unsigned char *)(0xff)) #define D0 0 #define D1 1 #define D2 2 // etc int main(void) { REGD = 0x00; // whole register REGD |= (1 << D1); // set bit 1 of REGD REGD &= ~(1 << D0); // unset bit 2 of REGD return 0; } ``` ## Transpose a dataBus value on spread registers' bits ```C void Xpos(unsigned char x, unsigned char xIndex, unsigned char *dest, unsigned char destIndex, unsigned char bitLength) { unsigned char mask = 0x1; unsigned char i = 1; while (i++ < bitLength) mask = (mask << 1) | 0x1; *dest &= ~(mask << destIndex); // set the 0s *dest |= (((x >> xIndex) & mask) << destIndex); // set the 1s } /*** Example ***/ // Registers unsigned char E = 0xa5; unsigned char D = 0xa5; unsigned char C = 0xa5; void drawBar(unsigned char x) // outputs x value on spread bits of E, D and C { Xpos(x, 7, &E, 6, 1); // x7 is on E6 Xpos(x, 6, &D, 7, 1); // x6 is on D7 Xpos(x, 5, &C, 6, 1); // x5 is on C6 Xpos(x, 4, &D, 4, 1); // x4 is on D4 Xpos(x, 3, &D, 0, 1); // x3 is on D0 Xpos(x, 2, &D, 1, 1); // x2 is on D1 Xpos(x, 0, &D, 2, 2); // x1-0 is on D3-2 } int main(void) { unsigned char x = 0xff; drawBar(x); return 0; } ``` ## Some macros ```C #define MIN(A, B) ((A) <= (B) ? (A) : (B)) #define MAX(A, B) ((A) >= (B) ? (A) : (B)) #ifndef ARRAY_SIZE #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) #endif #ifndef NULL #define NULL ((void *)0) #endif ``` ## Bit-fields **Careful:** the bit order is compiler-dependent, this implementation is *not* portable. ```C #include union byte_t { unsigned char val; struct { unsigned char A : 2; unsigned char B : 1; unsigned char C : 3; }; struct { unsigned char A0 : 1; unsigned char A1 : 1; unsigned char : 1; unsigned char C0 : 1; unsigned char C1 : 1; unsigned char C2 : 1; }; }; int main(void) { union byte_t reg = {0x2a}; // 0b101010 printf("%hhu\t0x%x\t0x%x\n", reg.C1, reg.A, reg.val); // gives: 1, 0x2, 0x2a return 0; } ``` ## Tricky definitions ### Static Static has three distinct uses in `C`: 1. A variable declared static within the body of a function maintains its value between function invocations. 2. A variable declared static within a module, (but outside the body of a function) is accessible by all functions within that module. It is not accessible by functions within any other module. That is, it is a localized global. 3. Functions declared static within a module may only be called by other functions within that module. That is, the scope of the function is localized to the module within which it is declared. ### Volatile Examples of volatile variables are: 1. Hardware registers in peripherals (e.g., status registers). 2. Non-stack variables referenced within an interrupt service routine. 3. Variables shared by multiple tasks in a multi-threaded application.