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
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
|
/*
* Copyright (c) 2021-2025 HPMicro
*
* SPDX-License-Identifier: BSD-3-Clause
*
* @file drv_gpio.c
* @brief GPIO driver implementation for RT-Thread
*
* This file implements the GPIO (General Purpose Input/Output) driver for HPMicro
* microcontrollers, providing a complete interface between RT-Thread's pin framework
* and the HPM GPIO hardware.
*
* Features:
* - Support for multiple GPIO ports (A, B, C, D, E, F, V, W, X, Y, Z)
* - Configurable pin modes (input, output, pull-up, pull-down, open-drain)
* - Interrupt-driven GPIO operations with configurable triggers
* - Pin naming convention: P<PORT><INDEX> (e.g., PA00, PZ03)
* - Configurable interrupt priorities
* - Real-time pin state monitoring
*
* Change Logs:
* Date Author Notes
* 2022-01-11 HPMicro First version
* 2022-07-28 HPMicro Fixed compiling warnings
* 2023-05-08 HPMicro Adapt RT-Thread V5.0.0
* 2023-08-15 HPMicro Enable pad loopback feature
* 2024-01-08 HPMicro Implemented pin_get
* 2024-04-17 HPMicro Refined pin irq implementation
* 2024-07-03 HPMicro Determined the existence of GPIO via GPIO_DO_GPIOx macro
* 2025-09-01 HPMicro Revised the code, reduced repetitive codes, added comments
* 2025-12-04 HPMicro Changed the return type of hpm_pin_read from int to rt_ssize_t
*/
#include "rtconfig.h"
#ifdef BSP_USING_GPIO
/* RT-Thread includes */
#include <rthw.h>
#include <rtdevice.h>
/* HPM SDK includes */
#include "drv_gpio.h"
#include "hpm_gpio_drv.h"
#include "hpm_gpiom_drv.h"
#include "hpm_clock_drv.h"
#include "hpm_soc_feature.h"
#include <rtthread.h>
#include "hpm_soc.h"
/* ============================================================================
* Configuration Macros
* ============================================================================ */
/*
* GPIO Pin Interrupt Header Declaration Macro
* Creates a static array of pin interrupt headers for a specific GPIO port.
* Each GPIO port can have up to 32 pins, each with its own interrupt handler.
*
* @param port GPIO port letter (A, B, C, D, E, F, V, W, X, Y, Z)
*/
#define GPIO_PIN_HDR_DECLARE(port) \
static struct rt_pin_irq_hdr hpm_gpio0_##port##_pin_hdr[32]
/*
* GPIO Interrupt Priority Configuration Macro
* Handles the configuration of interrupt priority for GPIO controllers.
* If a specific priority is defined in the board configuration, it will be used;
* otherwise, a default priority of 1 is applied.
*/
#ifdef BSP_GPIO_IRQ_PRIORITY
#define GPIO_IRQ_PRIORITY_VAL BSP_GPIO_IRQ_PRIORITY
#else
#define GPIO_IRQ_PRIORITY_VAL 1
#endif
#ifndef HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK
/*
* GPIO Interrupt Mapping Entry Macro
* Creates an entry in the GPIO interrupt mapping table that associates
* GPIO port indices with their corresponding interrupt numbers, priorities,
* and pin interrupt header tables.
*
* @param port GPIO port letter (A, B, C, D, E, F, V, W, X, Y, Z)
*/
#define GPIO_IRQ_MAP_ENTRY(port) \
{ \
GPIO_IE_GPIO##port, \
IRQn_GPIO0_##port, \
GPIO_IRQ_PRIORITY_VAL, \
hpm_gpio0_##port##_pin_hdr \
}
/*
* GPIO ISR Definition Macro
* Creates the interrupt service routine function and its declaration
* for a specific GPIO port. The ISR handles pin-level interrupts
* and calls the common GPIO interrupt handler.
*
* @param port GPIO port letter (A, B, C, D, E, F, V, W, X, Y, Z)
*/
#define GPIO_ISR_DEFINE(port) \
SDK_DECLARE_EXT_ISR_M(IRQn_GPIO0_##port, gpio##port##_isr) \
void gpio##port##_isr(void) \
{ \
hpm_gpio_isr(GPIO_IF_GPIO##port, HPM_GPIO0); \
}
#else
/**
* @brief GPIO interrupt parameter structure
*
* This structure used for passing parameters to the GPIO interrupt service routine.
*/
typedef struct
{
uint32_t gpio_idx; /**< GPIO port index (0-11 for A-Z) */
GPIO_Type *base;
} gpio_irq_param_t;
/*
* GPIO Interrupt Mapping Entry Macro
* Creates an entry in the GPIO interrupt mapping table that associates
* GPIO port indices with their corresponding interrupt numbers, priorities,
* and pin interrupt header tables.
*
* @param port GPIO port letter (A, B, C, D, E, F, V, W, X, Y, Z)
*/
#define GPIO_IRQ_MAP_ENTRY(port) \
{ \
GPIO_IE_GPIO##port, \
IRQn_GPIO0_##port, \
GPIO_IRQ_PRIORITY_VAL, \
hpm_gpio0_##port##_pin_hdr, \
&(gpio_irq_param_t){ \
.gpio_idx = GPIO_IE_GPIO##port, \
.base = HPM_GPIO0 \
} \
}
#endif /* HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK */
/* ============================================================================
* Data Structures
* ============================================================================ */
/**
* @brief GPIO interrupt mapping structure
*
* This structure maps GPIO port indices to their corresponding interrupt
* configuration and pin interrupt handler tables.
*/
typedef struct
{
uint32_t gpio_idx; /**< GPIO port index (0-11 for A-Z) */
uint32_t irq_num; /**< Interrupt number for this GPIO port */
uint8_t irq_priority; /**< Interrupt priority for this GPIO port */
struct rt_pin_irq_hdr *pin_irq_tbl; /**< Pointer to pin interrupt handler table */
#ifdef HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK
gpio_irq_param_t *irq_param; /**< Pointer to GPIO interrupt parameter structure */
#endif
} gpio_irq_map_t;
/* ============================================================================
* GPIO Device Instances
* ============================================================================ */
/* GPIO Pin Interrupt Header Declarations - created for each enabled GPIO port */
#ifdef GPIO_DO_GPIOA
GPIO_PIN_HDR_DECLARE(A);
#endif
#ifdef GPIO_DO_GPIOB
GPIO_PIN_HDR_DECLARE(B);
#endif
#ifdef GPIO_DO_GPIOC
GPIO_PIN_HDR_DECLARE(C);
#endif
#ifdef GPIO_DO_GPIOD
GPIO_PIN_HDR_DECLARE(D);
#endif
#ifdef GPIO_DO_GPIOE
GPIO_PIN_HDR_DECLARE(E);
#endif
#ifdef GPIO_DO_GPIOF
GPIO_PIN_HDR_DECLARE(F);
#endif
#ifdef GPIO_DO_GPIOV
GPIO_PIN_HDR_DECLARE(V);
#endif
#ifdef GPIO_DO_GPIOW
GPIO_PIN_HDR_DECLARE(W);
#endif
#ifdef GPIO_DO_GPIOX
GPIO_PIN_HDR_DECLARE(X);
#endif
#ifdef GPIO_DO_GPIOY
GPIO_PIN_HDR_DECLARE(Y);
#endif
#ifdef GPIO_DO_GPIOZ
GPIO_PIN_HDR_DECLARE(Z);
#endif
/* GPIO Interrupt Mapping Table - maps GPIO ports to their interrupt configuration */
static const gpio_irq_map_t hpm_gpio_irq_map[] = {
#ifdef GPIO_DO_GPIOA
GPIO_IRQ_MAP_ENTRY(A),
#endif
#ifdef GPIO_DO_GPIOB
GPIO_IRQ_MAP_ENTRY(B),
#endif
#ifdef GPIO_DO_GPIOC
GPIO_IRQ_MAP_ENTRY(C),
#endif
#ifdef GPIO_DO_GPIOD
GPIO_IRQ_MAP_ENTRY(D),
#endif
#ifdef GPIO_DO_GPIOE
GPIO_IRQ_MAP_ENTRY(E),
#endif
#ifdef GPIO_DO_GPIOF
GPIO_IRQ_MAP_ENTRY(F),
#endif
#ifdef GPIO_DO_GPIOV
GPIO_IRQ_MAP_ENTRY(V),
#endif
#ifdef GPIO_DO_GPIOW
GPIO_IRQ_MAP_ENTRY(W),
#endif
#ifdef GPIO_DO_GPIOX
GPIO_IRQ_MAP_ENTRY(X),
#endif
#ifdef GPIO_DO_GPIOY
GPIO_IRQ_MAP_ENTRY(Y),
#endif
#ifdef GPIO_DO_GPIOZ
GPIO_IRQ_MAP_ENTRY(Z),
#endif
};
/* ============================================================================
* Function Implementations
* ============================================================================ */
/**
* @brief Lookup pin interrupt header table for a given pin
*
* This function finds the appropriate pin interrupt header table for a given
* pin number by extracting the GPIO port index and searching the interrupt
* mapping table.
*
* @param [in] pin Pin number (encoded as gpio_idx * 32 + pin_idx)
* @return Pointer to pin interrupt header table, or RT_NULL if not found
*/
static struct rt_pin_irq_hdr *lookup_pin_irq_hdr_tbl(rt_base_t pin)
{
struct rt_pin_irq_hdr *pin_irq_hdr_tbl = RT_NULL;
uint32_t gpio_idx = pin >> 5; /* Extract GPIO port index (upper bits) */
/* Search through the GPIO interrupt mapping table */
for (uint32_t i = 0; i < ARRAY_SIZE(hpm_gpio_irq_map); i++)
{
if (hpm_gpio_irq_map[i].gpio_idx == gpio_idx)
{
pin_irq_hdr_tbl = hpm_gpio_irq_map[i].pin_irq_tbl;
break;
}
}
return pin_irq_hdr_tbl;
}
/**
* @brief Get interrupt number for a GPIO port
*
* This function retrieves the interrupt number associated with a specific
* GPIO port index by searching the interrupt mapping table.
*
* @param [in] gpio_idx GPIO port index (0-11 for A-Z)
* @return Interrupt number, or -1 if not found
*/
static int hpm_get_gpio_irq_num(uint32_t gpio_idx)
{
int irq_num = -1;
/* Search through the GPIO interrupt mapping table */
for (uint32_t i = 0; i < ARRAY_SIZE(hpm_gpio_irq_map); i++)
{
if (hpm_gpio_irq_map[i].gpio_idx == gpio_idx)
{
irq_num = hpm_gpio_irq_map[i].irq_num;
break;
}
}
return irq_num;
}
/**
* @brief Get interrupt priority for a GPIO port
*
* This function retrieves the interrupt priority associated with a specific
* GPIO port index by searching the interrupt mapping table.
*
* @param [in] gpio_idx GPIO port index (0-11 for A-Z)
* @return Interrupt priority, or -1 if not found
*/
static int hpm_get_gpio_irq_priority(uint32_t gpio_idx)
{
int irq_priority = -1;
/* Search through the GPIO interrupt mapping table */
for (uint32_t i = 0; i < ARRAY_SIZE(hpm_gpio_irq_map); i++)
{
if (hpm_gpio_irq_map[i].gpio_idx == gpio_idx)
{
irq_priority = hpm_gpio_irq_map[i].irq_priority;
break;
}
}
return irq_priority;
}
/**
* @brief GPIO interrupt service routine
*
* This function is called when a GPIO interrupt occurs. It handles pin-level
* interrupts by checking which pins have triggered interrupts, clearing the
* interrupt flags, and calling the appropriate user-defined interrupt handlers.
*
* @param [in] gpio_idx GPIO port index that triggered the interrupt
* @param [in] base GPIO hardware base address
*/
#ifndef HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK
static void hpm_gpio_isr(uint32_t gpio_idx, GPIO_Type *base)
#else
static void hpm_gpio_isr(int vector, gpio_irq_param_t *param)
#endif /* HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK */
{
#ifdef HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK
uint32_t gpio_idx = param->gpio_idx;
GPIO_Type *base = param->base;
#endif /* HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK */
/* Lookup the Pin IRQ Header Table for this GPIO port */
struct rt_pin_irq_hdr *pin_irq_hdr = RT_NULL;
for (uint32_t i = 0; i < ARRAY_SIZE(hpm_gpio_irq_map); i++)
{
if (hpm_gpio_irq_map[i].gpio_idx == gpio_idx)
{
pin_irq_hdr = hpm_gpio_irq_map[i].pin_irq_tbl;
break;
}
}
/* Check each pin in the GPIO port for interrupt flags */
for(uint32_t pin_idx = 0; pin_idx < 32; pin_idx++)
{
if (gpio_check_pin_interrupt_flag(base, gpio_idx, pin_idx))
{
/* Clear the interrupt flag for this pin */
gpio_clear_pin_interrupt_flag(base, gpio_idx, pin_idx);
/* Call user-defined interrupt handler if registered */
if (pin_irq_hdr[pin_idx].hdr != RT_NULL)
{
pin_irq_hdr[pin_idx].hdr(pin_irq_hdr[pin_idx].args);
}
}
}
}
#ifndef HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK
/* ============================================================================
* GPIO Interrupt Service Routines
* ============================================================================ */
/* GPIOA ISR - created if GPIO_DO_GPIOA is enabled */
#ifdef GPIO_DO_GPIOA
GPIO_ISR_DEFINE(A);
#endif
/* GPIOB ISR - created if GPIO_DO_GPIOB is enabled */
#ifdef GPIO_DO_GPIOB
GPIO_ISR_DEFINE(B);
#endif
/* GPIOC ISR - created if GPIO_DO_GPIOC is enabled */
#ifdef GPIO_DO_GPIOC
GPIO_ISR_DEFINE(C);
#endif
/* GPIOD ISR - created if GPIO_DO_GPIOD is enabled */
#ifdef GPIO_DO_GPIOD
GPIO_ISR_DEFINE(D);
#endif
/* GPIOE ISR - created if GPIO_DO_GPIOE is enabled */
#ifdef GPIO_DO_GPIOE
GPIO_ISR_DEFINE(E);
#endif
/* GPIOF ISR - created if GPIO_DO_GPIOF is enabled */
#ifdef GPIO_DO_GPIOF
GPIO_ISR_DEFINE(F);
#endif
/* GPIOV ISR - created if GPIO_DO_GPIOV is enabled */
#ifdef GPIO_DO_GPIOV
GPIO_ISR_DEFINE(V);
#endif
/* GPIOW ISR - created if GPIO_DO_GPIOW is enabled */
#ifdef GPIO_DO_GPIOW
GPIO_ISR_DEFINE(W);
#endif
/* GPIOX ISR - created if GPIO_DO_GPIOX is enabled */
#ifdef GPIO_DO_GPIOX
GPIO_ISR_DEFINE(X);
#endif
/* GPIOY ISR - created if GPIO_DO_GPIOY is enabled */
#ifdef GPIO_DO_GPIOY
GPIO_ISR_DEFINE(Y);
#endif
/* GPIOZ ISR - created if GPIO_DO_GPIOZ is enabled */
#ifdef GPIO_DO_GPIOZ
GPIO_ISR_DEFINE(Z);
#endif
#endif /* HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK */
/**
* @brief Get Pin index from name string
*
* This function converts a pin name string to a pin index number.
* The pin naming convention follows the pattern: P<PORT><INDEX>
*
* Supported formats:
* - PA00 to PF99: GPIO ports A-F with pin indices 00-99
* - PV00 to PZ99: GPIO ports V-Z with pin indices 00-99
*
* Examples:
* - "PA00" -> pin index 0 (GPIO A, pin 0)
* - "PZ03" -> pin index 355 (GPIO Z, pin 3)
* - "PB15" -> pin index 47 (GPIO B, pin 15)
*
* @param [in] name Pin name string (must be exactly 4 characters)
* @return Pin index number, or -RT_EINVAL if name is invalid
*/
static rt_base_t hpm_pin_get(const char *name)
{
/* Validate pin name format: P<PORT><INDEX> (4 characters total) */
if (!( (rt_strlen(name) == 4) && /* Exactly 4 characters */
(name[0] == 'P') && /* First character is 'P' */
((('A' <= name[1]) && (name[1] <= 'F')) || /* Port A-F or V-Z */
(('V' <= name[1]) && (name[1] <= 'Z'))) &&
(('0' <= name[2]) && (name[2] <= '9')) && /* Third character is digit */
(('0' <= name[3]) && (name[3] <= '9')) /* Fourth character is digit */
))
{
return -RT_EINVAL;
}
/* Calculate GPIO port index */
uint32_t gpio_idx = (name[1] <= 'F') ? (name[1] - 'A') : (11 + name[1] - 'V');
/* Calculate pin index within the port */
uint32_t pin_idx = (uint32_t)(name[2] - '0') * 10 + (name[3] - '0');
/* Return combined pin index (gpio_idx * 32 + pin_idx) */
return (gpio_idx * 32 + pin_idx);
}
/**
* @brief Configure GPIO pin mode
*
* This function configures a GPIO pin for the specified mode, including
* direction (input/output), pull-up/pull-down resistors, and open-drain settings.
* It also configures the pin controller and pad settings.
*
* @param [in] dev Device pointer (unused)
* @param [in] pin Pin number (encoded as gpio_idx * 32 + pin_idx)
* @param [in] mode Pin mode (PIN_MODE_*)
*/
static void hpm_pin_mode(rt_device_t dev, rt_base_t pin, rt_uint8_t mode)
{
/* TODO: Check the validity of the pin value */
uint32_t gpio_idx = pin >> 5; /* Extract GPIO port index */
uint32_t pin_idx = pin & 0x1FU; /* Extract pin index within port */
/* Configure pin controller to use GPIO0 */
gpiom_set_pin_controller(HPM_GPIOM, gpio_idx, pin_idx, gpiom_soc_gpio0);
/* Clear function control register */
HPM_IOC->PAD[pin].FUNC_CTL = 0;
/* Handle special cases for specific GPIO ports */
switch (gpio_idx)
{
case GPIO_DI_GPIOY :
/* GPIOY requires special PIOC configuration */
HPM_PIOC->PAD[pin].FUNC_CTL = 3;
break;
#ifdef GPIO_DI_GPIOZ
case GPIO_DI_GPIOZ :
#ifdef HPM_BIOC
/* GPIOZ requires special BIOC configuration if available */
HPM_BIOC->PAD[pin].FUNC_CTL = 3;
#endif
break;
#endif
default :
break;
}
/* Configure pin mode and pad settings */
switch (mode)
{
case PIN_MODE_OUTPUT:
/* Configure as output with push-pull driver */
gpio_set_pin_output(HPM_GPIO0, gpio_idx, pin_idx);
HPM_IOC->PAD[pin].PAD_CTL &= ~(IOC_PAD_PAD_CTL_PS_MASK | IOC_PAD_PAD_CTL_PE_MASK | IOC_PAD_PAD_CTL_OD_MASK);
break;
case PIN_MODE_INPUT:
/* Configure as input with no pull resistors */
gpio_set_pin_input(HPM_GPIO0, gpio_idx, pin_idx);
HPM_IOC->PAD[pin].PAD_CTL &= ~(IOC_PAD_PAD_CTL_PS_MASK | IOC_PAD_PAD_CTL_PE_MASK);
break;
case PIN_MODE_INPUT_PULLDOWN:
/* Configure as input with pull-down resistor */
gpio_set_pin_input(HPM_GPIO0, gpio_idx, pin_idx);
HPM_IOC->PAD[pin].PAD_CTL = (HPM_IOC->PAD[pin].PAD_CTL & ~IOC_PAD_PAD_CTL_PS_MASK) | IOC_PAD_PAD_CTL_PE_SET(1);
break;
case PIN_MODE_INPUT_PULLUP:
/* Configure as input with pull-up resistor */
gpio_set_pin_input(HPM_GPIO0, gpio_idx, pin_idx);
HPM_IOC->PAD[pin].PAD_CTL = IOC_PAD_PAD_CTL_PE_SET(1) | IOC_PAD_PAD_CTL_PS_SET(1);
break;
case PIN_MODE_OUTPUT_OD:
/* Configure as output with open-drain driver */
gpio_set_pin_output(HPM_GPIO0, gpio_idx, pin_idx);
HPM_IOC->PAD[pin].PAD_CTL = (HPM_IOC->PAD[pin].PAD_CTL & ~(IOC_PAD_PAD_CTL_PS_MASK | IOC_PAD_PAD_CTL_PE_MASK)) | IOC_PAD_PAD_CTL_OD_SET(1);
break;
default:
/* Invalid mode - no action taken */
break;
}
/* Enable pad loopback feature */
HPM_IOC->PAD[pin].FUNC_CTL = IOC_PAD_FUNC_CTL_LOOP_BACK_MASK;
}
/**
* @brief Read GPIO pin state
*
* This function reads the current state (high/low) of a GPIO pin.
*
* @param [in] dev Device pointer (unused)
* @param [in] pin Pin number (encoded as gpio_idx * 32 + pin_idx)
* @return Pin state: 1 for high, 0 for low
*/
static rt_ssize_t hpm_pin_read(rt_device_t dev, rt_base_t pin)
{
/* TODO: Check the validity of the pin value */
uint32_t gpio_idx = pin >> 5; /* Extract GPIO port index */
uint32_t pin_idx = pin & 0x1FU; /* Extract pin index within port */
return (rt_ssize_t) gpio_read_pin(HPM_GPIO0, gpio_idx, pin_idx);
}
/**
* @brief Write GPIO pin state
*
* This function sets the output state of a GPIO pin to high or low.
*
* @param [in] dev Device pointer (unused)
* @param [in] pin Pin number (encoded as gpio_idx * 32 + pin_idx)
* @param [in] value Pin state: 1 for high, 0 for low
*/
static void hpm_pin_write(rt_device_t dev, rt_base_t pin, rt_uint8_t value)
{
/* TODO: Check the validity of the pin value */
uint32_t gpio_idx = pin >> 5; /* Extract GPIO port index */
uint32_t pin_idx = pin & 0x1FU; /* Extract pin index within port */
gpio_write_pin(HPM_GPIO0, gpio_idx, pin_idx, value);
}
/**
* @brief Attach interrupt handler to GPIO pin
*
* This function registers an interrupt handler for a specific GPIO pin.
* The handler will be called when the pin triggers an interrupt based on
* the specified mode (rising edge, falling edge, high level, low level).
*
* @param [in] device Device pointer (unused)
* @param [in] pin Pin number (encoded as gpio_idx * 32 + pin_idx)
* @param [in] mode Interrupt mode (PIN_IRQ_MODE_*)
* @param [in] hdr Interrupt handler function pointer
* @param [in] args Arguments to pass to the interrupt handler
* @retval RT_EOK Interrupt handler attached successfully
* @retval -RT_EINVAL Invalid pin number
*/
static rt_err_t hpm_pin_attach_irq(struct rt_device *device,
rt_base_t pin,
rt_uint8_t mode,
void (*hdr)(void *args),
void *args)
{
/* Lookup the pin interrupt header table for this pin */
struct rt_pin_irq_hdr *pin_irq_hdr_tbl = lookup_pin_irq_hdr_tbl(pin);
if (pin_irq_hdr_tbl == RT_NULL)
{
return -RT_EINVAL;
}
/* Disable interrupts during registration to ensure atomic operation */
rt_base_t level = rt_hw_interrupt_disable();
uint32_t pin_idx = pin & 0x1FUL; /* Extract pin index within port */
/* Register the interrupt handler */
pin_irq_hdr_tbl[pin_idx].pin = pin;
pin_irq_hdr_tbl[pin_idx].hdr = hdr;
pin_irq_hdr_tbl[pin_idx].mode = mode;
pin_irq_hdr_tbl[pin_idx].args = args;
/* Re-enable interrupts */
rt_hw_interrupt_enable(level);
return RT_EOK;
}
/**
* @brief Detach interrupt handler from GPIO pin
*
* This function removes the interrupt handler registration for a specific GPIO pin.
* After calling this function, the pin will no longer trigger interrupts.
*
* @param [in] device Device pointer (unused)
* @param [in] pin Pin number (encoded as gpio_idx * 32 + pin_idx)
* @retval RT_EOK Interrupt handler detached successfully
* @retval -RT_EINVAL Invalid pin number
*/
static rt_err_t hpm_pin_detach_irq(struct rt_device *device, rt_base_t pin)
{
/* Lookup the pin interrupt header table for this pin */
struct rt_pin_irq_hdr *pin_irq_hdr_tbl = lookup_pin_irq_hdr_tbl(pin);
if (pin_irq_hdr_tbl == RT_NULL)
{
return -RT_EINVAL;
}
/* Disable interrupts during deregistration to ensure atomic operation */
rt_base_t level = rt_hw_interrupt_disable();
uint32_t pin_idx = pin & 0x1FUL; /* Extract pin index within port */
/* Clear the interrupt handler registration */
pin_irq_hdr_tbl[pin_idx].pin = -1;
pin_irq_hdr_tbl[pin_idx].hdr = RT_NULL;
pin_irq_hdr_tbl[pin_idx].mode = 0;
pin_irq_hdr_tbl[pin_idx].args = RT_NULL;
/* Re-enable interrupts */
rt_hw_interrupt_enable(level);
return RT_EOK;
}
/**
* @brief Enable or disable GPIO pin interrupt
*
* This function enables or disables interrupt generation for a specific GPIO pin.
* When enabling, it configures the interrupt trigger mode and enables the hardware
* interrupt. When disabling, it disables the hardware interrupt.
*
* @param [in] device Device pointer (unused)
* @param [in] pin Pin number (encoded as gpio_idx * 32 + pin_idx)
* @param [in] enabled Enable/disable flag (PIN_IRQ_ENABLE or PIN_IRQ_DISABLE)
* @retval RT_EOK Operation completed successfully
* @retval -RT_EINVAL Invalid pin number or enable flag
*/
static rt_err_t hpm_pin_irq_enable(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled)
{
/* TODO: Check the validity of the pin value */
uint32_t gpio_idx = pin >> 5; /* Extract GPIO port index */
uint32_t pin_idx = pin & 0x1FU; /* Extract pin index within port */
/* Lookup the pin interrupt header table for this pin */
struct rt_pin_irq_hdr *pin_irq_hdr_tbl = lookup_pin_irq_hdr_tbl(pin);
if (pin_irq_hdr_tbl == RT_NULL)
{
return -RT_EINVAL;
}
if (enabled == PIN_IRQ_ENABLE)
{
/* Configure interrupt trigger mode based on registered mode */
gpio_interrupt_trigger_t trigger;
switch(pin_irq_hdr_tbl[pin_idx].mode)
{
case PIN_IRQ_MODE_RISING:
trigger = gpio_interrupt_trigger_edge_rising;
break;
case PIN_IRQ_MODE_FALLING:
trigger = gpio_interrupt_trigger_edge_falling;
break;
case PIN_IRQ_MODE_HIGH_LEVEL:
trigger = gpio_interrupt_trigger_level_high;
break;
case PIN_IRQ_MODE_LOW_LEVEL:
trigger = gpio_interrupt_trigger_level_low;
break;
default:
trigger = gpio_interrupt_trigger_edge_rising;
break;
}
/* Configure and enable the pin interrupt */
gpio_config_pin_interrupt(HPM_GPIO0, gpio_idx, pin_idx, trigger);
/* Get interrupt number and priority for this GPIO port */
rt_uint32_t irq_num = hpm_get_gpio_irq_num(gpio_idx);
rt_uint32_t irq_priority = hpm_get_gpio_irq_priority(gpio_idx);
/* Enable pin interrupt and system interrupt */
gpio_enable_pin_interrupt(HPM_GPIO0, gpio_idx, pin_idx);
intc_m_enable_irq_with_priority(irq_num, irq_priority);
}
else if (enabled == PIN_IRQ_DISABLE)
{
/* Disable the pin interrupt */
gpio_disable_pin_interrupt(HPM_GPIO0, gpio_idx, pin_idx);
}
else
{
return -RT_EINVAL;
}
return RT_EOK;
}
/* ============================================================================
* RT-Thread Interface Structures
* ============================================================================ */
/* RT-Thread pin operations structure - defines the interface between RT-Thread and this driver */
const static struct rt_pin_ops hpm_pin_ops = {
.pin_mode = hpm_pin_mode, /* Configure pin mode */
.pin_write = hpm_pin_write, /* Write pin state */
.pin_read = hpm_pin_read, /* Read pin state */
.pin_attach_irq = hpm_pin_attach_irq, /* Attach interrupt handler */
.pin_detach_irq = hpm_pin_detach_irq, /* Detach interrupt handler */
.pin_irq_enable = hpm_pin_irq_enable, /* Enable/disable interrupt */
.pin_get = hpm_pin_get, /* Get pin index from name */
};
/* ============================================================================
* Driver Initialization
* ============================================================================ */
/**
* @brief Initialize GPIO driver
*
* This function initializes the GPIO driver and registers it with the
* RT-Thread device framework. It sets up the pin operations structure
* and makes the GPIO functionality available to the system.
*
* @retval RT_EOK Initialization successful
* @retval -RT_ERROR Initialization failed
*/
int rt_hw_pin_init(void)
{
int ret = RT_EOK;
/* Register the pin device with RT-Thread */
ret = rt_device_pin_register("pin", &hpm_pin_ops, RT_NULL);
#ifdef HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK
for (uint32_t i = 0; i < ARRAY_SIZE(hpm_gpio_irq_map); i++)
{
/* Register GPIO device to irq table */
rt_hw_interrupt_install(hpm_gpio_irq_map[i].irq_num, (rt_isr_handler_t)hpm_gpio_isr, hpm_gpio_irq_map[i].irq_param, "GPIO");
}
#endif /* HPM_USING_RTTHREAD_INTERRUPT_FRAMEWORK */
return ret;
}
/* Register initialization function to be called during system startup */
INIT_BOARD_EXPORT(rt_hw_pin_init);
#endif /* BSP_USING_GPIO */
|