-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathgpio2.c
266 lines (213 loc) · 6.04 KB
/
gpio2.c
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/**
* @file gpio.c
* @brief GPIO HAL for EFM32GG
* @version 2.0
*
* @note This is a version 2 of the GPIO library. It implements support
* for interrupts.
*
* @note The GPIO architecture in the EFM32 series only enables one pin
* with a specific number to generate interrupts. For example, only
* one Px10 can generate an interrupt.
*
* @note There are two interrupts associated with the GPIOs.
*
* * GPIO_ODD_IRQHandler: Generated by pins with odd numbers.
* * GPIO_EVEN_IRQHandler: Generated by pins with EVEN numbers.
*
* @note There are two registers (EXTIPSELL and EXTIPSELH), that specifies
* which GPIO port generates an interrupt. EXPTISELL specifies which
* GPIO port generates interrupts for pins 0 to 7 and EXPTISELH, for
* pins 8 to 15.
*
* @note Additionally, the register IEN specifies which pins can generate
* interrupts. The registers EXTIRISE and EXTIFRALL enable interrupts
* when the signal on a pin rises or falls.
*
*
*/
#include <stdint.h>
#include "gpio.h"
#define BIT(N) (1U<<(N))
/**
* @brief callbacks routines
*
* @note A struct is used to enable the sharing of callback functions
*
* @note They are called when an interrupt is generated.
*
* @note There are 16 of them, one for each pin number.
*
*/
typedef struct {
void (*callback)(uint32_t);
GPIO_t gpio;
uint32_t mask;
} CallbackInfo;
static CallbackInfo GPIO_Callback[16] = {0};
/**
* @brief Find gpio port index
*
* @note GPIO is referenced by a pointer, but sometimes it must be
* referenced by an index. This functions returns the index
* cor
*/
static inline int GPIO_FindIndex(GPIO_t gpio) {
return gpio-GPIOA;
}
/**
* @brief
*
* @param pin
* @return int
*/
static inline int GPIO_EnableIRQ(int pin) {
GPIO->IEN |= BIT(pin);
}
/**
* @brief
*
* @param pin
* @return int
*/
static inline int GPIO_DisableIRQ(int pin) {
GPIO->IEN &= ~BIT(pin);
}
/**
* @brief
*
* @param pin
* @return int
*/
static inline int GPIO_ClearIRQ(int pin) {
GPIO->IFC = BIT(pin);
}
/**
* @brief Configure pins
* @param gpio: pointer to gpio registers
* @param pins: bit mask containing 1 in the position to be configured
* @param mode: GPIO_MODE_INPUT, GPIO_MODE_PUSHPULL, etc.
*/
void
GPIO_ConfigPins(GPIO_t gpio, uint32_t pins, uint32_t mode) {
const uint32_t mask = 0xF; // Fields are 4 bits wide
int i;
uint32_t mbit;
mbit = 0x0001; // Bit 0 set
/* Configure low order bits/pins 0-7 */
for(i=0;i<32;i+=4) {
if( (pins&mbit) != 0 ) {
gpio->MODEL &= ~(mask<<i);
gpio->MODEL |= mode<<i;
}
mbit <<= 1;
}
/* Configure High order bits/pins 8-15 */
for(i=0;i<32;i+=4) {
if( (pins&mbit) != 0 ) {
gpio->MODEH &= ~(mask<<i);
gpio->MODEH |= mode<<i;
}
mbit <<= 1;
}
}
/**
* @brief Configure pins for input or output
* @param gpio: pointer to gpio registers
* @param inputs: bit mask containing 1 in the position to be configured as input
* @param outputs: bit mask containing 1 in the position to be configured as output
*/
void
GPIO_Init(GPIO_t gpio, uint32_t inputs, uint32_t outputs) {
/* Enable Clock for GPIO */
CMU->HFPERCLKDIV |= CMU_HFPERCLKDIV_HFPERCLKEN; // Enable HFPERCLK
CMU->HFPERCLKEN0 |= CMU_HFPERCLKEN0_GPIO; // Enable HFPERCKL for GPIO
GPIO_ConfigPins(gpio,outputs,GPIO_MODE_PUSHPULL);
GPIO_ConfigPins(gpio,inputs,GPIO_MODE_INPUT); // Safer when both specified
}
#define GPIO_IRQ_SENSE_RISING 1
#define GPIO_IRQ_SENSE_FALLING 2
#define GPIO_IRQ_SENSE_BOTH (GPIO_IRQ_SENSE_RISING|GPIO_IRQ_SENSE_FALLING)
/**
* @brief Set IRQ routine
*
* @param gpio
* @param pin
* @param callback
* @return int
*/
int GPIO_SetIRQ(GPIO_t gpio, int pin, int sense, void (*callback)(uint32_t)) {
int index;
uint32_t m = BIT(pin);
// Disable interrupt for pin
GPIO->IEN &= ~m;
// Set CallbackInfo struct for pin
index = GPIO_FindIndex(gpio);
GPIO_Callback[index].callback = callback;
GPIO_Callback[index].mask = m;
GPIO_Callback[index].gpio = gpio;
// Configure sense
if( (sense&GPIO_IRQ_SENSE_RISING)!=0 ) GPIO->EXTIRISE |= m;
if( (sense&GPIO_IRQ_SENSE_FALLING)!=0 ) GPIO->EXTIFALL |= m;
// Set which gpio will trigger interrupt
if( index < 8 ) {
GPIO->EXTIPSELL = (index<<(pin*4));
} else {
GPIO->EXTIPSELH = (index<<((pin-8)*4));
}
// Clear any interrupt from pin
GPIO->IFC = m;
// Enable interrupt for pin
GPIO->IEN |= m;
return 0;
}
/**
* @brief
*
*/
static void ProcessInterrupt(int pin) {
CallbackInfo *p = GPIO_Callback + pin;
uint32_t in;
GPIO_t gpio;
// Get gpio port address
if( !p->gpio )
return;
gpio = p->gpio;
// Read Port
in = gpio->DIN;
// Clear interrupt
GPIO->IFC = p->mask;
// Call callback routine
if( p->callback ) p->callback(in);
}
/**
* @brief Interrupt routines
*
* @note Loop unrolled to make it faster
*/
///@{
void GPIO_ODD_IRQHandler(void) {
uint32_t ie;
ie = GPIO->IF;
if( (ie&BIT(1))!=0 ) ProcessInterrupt(1);
if( (ie&BIT(3))!=0 ) ProcessInterrupt(3);
if( (ie&BIT(5))!=0 ) ProcessInterrupt(5);
if( (ie&BIT(7))!=0 ) ProcessInterrupt(7);
if( (ie&BIT(9))!=0 ) ProcessInterrupt(9);
if( (ie&BIT(11))!=0 ) ProcessInterrupt(11);
if( (ie&BIT(13))!=0 ) ProcessInterrupt(13);
if( (ie&BIT(15))!=0 ) ProcessInterrupt(15);
}
void GPIO_EVEN_IRQHandler(void) {
uint32_t ie;
ie = GPIO->IF;
if( (ie&BIT(0))!=0 ) ProcessInterrupt(0);
if( (ie&BIT(2))!=0 ) ProcessInterrupt(2);
if( (ie&BIT(4))!=0 ) ProcessInterrupt(4);
if( (ie&BIT(6))!=0 ) ProcessInterrupt(6);
if( (ie&BIT(8))!=0 ) ProcessInterrupt(8);
if( (ie&BIT(10))!=0 ) ProcessInterrupt(10);
if( (ie&BIT(12))!=0 ) ProcessInterrupt(12);
if( (ie&BIT(14))!=0 ) ProcessInterrupt(14);
}
///@}