aboutsummaryrefslogtreecommitdiffstats
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
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.
-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
};