diff options
| -rw-r--r-- | libmaple/dma.c | 112 | ||||
| -rw-r--r-- | libmaple/dma.h | 57 | ||||
| -rw-r--r-- | libmaple/exti.c | 8 | ||||
| -rw-r--r-- | libmaple/exti.h | 5 | ||||
| -rw-r--r-- | libmaple/nvic.c | 12 | ||||
| -rw-r--r-- | 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  }; | 
