aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@mit.edu>2010-12-29 21:30:42 -0500
committerMarti Bolivar <mbolivar@mit.edu>2010-12-30 02:40:16 -0500
commit8e973f3d1ef0324e213824dc05af0f9713e7b3cb (patch)
treeb5ff3aebf87116b5de0e968a1f44309478a6c430 /libmaple
parentd1a8d832af96efdd1b399799dfae81517dc04dfa (diff)
downloadlibrambutan-8e973f3d1ef0324e213824dc05af0f9713e7b3cb.tar.gz
librambutan-8e973f3d1ef0324e213824dc05af0f9713e7b3cb.zip
Changed nzmichaelh's initial DMA interface to be more flexible.
Some bugfixes in the external interrupt code were found along the way. Defines for nonexistent registers removed from nvic interface.
Diffstat (limited to 'libmaple')
-rw-r--r--libmaple/dma.c112
-rw-r--r--libmaple/dma.h57
-rw-r--r--libmaple/exti.c8
-rw-r--r--libmaple/exti.h5
-rw-r--r--libmaple/nvic.c12
-rw-r--r--libmaple/nvic.h18
6 files changed, 153 insertions, 59 deletions
diff --git a/libmaple/dma.c b/libmaple/dma.c
index 88c1342..a6c6ccd 100644
--- a/libmaple/dma.c
+++ b/libmaple/dma.c
@@ -31,76 +31,118 @@
#include "libmaple.h"
#include "dma.h"
#include "rcc.h"
+#include "nvic.h"
#define DMA_EN BIT(0)
-typedef struct dma_regs
-{
+typedef struct dma_regs {
uint32 CCR;
uint32 CNDTR;
uint32 CPAR;
uint32 CMAR;
} dma_regs;
+typedef struct DMAChannel {
+ void (*handler)(void);
+ uint32 irq_line;
+} DMAChannel;
+
+static DMAChannel dma_channels[] = {
+ { .handler = NULL, .irq_line = NVIC_DMA_CH1 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH2 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH3 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH4 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH5 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH6 },
+ { .handler = NULL, .irq_line = NVIC_DMA_CH7 }
+};
+
/** Get the base address of the given channel, asserting and returning
* NULL on illegal
*/
static dma_regs *dma_get_regs(uint8 channel) {
if (channel >= 1 && channel <= 7) {
return (dma_regs *)(DMA1_CCR1 + DMA_CHANNEL_STRIDE * (channel-1));
- }
- else {
+ } else {
ASSERT(false);
return NULL;
}
}
-/** Initialise a DMA channel. Start the transfer using dma_start().
- *
- * @param channel the channel number (1..7)
- * @param peripheral address of the peripheral
- * @param from_peripheral true if transfer goes from the peripheral
- * to memory
- * @param mode OR of the dma_mode_flags
- */
-void dma_init(uint8 channel, volatile void *peripheral, int from_peripheral,
+/* Zero-based indexing */
+static inline void dispatch_handler(uint8 channel_idx) {
+ ASSERT(dma_channels[channel_idx].handler);
+ if (dma_channels[channel_idx].handler) {
+ (dma_channels[channel_idx].handler)();
+ }
+}
+
+void DMAChannel1_IRQHandler(void) {
+ dispatch_handler(0);
+}
+
+void DMAChannel2_IRQHandler(void) {
+ dispatch_handler(1);
+}
+
+void DMAChannel3_IRQHandler(void) {
+ dispatch_handler(2);
+}
+
+void DMAChannel4_IRQHandler(void) {
+ dispatch_handler(3);
+}
+
+void DMAChannel5_IRQHandler(void) {
+ dispatch_handler(4);
+}
+
+void DMAChannel6_IRQHandler(void) {
+ dispatch_handler(5);
+}
+
+void DMAChannel7_IRQHandler(void) {
+ dispatch_handler(6);
+}
+
+void dma_init(uint8 channel, volatile void *paddr,
+ dma_transfer_size psize, dma_transfer_size msize,
dma_mode_flags mode) {
volatile dma_regs *regs = dma_get_regs(channel);
if (regs != NULL) {
rcc_clk_enable(RCC_DMA1);
- /* Disable the channel. PENDING: May not be needed */
- regs->CCR = 0;
+ regs->CCR = ((0 << 12) /* Low priority */
+ | (msize << 10)
+ | (psize << 8)
+ | (0 << 0) /* Disabled (until started) */
+ | mode);
- uint32 cr = 0
- | (0 << 12) // Low priority
- | (0 << 10) // Memory size = 8 bits
- | (1 << 8) // Peripheral size = 16 bits
- | (mode << 5) // Increment and circular modes
- | (0 << 0); // Not enabled
-
- /* FIXME XXX integer from pointer without a cast. */
- regs->CPAR = peripheral;
-
- if (!from_peripheral) {
- cr |= 1 << 4; // From memory
- }
-
- /* Stay disabled until started */
- regs->CCR = cr;
+ regs->CPAR = (uint32)paddr;
}
}
-void dma_start(uint8 channel, volatile void *buffer, uint16 count)
-{
+void dma_start(uint8 channel, volatile void *buffer, uint16 count) {
volatile dma_regs *regs = dma_get_regs(channel);
if (regs != NULL) {
- regs->CCR &= ~DMA_EN;
+ regs->CCR &= ~DMA_EN; /* CMAR may not be written with EN set */
regs->CMAR = (uint32)buffer;
regs->CNDTR = count;
- regs->CCR |= DMA_EN;
+ regs->CCR |= DMA_EN; /* Start the transfer */
}
}
+
+void dma_attach_interrupt(uint8 channel, voidFuncPtr handler) {
+ channel--; /* 1-based -> 0-based indexing */
+ dma_channels[channel].handler = handler;
+ nvic_irq_enable(dma_channels[channel].irq_line);
+}
+
+void dma_detach_interrupt(uint8 channel) {
+ channel--;
+ nvic_irq_disable(dma_channels[channel].irq_line);
+ dma_channels[channel].handler = NULL;
+}
diff --git a/libmaple/dma.h b/libmaple/dma.h
index 8a6ca34..7072a1a 100644
--- a/libmaple/dma.h
+++ b/libmaple/dma.h
@@ -26,11 +26,15 @@
* @file dma.h
*
* @brief Direct Memory Access peripheral support
+ *
+ * TODO: add DMA2 support for high-density devices.
*/
#ifndef _DMA_H_
#define _DMA_H_
+#include "libmaple_types.h"
+
#ifdef __cplusplus
extern "C"{
#endif
@@ -39,7 +43,7 @@ extern "C"{
#define DMA1_BASE 0x40020000
/** DMA Interrupt Status Register */
#define DMA1_ISR (DMA1_BASE + 0x00)
-/** DMA Interrput Flag Clear Register */
+/** DMA Interrupt Flag Clear Register */
#define DMA1_IFCR (DMA1_BASE + 0x04)
/** DMA Channel Configuration Register */
#define DMA1_CCR1 (DMA1_BASE + 0x08)
@@ -52,19 +56,56 @@ extern "C"{
/** Spacing between channel registers */
#define DMA_CHANNEL_STRIDE 20
+/** Flags for DMA transfer configuration. */
typedef enum dma_mode_flags {
- /** Auto-increment memory address */
- DMA_MINC_MODE = 4,
- /** Auto-increment peripheral address */
- DMA_PINC_MODE = 2,
- /** Circular mode */
- DMA_CIRC_MODE = 1,
+ DMA_MINC_MODE = 1 << 7, /**< Auto-increment memory address */
+ DMA_PINC_MODE = 1 << 6, /**< Auto-increment peripheral address */
+ DMA_CIRC_MODE = 1 << 5, /**< Circular mode */
+ DMA_FROM_MEM = 1 << 4, /**< Read from memory to peripheral */
+ DMA_TRNS_ERR = 1 << 3, /**< Interrupt on transfer error */
+ DMA_HALF_TRNS = 1 << 2, /**< Interrupt on half-transfer */
+ DMA_TRNS_CMPLT = 1 << 1 /**< Interrupt on transfer completion */
} dma_mode_flags;
-void dma_init(uint8 channel, volatile void *peripheral, int from_peripheral,
+typedef enum dma_transfer_size {
+ DMA_SIZE_8BITS = 0,
+ DMA_SIZE_16BITS = 1,
+ DMA_SIZE_32BITS = 2
+} dma_transfer_size;
+
+/**
+ * Initialize a DMA channel. If desired, attach an interrupt handler
+ * using dma_attach_interrupt(). Start the transfer using
+ * dma_start().
+ *
+ * @param channel the channel number (1..7)
+ * @param paddr address of the peripheral
+ * @param psize peripheral size
+ * @param msize memory size
+ * @param mode OR of the dma_mode_flags
+ * @see dma_mode_flags
+ * @see dma_attach_interrupt()
+ * @see dma_start() */
+void dma_init(uint8 channel, volatile void *paddr,
+ dma_transfer_size psize, dma_transfer_size msize,
dma_mode_flags mode);
+
+/**
+ * Begin a DMA transfer initialized with dma_init(). You may call
+ * this function repeatedly after a single call to dma_init().
+ *
+ * @param channel Channel transfer to start.
+ * @param buffer Buffer to write to (unless DMA_FROM_MEM was set in
+ * mode argument to dma_init(), in which case, buffer
+ * to read from). This must be aligned with msize
+ * argument to dma_init().
+ * @param count Number of elements to transfer.
+ * @see dma_init() */
void dma_start(uint8 channel, volatile void *buffer, uint16 count);
+void dma_attach_interrupt(uint8 channel, voidFuncPtr handler);
+void dma_detach_interrupt(uint8 channel);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/libmaple/exti.c b/libmaple/exti.c
index acd7c94..150dd05 100644
--- a/libmaple/exti.c
+++ b/libmaple/exti.c
@@ -23,8 +23,6 @@
*****************************************************************************/
/**
- * @file exti.c
- *
* @brief External interrupt control routines
*/
@@ -182,11 +180,11 @@ void exti_attach_interrupt(uint32 port,
break;
}
- /* Configure the enable interrupt bits for the NVIC */
- nvic_irq_enable(exti_channels[channel].irq_line);
-
/* Register the handler */
exti_channels[channel].handler = handler;
+
+ /* Configure the enable interrupt bits for the NVIC */
+ nvic_irq_enable(exti_channels[channel].irq_line);
}
diff --git a/libmaple/exti.h b/libmaple/exti.h
index 89cd986..1765045 100644
--- a/libmaple/exti.h
+++ b/libmaple/exti.h
@@ -153,8 +153,9 @@
extern "C"{
#endif
-void exti_attach_interrupt(uint32, uint32, voidFuncPtr, uint32);
-void exti_detach_interrupt(uint32);
+void exti_attach_interrupt(uint32 port, uint32 pin, voidFuncPtr handler,
+ uint32 mode);
+void exti_detach_interrupt(uint32 channel);
#ifdef __cplusplus
} // extern "C"
diff --git a/libmaple/nvic.c b/libmaple/nvic.c
index 5b32d16..ad816ba 100644
--- a/libmaple/nvic.c
+++ b/libmaple/nvic.c
@@ -23,8 +23,6 @@
*****************************************************************************/
/**
- * @file nvic.c
- *
* @brief Nested interrupt controller routines
*/
@@ -41,6 +39,7 @@ void nvic_set_vector_table(uint32 addr, uint32 offset) {
* @param n interrupt number
*/
void nvic_irq_enable(uint32 n) {
+ /* TODO: bit-banding would be faster */
uint32 *iser = &((uint32*)NVIC_ISER0)[(n/32)];
__write(iser, BIT(n % 32));
}
@@ -50,11 +49,16 @@ void nvic_irq_enable(uint32 n) {
* @param n interrupt number
*/
void nvic_irq_disable(uint32 n) {
+ /* TODO: bit-banding would be faster */
uint32 *icer = &((uint32*)NVIC_ICER0)[(n/32)];
__write(icer, BIT(n % 32));
}
void nvic_irq_disable_all(void) {
+ /* TODO why not:
+ __write(NVIC_ICER0, 0);
+ __write(NVIC_ICER1, 0);
+ */
short n;
for(n=0; n<65; n++) {
nvic_irq_disable(n);
@@ -62,8 +66,8 @@ void nvic_irq_disable_all(void) {
}
/**
- * @brief Initialice the NVIC at address addr
- * @param addr Address to set the vector table at
+ * @brief Initialize the NVIC according to VECT_TAB_FLASH,
+ * VECT_TAB_RAM, or VECT_TAB_BASE.
*/
void nvic_init(void) {
#ifdef VECT_TAB_FLASH
diff --git a/libmaple/nvic.h b/libmaple/nvic.h
index e8ca22d..c037a38 100644
--- a/libmaple/nvic.h
+++ b/libmaple/nvic.h
@@ -23,7 +23,8 @@
*****************************************************************************/
/**
- * @brief Nested interrupt controller defines and prototypes
+ * @file nvic.h
+ * @brief Nested interrupt controller defines and prototypes
*/
#ifndef _NVIC_H_
@@ -39,18 +40,17 @@ extern "C"{
/* NVIC Interrupt Enable registers */
#define NVIC_ISER0 0xE000E100
#define NVIC_ISER1 0xE000E104
-#define NVIC_ISER2 0xE000E108
-#define NVIC_ISER3 0xE000E10C // Non existant?
+/* NVIC_ISER2 only on connectivity line */
/* NVIC Interrupt Clear registers */
#define NVIC_ICER0 0xE000E180
#define NVIC_ICER1 0xE000E184
-#define NVIC_ICER2 0xE000E188
-#define NVIC_ICER3 0xE000E18C // Non existant?
+/* NVIC_ICER2 only on connectivity line */
/* System control registers */
#define SCB_VTOR 0xE000ED08 // Vector table offset register
+/* PENDING: aren't these obsolete? they're not used anywhere. */
#define NVIC_VectTab_RAM ((u32)0x20000000)
#define NVIC_VectTab_FLASH ((u32)0x08000000)
@@ -77,6 +77,14 @@ enum {
NVIC_EXTI4 = 10,
NVIC_EXTI9_5 = 23,
NVIC_EXTI15_10 = 40,
+
+ NVIC_DMA_CH1 = 11,
+ NVIC_DMA_CH2 = 12,
+ NVIC_DMA_CH3 = 13,
+ NVIC_DMA_CH4 = 14,
+ NVIC_DMA_CH5 = 15,
+ NVIC_DMA_CH6 = 16,
+ NVIC_DMA_CH7 = 17
};