1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/cpu/atmega_common/periph/gpio.c
bors[bot] 9d596734a1
Merge #19752
19752: cpu/atmega_common: checking features instead of CPU models r=benpicco a=hugueslarrive

### Contribution description
Splitted from:
- #19740

### Testing procedure
Tested on atmega8 with:
- #19755

This one probably need to be tested on others cpu.
### Tests on 1284p:
#### tests/periph/adc
```
tests/periph/adc$ BOARD=atmega1284p make -j64 clean all | grep '^  ' && BOARD=atmega1284p AVRDUDE_PROGRAMMER='usbasp -F' make flash 2>&1 | grep -- 'of flash' && BOARD=atmega1284p PORT=/dev/ttyACM0 make term
   text	   data	    bss	    dec	    hex	filename
  10712	    304	   1021	  12037	   2f05	/home/hugues/github/cpu_atmega_common/RIOT/tests/periph/adc/bin/atmega1284p/tests_adc.elf
avrdude: 11016 bytes of flash written
avrdude: 11016 bytes of flash verified
/home/hugues/github/cpu_atmega_common/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "9600"  
Twisted not available, please install it if you want to use pyterm's JSON capabilities
2023-06-22 18:44:54,846 # Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
2023-06-22 18:44:55,848 # 

2023-06-22 18:44:55,848 # main(): This is RIOT! (Version: 2020.07-devel-15351-gc5f75c-cpu/atmega_common)
2023-06-22 18:44:55,848 # 
2023-06-22 18:44:55,849 # RIOT ADC peripheral driver test
2023-06-22 18:44:55,849 # 
2023-06-22 18:44:55,850 # This test will sample all available ADC lines once every 100ms with
2023-06-22 18:44:55,850 # a 10-bit resolution and print the sampled results to STDIO
2023-06-22 18:44:55,850 # 
2023-06-22 18:44:55,850 # 
2023-06-22 18:44:55,851 # Successfully initialized ADC_LINE(0)
2023-06-22 18:44:55,851 # Successfully initialized ADC_LINE(1)
2023-06-22 18:44:55,851 # Successfully initialized ADC_LINE(2)
2023-06-22 18:44:55,852 # Successfully initialized ADC_LINE(3)
2023-06-22 18:44:55,852 # Successfully initialized ADC_LINE(4)
2023-06-22 18:44:55,852 # Successfully initialized ADC_LINE(5)
2023-06-22 18:44:55,853 # Successfully initialized ADC_LINE(6)
2023-06-22 18:44:55,853 # Successfully initialized ADC_LINE(7)
2023-06-22 18:44:55,853 # ADC_LINE(0): 796
2023-06-22 18:44:55,854 # ADC_LINE(1): 599
2023-06-22 18:44:55,854 # ADC_LINE(2): 522
2023-06-22 18:44:55,854 # ADC_LINE(3): 485
2023-06-22 18:44:55,854 # ADC_LINE(4): 466
2023-06-22 18:44:55,854 # ADC_LINE(5): 466
2023-06-22 18:44:55,854 # ADC_LINE(6): 478
2023-06-22 18:44:55,855 # ADC_LINE(7): 501
2023-06-22 18:44:55,855 #  Exiting Pyterm
make: *** [/home/hugues/github/cpu_atmega_common/RIOT/tests/periph/adc/../../../Makefile.include:879: term] Interrompre
```
#### tests/periph/gpio
```
tests/periph/gpio$ BOARD=atmega1284p make -j64 clean all | grep '^  ' && BOARD=atmega1284p AVRDUDE_PROGRAMMER='usbasp -F' make flash 2>&1 | grep -- 'of flash' && BOARD=atmega1284p PORT=/dev/ttyACM0 make term
   text	   data	    bss	    dec	    hex	filename
  17828	   2112	   1095	  21035	   522b	/home/hugues/github/cpu_atmega_common/RIOT/tests/periph/gpio/bin/atmega1284p/tests_gpio.elf
avrdude: 19940 bytes of flash written
avrdude: 19940 bytes of flash verified
/home/hugues/github/cpu_atmega_common/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "9600"  
Twisted not available, please install it if you want to use pyterm's JSON capabilities
2023-06-22 18:46:50,726 # Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
2023-06-22 18:46:51,734 # This is RIOT! (Version: 2020.07-devel-15351-gc5f75c-cpu/atmega_common)
2023-06-22 18:46:51,734 # GPIO peripheral driver test
2023-06-22 18:46:51,734 # 
2023-06-22 18:46:51,735 # In this test, pins are specified by integer port and pin numbers.
2023-06-22 18:46:51,735 # So if your platform has a pin PA01, it will be port=0 and pin=1,
2023-06-22 18:46:51,736 # PC14 would be port=2 and pin=14 etc.
2023-06-22 18:46:51,736 # 
2023-06-22 18:46:51,736 # NOTE: make sure the values you use exist on your platform! The
2023-06-22 18:46:51,738 #       behavior for not existing ports/pins is not defined!
init_out 1 5
2023-06-22 18:47:50,380 # init_out 1 5
toggle 1 5
2023-06-22 18:48:09,425 # toggle 1 5
> toggle 1 5
2023-06-22 18:48:12,477 # toggle 1 5
> 2023-06-22 18:48:15,013 # Exiting Pyterm
```
#### tests/periph/i2c
```
tests/periph/i2c$ BOARD=atmega1284p make -j64 clean all | grep '^  ' && BOARD=atmega1284p AVRDUDE_PROGRAMMER='usbasp -F' make flash 2>&1 | grep -- 'of flash' && BOARD=atmega1284p PORT=/dev/ttyACM0 make term
   text	   data	    bss	    dec	    hex	filename
  18634	   1288	   1215	  21137	   5291	/home/hugues/github/cpu_atmega_common/RIOT/tests/periph/i2c/bin/atmega1284p/tests_i2c.elf
avrdude: 19922 bytes of flash written
avrdude: 19922 bytes of flash verified
/home/hugues/github/cpu_atmega_common/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "9600"  
Twisted not available, please install it if you want to use pyterm's JSON capabilities
2023-06-22 18:50:37,434 # Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
2023-06-22 18:50:38,437 # 
2023-06-22 18:50:38,438 # main(): This is RIOT! (Version: 2020.07-devel-15351-gc5f75c-cpu/atmega_common)
2023-06-22 18:50:38,438 # Start: Test for the low-level I2C driver
> i2c_scan 0
2023-06-22 18:51:37,661 # i2c_scan 0
2023-06-22 18:51:37,687 # Scanning I2C device 0...
2023-06-22 18:51:37,765 # addr not ack'ed = "-", addr ack'ed = "X", addr reserved = "R", error = "E"
2023-06-22 18:51:37,804 #      0 1 2 3 4 5 6 7 8 9 a b c d e f
2023-06-22 18:51:37,842 # 0x00 R R R R R R R R R R R R R R - -
2023-06-22 18:51:37,881 # 0x10 - - - - - - - - - - - - - - - -
2023-06-22 18:51:37,919 # 0x20 - - - - - - - - - - - - - - - -
2023-06-22 18:51:37,958 # 0x30 - - - - - - - - - - - - - - - -
2023-06-22 18:51:37,996 # 0x40 - - - - - - - - - - - - - - - -
2023-06-22 18:51:38,035 # 0x50 - - - - - - - - - - - - - - - -
2023-06-22 18:51:38,073 # 0x60 - - - - - - - - - - - - - - - -
2023-06-22 18:51:38,112 # 0x70 - - - - - - - - R R R R R R R R
> 2023-06-22 18:52:54,462 # Exiting Pyterm
```
#### tests/periph/pwm
```
tests/periph/pwm$ BOARD=atmega1284p make -j64 clean all | grep '^  ' && BOARD=atmega1284p AVRDUDE_PROGRAMMER='usbasp -F' make flash 2>&1 | grep -- 'of flash' && BOARD=atmega1284p PORT=/dev/ttyACM0 make term
   text	   data	    bss	    dec	    hex	filename
  15382	    896	   1093	  17371	   43db	/home/hugues/github/cpu_atmega_common/RIOT/tests/periph/pwm/bin/atmega1284p/tests_pwm.elf
avrdude: 16278 bytes of flash written
avrdude: 16278 bytes of flash verified
/home/hugues/github/cpu_atmega_common/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "9600"  
Twisted not available, please install it if you want to use pyterm's JSON capabilities
2023-06-22 18:54:32,308 # Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
2023-06-22 18:54:33,310 # 
2023-06-22 18:54:33,312 # main(): This is RIOT! (Version: 2020.07-devel-15351-gc5f75c-cpu/atmega_common)
2023-06-22 18:54:33,312 # PWM peripheral driver test
2023-06-22 18:54:33,313 # 
> osci
2023-06-22 18:54:39,105 # osci
2023-06-22 18:54:39,106 # 
2023-06-22 18:54:39,121 # RIOT PWM test
2023-06-22 18:54:39,177 # Connect an LED or scope to PWM pins to see something.
2023-06-22 18:54:39,178 # 
2023-06-22 18:54:39,216 # Available PWM device between 0 and 1
2023-06-22 18:54:39,244 # Initialized PWM_0 @ 488Hz.
2023-06-22 18:54:39,273 # Initialized PWM_1 @ 976Hz.
2023-06-22 18:54:39,274 # 
2023-06-22 18:54:39,313 # Letting the PWM pins oscillate now...
2023-06-22 18:54:45,655 # Exiting Pyterm
```
#### tests/periph/spi
```
tests/periph/spi$ BOARD=atmega1284p make -j64 clean all | grep '^  ' && BOARD=atmega1284p AVRDUDE_PROGRAMMER='usbasp -F' make flash 2>&1 | grep -- 'of flash' && BOARD=atmega1284p PORT=/dev/ttyACM0 make term
   text	   data	    bss	    dec	    hex	filename
  19240	   1402	   2317	  22959	   59af	/home/hugues/github/cpu_atmega_common/RIOT/tests/periph/spi/bin/atmega1284p/tests_spi.elf
avrdude: 20642 bytes of flash written
avrdude: 20642 bytes of flash verified
/home/hugues/github/cpu_atmega_common/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "9600"  
Twisted not available, please install it if you want to use pyterm's JSON capabilities
2023-06-22 18:58:12,394 # Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
2023-06-22 18:58:13,396 # 
2023-06-22 18:58:13,398 # main(): This is RIOT! (Version: 2020.07-devel-15351-gc5f75c-cpu/atmega_common)
2023-06-22 18:58:13,398 # Manual SPI peripheral driver test (see README.md)
2023-06-22 18:58:13,399 # There are 1 SPI devices configured for your platform.
init 0 0 4
2023-06-22 18:58:32,086 # init 0 0 4
2023-06-22 18:58:32,161 # Trying to initialize SPI_DEV(0): mode: 0, clk: 4, cs_port: 0, cs_pin: 0
2023-06-22 18:58:32,270 # (if below the program crashes with a failed assertion, then it means the configuration is not supported)
2023-06-22 18:58:32,280 # Success.
bench
2023-06-22 18:58:41,590 # bench
2023-06-22 18:58:41,644 # ### Running some benchmarks, all values in [us] ###
2023-06-22 18:58:41,682 # ### Test				Transfer time	user time
2023-06-22 18:58:41,683 # 
2023-06-22 18:58:41,755 #  1 - write 1000 times 1 byte:			28456	28640
2023-06-22 18:58:41,827 #  2 - write 1000 times 2 byte:			28184	28352
2023-06-22 18:58:42,454 #  3 - write 1000 times 100 byte:		579528	579704
2023-06-22 18:58:42,563 #  4 - write 1000 times 1 byte to register:	54080	54256
2023-06-22 18:58:42,674 #  5 - write 1000 times 2 byte to register:	56720	56888
2023-06-22 18:58:43,340 #  6 - write 1000 times 100 byte to register:	608152	608320
2023-06-22 18:58:43,412 #  7 - read 1000 times 2 byte:			28304	28480
2023-06-22 18:58:44,025 #  8 - read 1000 times 100 byte:		567408	567576
2023-06-22 18:58:44,138 #  9 - read 1000 times 2 byte from register:	56840	57016
2023-06-22 18:58:44,793 # 10 - read 1000 times 100 byte from register:	596024	596200
2023-06-22 18:58:44,868 # 11 - transfer 1000 times 2 byte:		28336	28512
2023-06-22 18:58:45,510 # 12 - transfer 1000 times 100 byte:		592128	592304
2023-06-22 18:58:45,625 # 13 - transfer 1000 times 2 byte to register:	56960	57136
2023-06-22 18:58:46,306 # 14 - transfer 1000 times 100 byte to register:620744	620920
2023-06-22 18:58:46,373 # 15 - acquire/release 1000 times:		20960	21136
2023-06-22 18:58:46,879 # -- - SUM:					3922824	3925440
2023-06-22 18:58:46,880 # 
2023-06-22 18:58:46,907 # ### All runs complete ###
> 2023-06-22 18:58:54,706 # Exiting Pyterm
```
I always wonder how fast it really goes:
- #16727
- #18374
#### tests/periph/timer
```
tests/periph/timer$ BOARD=atmega1284p make -j64 clean all | grep '^  ' && BOARD=atmega1284p AVRDUDE_PROGRAMMER='usbasp -F' make flash 2>&1 | grep -- 'of flash' && BOARD=atmega1284p PORT=/dev/ttyACM0 make term
   text	   data	    bss	    dec	    hex	filename
   8968	    274	   1032	  10274	   2822	/home/hugues/github/cpu_atmega_common/RIOT/tests/periph/timer/bin/atmega1284p/tests_timer.elf
avrdude: 9242 bytes of flash written
avrdude: 9242 bytes of flash verified
/home/hugues/github/cpu_atmega_common/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "9600"  
Twisted not available, please install it if you want to use pyterm's JSON capabilities
2023-06-22 19:00:15,136 # Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
2023-06-22 19:00:16,138 # 
2023-06-22 19:00:16,139 # Help: Press s to start test, r to print it is ready
s
2023-06-22 19:00:31,118 # START
2023-06-22 19:00:31,200 # main(): This is RIOT! (Version: 2020.07-devel-15351-gc5f75c-cpu/atmega_common)
2023-06-22 19:00:31,201 # 
2023-06-22 19:00:31,230 # Test for peripheral TIMERs
2023-06-22 19:00:31,231 # 
2023-06-22 19:00:31,251 # Available timers: 2
2023-06-22 19:00:31,252 # 
2023-06-22 19:00:31,270 # Testing TIMER_0:
2023-06-22 19:00:31,306 # TIMER_0: initialization successful
2023-06-22 19:00:31,324 # TIMER_0: stopped
2023-06-22 19:00:31,356 # TIMER_0: set channel 0 to 5000
2023-06-22 19:00:31,390 # TIMER_0: set channel 1 to 10000
2023-06-22 19:00:31,408 # TIMER_0: starting
2023-06-22 19:00:31,482 # TIMER_0: channel 0 fired at SW count     1247 - init:     1247
2023-06-22 19:00:31,547 # TIMER_0: channel 1 fired at SW count     2488 - diff:     1241
2023-06-22 19:00:31,551 # 
2023-06-22 19:00:31,568 # Testing TIMER_1:
2023-06-22 19:00:31,605 # TIMER_1: initialization successful
2023-06-22 19:00:31,622 # TIMER_1: stopped
2023-06-22 19:00:31,655 # TIMER_1: set channel 0 to 5000
2023-06-22 19:00:31,688 # TIMER_1: set channel 1 to 10000
2023-06-22 19:00:31,707 # TIMER_1: starting
2023-06-22 19:00:31,780 # TIMER_1: channel 0 fired at SW count     1247 - init:     1247
2023-06-22 19:00:31,846 # TIMER_1: channel 1 fired at SW count     2488 - diff:     1241
2023-06-22 19:00:31,849 # 
2023-06-22 19:00:31,864 # TEST SUCCEEDED
2023-06-22 19:00:31,939 # { "threads": [{ "name": "idle", "stack_size": 128, "stack_used": 86 }]}
2023-06-22 19:00:32,015 # { "threads": [{ "name": "main", "stack_size": 640, "stack_used": 120 }]}
2023-06-22 19:00:34,259 # Exiting Pyterm
```
#### tests/periph/uart
```
tests/periph/uart$ BOARD=atmega1284p make -j64 clean all | grep '^  ' && BOARD=atmega1284p AVRDUDE_PROGRAMMER='usbasp -F' make flash 2>&1 | grep -- 'of flash' && BOARD=atmega1284p PORT=/dev/ttyACM0 make term
   text	   data	    bss	    dec	    hex	filename
  15918	   1044	   2000	  18962	   4a12	/home/hugues/github/cpu_atmega_common/RIOT/tests/periph/uart/bin/atmega1284p/tests_uart.elf
avrdude: 16962 bytes of flash written
avrdude: 16962 bytes of flash verified
/home/hugues/github/cpu_atmega_common/RIOT/dist/tools/pyterm/pyterm -p "/dev/ttyACM0" -b "9600"  
Twisted not available, please install it if you want to use pyterm's JSON capabilities
2023-06-22 19:01:25,894 # Connect to serial port /dev/ttyACM0
Welcome to pyterm!
Type '/exit' to exit.
2023-06-22 19:01:26,896 # 
2023-06-22 19:01:26,898 # main(): This is RIOT! (Version: 2020.07-devel-15351-gc5f75c-cpu/atmega_common)
2023-06-22 19:01:26,898 # 
2023-06-22 19:01:26,899 # Manual UART driver test application
2023-06-22 19:01:26,899 # ===================================
2023-06-22 19:01:26,900 # This application is intended for testing additional UART
2023-06-22 19:01:26,900 # interfaces, that might be defined for a board. The 'primary' UART
2023-06-22 19:01:26,901 # interface is tested implicitly, as it is running the shell...
2023-06-22 19:01:26,901 # 
2023-06-22 19:01:26,902 # When receiving data on one of the additional UART interfaces, this
2023-06-22 19:01:26,902 # data will be outputted via STDIO. So the easiest way to test an 
2023-06-22 19:01:26,903 # UART interface, is to simply connect the RX with the TX pin. Then 
2023-06-22 19:01:26,904 # you can send data on that interface and you should see the data 
2023-06-22 19:01:26,904 # being printed to STDOUT
2023-06-22 19:01:26,904 # 
2023-06-22 19:01:26,904 # NOTE: all strings need to be '\n' terminated!
2023-06-22 19:01:26,904 # 
2023-06-22 19:01:26,909 # UARD_DEV(0): test uart_poweron() and uart_poweroff()  ->  [OK]
2023-06-22 19:01:26,910 # 
2023-06-22 19:01:26,921 # UART INFO:
2023-06-22 19:01:26,958 # Available devices:               2
2023-06-22 19:01:27,004 # UART used for STDIO (the shell): UART_DEV(0)
2023-06-22 19:01:27,006 # 
init 1 9600
2023-06-22 19:01:50,464 # init 1 9600
send 1 ping
2023-06-22 19:04:12,912 # send 1 ping
2023-06-22 19:04:12,934 # UART_DEV(1) TX: ping
> 2023-06-22 19:04:12,973 # Success: UART_DEV(1) RX: [ping]\n
2023-06-22 19:04:23,894 # Exiting Pyterm
````

### Issues/PRs references

Depends on PR:
- #19751


Co-authored-by: Hugues Larrive <hlarrive@pm.me>
2023-07-04 22:12:54 +00:00

495 lines
12 KiB
C

/*
* Copyright (C) 2015 HAW Hamburg
* 2016 INRIA
* 2023 Hugues Larrive
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup cpu_atmega_common
* @ingroup drivers_periph_gpio
* @{
*
* @file
* @brief Low-level GPIO driver implementation for ATmega family
*
* @author René Herthel <rene-herthel@outlook.de>
* @author Francisco Acosta <francisco.acosta@inria.fr>
* @author Laurent Navet <laurent.navet@gmail.com>
* @author Robert Hartung <hartung@ibr.cs.tu-bs.de>
* @author Torben Petersen <petersen@ibr.cs.tu-bs.de>
* @author Marian Buschsieweke <marian.buschsieweke@ovgu.de>
* @author Hugues Larrive <hugues.larrive@pm.me>
*
* @}
*/
#include <stdio.h>
#include <avr/interrupt.h>
#include "cpu.h"
#include "board.h"
#include "periph/gpio.h"
#include "periph_conf.h"
#include "periph_cpu.h"
#include "atmega_gpio.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifdef MODULE_PERIPH_GPIO_IRQ
static gpio_isr_ctx_t config[GPIO_EXT_INT_NUMOF];
/* Detects amount of possible PCINTs */
#if defined(MODULE_ATMEGA_PCINT0) || defined(MODULE_ATMEGA_PCINT1) || \
defined(MODULE_ATMEGA_PCINT2) || defined(MODULE_ATMEGA_PCINT3)
#include "atmega_pcint.h"
#define ENABLE_PCINT
/* Check which pcints should be enabled */
#if defined(MODULE_ATMEGA_PCINT0) && !defined(ATMEGA_PCINT_MAP_PCINT0)
# error "Either mapping for pin change interrupt bank 0 is missing or not supported by the MCU"
#endif
#if defined(MODULE_ATMEGA_PCINT1) && !defined(ATMEGA_PCINT_MAP_PCINT1)
# error "Either mapping for pin change interrupt bank 1 is missing or not supported by the MCU"
#endif
#if defined(MODULE_ATMEGA_PCINT2) && !defined(ATMEGA_PCINT_MAP_PCINT2)
# error "Either mapping for pin change interrupt bank 2 is missing or not supported by the MCU"
#endif
#if defined(MODULE_ATMEGA_PCINT3) && !defined(ATMEGA_PCINT_MAP_PCINT3)
# error "Either mapping for pin change interrupt bank 3 is missing or not supported by the MCU"
#endif
/**
* @brief Use anonymous enum as for addressing the @ref pcint_state
*/
enum {
#ifdef MODULE_ATMEGA_PCINT0
PCINT0_IDX, /**< Index of PCINT0, if used */
#endif /* MODULE_ATMEGA_PCINT0 */
#ifdef MODULE_ATMEGA_PCINT1
PCINT1_IDX, /**< Index of PCINT1, if used */
#endif /* MODULE_ATMEGA_PCINT1 */
#ifdef MODULE_ATMEGA_PCINT2
PCINT2_IDX, /**< Index of PCINT2, if used */
#endif /* MODULE_ATMEGA_PCINT2 */
#ifdef MODULE_ATMEGA_PCINT3
PCINT3_IDX, /**< Index of PCINT3, if used */
#endif /* MODULE_ATMEGA_PCINT3 */
PCINT_NUM_BANKS /**< Number of PCINT banks used */
};
/**
* @brief stores the last pcint state of each port
*/
static uint8_t pcint_state[PCINT_NUM_BANKS];
/**
* @brief stores all cb and args for all defined pcint.
*/
typedef struct {
gpio_flank_t flank; /**< type of interrupt the flank should be triggered on */
gpio_cb_t cb; /**< interrupt callback */
void *arg; /**< optional argument */
} gpio_isr_ctx_pcint_t;
/**
* @brief
*/
static const gpio_t pcint_mapping[] = {
#ifdef MODULE_ATMEGA_PCINT0
ATMEGA_PCINT_MAP_PCINT0,
#endif /* PCINT0_IDX */
#ifdef MODULE_ATMEGA_PCINT1
ATMEGA_PCINT_MAP_PCINT1,
#endif /* PCINT1_IDX */
#ifdef MODULE_ATMEGA_PCINT2
ATMEGA_PCINT_MAP_PCINT2,
#endif /* PCINT2_IDX */
#ifdef MODULE_ATMEGA_PCINT3
ATMEGA_PCINT_MAP_PCINT3,
#endif /* PCINT3_IDX */
};
/**
* @brief
*/
static gpio_isr_ctx_pcint_t pcint_config[8 * PCINT_NUM_BANKS];
#endif /* MODULE_ATMEGA_PCINTn */
#endif /* MODULE_PERIPH_GPIO_IRQ */
int gpio_init(gpio_t pin, gpio_mode_t mode)
{
uint8_t pin_mask = (1 << atmega_pin_num(pin));
switch (mode) {
case GPIO_OUT:
_SFR_MEM8(atmega_ddr_addr(pin)) |= pin_mask;
break;
case GPIO_IN:
_SFR_MEM8(atmega_ddr_addr(pin)) &= ~pin_mask;
_SFR_MEM8(atmega_port_addr(pin)) &= ~pin_mask;
break;
case GPIO_IN_PU:
_SFR_MEM8(atmega_ddr_addr(pin)) &= ~pin_mask;
_SFR_MEM8(atmega_port_addr(pin)) |= pin_mask;
break;
default:
return -1;
}
return 0;
}
int gpio_read(gpio_t pin)
{
return (_SFR_MEM8(atmega_pin_addr(pin)) & (1 << atmega_pin_num(pin)));
}
void gpio_set(gpio_t pin)
{
_SFR_MEM8(atmega_port_addr(pin)) |= (1 << atmega_pin_num(pin));
}
void gpio_clear(gpio_t pin)
{
_SFR_MEM8(atmega_port_addr(pin)) &= ~(1 << atmega_pin_num(pin));
}
void gpio_toggle(gpio_t pin)
{
if (gpio_read(pin)) {
gpio_clear(pin);
}
else {
gpio_set(pin);
}
}
void gpio_write(gpio_t pin, int value)
{
if (value) {
gpio_set(pin);
}
else {
gpio_clear(pin);
}
}
#ifdef MODULE_PERIPH_GPIO_IRQ
static inline int8_t _int_num(gpio_t pin)
{
uint8_t num;
const gpio_t ext_ints[GPIO_EXT_INT_NUMOF] = CPU_ATMEGA_EXT_INTS;
/* find pin in ext_ints array to get the interrupt number */
for (num = 0; num < GPIO_EXT_INT_NUMOF; num++) {
if (pin == ext_ints[num]) {
return num;
}
}
return -1;
}
#ifdef ENABLE_PCINT
static inline int pcint_init_int(gpio_t pin, gpio_mode_t mode,
gpio_flank_t flank,
gpio_cb_t cb, void *arg)
{
int8_t offset = -1;
uint8_t pin_num = atmega_pin_num(pin);
for (unsigned i = 0; i < ARRAY_SIZE(pcint_mapping); i++) {
if (pin != GPIO_UNDEF && pin == pcint_mapping[i]) {
offset = i;
break;
}
}
/* if pcint was not found: return -1 */
if (offset < 0) {
return offset;
}
uint8_t bank = offset / 8;
uint8_t bank_idx = offset % 8;
DEBUG("PCINT enabled for bank %u offset %u\n",
(unsigned)bank, (unsigned)offset);
/* save configuration for pin change interrupt */
pcint_config[offset].flank = flank;
pcint_config[offset].arg = arg;
pcint_config[offset].cb = cb;
/* init gpio */
gpio_init(pin, mode);
/* configure pcint */
cli();
switch (bank) {
#ifdef MODULE_ATMEGA_PCINT0
case PCINT0_IDX:
PCMSK0 |= (1 << bank_idx);
PCICR |= (1 << PCIE0);
break;
#endif /* MODULE_ATMEGA_PCINT0 */
#ifdef MODULE_ATMEGA_PCINT1
case PCINT1_IDX:
PCMSK1 |= (1 << bank_idx);
PCICR |= (1 << PCIE1);
break;
#endif /* MODULE_ATMEGA_PCINT1 */
#ifdef MODULE_ATMEGA_PCINT2
case PCINT2_IDX:
PCMSK2 |= (1 << bank_idx);
PCICR |= (1 << PCIE2);
break;
#endif /* MODULE_ATMEGA_PCINT2 */
#ifdef MODULE_ATMEGA_PCINT3
case PCINT3_IDX:
PCMSK3 |= (1 << bank_idx);
PCICR |= (1 << PCIE3);
break;
#endif /* MODULE_ATMEGA_PCINT3 */
default:
return -1;
break;
}
/* As ports are mixed in a bank (e.g. PCINT0), we can only save a single bit here! */
uint8_t port_value = (_SFR_MEM8(atmega_pin_addr( pin )));
uint8_t pin_mask = (1 << pin_num);
uint8_t pin_value = ((port_value & pin_mask) != 0);
if (pin_value) {
pcint_state[bank] |= pin_mask;
}
else {
pcint_state[bank] &= ~pin_mask;
}
sei();
return 0;
}
#endif /* ENABLE_PCINT */
int gpio_init_int(gpio_t pin, gpio_mode_t mode, gpio_flank_t flank,
gpio_cb_t cb, void *arg)
{
int8_t int_num = _int_num(pin);
/* mode not supported */
if ((mode != GPIO_IN) && (mode != GPIO_IN_PU)) {
return -1;
}
/* not a valid interrupt pin. Set as pcint instead if pcints are enabled */
if (int_num < 0) {
#ifdef ENABLE_PCINT
/* If pin change interrupts are enabled, enable mask and interrupt */
return pcint_init_int(pin, mode, flank, cb, arg);
#else
return -1;
#endif /* ENABLE_PCINT */
}
/* flank not supported */
if (flank > GPIO_RISING) {
return -1;
}
gpio_init(pin, mode);
/* clear global interrupt flag */
cli();
/* enable interrupt number int_num */
#if defined(EIFR) && defined(EIMSK)
EIFR |= (1 << int_num);
EIMSK |= (1 << int_num);
#elif defined(GIFR) && defined(GICR)
GIFR |= (1 << (INTF0 + int_num));
GICR |= (1 << (INT0 + int_num));
#endif
/* apply flank to interrupt number int_num */
#if defined(EICRB)
if (int_num >= 4) {
EICRB &= ~(0x3 << ((int_num % 4) * 2));
EICRB |= (flank << ((int_num % 4) * 2));
}
else
#endif
{
#if defined(EICRA)
EICRA &= ~(0x3 << (int_num * 2));
EICRA |= (flank << (int_num * 2));
#elif defined(MCUCR)
MCUCR &= ~(0x3 << (int_num * 2));
MCUCR |= (flank << (int_num * 2));
#endif
}
/* set callback */
config[int_num].cb = cb;
config[int_num].arg = arg;
/* set global interrupt flag */
sei();
return 0;
}
void gpio_irq_enable(gpio_t pin)
{
#if defined(EIFR) && defined(EIMSK)
EIFR |= (1 << _int_num(pin));
EIMSK |= (1 << _int_num(pin));
#elif defined(GIFR) && defined(GICR)
GIFR |= (1 << (INTF0 + _int_num(pin)));
GICR |= (1 << (INT0 + _int_num(pin)));
#endif
}
void gpio_irq_disable(gpio_t pin)
{
#if defined(EIMSK)
EIMSK &= ~(1 << _int_num(pin));
#elif defined(GICR)
GICR &= ~(1 << (INT0 + _int_num(pin)));
#endif
}
static inline void irq_handler(uint8_t int_num)
{
avr8_enter_isr();
config[int_num].cb(config[int_num].arg);
avr8_exit_isr();
}
#ifdef ENABLE_PCINT
/* inline function that is used by the PCINT ISR */
static inline void pcint_handler(uint8_t bank, uint8_t enabled_pcints)
{
avr8_enter_isr();
/* Find right item */
uint8_t idx = 0;
while (enabled_pcints > 0) {
/* check if this pin is enabled & has changed */
if (enabled_pcints & 0x1) {
/* get pin from mapping (assumes 8 entries per bank!) */
gpio_t pin = pcint_mapping[bank * 8 + idx];
/* re-construct mask from pin */
uint8_t pin_mask = (1 << (atmega_pin_num(pin)));
uint8_t idx_mask = (1 << idx);
uint8_t port_value = (_SFR_MEM8(atmega_pin_addr( pin )));
uint8_t pin_value = ((port_value & pin_mask) != 0);
uint8_t old_state = ((pcint_state[bank] & idx_mask) != 0);
gpio_isr_ctx_pcint_t *conf = &pcint_config[bank * 8 + idx];
if (old_state != pin_value) {
pcint_state[bank] ^= idx_mask;
if ((conf->flank == GPIO_BOTH ||
(pin_value && conf->flank == GPIO_RISING) ||
(!pin_value && conf->flank == GPIO_FALLING))) {
/* execute callback routine */
conf->cb(conf->arg);
}
}
}
enabled_pcints = enabled_pcints >> 1;
idx++;
}
avr8_exit_isr();
}
#ifdef MODULE_ATMEGA_PCINT0
ISR(PCINT0_vect, ISR_BLOCK)
{
pcint_handler(PCINT0_IDX, PCMSK0);
}
#endif /* MODULE_ATMEGA_PCINT0 */
#ifdef MODULE_ATMEGA_PCINT1
ISR(PCINT1_vect, ISR_BLOCK)
{
pcint_handler(PCINT1_IDX, PCMSK1);
}
#endif /* MODULE_ATMEGA_PCINT1 */
#ifdef MODULE_ATMEGA_PCINT2
ISR(PCINT2_vect, ISR_BLOCK)
{
pcint_handler(PCINT2_IDX, PCMSK2);
}
#endif /* MODULE_ATMEGA_PCINT2 */
#ifdef MODULE_ATMEGA_PCINT3
ISR(PCINT3_vect, ISR_BLOCK)
{
pcint_handler(PCINT3_IDX, PCMSK3);
}
#endif /* MODULE_ATMEGA_PCINT3 */
#endif /* ENABLE_PCINT */
ISR(INT0_vect, ISR_BLOCK)
{
irq_handler(0); /**< predefined interrupt pin */
}
ISR(INT1_vect, ISR_BLOCK)
{
irq_handler(1); /**< predefined interrupt pin */
}
#if defined(INT2_vect)
ISR(INT2_vect, ISR_BLOCK)
{
irq_handler(2); /**< predefined interrupt pin */
}
#endif
#if defined(INT3_vect)
ISR(INT3_vect, ISR_BLOCK)
{
irq_handler(3); /**< predefined interrupt pin */
}
#endif
#if defined(INT4_vect)
ISR(INT4_vect, ISR_BLOCK)
{
irq_handler(4); /**< predefined interrupt pin */
}
#endif
#if defined(INT5_vect)
ISR(INT5_vect, ISR_BLOCK)
{
irq_handler(5); /**< predefined interrupt pin */
}
#endif
#if defined(INT6_vect)
ISR(INT6_vect, ISR_BLOCK)
{
irq_handler(6); /**< predefined interrupt pin */
}
#endif
#if defined(INT7_vect)
ISR(INT7_vect, ISR_BLOCK)
{
irq_handler(7); /**< predefined interrupt pin */
}
#endif
#endif /* MODULE_PERIPH_GPIO_IRQ */