diff options
Diffstat (limited to 'libmaple/stm32f1')
| -rw-r--r-- | libmaple/stm32f1/dma.c | 381 | ||||
| -rw-r--r-- | libmaple/stm32f1/include/series/dma.h | 411 | 
2 files changed, 452 insertions, 340 deletions
diff --git a/libmaple/stm32f1/dma.c b/libmaple/stm32f1/dma.c index 14ac645..fc502a1 100644 --- a/libmaple/stm32f1/dma.c +++ b/libmaple/stm32f1/dma.c @@ -35,6 +35,10 @@  #include <libmaple/dma.h>  #include <libmaple/bitband.h> +/* Hack to ensure inlining in dma_irq_handler() */ +#define DMA_GET_HANDLER(dev, tube) (dev->handlers[tube - 1].handler) +#include "dma_private.h" +  /*   * Devices   */ @@ -50,7 +54,7 @@ static dma_dev dma1 = {                   { .handler = NULL, .irq_line = NVIC_DMA_CH6 },                   { .handler = NULL, .irq_line = NVIC_DMA_CH7 }},  }; -/** DMA1 device */ +/** STM32F1 DMA1 device */  dma_dev *DMA1 = &dma1;  #if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) @@ -63,83 +67,95 @@ static dma_dev dma2 = {                   { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 },                   { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }}, /* !@#$ */  }; -/** DMA2 device */ +/** STM32F1 DMA2 device */  dma_dev *DMA2 = &dma2;  #endif  /* - * Routines + * Auxiliary routines   */ -/** - * @brief Set up a DMA transfer. - * - * The channel will be disabled before being reconfigured.  The - * transfer will have low priority by default.  You may choose another - * priority before the transfer begins using dma_set_priority(), as - * well as performing any other configuration you desire.  When the - * channel is configured to your liking, enable it using dma_enable(). - * - * @param dev DMA device. - * @param channel DMA channel. - * @param peripheral_address Base address of peripheral data register - *                           involved in the transfer. - * @param peripheral_size Peripheral data transfer size. - * @param memory_address Base memory address involved in the transfer. - * @param memory_size Memory data transfer size. - * @param mode Logical OR of dma_mode_flags - * @sideeffect Disables the given DMA channel. - * @see dma_xfer_size - * @see dma_mode_flags - * @see dma_set_num_transfers() - * @see dma_set_priority() - * @see dma_attach_interrupt() - * @see dma_enable() - */ -void dma_setup_transfer(dma_dev       *dev, -                        dma_channel    channel, -                        __io void     *peripheral_address, -                        dma_xfer_size  peripheral_size, -                        __io void     *memory_address, -                        dma_xfer_size  memory_size, -                        uint32         mode) { -    dma_channel_reg_map *channel_regs = dma_channel_regs(dev, channel); +/* Can channel serve cfg->tube_req_src? */ +static int cfg_req_ok(dma_channel channel, dma_tube_config *cfg) { +    return (cfg->tube_req_src & 0x7) == channel; +} -    dma_disable(dev, channel);  /* can't write to CMAR/CPAR otherwise */ -    channel_regs->CCR = (memory_size << 10) | (peripheral_size << 8) | mode; -    channel_regs->CMAR = (uint32)memory_address; -    channel_regs->CPAR = (uint32)peripheral_address; +/* Can dev serve cfg->tube_req_src? */ +static int cfg_dev_ok(dma_dev *dev, dma_tube_config *cfg) { +    return (rcc_clk_id)(cfg->tube_req_src >> 3) == dev->clk_id;  } -/** - * @brief Set the number of data to be transferred on a DMA channel. - * - * You may not call this function while the channel is enabled. - * - * @param dev DMA device - * @param channel Channel through which the transfer occurs. - * @param num_transfers - */ -void dma_set_num_transfers(dma_dev *dev, -                           dma_channel channel, -                           uint16 num_transfers) { -    dma_channel_reg_map *channel_regs; +/* Is addr acceptable for use as DMA src/dst? */ +static int cfg_mem_ok(__io void *addr) { +    enum dma_atype atype = _dma_addr_type(addr); +    return atype == DMA_ATYPE_MEM || atype == DMA_ATYPE_PER; +} -    ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); +/* Is the direction implied by src->dst supported? */ +static int cfg_dir_ok(dma_tube_config *cfg) { +    /* We can't do peripheral->peripheral transfers. */ +    return ((_dma_addr_type(cfg->tube_src) == DMA_ATYPE_MEM) || +            (_dma_addr_type(cfg->tube_dst) == DMA_ATYPE_MEM)); +} -    channel_regs = dma_channel_regs(dev, channel); -    channel_regs->CNDTR = num_transfers; +static int preconfig_check(dma_dev *dev, dma_channel channel, +                           dma_tube_config *cfg) { +    if (!cfg_req_ok(channel, cfg)) { +        return -DMA_TUBE_CFG_EREQ; +    } +    if (cfg->tube_nr_xfers > 65535) { +        return -DMA_TUBE_CFG_ENDATA; +    } +    if (!cfg_dev_ok(dev, cfg)) { +        return -DMA_TUBE_CFG_EDEV; +    } +    if (!cfg_mem_ok(cfg->tube_src)) { +        return -DMA_TUBE_CFG_ESRC; +    } +    if (!cfg_mem_ok(cfg->tube_dst)) { +        return -DMA_TUBE_CFG_EDST; +    } +    if (!cfg_dir_ok(cfg)) { +        return -DMA_TUBE_CFG_EDIR; +    } +    return DMA_TUBE_CFG_SUCCESS;  } -/** - * @brief Set the priority of a DMA transfer. - * - * You may not call this function while the channel is enabled. - * - * @param dev DMA device - * @param channel DMA channel - * @param priority priority to set. +/* Configure chregs according to cfg, where cfg->tube_dst is peripheral. */ +static int config_to_per(dma_tube_reg_map *chregs, dma_tube_config *cfg) { +    return -DMA_TUBE_CFG_ECFG;    /* FIXME implement */ +} + +/* Configure chregs according to cfg, where cfg->tube_dst is memory. */ +static int config_to_mem(dma_tube_reg_map *chregs, dma_tube_config *cfg) { +    return -DMA_TUBE_CFG_ECFG;    /* FIXME implement */ +} + +/* + * Routines   */ + +int dma_tube_cfg(dma_dev *dev, dma_channel channel, dma_tube_config *cfg) { +    dma_tube_reg_map *chregs; +    int ret = preconfig_check(dev, channel, cfg); + +    if (ret < 0) { +        return ret; +    } + +    chregs = dma_tube_regs(dev, channel); +    switch (_dma_addr_type(cfg->tube_dst)) { +    case DMA_ATYPE_PER: +        return config_to_per(chregs, cfg); +    case DMA_ATYPE_MEM: +        return config_to_mem(chregs, cfg); +    default: +        /* Can't happen */ +        ASSERT(0); +        return -DMA_TUBE_CFG_ECFG; +    } +} +  void dma_set_priority(dma_dev *dev,                        dma_channel channel,                        dma_priority priority) { @@ -151,134 +167,77 @@ void dma_set_priority(dma_dev *dev,      channel_regs = dma_channel_regs(dev, channel);      ccr = channel_regs->CCR;      ccr &= ~DMA_CCR_PL; -    ccr |= priority; +    ccr |= (priority << 12);      channel_regs->CCR = ccr;  } -/** - * @brief Attach an interrupt to a DMA transfer. - * - * Interrupts are enabled using appropriate mode flags in - * dma_setup_transfer(). - * - * @param dev DMA device - * @param channel Channel to attach handler to - * @param handler Interrupt handler to call when channel interrupt fires. - * @see dma_setup_transfer() - * @see dma_get_irq_cause() - * @see dma_detach_interrupt() - */ -void dma_attach_interrupt(dma_dev *dev, -                          dma_channel channel, +void dma_set_num_transfers(dma_dev *dev, +                           dma_channel channel, +                           uint16 num_transfers) { +    dma_channel_reg_map *channel_regs; + +    ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); + +    channel_regs = dma_channel_regs(dev, channel); +    channel_regs->CNDTR = num_transfers; +} + +void dma_attach_interrupt(dma_dev *dev, dma_channel channel,                            void (*handler)(void)) { -    dev->handlers[channel - 1].handler = handler; +    DMA_GET_HANDLER(dev, channel) = handler;      nvic_irq_enable(dev->handlers[channel - 1].irq_line);  } -/** - * @brief Detach a DMA transfer interrupt handler. - * - * After calling this function, the given channel's interrupts will be - * disabled. - * - * @param dev DMA device - * @param channel Channel whose handler to detach - * @sideeffect Clears interrupt enable bits in the channel's CCR register. - * @see dma_attach_interrupt() - */  void dma_detach_interrupt(dma_dev *dev, dma_channel channel) {      /* Don't use nvic_irq_disable()! Think about DMA2 channels 4 and 5. */      dma_channel_regs(dev, channel)->CCR &= ~0xF; -    dev->handlers[channel - 1].handler = NULL; +    DMA_GET_HANDLER(dev, channel) = NULL; +} + +void dma_enable(dma_dev *dev, dma_channel channel) { +    dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel); +    bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1); +} + +void dma_disable(dma_dev *dev, dma_channel channel) { +    dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel); +    bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0);  } -/** - * @brief Discover the reason why a DMA interrupt was called. - * - * You may only call this function within an attached interrupt - * handler for the given channel. - * - * This function resets the internal DMA register state which encodes - * the cause of the interrupt; consequently, it can only be called - * once per interrupt handler invocation. - * - * @param dev DMA device - * @param channel Channel whose interrupt is being handled. - * @return Reason why the interrupt fired. - * @sideeffect Clears channel status flags in dev->regs->ISR. - * @see dma_attach_interrupt() - * @see dma_irq_cause - */  dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel) { +    /* Grab and clear the ISR bits. */      uint8 status_bits = dma_get_isr_bits(dev, channel); +    dma_clear_isr_bits(dev, channel);      /* If the channel global interrupt flag is cleared, then       * something's very wrong. */ -    ASSERT(status_bits & BIT(0)); - -    dma_clear_isr_bits(dev, channel); +    ASSERT(status_bits & 0x1); +    /* If GIF is set, then some other flag should be set, barring +     * something unexpected (e.g. the user making an unforeseen IFCR +     * write). */ +    ASSERT(status_bits != 0x1);      /* ISR flags get set even if the corresponding interrupt enable       * bits in the channel's configuration register are cleared, so we       * can't use a switch here.       *       * Don't change the order of these if statements. */ -    if (status_bits & BIT(3)) { +    if (status_bits & 0x8) {          return DMA_TRANSFER_ERROR; -    } else if (status_bits & BIT(1)) { +    } else if (status_bits & 0x2) {          return DMA_TRANSFER_COMPLETE; -    } else if (status_bits & BIT(2)) { +    } else if (status_bits & 0x4) {          return DMA_TRANSFER_HALF_COMPLETE; -    } else if (status_bits & BIT(0)) { -        /* Shouldn't happen (unless someone messed up an IFCR write). */ -        throb(); -    } -#if DEBUG_LEVEL < DEBUG_ALL -    else { -        /* We shouldn't have been called, but the debug level is too -         * low for the above ASSERT() to have had any effect.  In -         * order to fail fast, mimic the DMA controller's behavior -         * when an error occurs. */ -        dma_disable(dev, channel);      } -#endif -    return DMA_TRANSFER_ERROR; -} -/** - * @brief Enable a DMA channel. - * @param dev DMA device - * @param channel Channel to enable - */ -void dma_enable(dma_dev *dev, dma_channel channel) { -    dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel); -    bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1); -} - -/** - * @brief Disable a DMA channel. - * @param dev DMA device - * @param channel Channel to disable - */ -void dma_disable(dma_dev *dev, dma_channel channel) { -    dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel); -    bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0); +    /* If we get here, one of our assumptions has been violated, but +     * the debug level is too low for the above ASSERTs() to have had +     * any effect. In order to fail fast, mimic the DMA controller's +     * behavior when an error occurs. */ +    dma_disable(dev, channel); +    return DMA_TRANSFER_ERROR;  } -/** - * @brief Set the base memory address where data will be read from or - *        written to. - * - * You must not call this function while the channel is enabled. - * - * If the DMA memory size is 16 bits, the address is automatically - * aligned to a half-word.  If the DMA memory size is 32 bits, the - * address is aligned to a word. - * - * @param dev DMA Device - * @param channel Channel whose base memory address to set. - * @param addr Memory base address to use. - */  void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *addr) {      dma_channel_reg_map *chan_regs; @@ -288,20 +247,6 @@ void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *addr) {      chan_regs->CMAR = (uint32)addr;  } -/** - * @brief Set the base peripheral address where data will be read from - *        or written to. - * - * You must not call this function while the channel is enabled. - * - * If the DMA peripheral size is 16 bits, the address is automatically - * aligned to a half-word.  If the DMA peripheral size is 32 bits, the - * address is aligned to a word. - * - * @param dev DMA Device - * @param channel Channel whose peripheral data register base address to set. - * @param addr Peripheral memory base address to use. - */  void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) {      dma_channel_reg_map *chan_regs; @@ -311,61 +256,103 @@ void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) {      chan_regs->CPAR = (uint32)addr;  } -/* - * IRQ handlers +/** + * @brief Deprecated. Use dma_tube_cfg() instead. + * + * Set up a DMA transfer. + * + * The channel will be disabled before being reconfigured.  The + * transfer will have low priority by default.  You may choose another + * priority before the transfer begins using dma_set_priority(), as + * well as performing any other configuration you desire.  When the + * channel is configured to your liking, enable it using dma_enable(). + * + * @param dev DMA device. + * @param channel DMA channel. + * @param peripheral_address Base address of peripheral data register + *                           involved in the transfer. + * @param peripheral_size Peripheral data transfer size. + * @param memory_address Base memory address involved in the transfer. + * @param memory_size Memory data transfer size. + * @param mode Logical OR of dma_mode_flags + * + * @see dma_tube_cfg() + * + * @sideeffect Disables the given DMA channel. + * @see dma_xfer_size + * @see dma_mode_flags + * @see dma_set_num_transfers() + * @see dma_set_priority() + * @see dma_attach_interrupt() + * @see dma_enable()   */ +__deprecated +void dma_setup_transfer(dma_dev       *dev, +                        dma_channel    channel, +                        __io void     *peripheral_address, +                        dma_xfer_size  peripheral_size, +                        __io void     *memory_address, +                        dma_xfer_size  memory_size, +                        uint32         mode) { +    dma_channel_reg_map *channel_regs = dma_channel_regs(dev, channel); -static __always_inline void dispatch_handler(dma_dev *dev, int channel) { -    void (*handler)(void) = dev->handlers[channel - 1].handler; -    if (handler) { -        handler(); -        dma_clear_isr_bits(dev, channel); /* in case handler doesn't */ -    } +    dma_disable(dev, channel);  /* can't write to CMAR/CPAR otherwise */ +    channel_regs->CCR = (memory_size << 10) | (peripheral_size << 8) | mode; +    channel_regs->CMAR = (uint32)memory_address; +    channel_regs->CPAR = (uint32)peripheral_address;  } +/* + * IRQ handlers + */ +  void __irq_dma1_channel1(void) { -    dispatch_handler(DMA1, DMA_CH1); +    dma_irq_handler(DMA1, DMA_CH1);  }  void __irq_dma1_channel2(void) { -    dispatch_handler(DMA1, DMA_CH2); +    dma_irq_handler(DMA1, DMA_CH2);  }  void __irq_dma1_channel3(void) { -    dispatch_handler(DMA1, DMA_CH3); +    dma_irq_handler(DMA1, DMA_CH3);  }  void __irq_dma1_channel4(void) { -    dispatch_handler(DMA1, DMA_CH4); +    dma_irq_handler(DMA1, DMA_CH4);  }  void __irq_dma1_channel5(void) { -    dispatch_handler(DMA1, DMA_CH5); +    dma_irq_handler(DMA1, DMA_CH5);  }  void __irq_dma1_channel6(void) { -    dispatch_handler(DMA1, DMA_CH6); +    dma_irq_handler(DMA1, DMA_CH6);  }  void __irq_dma1_channel7(void) { -    dispatch_handler(DMA1, DMA_CH7); +    dma_irq_handler(DMA1, DMA_CH7);  } -#ifdef STM32_HIGH_DENSITY +#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)  void __irq_dma2_channel1(void) { -    dispatch_handler(DMA2, DMA_CH1); +    dma_irq_handler(DMA2, DMA_CH1);  }  void __irq_dma2_channel2(void) { -    dispatch_handler(DMA2, DMA_CH2); +    dma_irq_handler(DMA2, DMA_CH2);  }  void __irq_dma2_channel3(void) { -    dispatch_handler(DMA2, DMA_CH3); +    dma_irq_handler(DMA2, DMA_CH3);  }  void __irq_dma2_channel4_5(void) { -    dispatch_handler(DMA2, DMA_CH4); -    dispatch_handler(DMA2, DMA_CH5); +    if ((DMA2_BASE->CCR4 & DMA_CCR_EN) && (DMA2_BASE->ISR & DMA_ISR_GIF4)) { +        dma_irq_handler(DMA2, DMA_CH4); +    } +    if ((DMA2_BASE->CCR5 & DMA_CCR_EN) && (DMA2_BASE->ISR & DMA_ISR_GIF5)) { +        dma_irq_handler(DMA2, DMA_CH5); +    }  }  #endif diff --git a/libmaple/stm32f1/include/series/dma.h b/libmaple/stm32f1/include/series/dma.h index 60582ea..6da3246 100644 --- a/libmaple/stm32f1/include/series/dma.h +++ b/libmaple/stm32f1/include/series/dma.h @@ -29,7 +29,7 @@   * @file libmaple/stm32f1/include/series/dma.h   * @author Marti Bolivar <mbolivar@leaflabs.com>;   *         Original implementation by Michael Hope - * @brief STM32F1 Direct Memory Access header + * @brief STM32F1 DMA series header.   */  /* @@ -44,13 +44,14 @@ extern "C"{  #endif  #include <libmaple/libmaple_types.h> +#include <libmaple/dma_common.h>  /* - * Register map and base pointers + * Register maps and base pointers   */  /** - * @brief DMA register map type. + * @brief STM32F1 DMA register map type.   *   * Note that DMA controller 2 (register map base pointer DMA2_BASE)   * only supports channels 1--5. @@ -100,6 +101,44 @@ typedef struct dma_reg_map {  /** DMA controller 2 register map base pointer */  #define DMA2_BASE                       ((struct dma_reg_map*)0x40020400) +/** + * @brief STM32F1 DMA channel (i.e. tube) register map type. + * Provides access to an individual channel's registers. + * @see dma_tube_regs() + */ +typedef struct dma_tube_reg_map { +    __io uint32 CCR;           /**< Channel configuration register */ +    __io uint32 CNDTR;         /**< Channel number of data register */ +    __io uint32 CPAR;          /**< Channel peripheral address register */ +    __io uint32 CMAR;          /**< Channel memory address register */ +} dma_tube_reg_map; + +/** DMA1 channel 1 register map base pointer */ +#define DMA1CH1_BASE                ((struct dma_tube_reg_map*)0x40020008) +/** DMA1 channel 2 register map base pointer */ +#define DMA1CH2_BASE                ((struct dma_tube_reg_map*)0x4002001C) +/** DMA1 channel 3 register map base pointer */ +#define DMA1CH3_BASE                ((struct dma_tube_reg_map*)0x40020030) +/** DMA1 channel 4 register map base pointer */ +#define DMA1CH4_BASE                ((struct dma_tube_reg_map*)0x40020044) +/** DMA1 channel 5 register map base pointer */ +#define DMA1CH5_BASE                ((struct dma_tube_reg_map*)0x40020058) +/** DMA1 channel 6 register map base pointer */ +#define DMA1CH6_BASE                ((struct dma_tube_reg_map*)0x4002006C) +/** DMA1 channel 7 register map base pointer */ +#define DMA1CH7_BASE                ((struct dma_tube_reg_map*)0x40020080) + +/** DMA2 channel 1 register map base pointer */ +#define DMA2CH1_BASE                ((struct dma_tube_reg_map*)0x40020408) +/** DMA2 channel 2 register map base pointer */ +#define DMA2CH2_BASE                ((struct dma_tube_reg_map*)0x4002041C) +/** DMA2 channel 3 register map base pointer */ +#define DMA2CH3_BASE                ((struct dma_tube_reg_map*)0x40020430) +/** DMA2 channel 4 register map base pointer */ +#define DMA2CH4_BASE                ((struct dma_tube_reg_map*)0x40020444) +/** DMA2 channel 5 register map base pointer */ +#define DMA2CH5_BASE                ((struct dma_tube_reg_map*)0x40020458) +  /*   * Register bit definitions   */ @@ -263,173 +302,259 @@ typedef struct dma_reg_map {   * Devices   */ -struct dma_dev; -extern struct dma_dev *DMA1; +extern dma_dev *DMA1;  #if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) -extern struct dma_dev *DMA2; +extern dma_dev *DMA2;  #endif  /* - * Convenience routines. + * Other types needed by, or useful for, <libmaple/dma.h>.   */ -/* This hack is due to a circular dependency between us and - * <libmaple/dma.h>. */ -static __always_inline dma_reg_map* _dma_dev_regs(struct dma_dev*); - -/** Flags for DMA transfer configuration. */ -typedef enum dma_mode_flags { -    DMA_MEM_2_MEM  = 1 << 14, /**< Memory to memory mode */ -    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; - -/** Source and destination transfer sizes. */ -typedef enum dma_xfer_size { -    DMA_SIZE_8BITS  = 0,        /**< 8-bit transfers */ -    DMA_SIZE_16BITS = 1,        /**< 16-bit transfers */ -    DMA_SIZE_32BITS = 2         /**< 32-bit transfers */ -} dma_xfer_size; - -/** DMA channel */ -typedef enum dma_channel { -    DMA_CH1 = 1,                /**< Channel 1 */ -    DMA_CH2 = 2,                /**< Channel 2 */ -    DMA_CH3 = 3,                /**< Channel 3 */ -    DMA_CH4 = 4,                /**< Channel 4 */ -    DMA_CH5 = 5,                /**< Channel 5 */ -    DMA_CH6 = 6,                /**< Channel 6 */ -    DMA_CH7 = 7,                /**< Channel 7 */ -} dma_channel; - -void dma_setup_transfer(struct dma_dev *dev, -                        dma_channel     channel, -                        __io void      *peripheral_address, -                        dma_xfer_size   peripheral_size, -                        __io void      *memory_address, -                        dma_xfer_size   memory_size, -                        uint32          mode); - -void dma_set_num_transfers(struct dma_dev *dev, -                           dma_channel channel, -                           uint16 num_transfers); - -/** DMA transfer priority. */ -typedef enum dma_priority { -    DMA_PRIORITY_LOW       = DMA_CCR_PL_LOW,      /**< Low priority */ -    DMA_PRIORITY_MEDIUM    = DMA_CCR_PL_MEDIUM,   /**< Medium priority */ -    DMA_PRIORITY_HIGH      = DMA_CCR_PL_HIGH,     /**< High priority */ -    DMA_PRIORITY_VERY_HIGH = DMA_CCR_PL_VERY_HIGH /**< Very high priority */ -} dma_priority; - -void dma_set_priority(struct dma_dev *dev, -                      dma_channel channel, -                      dma_priority priority); - -void dma_attach_interrupt(struct dma_dev *dev, -                          dma_channel channel, -                          void (*handler)(void)); -void dma_detach_interrupt(struct dma_dev *dev, dma_channel channel); -  /** - * Encodes the reason why a DMA interrupt was called. - * @see dma_get_irq_cause() + * @brief STM32F1 dma_tube. + * On STM32F1, DMA tubes are just channels.   */ -typedef enum dma_irq_cause { -    DMA_TRANSFER_COMPLETE,      /**< Transfer is complete. */ -    DMA_TRANSFER_HALF_COMPLETE, /**< Transfer is half complete. */ -    DMA_TRANSFER_ERROR,         /**< Error occurred during transfer. */ -} dma_irq_cause; - -dma_irq_cause dma_get_irq_cause(struct dma_dev *dev, dma_channel channel); +#define dma_tube dma_channel -void dma_enable(struct dma_dev *dev, dma_channel channel); -void dma_disable(struct dma_dev *dev, dma_channel channel); - -void dma_set_mem_addr(struct dma_dev *dev, -                      dma_channel channel, -                      __io void *address); -void dma_set_per_addr(struct dma_dev *dev, -                      dma_channel channel, -                      __io void *address); +/** + * @brief On STM32F1, dma_channel_reg_map is an alias for dma_tube_reg_map. + * This is for backwards compatibility. */ +#define dma_channel_reg_map dma_tube_reg_map  /** - * @brief DMA channel register map type. - * - * Provides access to an individual channel's registers. + * @brief STM32F1 configuration flags for dma_tube_config + * @see struct dma_tube_config   */ -typedef struct dma_channel_reg_map { -    __io uint32 CCR;           /**< Channel configuration register */ -    __io uint32 CNDTR;         /**< Channel number of data register */ -    __io uint32 CPAR;          /**< Channel peripheral address register */ -    __io uint32 CMAR;          /**< Channel memory address register */ -} dma_channel_reg_map; - -#define DMA_CHANNEL_NREGS 5 +typedef enum dma_cfg_flags { +    /** +     * Source address increment mode +     * +     * If this flag is set, the source address is incremented (by the +     * source size) after each DMA transfer. +     */ +    DMA_CFG_SRC_INC = 1U << 31, + +    /** +     * Destination address increment mode +     * +     * If this flag is set, the destination address is incremented (by +     * the destination size) after each DMA transfer. +     */ +    DMA_CFG_DST_INC = 1U << 30, + +    /** +     * Circular mode +     * +     * This mode is not available for memory-to-memory transfers. +     */ +    DMA_CFG_CIRC = DMA_CCR_CIRC, + +    /** Transfer complete interrupt enable */ +    DMA_CFG_CMPLT_IE      = DMA_CCR_TCIE, +    /** Transfer half-complete interrupt enable  */ +    DMA_CFG_HALF_CMPLT_IE = DMA_CCR_HTIE, +    /** Transfer error interrupt enable */ +    DMA_CFG_ERR_IE        = DMA_CCR_TEIE, +} dma_cfg_flags;  /** - * @brief Obtain a pointer to an individual DMA channel's registers. + * @brief STM32F1 DMA request sources. + * + * IMPORTANT:   * - * For example, dma_channel_regs(DMA1, DMA_CH1)->CCR is DMA1_BASE->CCR1. + * 1. On STM32F1, each dma_request_src can only be used by a + * particular tube on a particular DMA controller. For example, + * DMA_REQ_SRC_ADC1 belongs to DMA1, tube 1. DMA2 cannot serve + * requests from ADC1, nor can DMA1 tube 2, etc. If you try to use a + * request source with the wrong DMA controller or tube on STM32F1, + * dma_tube_cfg() will fail.   * - * @param dev DMA device - * @param channel DMA channel whose channel register map to obtain. + * 2. In general, a DMA tube can only serve a single request source at + * a time, and on STM32F1, Terrible Super-Bad Things will happen if + * two request sources are active for a single tube. + * + * To make all this easier to sort out, these dma_request_src + * enumerators are grouped by DMA controller and tube. + * + * @see struct dma_tube_config + * @see dma_tube_cfg() + */ +typedef enum dma_request_src { +    /* Each request source encodes the DMA controller and channel it +     * belongs to, for error checking in dma_tube_cfg(). */ + +    /* DMA1 request sources */ + +    /**@{*/ +    /** (DMA1, tube 1) */ +    DMA_REQ_SRC_ADC1      = (RCC_DMA1 << 3) | 1, +    DMA_REQ_SRC_TIM2_CH3  = (RCC_DMA1 << 3) | 1, +    DMA_REQ_SRC_TIM4_CH1  = (RCC_DMA1 << 3) | 1, +    /**@}*/ + +    /**@{*/ +    /** (DMA1, tube 2)*/ +    DMA_REQ_SRC_SPI1_RX   = (RCC_DMA1 << 3) | 2, +    DMA_REQ_SRC_USART3_TX = (RCC_DMA1 << 3) | 2, +    DMA_REQ_SRC_TIM1_CH1  = (RCC_DMA1 << 3) | 2, +    DMA_REQ_SRC_TIM2_UP   = (RCC_DMA1 << 3) | 2, +    DMA_REQ_SRC_TIM3_CH3  = (RCC_DMA1 << 3) | 2, +    /**@}*/ + +    /**@{*/ +    /** (DMA1, tube 3)*/ +    DMA_REQ_SRC_SPI1_TX   = (RCC_DMA1 << 3) | 3, +    DMA_REQ_SRC_USART3_RX = (RCC_DMA1 << 3) | 3, +    DMA_REQ_SRC_TIM1_CH2  = (RCC_DMA1 << 3) | 3, +    DMA_REQ_SRC_TIM3_CH4  = (RCC_DMA1 << 3) | 3, +    DMA_REQ_SRC_TIM3_UP   = (RCC_DMA1 << 3) | 3, +    /**@}*/ + +    /**@{*/ +    /** (DMA1, tube 4)*/ +    DMA_REQ_SRC_SPI2_RX   = (RCC_DMA1 << 3) | 4, +    DMA_REQ_SRC_I2S2_RX   = (RCC_DMA1 << 3) | 4, +    DMA_REQ_SRC_USART1_TX = (RCC_DMA1 << 3) | 4, +    DMA_REQ_SRC_I2C2_TX   = (RCC_DMA1 << 3) | 4, +    DMA_REQ_SRC_TIM1_CH4  = (RCC_DMA1 << 3) | 4, +    DMA_REQ_SRC_TIM1_TRIG = (RCC_DMA1 << 3) | 4, +    DMA_REQ_SRC_TIM1_COM  = (RCC_DMA1 << 3) | 4, +    DMA_REQ_SRC_TIM4_CH2  = (RCC_DMA1 << 3) | 4, +    /**@}*/ + +    /**@{*/ +    /** (DMA1, tube 5)*/ +    DMA_REQ_SRC_SPI2_TX   = (RCC_DMA1 << 3) | 5, +    DMA_REQ_SRC_I2S2_TX   = (RCC_DMA1 << 3) | 5, +    DMA_REQ_SRC_USART1_RX = (RCC_DMA1 << 3) | 5, +    DMA_REQ_SRC_I2C2_RX   = (RCC_DMA1 << 3) | 5, +    DMA_REQ_SRC_TIM1_UP   = (RCC_DMA1 << 3) | 5, +    DMA_REQ_SRC_TIM2_CH1  = (RCC_DMA1 << 3) | 5, +    DMA_REQ_SRC_TIM4_CH3  = (RCC_DMA1 << 3) | 5, +    /**@}*/ + +    /**@{*/ +    /** (DMA1, tube 6)*/ +    DMA_REQ_SRC_USART2_RX = (RCC_DMA1 << 3) | 6, +    DMA_REQ_SRC_I2C1_TX   = (RCC_DMA1 << 3) | 6, +    DMA_REQ_SRC_TIM1_CH3  = (RCC_DMA1 << 3) | 6, +    DMA_REQ_SRC_TIM3_CH1  = (RCC_DMA1 << 3) | 6, +    DMA_REQ_SRC_TIM3_TRIG = (RCC_DMA1 << 3) | 6, +    /**@}*/ + +    /**@{*/ +    /* Tube 7 */ +    DMA_REQ_SRC_USART2_TX = (RCC_DMA1 << 3) | 7, +    DMA_REQ_SRC_I2C1_RX   = (RCC_DMA1 << 3) | 7, +    DMA_REQ_SRC_TIM2_CH2  = (RCC_DMA1 << 3) | 7, +    DMA_REQ_SRC_TIM2_CH4  = (RCC_DMA1 << 3) | 7, +    DMA_REQ_SRC_TIM4_UP   = (RCC_DMA1 << 3) | 7, +    /**@}*/ + +    /* DMA2 request sources */ + +    /**@{*/ +    /** (DMA2, tube 1)*/ +    DMA_REQ_SRC_SPI3_RX   = (RCC_DMA2 << 3) | 1, +    DMA_REQ_SRC_I2S3_RX   = (RCC_DMA2 << 3) | 1, +    DMA_REQ_SRC_TIM5_CH4  = (RCC_DMA2 << 3) | 1, +    DMA_REQ_SRC_TIM5_TRIG = (RCC_DMA2 << 3) | 1, +    /**@}*/ + +    /**@{*/ +    /** (DMA2, tube 2)*/ +    DMA_REQ_SRC_SPI3_TX   = (RCC_DMA2 << 3) | 2, +    DMA_REQ_SRC_I2S3_TX   = (RCC_DMA2 << 3) | 2, +    DMA_REQ_SRC_TIM5_CH3  = (RCC_DMA2 << 3) | 2, +    DMA_REQ_SRC_TIM5_UP   = (RCC_DMA2 << 3) | 2, +    /**@}*/ + +    /**@{*/ +    /** (DMA2, tube 3)*/ +    DMA_REQ_SRC_UART4_RX  = (RCC_DMA2 << 3) | 3, +    DMA_REQ_SRC_TIM6_UP   = (RCC_DMA2 << 3) | 3, +    DMA_REQ_SRC_DAC_CH1   = (RCC_DMA2 << 3) | 3, +    /**@}*/ + +    /**@{*/ +    /** (DMA2, tube 4)*/ +    DMA_REQ_SRC_SDIO      = (RCC_DMA2 << 3) | 4, +    DMA_REQ_SRC_TIM5_CH2  = (RCC_DMA2 << 3) | 4, +    /**@}*/ + +    /**@{*/ +    /** (DMA2, tube 5)*/ +    DMA_REQ_SRC_ADC3      = (RCC_DMA2 << 3) | 5, +    DMA_REQ_SRC_UART4_TX  = (RCC_DMA2 << 3) | 5, +    DMA_REQ_SRC_TIM5_CH1  = (RCC_DMA2 << 3) | 5, +    /**@}*/ +} dma_request_src; + +/* + * Convenience routines.   */ -static inline dma_channel_reg_map* dma_channel_regs(struct dma_dev *dev, -                                                    dma_channel channel) { -    __io uint32 *ccr1 = &_dma_dev_regs(dev)->CCR1; -    return (dma_channel_reg_map*)(ccr1 + DMA_CHANNEL_NREGS * (channel - 1)); -}  /** - * @brief Check if a DMA channel is enabled - * @param dev DMA device - * @param channel Channel whose enabled bit to check. + * @brief On STM32F1, dma_is_channel_enabled() is an alias for + *        dma_is_enabled(). + * This is for backwards compatibility.   */ -static inline uint8 dma_is_channel_enabled(struct dma_dev *dev, -                                           dma_channel channel) { -    return (uint8)(dma_channel_regs(dev, channel)->CCR & DMA_CCR_EN); +#define dma_is_channel_enabled dma_is_enabled + +#define DMA_CHANNEL_NREGS 5     /* accounts for reserved word */ +static inline dma_tube_reg_map* dma_tube_regs(dma_dev *dev, dma_tube tube) { +    __io uint32 *ccr1 = &dev->regs->CCR1; +    return (dma_channel_reg_map*)(ccr1 + DMA_CHANNEL_NREGS * (tube - 1));  }  /** - * @brief Get the ISR status bits for a DMA channel. - * - * The bits are returned right-aligned, in the following order: - * transfer error flag, half-transfer flag, transfer complete flag, - * global interrupt flag. - * - * If you're attempting to figure out why a DMA interrupt fired; you - * may find dma_get_irq_cause() more convenient. - * - * @param dev DMA device - * @param channel Channel whose ISR bits to return. - * @see dma_get_irq_cause(). - */ -static inline uint8 dma_get_isr_bits(struct dma_dev *dev, -                                     dma_channel channel) { -    uint8 shift = (channel - 1) * 4; -    return (_dma_dev_regs(dev)->ISR >> shift) & 0xF; + * @brief On STM32F1, dma_channel_regs() is an alias for dma_tube_regs(). + * This is for backwards compatibility. */ +#define dma_channel_regs(dev, ch) dma_tube_regs(dev, ch) + +static inline uint8 dma_is_enabled(dma_dev *dev, dma_tube tube) { +    return (uint8)(dma_tube_regs(dev, tube)->CCR & DMA_CCR_EN); +} + +static inline uint8 dma_get_isr_bits(dma_dev *dev, dma_tube tube) { +    uint8 shift = (tube - 1) * 4; +    return (dev->regs->ISR >> shift) & 0xF; +} + +static inline void dma_clear_isr_bits(dma_dev *dev, dma_tube tube) { +    dev->regs->IFCR = (1U << (4 * (tube - 1)));  }  /** - * @brief Clear the ISR status bits for a given DMA channel. - * - * If you're attempting to clean up after yourself in a DMA interrupt, - * you may find dma_get_irq_cause() more convenient. - * - * @param dev DMA device - * @param channel Channel whose ISR bits to clear. - * @see dma_get_irq_cause() + * @brief Deprecated + * STM32F1 mode flags for dma_setup_xfer(). Use dma_tube_cfg() instead. + * @see dma_tube_cfg()   */ -static inline void dma_clear_isr_bits(struct dma_dev *dev, -                                      dma_channel channel) { -    _dma_dev_regs(dev)->IFCR = (1U << (4 * (channel - 1))); -} +typedef enum dma_mode_flags { +    DMA_MEM_2_MEM  = 1 << 14, /**< Memory to memory mode */ +    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; + +/* Keep this around for backwards compatibility, but it's deprecated. + * New code should use dma_tube_cfg() instead. + * + * (It's not possible to fully configure a DMA stream on F2 with just + * this information, so this interface is too tied to the F1.) */ +__deprecated +void dma_setup_transfer(dma_dev       *dev, +                        dma_channel    channel, +                        __io void     *peripheral_address, +                        dma_xfer_size  peripheral_size, +                        __io void     *memory_address, +                        dma_xfer_size  memory_size, +                        uint32         mode);  #ifdef __cplusplus  } // extern "C"  | 
