From 8e973f3d1ef0324e213824dc05af0f9713e7b3cb Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Wed, 29 Dec 2010 21:30:42 -0500 Subject: 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. --- libmaple/dma.c | 112 ++++++++++++++++++++++++++++++++++++++------------------ libmaple/dma.h | 57 ++++++++++++++++++++++++---- libmaple/exti.c | 8 ++-- libmaple/exti.h | 5 ++- libmaple/nvic.c | 12 ++++-- libmaple/nvic.h | 18 ++++++--- 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 }; -- cgit v1.2.3