Contiki-NG
Loading...
Searching...
No Matches
gpio-hal-arch.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2026, RISE Research Institutes of Sweden AB
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * 3. Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived
18 * from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
31 * OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/**
35 * \file
36 * GPIO HAL backend for MSP-EXP430FR5969 LaunchPad
37 *
38 * Configuration helpers and port ISRs that dispatch to the
39 * platform-independent gpio_hal_event_handler().
40 */
41
42#include "contiki.h"
43#include "dev/gpio-hal.h"
44#include "sys/process.h"
45#include "sys/etimer.h"
46#include "isr_compat.h"
47#include <msp430.h>
48
49/*---------------------------------------------------------------------------*/
50/* MSP430 port interrupts fire on a single configured edge only. Rather than
51 * emulate a both-edge interrupt in software (which is racy against contact
52 * bounce), we arm the falling edge to catch a press, then poll the pin to
53 * detect the release. Polling runs only while a pin is held, so the idle path
54 * stays in interrupt-driven sleep -- important for low power and intermittent
55 * operation. */
56#define RELEASE_POLL_INTERVAL (CLOCK_SECOND / 16)
57/* Pins awaiting a release, indexed by port (1..4). Set in the ISR, cleared in
58 * the poll process. */
59static volatile uint8_t release_poll_mask[5];
60
61PROCESS(gpio_hal_release_poll_process, "GPIO HAL release poll");
62/*---------------------------------------------------------------------------*/
63void
65{
66 process_start(&gpio_hal_release_poll_process, NULL);
67}
68/*---------------------------------------------------------------------------*/
69void
72{
73 uint8_t m = (uint8_t)1 << pin;
74 uint8_t nm = (uint8_t)~m;
75 uint8_t pull = cfg & GPIO_HAL_PIN_CFG_PULL_MASK;
76 uint8_t edge = cfg & GPIO_HAL_PIN_CFG_EDGE_BOTH;
77 uint8_t int_en = (cfg & GPIO_HAL_PIN_CFG_INT_MASK) ==
78 GPIO_HAL_PIN_CFG_INT_ENABLE;
79
80 /* Pull resistor: REN=0 disables it; REN=1 + OUT selects up (1) or down (0). */
81 switch(port) {
82 case 1:
83 if(pull == GPIO_HAL_PIN_CFG_PULL_NONE) {
84 P1REN &= nm;
85 } else {
86 P1REN |= m;
87 if(pull == GPIO_HAL_PIN_CFG_PULL_UP) { P1OUT |= m; }
88 else { P1OUT &= nm; }
89 }
90 /* Edge: IES=1 selects high-to-low. The hardware supports one edge per pin;
91 * for EDGE_BOTH we arm the falling edge and detect the rising (release)
92 * transition by polling (see gpio_hal_release_poll_process). */
93 if(edge == GPIO_HAL_PIN_CFG_EDGE_RISING) { P1IES &= nm; }
94 else { P1IES |= m; }
95 P1IFG &= nm;
96 if(int_en) { P1IE |= m; } else { P1IE &= nm; }
97 break;
98 case 2:
99 if(pull == GPIO_HAL_PIN_CFG_PULL_NONE) {
100 P2REN &= nm;
101 } else {
102 P2REN |= m;
103 if(pull == GPIO_HAL_PIN_CFG_PULL_UP) { P2OUT |= m; }
104 else { P2OUT &= nm; }
105 }
106 if(edge == GPIO_HAL_PIN_CFG_EDGE_RISING) { P2IES &= nm; }
107 else { P2IES |= m; }
108 P2IFG &= nm;
109 if(int_en) { P2IE |= m; } else { P2IE &= nm; }
110 break;
111 case 3:
112 if(pull == GPIO_HAL_PIN_CFG_PULL_NONE) {
113 P3REN &= nm;
114 } else {
115 P3REN |= m;
116 if(pull == GPIO_HAL_PIN_CFG_PULL_UP) { P3OUT |= m; }
117 else { P3OUT &= nm; }
118 }
119 if(edge == GPIO_HAL_PIN_CFG_EDGE_RISING) { P3IES &= nm; }
120 else { P3IES |= m; }
121 P3IFG &= nm;
122 if(int_en) { P3IE |= m; } else { P3IE &= nm; }
123 break;
124 case 4:
125 if(pull == GPIO_HAL_PIN_CFG_PULL_NONE) {
126 P4REN &= nm;
127 } else {
128 P4REN |= m;
129 if(pull == GPIO_HAL_PIN_CFG_PULL_UP) { P4OUT |= m; }
130 else { P4OUT &= nm; }
131 }
132 if(edge == GPIO_HAL_PIN_CFG_EDGE_RISING) { P4IES &= nm; }
133 else { P4IES |= m; }
134 P4IFG &= nm;
135 if(int_en) { P4IE |= m; } else { P4IE &= nm; }
136 break;
137 }
138}
139/*---------------------------------------------------------------------------*/
142{
143 uint8_t m = (uint8_t)1 << pin;
144 uint8_t ren = 0, out = 0, ies = 0, ie = 0;
145 gpio_hal_pin_cfg_t cfg = 0;
146
147 switch(port) {
148 case 1: ren = P1REN; out = P1OUT; ies = P1IES; ie = P1IE; break;
149 case 2: ren = P2REN; out = P2OUT; ies = P2IES; ie = P2IE; break;
150 case 3: ren = P3REN; out = P3OUT; ies = P3IES; ie = P3IE; break;
151 case 4: ren = P4REN; out = P4OUT; ies = P4IES; ie = P4IE; break;
152 default: return 0;
153 }
154
155 if(ren & m) {
156 cfg |= (out & m) ? GPIO_HAL_PIN_CFG_PULL_UP : GPIO_HAL_PIN_CFG_PULL_DOWN;
157 } else {
158 cfg |= GPIO_HAL_PIN_CFG_PULL_NONE;
159 }
160 cfg |= (ies & m) ? GPIO_HAL_PIN_CFG_EDGE_FALLING : GPIO_HAL_PIN_CFG_EDGE_RISING;
161 cfg |= (ie & m) ? GPIO_HAL_PIN_CFG_INT_ENABLE : GPIO_HAL_PIN_CFG_INT_DISABLE;
162 return cfg;
163}
164/*---------------------------------------------------------------------------*/
165void
167{
168 uint8_t m = (uint8_t)1 << pin;
169 switch(port) {
170 case 1: P1IFG &= (uint8_t)~m; P1IE |= m; break;
171 case 2: P2IFG &= (uint8_t)~m; P2IE |= m; break;
172 case 3: P3IFG &= (uint8_t)~m; P3IE |= m; break;
173 case 4: P4IFG &= (uint8_t)~m; P4IE |= m; break;
174 }
175}
176/*---------------------------------------------------------------------------*/
177void
179{
180 uint8_t nm = (uint8_t)~((uint8_t)1 << pin);
181 switch(port) {
182 case 1: P1IE &= nm; break;
183 case 2: P2IE &= nm; break;
184 case 3: P3IE &= nm; break;
185 case 4: P4IE &= nm; break;
186 }
187}
188/*---------------------------------------------------------------------------*/
189/* The PnIV register reports the lowest-numbered pending interrupt and
190 * clears the corresponding flag when read. We loop until PnIV reads zero
191 * to drain bursts in a single ISR entry. */
192/*---------------------------------------------------------------------------*/
193static inline gpio_hal_pin_mask_t
194piv_to_mask(uint16_t iv)
195{
196 /* PnIV values 0x02..0x10 map to pins 0..7. */
197 if(iv < 0x02 || iv > 0x10) {
198 return 0;
199 }
200 return (gpio_hal_pin_mask_t)1 << ((iv - 2) >> 1);
201}
202/*---------------------------------------------------------------------------*/
203static uint8_t
204port_in(uint8_t port)
205{
206 switch(port) {
207 case 1: return P1IN;
208 case 2: return P2IN;
209 case 3: return P3IN;
210 case 4: return P4IN;
211 }
212 return 0;
213}
214/*---------------------------------------------------------------------------*/
215/* Clear stale flags and re-enable the (falling-edge) press interrupt. */
216static void
217rearm_press(uint8_t port, uint8_t mask)
218{
219 switch(port) {
220 case 1: P1IFG &= (uint8_t)~mask; P1IE |= mask; break;
221 case 2: P2IFG &= (uint8_t)~mask; P2IE |= mask; break;
222 case 3: P3IFG &= (uint8_t)~mask; P3IE |= mask; break;
223 case 4: P4IFG &= (uint8_t)~mask; P4IE |= mask; break;
224 }
225}
226/*---------------------------------------------------------------------------*/
227/* Called from a port ISR on a press (falling) edge: disable the pin's
228 * interrupt, record it for release polling and wake the poll process. */
229static void
230start_release_poll(uint8_t port, uint8_t mask)
231{
232 switch(port) {
233 case 1: P1IE &= (uint8_t)~mask; break;
234 case 2: P2IE &= (uint8_t)~mask; break;
235 case 3: P3IE &= (uint8_t)~mask; break;
236 case 4: P4IE &= (uint8_t)~mask; break;
237 }
238 release_poll_mask[port] |= mask;
239 process_poll(&gpio_hal_release_poll_process);
240}
241/*---------------------------------------------------------------------------*/
242PROCESS_THREAD(gpio_hal_release_poll_process, ev, data)
243{
244 static struct etimer et;
245 uint8_t port, released, active;
246
248
249 while(1) {
251
252 active = 0;
253 for(port = 1; port <= 4; port++) {
254 if(release_poll_mask[port] == 0) {
255 continue;
256 }
257 /* A pull-up pin reading high again has been released. */
258 released = (uint8_t)(release_poll_mask[port] & port_in(port));
259 if(released) {
260 int s = splhigh();
261 release_poll_mask[port] &= (uint8_t)~released;
262 splx(s);
263 rearm_press(port, released);
264 gpio_hal_event_handler(port, released);
265 }
266 if(release_poll_mask[port]) {
267 active = 1;
268 }
269 }
270 if(active) {
271 etimer_set(&et, RELEASE_POLL_INTERVAL);
272 }
273 }
274
275 PROCESS_END();
276}
277/*---------------------------------------------------------------------------*/
278ISR(PORT1, port1_isr)
279{
280 gpio_hal_pin_mask_t pins = 0;
281 uint16_t iv;
282 while((iv = P1IV) != 0) {
283 pins |= piv_to_mask(iv);
284 }
285 if(pins) {
286 start_release_poll(1, (uint8_t)pins);
287 gpio_hal_event_handler(1, pins);
288 /* Wake the main loop so the posted events are processed instead of the
289 * CPU falling straight back into LPM. */
290 LPM4_EXIT;
291 }
292}
293/*---------------------------------------------------------------------------*/
294ISR(PORT2, port2_isr)
295{
296 gpio_hal_pin_mask_t pins = 0;
297 uint16_t iv;
298 while((iv = P2IV) != 0) {
299 pins |= piv_to_mask(iv);
300 }
301 if(pins) {
302 start_release_poll(2, (uint8_t)pins);
303 gpio_hal_event_handler(2, pins);
304 LPM4_EXIT;
305 }
306}
307/*---------------------------------------------------------------------------*/
308ISR(PORT3, port3_isr)
309{
310 gpio_hal_pin_mask_t pins = 0;
311 uint16_t iv;
312 while((iv = P3IV) != 0) {
313 pins |= piv_to_mask(iv);
314 }
315 if(pins) {
316 start_release_poll(3, (uint8_t)pins);
317 gpio_hal_event_handler(3, pins);
318 LPM4_EXIT;
319 }
320}
321/*---------------------------------------------------------------------------*/
322ISR(PORT4, port4_isr)
323{
324 gpio_hal_pin_mask_t pins = 0;
325 uint16_t iv;
326 while((iv = P4IV) != 0) {
327 pins |= piv_to_mask(iv);
328 }
329 if(pins) {
330 start_release_poll(4, (uint8_t)pins);
331 gpio_hal_event_handler(4, pins);
332 LPM4_EXIT;
333 }
334}
335/*---------------------------------------------------------------------------*/
Event timer header file.
Header file for the GPIO HAL.
void etimer_set(struct etimer *et, clock_time_t interval)
Set an event timer.
Definition etimer.c:177
void gpio_hal_arch_port_interrupt_enable(gpio_hal_port_t port, gpio_hal_pin_t pin)
Enable interrupts for a gpio pin.
gpio_hal_pin_cfg_t gpio_hal_arch_port_pin_cfg_get(gpio_hal_port_t port, gpio_hal_pin_t pin)
Read the configuration of a GPIO pin.
void gpio_hal_arch_port_interrupt_disable(gpio_hal_port_t port, gpio_hal_pin_t pin)
Disable interrupts for a gpio pin.
void gpio_hal_arch_init(void)
Perform architecture specific gpio initaliaztion.
void gpio_hal_arch_port_pin_cfg_set(gpio_hal_port_t port, gpio_hal_pin_t pin, gpio_hal_pin_cfg_t cfg)
Configure a gpio pin.
uint32_t gpio_hal_pin_mask_t
GPIO pin mask representation.
Definition gpio-hal.h:142
uint32_t gpio_hal_pin_cfg_t
GPIO pin configuration.
Definition gpio-hal.h:118
uint8_t gpio_hal_port_t
A data structure that represents ports.
Definition gpio-hal.h:110
uint8_t gpio_hal_pin_t
GPIO pin number representation.
Definition gpio-hal.h:103
#define PROCESS(name, strname)
Declare a process.
Definition process.h:309
#define PROCESS_WAIT_EVENT()
Wait for an event to be posted to the process.
Definition process.h:143
#define PROCESS_BEGIN()
Define the beginning of a process.
Definition process.h:122
#define PROCESS_END()
Define the end of a process.
Definition process.h:133
void process_start(struct process *p, process_data_t data)
Start a process.
Definition process.c:121
#define PROCESS_THREAD(name, ev, data)
Define the body of a process.
Definition process.h:275
void process_poll(struct process *p)
Request a process to be polled.
Definition process.c:366
Header file for the Contiki process interface.
A timer.
Definition etimer.h:79