2015-04-16 08:33:35 +02:00
|
|
|
/*
|
2015-06-03 18:22:24 +02:00
|
|
|
* Copyright (C) 2014-2015 Freie Universität Berlin
|
2015-04-16 08:33:35 +02:00
|
|
|
* 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
|
|
|
* 2015 FreshTemp, LLC.
|
|
|
|
*
|
|
|
|
* 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 driver_periph
|
|
|
|
* @{
|
|
|
|
*
|
|
|
|
* @file gpio.c
|
|
|
|
* @brief Low-level GPIO driver implementation
|
|
|
|
*
|
|
|
|
* @author Troels Hoffmeyer <troels.d.hoffmeyer@gmail.com>
|
|
|
|
* @author Thomas Eichinger <thomas.eichinger@fu-berlin.de>
|
|
|
|
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
2015-06-03 18:22:24 +02:00
|
|
|
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
2015-04-16 08:33:35 +02:00
|
|
|
*
|
|
|
|
* @}
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "cpu.h"
|
|
|
|
#include "periph/gpio.h"
|
|
|
|
|
|
|
|
#include "periph_conf.h"
|
|
|
|
#include "saml21_periph.h"
|
|
|
|
|
|
|
|
#include "sched.h"
|
|
|
|
#include "thread.h"
|
|
|
|
#include "panic.h"
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
#define ENABLE_DEBUG (0)
|
2015-04-16 08:33:35 +02:00
|
|
|
#include "debug.h"
|
|
|
|
|
|
|
|
/**
|
2015-06-03 18:22:24 +02:00
|
|
|
* @brief Number of external interrupt lines
|
2015-04-16 08:33:35 +02:00
|
|
|
*/
|
2015-06-03 18:22:24 +02:00
|
|
|
#define NUMOF_IRQS (16U)
|
2015-04-16 08:33:35 +02:00
|
|
|
|
|
|
|
/**
|
2015-06-03 18:22:24 +02:00
|
|
|
* @brief Mapping of pins to EXTI lines, -1 means not EXTI possible
|
2015-04-16 08:33:35 +02:00
|
|
|
*/
|
2015-06-03 18:22:24 +02:00
|
|
|
static const int8_t exti_config[2][32] = {
|
|
|
|
{ 0, 1, 2, 3, 4, 5, 6, 7, -1, 9, 10, 11, 12, 13, 14, 15,
|
|
|
|
0, 1, 2, 3, 4, 5, 6, 7, 12, 13, -1, 15, -1, -1, 10, 11},
|
|
|
|
{-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
|
|
|
0, 1, -1, -1, -1, -1, 6, 7, -1, -1, -1, -1, -1, -1, 14, 15},
|
2015-04-16 08:33:35 +02:00
|
|
|
};
|
|
|
|
|
2016-01-27 17:00:37 +01:00
|
|
|
static gpio_isr_ctx_t gpio_config[NUMOF_IRQS];
|
2015-04-16 08:33:35 +02:00
|
|
|
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
static inline PortGroup *_port(gpio_t pin)
|
|
|
|
{
|
|
|
|
return (PortGroup *)(pin & ~(0x1f));
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
2015-06-03 18:22:24 +02:00
|
|
|
|
|
|
|
static inline int _pin_pos(gpio_t pin)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
return (pin & 0x1f);
|
|
|
|
}
|
2015-04-16 08:33:35 +02:00
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
static inline int _pin_mask(gpio_t pin)
|
|
|
|
{
|
|
|
|
return (1 << _pin_pos(pin));
|
|
|
|
}
|
2015-04-16 08:33:35 +02:00
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
static int _exti(gpio_t pin)
|
|
|
|
{
|
|
|
|
int port_num = ((pin >> 7) & 0x03);
|
2015-04-16 08:33:35 +02:00
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
if (port_num > 1) {
|
|
|
|
return -1;
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
2015-06-03 18:22:24 +02:00
|
|
|
return exti_config[port_num][_pin_pos(pin)];
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
void gpio_init_mux(gpio_t pin, gpio_mux_t mux)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
PortGroup* port = _port(pin);
|
|
|
|
int pin_pos = _pin_pos(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
port->PINCFG[pin_pos].reg |= PORT_PINCFG_PMUXEN;
|
|
|
|
port->PMUX[pin_pos >> 1].reg &= ~(0xf << (4 * (pin_pos & 0x1)));
|
|
|
|
port->PMUX[pin_pos >> 1].reg |= (mux << (4 * (pin_pos & 0x1)));
|
|
|
|
}
|
2015-04-16 08:33:35 +02:00
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
int gpio_init(gpio_t pin, gpio_dir_t dir, gpio_pp_t pushpull)
|
|
|
|
{
|
|
|
|
PortGroup* port = _port(pin);
|
|
|
|
int pin_pos = _pin_pos(pin);
|
|
|
|
int pin_mask = _pin_mask(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
/* configure the pin's pull resistor and reset all other configuration */
|
2015-04-16 08:33:35 +02:00
|
|
|
switch (pushpull) {
|
|
|
|
case GPIO_PULLDOWN:
|
2015-06-03 18:22:24 +02:00
|
|
|
port->OUTCLR.reg = pin_mask;
|
|
|
|
port->PINCFG[pin_pos].reg = PORT_PINCFG_PULLEN;
|
2015-04-16 08:33:35 +02:00
|
|
|
break;
|
|
|
|
case GPIO_PULLUP:
|
2015-06-03 18:22:24 +02:00
|
|
|
port->OUTSET.reg = pin_mask;
|
|
|
|
port->PINCFG[pin_pos].reg = PORT_PINCFG_PULLEN;
|
2015-04-16 08:33:35 +02:00
|
|
|
break;
|
|
|
|
case GPIO_NOPULL:
|
2015-06-03 18:22:24 +02:00
|
|
|
port->PINCFG[pin_pos].reg = 0;
|
2015-04-16 08:33:35 +02:00
|
|
|
break;
|
|
|
|
}
|
2015-06-03 18:22:24 +02:00
|
|
|
/* set pin_pos direction */
|
|
|
|
if (dir == GPIO_DIR_OUT) {
|
|
|
|
if (pushpull == GPIO_PULLDOWN) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
port->DIRSET.reg = pin_mask; /* configure as output */
|
|
|
|
port->OUTCLR.reg = pin_mask; /* set pin LOW on init */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
port->DIRCLR.reg = pin_mask; /* configure as input */
|
|
|
|
port->PINCFG[pin_pos].reg |= PORT_PINCFG_INEN;
|
|
|
|
}
|
2015-04-16 08:33:35 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
int gpio_init_int(gpio_t pin, gpio_pp_t pullup, gpio_flank_t flank,
|
|
|
|
gpio_cb_t cb, void *arg)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
int exti = _exti(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
/* make sure EIC channel is valid */
|
|
|
|
if (exti == -1) {
|
2015-04-16 08:33:35 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
/* save callback */
|
|
|
|
gpio_config[exti].cb = cb;
|
|
|
|
gpio_config[exti].arg = arg;
|
2015-10-31 17:35:41 +01:00
|
|
|
/* configure pin as input and set MUX to peripheral function A */
|
2015-06-03 18:22:24 +02:00
|
|
|
gpio_init(pin, GPIO_DIR_IN, pullup);
|
|
|
|
gpio_init_mux(pin, GPIO_MUX_A);
|
|
|
|
/* enable clocks for the EIC module */
|
2015-04-16 08:33:35 +02:00
|
|
|
MCLK->APBAMASK.reg |= MCLK_APBAMASK_EIC;
|
|
|
|
GCLK->PCHCTRL[EIC_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0;
|
2015-06-03 18:22:24 +02:00
|
|
|
/* configure the active flank */
|
|
|
|
EIC->CONFIG[exti >> 3].reg &= ~(0xf << ((exti & 0x7) * 4));
|
|
|
|
EIC->CONFIG[exti >> 3].reg |= (flank << ((exti & 0x7) * 4));
|
|
|
|
/* enable the global EIC interrupt */
|
2015-04-16 08:33:35 +02:00
|
|
|
NVIC_EnableIRQ(EIC_IRQn);
|
|
|
|
/*Enable pin interrupt */
|
2015-06-03 18:22:24 +02:00
|
|
|
EIC->INTFLAG.reg = (1 << exti);
|
|
|
|
EIC->INTENSET.reg = (1 << exti);
|
|
|
|
/* enable the EIC module*/
|
|
|
|
EIC->CTRLA.reg = EIC_CTRLA_ENABLE;
|
2016-01-26 20:01:06 +01:00
|
|
|
while (EIC->SYNCBUSY.reg & EIC_SYNCBUSY_ENABLE) {}
|
2015-04-16 08:33:35 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
void gpio_irq_enable(gpio_t pin)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
int exti = _exti(pin);
|
|
|
|
if (exti == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
EIC->INTENSET.reg = (1 << exti);
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
void gpio_irq_disable(gpio_t pin)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
int exti = _exti(pin);
|
|
|
|
if (exti == -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
EIC->INTENCLR.reg = (1 << exti);
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
int gpio_read(gpio_t pin)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
PortGroup *port = _port(pin);
|
|
|
|
int mask = _pin_mask(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
if (port->DIR.reg & mask) {
|
|
|
|
return (port->OUT.reg & mask) ? 1 : 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return (port->IN.reg & mask) ? 1 : 0;
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
void gpio_set(gpio_t pin)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
_port(pin)->OUTSET.reg = _pin_mask(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
void gpio_clear(gpio_t pin)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
_port(pin)->OUTCLR.reg = _pin_mask(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
void gpio_toggle(gpio_t pin)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
_port(pin)->OUTTGL.reg = _pin_mask(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
|
2015-06-03 18:22:24 +02:00
|
|
|
void gpio_write(gpio_t pin, int value)
|
2015-04-16 08:33:35 +02:00
|
|
|
{
|
|
|
|
if (value) {
|
2015-06-03 18:22:24 +02:00
|
|
|
_port(pin)->OUTSET.reg = _pin_mask(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
} else {
|
2015-06-03 18:22:24 +02:00
|
|
|
_port(pin)->OUTCLR.reg = _pin_mask(pin);
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void isr_eic(void)
|
|
|
|
{
|
2015-06-03 18:22:24 +02:00
|
|
|
for (int i = 0; i < NUMOF_IRQS; i++) {
|
|
|
|
if (EIC->INTFLAG.reg & (1 << i)) {
|
|
|
|
EIC->INTFLAG.reg = (1 << i);
|
|
|
|
gpio_config[i].cb(gpio_config[i].arg);
|
2015-04-16 08:33:35 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sched_context_switch_request) {
|
|
|
|
thread_yield();
|
|
|
|
}
|
|
|
|
}
|