summaryrefslogtreecommitdiff
path: root/content/projects/uC_bare.md
blob: eeded172f9883ec0590c1364f32442c66c65afa6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
+++
title = "µC firmware: interacting with bare-systems"
date = 2023-08-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 <stdio.h>

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.