aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libmaple/dma.c35
-rw-r--r--libmaple/dma_private.h61
-rw-r--r--libmaple/include/libmaple/dma.h402
-rw-r--r--libmaple/include/libmaple/dma_common.h114
-rw-r--r--libmaple/stm32f1/dma.c381
-rw-r--r--libmaple/stm32f1/include/series/dma.h411
6 files changed, 1038 insertions, 366 deletions
diff --git a/libmaple/dma.c b/libmaple/dma.c
index 6442e4d..d13de10 100644
--- a/libmaple/dma.c
+++ b/libmaple/dma.c
@@ -33,6 +33,8 @@
*/
#include <libmaple/dma.h>
+#include "dma_private.h"
+#include "stm32_private.h"
/*
* Convenience routines
@@ -45,3 +47,36 @@
void dma_init(dma_dev *dev) {
rcc_clk_enable(dev->clk_id);
}
+
+/*
+ * Private API
+ */
+
+enum dma_atype _dma_addr_type(__io void *addr) {
+ switch (stm32_block_purpose((void*)addr)) {
+ /* Notice we're treating the code block as memory here. That's
+ * correct for addresses in Flash and in [0x0, 0x7FFFFFF]
+ * (provided that those addresses are aliased to Flash, SRAM, or
+ * FSMC, depending on BOOT[01] and possibly SYSCFG_MEMRMP). It's
+ * not correct for other addresses in the code block, but those
+ * will (hopefully) just fail-fast with transfer or bus errors. If
+ * lots of people get confused, it might be worth being more
+ * careful here. */
+ case STM32_BLOCK_CODE: /* Fall through */
+ case STM32_BLOCK_SRAM: /* ... */
+ case STM32_BLOCK_FSMC_1_2: /* ... */
+ case STM32_BLOCK_FSMC_3_4:
+ return DMA_ATYPE_MEM;
+ case STM32_BLOCK_PERIPH:
+ return DMA_ATYPE_PER;
+ case STM32_BLOCK_FSMC_REG: /* Fall through */
+ /* Is this right? I can't think of a reason to DMA into or out
+ * of the FSMC registers. [mbolivar] */
+ case STM32_BLOCK_UNUSED: /* ... */
+ case STM32_BLOCK_CORTEX_INTERNAL: /* ... */
+ return DMA_ATYPE_OTHER;
+ default:
+ ASSERT(0); /* Can't happen */
+ return DMA_ATYPE_OTHER;
+ }
+}
diff --git a/libmaple/dma_private.h b/libmaple/dma_private.h
new file mode 100644
index 0000000..b25ded2
--- /dev/null
+++ b/libmaple/dma_private.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2012 LeafLabs, LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+*****************************************************************************/
+
+#ifndef _LIBMAPLE_DMA_PRIVATE_H_
+#define _LIBMAPLE_DMA_PRIVATE_H_
+
+#include <libmaple/dma.h>
+#include <libmaple/libmaple_types.h>
+
+/*
+ * IRQ handling
+ */
+
+/* Wrap this in an ifdef to shut up GCC. (We provide DMA_GET_HANDLER
+ * in the series support files, which need dma_irq_handler().) */
+#ifdef DMA_GET_HANDLER
+static __always_inline void dma_irq_handler(dma_dev *dev, dma_tube tube) {
+ void (*handler)(void) = DMA_GET_HANDLER(dev, tube);
+ if (handler) {
+ handler();
+ dma_clear_isr_bits(dev, tube); /* in case handler doesn't */
+ }
+}
+#endif
+
+/*
+ * Conveniences for dealing with tube sources/destinations
+ */
+
+enum dma_atype {
+ DMA_ATYPE_MEM,
+ DMA_ATYPE_PER,
+ DMA_ATYPE_OTHER,
+};
+
+enum dma_atype _dma_addr_type(__io void *addr);
+
+#endif
diff --git a/libmaple/include/libmaple/dma.h b/libmaple/include/libmaple/dma.h
index 0aed572..0ef495a 100644
--- a/libmaple/include/libmaple/dma.h
+++ b/libmaple/include/libmaple/dma.h
@@ -42,49 +42,399 @@ extern "C"{
/* <series/dma.h> provides:
*
+ * - An opaque dma_tube type, and predefined rvalues for each tube
+ * supported by the series.
+ *
+ * A "DMA tube" is a series-specific (hopefully integer) datatype
+ * that abstracts the conduit through which DMA-ed data flow.
+ *
+ * Examples: On STM32F1, dma_tube is just an alias for dma_channel,
+ * and the tube values are just DMA_CH1 (=1), DMA_CH2 (=2), etc.
+ *
+ * Note that a dma_tube doesn't have to be an enum, and its values
+ * don't have to be integral. They _do_ need to be cheap to pass as
+ * arguments, though.
+ *
+ * - struct dma_tube_reg_map (and typedef to dma_tube_reg_map). DMA
+ * register maps tend to be split into global registers and per-tube
+ * registers. It's convenient to pass around pointers to a tube's
+ * registers, since that makes it possible to configure or otherwise
+ * mess with a tube without knowing which one you're dealing with.
+ *
+ * - Base pointers to the various dma_tube_reg_maps.
+ *
+ * Examples: On STM32F1, these are DMAxCHy_BASE. You can access
+ * registers like DMAxCHy_BASE->CPAR, etc.
+ *
+ * - enum dma_request_src (and typedef to dma_request_src). This
+ * specifies the peripheral DMA request sources (e.g. USART TX DMA
+ * requests, etc.).
+ *
+ * - enum dma_mode_flags (and typedef to dma_mode_flags). Used in
+ * dma_tube_config. If two series both support the same mode flags,
+ * they must use the same enumerator names for those flags (the
+ * values of those enumerators are of course allowed to differ).
+ *
* - Normal stuff: dma_reg_map and base pointers, register bit
* definitions, dma_dev pointer declarations, and any other
- * convenience functions useful for that series.
- */
+ * convenience functions useful for the series. */
#include <series/dma.h>
-
+/* <libmaple/dma_common.h> buys us dma_dev and other necessities. */
+#include <libmaple/dma_common.h>
#include <libmaple/libmaple_types.h>
-#include <libmaple/nvic.h>
-#include <libmaple/rcc.h>
/*
- * Devices
+ * Declarations/documentation for some of the series-provided types.
+ */
+
+/**
+ * @brief (Series-dependent) DMA request sources.
+ *
+ * These specify the various pieces of peripheral functionality which
+ * may make DMA requests. Use them to set up a DMA transfer (see
+ * struct dma_tube_config, dma_tube_cfg()).
*/
+enum dma_request_src;
-/* Encapsulates state related to user interrupt handlers. You
- * shouldn't touch these directly; use dma_attach_interrupt() and
- * dma_detach_interupt() instead. */
-typedef struct dma_handler_config {
- void (*handler)(void); /* User handler */
- nvic_irq_num irq_line; /* IRQ line for interrupt */
-} dma_handler_config;
+/**
+ * @brief (Series-dependent) DMA tube configuration flags.
+ * These specify miscellaneous bits of configuration for a DMA tube.
+ * @see struct dma_mode_config
+ */
+enum dma_cfg_flags;
-/** DMA device type */
-typedef struct dma_dev {
- dma_reg_map *regs; /**< Register map */
- rcc_clk_id clk_id; /**< Clock ID */
- struct dma_handler_config handlers[]; /**< For internal use */
-} dma_dev;
+/**
+ * @brief (Series-dependent) DMA tube register map type.
+ * This allows you to access a tube's registers as a group.
+ * @see dma_tube_regs()
+ */
+struct dma_tube_reg_map;
/*
* Convenience functions
*/
+/* Initialization */
+
void dma_init(dma_dev *dev);
-/*
- * Hack: This is here so the series header can declare it and access
- * dma_dev->regs without knowing the structure of dma_dev. Don't use
- * it outside of a series header.
+/* dma_tube configuration
+ *
+ * Use these types and functions to set up DMA transfers, handle
+ * interrupts, etc. The main function of interest is dma_tube_cfg(),
+ * which the various series implement separately. */
+
+/**
+ * @brief Specifies a DMA tube configuration.
+ *
+ * Use one of these to set up a DMA transfer by passing it to
+ * dma_tube_cfg().
+ *
+ * @see dma_tube_cfg()
+ * @see dma_xfer_size
+ */
+typedef struct dma_tube_config {
+ /** Source of data */
+ __io void *tube_src;
+ /** Source transfer size */
+ dma_xfer_size tube_src_size;
+
+ /** Destination of data */
+ __io void *tube_dst;
+ /** Destination transfer size */
+ dma_xfer_size tube_dst_size;
+
+ /**
+ * Number of data to transfer (0 to 65,535).
+ *
+ * Note that this is NOT measured in bytes; it's measured in
+ * number of data, which occur in multiples of tube_src_size. For
+ * example, if tube_src_size is DMA_SIZE_32BITS and tube_nr_xfers
+ * is 2, then 8 total bytes will be transferred.
+ */
+ unsigned tube_nr_xfers;
+
+ /**
+ * Target-specific configuration flags.
+ *
+ * These are an OR of series-specific enum dma_mode_flags values.
+ * Consult the documentation for your target for what flags you
+ * can use here.
+ *
+ * Typical flag examples: DMA_CFG_SRC_INC, DMA_CFG_DST_INC,
+ * DMA_CFG_CIRC, DMA_CFG_CMPLT_IE, etc.
+ */
+ unsigned tube_flags;
+
+ /**
+ * Currently unused. You must set this to 0 or something valid for
+ * your target. */
+ void *target_data;
+
+ /**
+ * Hardware DMA request source.
+ *
+ * This is ignored for memory-to-memory transfers.
+ */
+ enum dma_request_src tube_req_src;
+} dma_tube_config;
+
+#define DMA_TUBE_CFG_SUCCESS 0
+#define DMA_TUBE_CFG_EREQ 1
+#define DMA_TUBE_CFG_ENDATA 2
+#define DMA_TUBE_CFG_EDEV 3
+#define DMA_TUBE_CFG_ESRC 4
+#define DMA_TUBE_CFG_EDST 5
+#define DMA_TUBE_CFG_EDIR 6
+#define DMA_TUBE_CFG_ESIZE 7
+#define DMA_TUBE_CFG_ECFG 0xFF
+/**
+ * @brief Configure a DMA tube.
+ *
+ * Use this function to set up a DMA transfer. The tube will be
+ * disabled before being reconfigured. The transfer will have low
+ * priority by default. You can choose another priority before the
+ * transfer begins using dma_set_priority(). You can manage your
+ * interrupt handlers for the tube using dma_attach_interrupt() and
+ * dma_detach_interrupt().
+ *
+ * After calling dma_tube_cfg() and performing any other desired
+ * configuration, start the transfer using dma_enable().
+ *
+ * @param dev DMA device.
+ * @param tube DMA tube to configure.
+ * @param cfg Configuration to apply to tube.
+ *
+ * @return DMA_TUBE_CFG_SUCCESS (0) on success, <0 on failure. On
+ * failure, returned value will be the opposite (-) of one of:
+ *
+ * - DMA_TUBE_CFG_EREQ: tube doesn't work with cfg->tube_req_src
+ * - DMA_TUBE_CFG_ENDATA: cfg->tube_[src,dst]_size are
+ * incompatible with cfg->tube_nr_xfers, or cfg->tube_nr_xfers
+ * is out of bounds.
+ * - DMA_TUBE_CFG_EDEV: dev does not support cfg
+ * - DMA_TUBE_CFG_ESRC: bad cfg->tube_src
+ * - DMA_TUBE_CFG_EDST: bad cfg->tube_dst
+ * - DMA_TUBE_CFG_EDIR: dev can't transfer from cfg->tube_src to
+ * cfg->tube_dst
+ * - DMA_TUBE_CFG_ESIZE: something ended up wrong due to MSIZE/PSIZE
+ * - DMA_TUBE_CFG_ECFG: generic "something's wrong"
+ *
+ * @sideeffect Disables tube. May alter tube's registers even when an
+ * error occurs.
+ * @see struct dma_tube_config
+ * @see dma_attach_interrupt()
+ * @see dma_detach_interrupt()
+ * @see dma_enable()
+ */
+extern int dma_tube_cfg(dma_dev *dev, dma_tube tube, dma_tube_config *cfg);
+
+/* Other tube configuration functions. You can use these if
+ * dma_tube_cfg() isn't enough, or to adjust parts of an existing tube
+ * configuration. */
+
+/** DMA transfer priority. */
+typedef enum dma_priority {
+ DMA_PRIORITY_LOW = 0, /**< Low priority */
+ DMA_PRIORITY_MEDIUM = 1, /**< Medium priority */
+ DMA_PRIORITY_HIGH = 2, /**< High priority */
+ DMA_PRIORITY_VERY_HIGH = 3, /**< Very high priority */
+} dma_priority;
+
+/**
+ * @brief Set the priority of a DMA transfer.
+ *
+ * You may not call this function while the tube is enabled.
+ *
+ * @param dev DMA device
+ * @param tube DMA tube
+ * @param priority priority to set.
+ */
+extern void dma_set_priority(dma_dev *dev, dma_tube tube,
+ dma_priority priority);
+
+/**
+ * @brief Set the number of data transfers on a DMA tube.
+ *
+ * You may not call this function while the tube is enabled.
+ *
+ * @param dev DMA device
+ * @param tube Tube through which the transfer will occur.
+ * @param num_transfers Number of DMA transactions to set.
+ */
+extern void dma_set_num_transfers(dma_dev *dev, dma_tube tube,
+ uint16 num_transfers);
+
+/**
+ * @brief Set the base memory address where data will be read from or
+ * written to.
+ *
+ * You must not call this function while the tube 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 tube Tube whose base memory address to set.
+ * @param address Memory base address to use.
+ */
+extern void dma_set_mem_addr(dma_dev *dev, dma_tube tube, __io void *address);
+
+/**
+ * @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 tube Tube whose peripheral data register base address to set.
+ * @param addr Peripheral memory base address to use.
+ */
+extern void dma_set_per_addr(dma_dev *dev, dma_tube tube, __io void *address);
+
+/* Interrupt handling */
+
+/**
+ * @brief Attach an interrupt to a DMA transfer.
+ *
+ * Interrupts are enabled using series-specific mode flags in
+ * dma_tube_cfg().
+ *
+ * @param dev DMA device
+ * @param tube Tube to attach handler to
+ * @param handler Interrupt handler to call when tube interrupt fires.
+ * @see dma_tube_cfg()
+ * @see dma_get_irq_cause()
+ * @see dma_detach_interrupt()
+ */
+extern void dma_attach_interrupt(dma_dev *dev, dma_tube tube,
+ void (*handler)(void));
+
+
+/**
+ * @brief Detach a DMA transfer interrupt handler.
+ *
+ * After calling this function, the given tube's interrupts will be
+ * disabled.
+ *
+ * @param dev DMA device
+ * @param tube Tube whose handler to detach
+ * @sideeffect Clears the tube's interrupt enable bits.
+ * @see dma_attach_interrupt()
+ */
+extern void dma_detach_interrupt(dma_dev *dev, dma_tube tube);
+
+/* Tube enable/disable */
+
+/**
+ * @brief Enable a DMA tube.
+ *
+ * If the tube has been properly configured, calling this function
+ * allows it to start serving DMA requests.
+ *
+ * @param dev DMA device
+ * @param tube Tube to enable
+ * @see dma_tube_cfg()
+ */
+extern void dma_enable(dma_dev *dev, dma_tube tube);
+
+/**
+ * @brief Disable a DMA channel.
+ *
+ * Calling this function makes the tube stop serving DMA requests.
+ *
+ * @param dev DMA device
+ * @param tube Tube to disable
+ */
+extern void dma_disable(dma_dev *dev, dma_tube tube);
+
+/**
+ * @brief Check if a DMA tube is enabled.
+ * @param dev DMA device.
+ * @param tube Tube to check.
+ * @return 0 if the tube is disabled, >0 if it is enabled.
+ */
+static inline uint8 dma_is_enabled(dma_dev *dev, dma_tube tube);
+
+/* Other conveniences */
+
+/**
+ * @brief Obtain a pointer to an individual DMA tube's registers.
+ *
+ * Examples:
+ *
+ * - On STM32F1, dma_channel_regs(DMA1, DMA_CH1)->CCR is DMA1_BASE->CCR1.
+ *
+ * @param dev DMA device.
+ * @param tube DMA tube whose register map to obtain.
+ * @return (Series-specific) tube register map.
+ */
+static inline dma_tube_reg_map* dma_tube_regs(dma_dev *dev, dma_tube tube);
+
+/**
+ * Encodes the reason why a DMA interrupt was called.
+ * @see dma_get_irq_cause()
+ */
+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_TRANSFER_DME_ERROR, /**<
+ * @brief Direct mode error occurred during
+ * transfer. */
+ DMA_TRANSFER_FIFO_ERROR, /**< FIFO error occurred during transfer. */
+} dma_irq_cause;
+
+/**
+ * @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 tube Tube whose interrupt is being handled.
+ * @return Reason why the interrupt fired.
+ * @sideeffect Clears flags in dev's interrupt status registers.
+ * @see dma_attach_interrupt()
+ * @see dma_irq_cause
+ */
+extern dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_tube tube);
+
+/**
+ * @brief Get the ISR status bits for a DMA channel.
+ *
+ * The bits are returned right-aligned, in the order they appear in
+ * the corresponding ISR register.
+ *
+ * If you're trying to figure out why a DMA interrupt fired, you may
+ * find dma_get_irq_cause() more convenient.
+ *
+ * @param dev DMA device
+ * @param tube Tube whose ISR bits to return.
+ * @see dma_get_irq_cause().
+ */
+static inline uint8 dma_get_isr_bits(dma_dev *dev, dma_tube tube);
+
+/**
+ * @brief Clear the ISR status bits for a given DMA tube.
+ *
+ * If you're trying to clean up after yourself in a DMA interrupt, you
+ * may find dma_get_irq_cause() more convenient.
+ *
+ * @param dev DMA device
+ * @param tube Tube whose ISR bits to clear.
+ * @see dma_get_irq_cause()
*/
-static __always_inline dma_reg_map* _dma_dev_regs(dma_dev *dev) {
- return dev->regs;
-}
+static inline void dma_clear_isr_bits(dma_dev *dev, dma_tube tube);
#ifdef __cplusplus
} // extern "C"
diff --git a/libmaple/include/libmaple/dma_common.h b/libmaple/include/libmaple/dma_common.h
new file mode 100644
index 0000000..67475f7
--- /dev/null
+++ b/libmaple/include/libmaple/dma_common.h
@@ -0,0 +1,114 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2012 LeafLabs, LLC.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+
+/**
+ * @file libmaple/include/libmaple/dma_common.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>
+ * @brief Common DMA sub-header for <series/dma.h> and <libmaple/dma.h>.
+ *
+ * WARNING: CONTENTS UNSTABLE
+ *
+ * The existence of this file is an implementation detail. Its
+ * contents are not stable, so never include it directly. If you need
+ * something from here, #include <libmaple/dma.h> instead.
+ */
+
+/*
+ * There's a fair amount of common DMA functionality needed by each
+ * <series/dma.h> and <libmaple/dma.h>. This header exists in order
+ * to provide it to both, avoiding some hacks and circular
+ * dependencies.
+ */
+
+#ifndef _LIBMAPLE_DMA_COMMON_H_
+#define _LIBMAPLE_DMA_COMMON_H_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include <libmaple/libmaple_types.h>
+#include <libmaple/nvic.h>
+#include <libmaple/rcc.h>
+
+/*
+ * Devices
+ */
+
+struct dma_reg_map;
+
+/* Encapsulates state related to user interrupt handlers. You
+ * shouldn't touch these directly; use dma_attach_interrupt() and
+ * dma_detach_interupt() instead. */
+typedef struct dma_handler_config {
+ void (*handler)(void); /* User handler */
+ nvic_irq_num irq_line; /* IRQ line for interrupt */
+} dma_handler_config;
+
+/** DMA device type */
+typedef struct dma_dev {
+ struct dma_reg_map *regs; /**< Register map */
+ rcc_clk_id clk_id; /**< Clock ID */
+ struct dma_handler_config handlers[]; /**< For internal use */
+} dma_dev;
+
+/**
+ * @brief DMA channels
+ *
+ * Notes:
+ * - This is also the dma_tube type for STM32F1.
+ * - Channel 0 is not available on all STM32 series.
+ *
+ * @see dma_tube
+ */
+typedef enum dma_channel {
+ DMA_CH0 = 0, /**< Channel 0 */
+ 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;
+
+/**
+ * @brief Source and destination transfer sizes.
+ * Use these when initializing a struct dma_tube_config.
+ * @see struct dma_tube_config
+ * @see dma_tube_cfg
+ */
+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;
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
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"