Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 171 additions & 1 deletion sam4s/pio.c
Original file line number Diff line number Diff line change
@@ -1,2 +1,172 @@
/* Nothing to do. All the functions are inline and defined in pio.h */
/**
* @file irq.c
* @brief PIO interrupt input
* @author Ran Bao (rba90@uclive.ac.nz)
* @date 21 April 2017
*/

#include <stdint.h>
#include "irq.h"
#include "pio.h"

/**
* Interrupt vector table for each PIO pins
* According to datasheet, there are 32 pins on each port groups, there are three port groups in
* total. In sam4s8b, there are 32 pins in Port A and 15 pins in Port B
*/
#warning "Make sure you are using sam4s8b, otherwise you will need to extent pio interrupt vector table"
static irq_handler_t pio_interrupt_vector_table[PB15_PIO - PA0_PIO];

/**
* Internal symbols that indicates whether interrupt vector handler has been added to the dynamic
* interrupt vector table (exception_table in crt0.c)
*/
static bool irq_port_registered_a = false;
static bool irq_port_registered_b = false;
static bool irq_port_registered_c = false;

/**
* Dynamic Vector Table, defined in crt0.c, can be modified at run time
*/
extern irq_handler_t exception_table[];

/**
* Handle interrupt on specific port groups. PIO IRQs are served and cleared in this function
* @param port PORT_A or PORT_B or PORT_C
*/
static void pio_irq_common_handler(uint8_t port)
{
// find base
Pio *base = PIO_BASE(port);

// look for those pins with both interrupt is enabled and interrupt stats has changed
// NOTE: ISR register is cleared after reading
uint32_t pio_activated = base->PIO_IMR & base->PIO_ISR;

// to through each bit
for (uint8_t bit = (uint8_t) IRQ_ID_MIN; bit <= (uint8_t) IRQ_ID_MAX; bit++, pio_activated >>= 1)
{
if (pio_activated & 0x1)
{
// execute registered interrupt handler
pio_t pin = PIO_DEFINE(port, bit);
if (pio_interrupt_vector_table[pin])
{
pio_interrupt_vector_table[pin]();
}
}
}
}

static void pio_irq_handler_a(void)
{
pio_irq_common_handler(PORT_A);
}

static void pio_irq_handler_b(void)
{
pio_irq_common_handler(PORT_B);
}

static void pio_irq_handler_c(void)
{
pio_irq_common_handler(PORT_C);
}

static void irq_port_enable(irq_id_t port_id)
{
// register interrupt handler in dynamic vector table
switch(port_id)
{
case ID_PIOA:
if (!irq_port_registered_a)
{
// disable NVIC interrupts on port groups
NVIC_DisableIRQ((IRQn_Type) ID_PIOA);

// insert corresponding handler into exception table
exception_table[16 + ID_PIOA] = pio_irq_handler_a;

// clear existing interrupt signal
NVIC_ClearPendingIRQ((IRQn_Type) ID_PIOA);

// enable interrupt on port
NVIC_EnableIRQ((IRQn_Type) ID_PIOA);

// enable clock signal on specific port
mcu_pmc_enable(ID_PIOA);

irq_port_registered_a = true;
}
break;
case ID_PIOB:
if (!irq_port_registered_b)
{
// disable NVIC interrupts on port groups
NVIC_DisableIRQ((IRQn_Type) ID_PIOB);

// insert corresponding handler into exception table
exception_table[16 + ID_PIOB] = pio_irq_handler_b;

// clear existing interrupt signal
NVIC_ClearPendingIRQ((IRQn_Type) ID_PIOB);

// enable interrupt on port
NVIC_EnableIRQ((IRQn_Type) ID_PIOB);

// enable clock signal on specific port
mcu_pmc_enable(ID_PIOB);

irq_port_registered_b = true;
}
break;
case ID_PIOC:
if (!irq_port_registered_c)
{
// disable NVIC interrupts on port groups
NVIC_DisableIRQ((IRQn_Type) ID_PIOC);

// insert corresponding handler into exception table
exception_table[16 + ID_PIOC] = pio_irq_handler_c;

// clear existing interrupt signal
NVIC_ClearPendingIRQ((IRQn_Type) ID_PIOC);

// enable interrupt on port
NVIC_EnableIRQ((IRQn_Type) ID_PIOC);

// enable clock signal on specific port
mcu_pmc_enable(ID_PIOC);

irq_port_registered_c = true;
}
}
}

/**
* Initialize PIO pin as interrupt input
* @param pio PIO pin
* @param config trigger condition of interrupt
* @param isr interrupt service routine (interrupt handler) on specific pin
*/
void
pio_irq_init(pio_t pio, pio_irq_config_t config, irq_handler_t isr)
{
// get port id from pin address
irq_id_t id = PIO_ID(pio);

// assign interrupt function
pio_interrupt_vector_table[pio] = isr;

// config pin as input
pio_config_set(pio, PIO_INPUT);

// config pin as interrupt
pio_irq_config_set(pio, config);

// enable port irq
irq_port_enable(id);

// enable pin irq
pio_irq_enable(pio);
}
30 changes: 19 additions & 11 deletions sam4s/pio.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
@date 2 Jun 2007
@brief PIO abstraction for SAM4S microcontroller.
@note Macros and inline functions are used to avoid function
call overhead and to allow compile-time constant folding.
call overhead and to allow compile-time constant folding.
*/
#ifndef PIO_H
#define PIO_H

#include "config.h"
#include "mcu.h"

#include "irq.h"

#define PIO_SAM4S

Expand Down Expand Up @@ -46,7 +46,7 @@ enum {PORT_A, PORT_B, PORT_C};

typedef uint32_t pio_mask_t;

typedef enum pio_config_enum
typedef enum pio_config_enum
{
PIO_INPUT = 1, /* Configure as input pin. */
PIO_PULLUP, /* Configure as input pin with pullup. */
Expand Down Expand Up @@ -319,15 +319,15 @@ pio_output_high (pio_t pio)

/** Set PIO low.
@param pio */
static inline void
static inline void
pio_output_low (pio_t pio)
{
PIO_BASE (pio)->PIO_CODR = PIO_BITMASK_ (pio);
}


/** Set PIO to desired state.
@param pio
@param pio
@param state */
static inline void
pio_output_set (pio_t pio, bool state)
Expand Down Expand Up @@ -373,19 +373,27 @@ void
pio_shutdown (pio_t pio);


typedef enum pio_irq_config_enum
typedef enum pio_irq_config_enum
{
PIO_IRQ_FALLING_EDGE = 1,
PIO_IRQ_RISING_EDGE,
PIO_IRQ_ANY_EDGE,
PIO_IRQ_LOW_LEVEL,
PIO_IRQ_FALLING_EDGE = 1,
PIO_IRQ_RISING_EDGE,
PIO_IRQ_ANY_EDGE,
PIO_IRQ_LOW_LEVEL,
PIO_IRQ_HIGH_LEVEL
} pio_irq_config_t;

/**
* Initialize PIO pin as interrupt input
* @param pio PIO pin
* @param config trigger condition of interrupt
* @param isr interrupt service routine (interrupt handler) on specific pin
*/
void
pio_irq_init(pio_t pio, pio_irq_config_t config, irq_handler_t isr);

/** Configure PIO for interrupt
@param pio */
static inline bool
static inline bool
pio_irq_config_set (pio_t pio, pio_irq_config_t config)
{

Expand Down