]> begriffs open source - cmsis-driver-bare/blob - lib/driver_gpio.c
Pass cmsis validator test
[cmsis-driver-bare] / lib / driver_gpio.c
1 /*
2  * CMSIS-Driver for GPIO on STM32F411 based on ARM driver template.
3  */
4
5 #include "Driver_GPIO.h"
6 #include "stm32f4xx.h"
7
8 // #include <assert.h>
9
10 #define GPIO_MAX_PINS           16U
11 #define PIN_IS_AVAILABLE(n)     ((n) < GPIO_MAX_PINS)
12
13 ARM_GPIO_SignalEvent_t g_pin_cb[GPIO_MAX_PINS];
14 ARM_GPIO_EVENT_TRIGGER g_pin_trigger[GPIO_MAX_PINS];
15 GPIO_TypeDef *g_pin_gpio[GPIO_MAX_PINS];  // Track which GPIO port owns each pin
16
17 static void
18 dispatch_callback(ARM_GPIO_Pin_t pin)
19 {
20         // Clear the pending interrupt bit
21         EXTI->PR = (1 << pin);
22         
23         if (!g_pin_cb[pin])
24                 return;
25         
26         // Determine which event to report
27         ARM_GPIO_EVENT_TRIGGER event = g_pin_trigger[pin];
28         
29         // For either-edge mode, report the specific edge that occurred
30         if (event == ARM_GPIO_TRIGGER_EITHER_EDGE)
31         {
32                 // Read current pin state to determine which edge occurred
33                 // Note: Using direct IDR read instead of GPIO_GetInput() for performance in ISR
34                 uint32_t pin_high = g_pin_gpio[pin]->IDR & (1 << pin);
35                 event = pin_high ? ARM_GPIO_EVENT_RISING_EDGE : ARM_GPIO_EVENT_FALLING_EDGE;
36         }
37         
38         g_pin_cb[pin](pin, event);
39 }
40
41 static void gpio_exti_isr_0(void) { dispatch_callback(0); }
42
43 static void gpio_exti_isr_1(void) { dispatch_callback(1); }
44
45 static void gpio_exti_isr_2(void) { dispatch_callback(2); }
46
47 static void gpio_exti_isr_3(void) { dispatch_callback(3); }
48
49 static void gpio_exti_isr_4(void) { dispatch_callback(4); }
50
51 static void gpio_exti_isr_9_5(void)
52 {
53         uint32_t pending = EXTI->PR;
54         for (ARM_GPIO_Pin_t i = 5; i < 10; i++)
55                 if (pending & (1 << i))
56                         dispatch_callback(i);
57 }
58
59 static void gpio_exti_isr_15_10(void)
60 {
61         uint32_t pending = EXTI->PR;
62         for (ARM_GPIO_Pin_t i = 10; i < 16; i++)
63                 if (pending & (1 << i))
64                         dispatch_callback(i);
65 }
66
67 static void assign_exti(GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin)
68 {
69         // masks from reference manual 7.2.3
70         // SYSCFG external interrupt configuration register
71         unsigned port_mask =
72                   gpio == GPIOA ? 0
73                 : gpio == GPIOB ? 1
74                 : gpio == GPIOC ? 2
75                 : gpio == GPIOD ? 3
76                 : gpio == GPIOE ? 4
77                 : gpio == GPIOH ? 7
78                 : 999;
79         if (port_mask != 999)
80         {
81                 if (gpio)
82                 {
83                         // Clear the 4-bit field first, then set the new port
84                         SYSCFG->EXTICR[pin / 4] &= ~(0xF << 4*(pin % 4));
85                         SYSCFG->EXTICR[pin / 4] |=  (port_mask << 4*(pin % 4));
86                 }
87                 else
88                         SYSCFG->EXTICR[pin / 4] &= ~(0xF << 4*(pin % 4));
89         }
90 }
91
92 static uintptr_t
93 isr_for_irq(IRQn_Type irq)
94 {
95         switch (irq)
96         {
97                 case EXTI0_IRQn:     return (uintptr_t)gpio_exti_isr_0;
98                 case EXTI1_IRQn:     return (uintptr_t)gpio_exti_isr_1;
99                 case EXTI2_IRQn:     return (uintptr_t)gpio_exti_isr_2;
100                 case EXTI3_IRQn:     return (uintptr_t)gpio_exti_isr_3;
101                 case EXTI4_IRQn:     return (uintptr_t)gpio_exti_isr_4;
102                 case EXTI9_5_IRQn:   return (uintptr_t)gpio_exti_isr_9_5;
103                 case EXTI15_10_IRQn: return (uintptr_t)gpio_exti_isr_15_10;
104                 default:             return (uintptr_t)NULL;
105         }
106 }
107
108 static IRQn_Type
109 irq_for_pin(ARM_GPIO_Pin_t pin)
110 {
111         if (pin < 5)
112                 return EXTI0_IRQn + pin;
113         if (5 <= pin && pin < 10)
114                 return EXTI9_5_IRQn;
115         return EXTI15_10_IRQn;
116 }
117
118 static int32_t
119 GPIO_SetDirection(
120         GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin,
121         ARM_GPIO_DIRECTION direction)
122 {
123         int32_t result = ARM_DRIVER_OK;
124
125         // Ref 8.4.1: MODER has two bits for each pin, with
126         // 00: Input (reset state)
127         // 01: General purpose output mode
128         // 10: Alternate function mode
129         // 11: Analog mode
130
131         if (PIN_IS_AVAILABLE(pin))
132         {
133                 uint32_t tmp = 0;
134                 switch (direction)
135                 {
136                         case ARM_GPIO_INPUT:
137                                 gpio->MODER &= ~(GPIO_MODER_MODER0_Msk << (pin * 2));
138                                 break;
139                         case ARM_GPIO_OUTPUT:
140                                 tmp  =   gpio->MODER;
141                                 tmp &= ~(GPIO_MODER_MODER0_Msk << (pin * 2));
142                                 tmp |=   GPIO_MODER_MODER0_0 << (pin * 2);
143                                 gpio->MODER = tmp;
144                                 break;
145                         default:
146                                 result = ARM_DRIVER_ERROR_PARAMETER;
147                                 break;
148                 }
149         }
150         else
151                 result = ARM_GPIO_ERROR_PIN;
152
153         return result;
154 }
155
156 static int32_t
157 GPIO_SetOutputMode(
158         GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin,
159         ARM_GPIO_OUTPUT_MODE mode)
160 {
161         int32_t result = ARM_DRIVER_OK;
162
163         // Ref 8.4.2: OTYPER has one bit for each pin, with
164         // 0: Output push-pull (reset state)
165         // 1: Output open-drain
166
167         if (PIN_IS_AVAILABLE(pin))
168         {
169                 uint32_t pin_mask = 1 << pin;
170                 switch (mode)
171                 {
172                         case ARM_GPIO_PUSH_PULL:
173                                 gpio->OTYPER &= ~pin_mask;
174                                 break;
175                         case ARM_GPIO_OPEN_DRAIN:
176                                 gpio->OTYPER |=  pin_mask;
177                                 break;
178                         default:
179                                 result = ARM_DRIVER_ERROR_PARAMETER;
180                                 break;
181                 }
182         }
183         else
184                 result = ARM_GPIO_ERROR_PIN;
185
186         return result;
187 }
188
189 static int32_t
190 GPIO_SetPullResistor(
191         GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin,
192         ARM_GPIO_PULL_RESISTOR resistor)
193 {
194         int32_t result = ARM_DRIVER_OK;
195
196         // Ref 8.4.4: PUPDR has two bits for each pin, with
197         // 00 = no pull up, no pull down
198         // 01 = pull up
199         // 10 = pull down
200         // 11 = reserved
201
202         if (PIN_IS_AVAILABLE(pin))
203         {
204                 uint32_t tmp = 0;
205                 switch (resistor)
206                 {
207                         case ARM_GPIO_PULL_NONE:
208                                 // zero out the two bits
209                                 gpio->PUPDR &= ~(GPIO_PUPDR_PUPD0_Msk << (pin * 2));
210                                 break;
211                         case ARM_GPIO_PULL_UP:
212                                 tmp  =   gpio->PUPDR;
213                                 tmp &= ~(GPIO_PUPDR_PUPD0_Msk << (pin * 2));
214                                 tmp |=   GPIO_PUPDR_PUPD0_0 << (pin * 2);
215                                 gpio->PUPDR = tmp;
216                                 break;
217                         case ARM_GPIO_PULL_DOWN:
218                                 tmp  =   gpio->PUPDR;
219                                 tmp &= ~(GPIO_PUPDR_PUPD0_Msk << (pin * 2));
220                                 tmp |=   GPIO_PUPDR_PUPD0_1 << (pin * 2);
221                                 gpio->PUPDR = tmp;
222                                 break;
223                         default:
224                                 result = ARM_DRIVER_ERROR_PARAMETER;
225                                 break;
226                 }
227         }
228         else
229                 result = ARM_GPIO_ERROR_PIN;
230
231         return result;
232 }
233
234 static int32_t
235 GPIO_SetEventTrigger(
236         ARM_GPIO_Pin_t pin,
237         ARM_GPIO_EVENT_TRIGGER trigger)
238 {
239         int32_t result = ARM_DRIVER_OK;
240
241         // SYSCFG_EXTICR1 has four-bit regions:
242         //  each EXTIn handles PAn, PBn, PCn, ...
243
244         if (PIN_IS_AVAILABLE(pin))
245         {
246                 uint32_t pin_mask = 1 << pin;
247                 switch (trigger) {
248                         case ARM_GPIO_TRIGGER_NONE:
249                                 EXTI->RTSR &= ~pin_mask;
250                                 EXTI->FTSR &= ~pin_mask;
251                                 EXTI->IMR  &= ~pin_mask;
252                                 break;
253                         case ARM_GPIO_TRIGGER_RISING_EDGE:
254                                 EXTI->RTSR |=  pin_mask;
255                                 EXTI->FTSR &= ~pin_mask;
256                                 EXTI->IMR  |=  pin_mask;
257                                 break;
258                         case ARM_GPIO_TRIGGER_FALLING_EDGE:
259                                 EXTI->RTSR &= ~pin_mask;
260                                 EXTI->FTSR |=  pin_mask;
261                                 EXTI->IMR  |=  pin_mask;
262                                 break;
263                         case ARM_GPIO_TRIGGER_EITHER_EDGE:
264                                 EXTI->RTSR |=  pin_mask;
265                                 EXTI->FTSR |=  pin_mask;
266                                 EXTI->IMR  |=  pin_mask;
267                                 break;
268                         default:
269                                 result = ARM_DRIVER_ERROR_PARAMETER;
270                                 break;
271                 }
272         }
273         else
274                 result = ARM_GPIO_ERROR_PIN;
275
276         if (result == ARM_DRIVER_OK)
277                 g_pin_trigger[pin] = trigger;
278
279         return result;
280 }
281
282 // Setup GPIO Interface
283 //
284 // The function ARM_GPIO_Setup sets-up the specified pin
285 // as GPIO with default configuration. Pin is configured
286 // as input without pull-resistor and without event
287 // trigger.
288 //
289 // The parameter cb_event specifies a pointer to the
290 // ARM_GPIO_SignalEvent callback function to register.
291 // Use a NULL pointer when no callback events are
292 // required or to deregister a callback function. 
293 static int32_t
294 GPIO_Setup(
295         GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin,
296         ARM_GPIO_SignalEvent_t cb_event)
297 {
298         int32_t result = ARM_DRIVER_OK;
299
300         if (PIN_IS_AVAILABLE(pin))
301         {
302                 GPIO_SetPullResistor(gpio, pin, ARM_GPIO_PULL_NONE);
303                 GPIO_SetDirection(gpio, pin, ARM_GPIO_INPUT);
304                 GPIO_SetEventTrigger(pin, ARM_GPIO_TRIGGER_NONE);
305
306                 if (cb_event)
307                 {
308                         IRQn_Type irq = irq_for_pin(pin);
309
310                         if (g_pin_cb[pin])
311                         {
312                                 // SYSCFG_EXTICRx can't assign events for
313                                 // the same pins on multiple ports at once
314                                 return ARM_DRIVER_ERROR_PARAMETER;
315                         }
316                         g_pin_cb[pin] = cb_event;
317                         g_pin_gpio[pin] = gpio;  // Store GPIO port for this pin
318                         NVIC_SetVector(irq, isr_for_irq(irq));
319                         NVIC_EnableIRQ(irq);
320                         assign_exti(gpio, pin);
321                 }
322                 else
323                 {
324                         g_pin_cb[pin] = NULL;
325                         g_pin_gpio[pin] = NULL;
326                 }
327         }
328         else
329                 result = ARM_GPIO_ERROR_PIN;
330
331         return result;
332 }
333
334 static void
335 GPIO_SetOutput(GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin, uint32_t val)
336 {
337         if (PIN_IS_AVAILABLE(pin)) {
338                 gpio->BSRR = 1 << (val ? pin : 16+pin);
339         }
340 }
341
342 static uint32_t
343 GPIO_GetInput(GPIO_TypeDef *gpio, ARM_GPIO_Pin_t pin)
344 {
345         return PIN_IS_AVAILABLE(pin) && (gpio->IDR & (1 << pin));
346 }
347
348 /* Publicly visible parts *******************************************/
349
350 #define GPIO_PORTS \
351         X(GPIOA) \
352         X(GPIOB) \
353         X(GPIOC) \
354         X(GPIOD) \
355         X(GPIOE) \
356         X(GPIOH)
357
358 #define X(port) \
359         static int32_t  port ## _Setup (ARM_GPIO_Pin_t pin, ARM_GPIO_SignalEvent_t cb_event) { \
360                 return GPIO_Setup(port, pin, cb_event ); } \
361         static int32_t  port ## _SetDirection (ARM_GPIO_Pin_t pin, ARM_GPIO_DIRECTION direction) { \
362                 return GPIO_SetDirection(port, pin, direction); } \
363         static int32_t  port ## _SetOutputMode (ARM_GPIO_Pin_t pin, ARM_GPIO_OUTPUT_MODE mode) { \
364                 return GPIO_SetOutputMode (port, pin, mode); } \
365         static int32_t  port ## _SetPullResistor (ARM_GPIO_Pin_t pin, ARM_GPIO_PULL_RESISTOR resistor) { \
366                 return GPIO_SetPullResistor(port, pin, resistor); } \
367         static int32_t  port ## _SetEventTrigger (ARM_GPIO_Pin_t pin, ARM_GPIO_EVENT_TRIGGER trigger) { \
368                 return GPIO_SetEventTrigger(pin, trigger); } \
369         static void     port ## _SetOutput (ARM_GPIO_Pin_t pin, uint32_t val) { \
370                 GPIO_SetOutput(port, pin, val); } \
371         static uint32_t port ## _GetInput (ARM_GPIO_Pin_t pin) { \
372                 return GPIO_GetInput(port, pin); } \
373         \
374         ARM_DRIVER_GPIO Driver_ ## port = { \
375                 port ## _Setup, \
376                 port ## _SetDirection, \
377                 port ## _SetOutputMode, \
378                 port ## _SetPullResistor, \
379                 port ## _SetEventTrigger, \
380                 port ## _SetOutput, \
381                 port ## _GetInput \
382         };
383
384 GPIO_PORTS
385
386 #undef X
387 #undef GPIO_PORTS