aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2012-04-26 17:33:56 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2012-05-03 14:09:05 -0400
commit1f67ed8f23048c45b0a3deec232b19047966bce7 (patch)
treeda3aec665bda333922c249f5094942ee3da7eb46
parenta81a02257d69454412d4ef062b56a3cc5c758815 (diff)
downloadlibrambutan-1f67ed8f23048c45b0a3deec232b19047966bce7.tar.gz
librambutan-1f67ed8f23048c45b0a3deec232b19047966bce7.zip
stm32f1: Resurrect DMA support. (sets up breaking change)
Breaking change set up: struct dma_handler_config is no longer part of the public API in <libmaple/dma.h>. User code which was touching these was always mistaken; it should be using dma_attach_interrupt() or dma_detach_interrupt() instead. Other than that, just move the nonportable bits in <libmaple/dma.h> and libmaple/dma.c to the appropriate places under libmaple/stm32f1/. (Ouch. This is almost everything.) Patch the (new) STM32F1 <series/dma.h> here and there to make everything compile; this is mostly limited to forward-declaring struct dma_dev and providing a hack _dma_dev_regs() declaration so inline functions in the series header can still access a device's registers. Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
-rw-r--r--libmaple/dma.c338
-rw-r--r--libmaple/include/libmaple/dma.h404
-rw-r--r--libmaple/rules.mk2
-rw-r--r--libmaple/stm32f1/dma.c371
-rw-r--r--libmaple/stm32f1/include/series/dma.h438
-rw-r--r--libmaple/stm32f1/rules.mk1
6 files changed, 836 insertions, 718 deletions
diff --git a/libmaple/dma.c b/libmaple/dma.c
index f20613b..6442e4d 100644
--- a/libmaple/dma.c
+++ b/libmaple/dma.c
@@ -2,6 +2,7 @@
* The MIT License
*
* Copyright (c) 2010 Michael Hope.
+ * Copyright (c) 2012 LeafLabs, LLC.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
@@ -25,47 +26,13 @@
*****************************************************************************/
/**
- * @file dma.c
+ * @file libmaple/dma.c
* @author Marti Bolivar <mbolivar@leaflabs.com>;
* Original implementation by Michael Hope
- * @brief Direct Memory Access peripheral support
+ * @brief Portable DMA routines.
*/
#include <libmaple/dma.h>
-#include <libmaple/bitband.h>
-#include <libmaple/util.h>
-
-/*
- * Devices
- */
-
-static dma_dev dma1 = {
- .regs = DMA1_BASE,
- .clk_id = RCC_DMA1,
- .handlers = {{ .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 }}
-};
-/** DMA1 device */
-dma_dev *DMA1 = &dma1;
-
-#ifdef STM32_HIGH_DENSITY
-static dma_dev dma2 = {
- .regs = DMA2_BASE,
- .clk_id = RCC_DMA2,
- .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_CH1 },
- { .handler = NULL, .irq_line = NVIC_DMA2_CH2 },
- { .handler = NULL, .irq_line = NVIC_DMA2_CH3 },
- { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 },
- { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }} /* !@#$ */
-};
-/** DMA2 device */
-dma_dev *DMA2 = &dma2;
-#endif
/*
* Convenience routines
@@ -78,302 +45,3 @@ dma_dev *DMA2 = &dma2;
void dma_init(dma_dev *dev) {
rcc_clk_enable(dev->clk_id);
}
-
-/**
- * @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);
-
- 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;
-}
-
-/**
- * @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;
-
- ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
-
- channel_regs = dma_channel_regs(dev, channel);
- channel_regs->CNDTR = num_transfers;
-}
-
-/**
- * @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.
- */
-void dma_set_priority(dma_dev *dev,
- dma_channel channel,
- dma_priority priority) {
- dma_channel_reg_map *channel_regs;
- uint32 ccr;
-
- ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
-
- channel_regs = dma_channel_regs(dev, channel);
- ccr = channel_regs->CCR;
- ccr &= ~DMA_CCR_PL;
- ccr |= priority;
- 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 (*handler)(void)) {
- dev->handlers[channel - 1].handler = 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;
-}
-
-/**
- * @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) {
- uint8 status_bits = dma_get_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);
-
- /* 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)) {
- return DMA_TRANSFER_ERROR;
- } else if (status_bits & BIT(1)) {
- return DMA_TRANSFER_COMPLETE;
- } else if (status_bits & BIT(2)) {
- 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);
-}
-
-/**
- * @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;
-
- ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
-
- chan_regs = dma_channel_regs(dev, channel);
- 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;
-
- ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
-
- chan_regs = dma_channel_regs(dev, channel);
- chan_regs->CPAR = (uint32)addr;
-}
-
-/*
- * IRQ handlers
- */
-
-static inline void dispatch_handler(dma_dev *dev, dma_channel channel) {
- void (*handler)(void) = dev->handlers[channel - 1].handler;
- if (handler) {
- handler();
- dma_clear_isr_bits(dev, channel); /* in case handler doesn't */
- }
-}
-
-void __irq_dma1_channel1(void) {
- dispatch_handler(DMA1, DMA_CH1);
-}
-
-void __irq_dma1_channel2(void) {
- dispatch_handler(DMA1, DMA_CH2);
-}
-
-void __irq_dma1_channel3(void) {
- dispatch_handler(DMA1, DMA_CH3);
-}
-
-void __irq_dma1_channel4(void) {
- dispatch_handler(DMA1, DMA_CH4);
-}
-
-void __irq_dma1_channel5(void) {
- dispatch_handler(DMA1, DMA_CH5);
-}
-
-void __irq_dma1_channel6(void) {
- dispatch_handler(DMA1, DMA_CH6);
-}
-
-void __irq_dma1_channel7(void) {
- dispatch_handler(DMA1, DMA_CH7);
-}
-
-#ifdef STM32_HIGH_DENSITY
-void __irq_dma2_channel1(void) {
- dispatch_handler(DMA2, DMA_CH1);
-}
-
-void __irq_dma2_channel2(void) {
- dispatch_handler(DMA2, DMA_CH2);
-}
-
-void __irq_dma2_channel3(void) {
- dispatch_handler(DMA2, DMA_CH3);
-}
-
-void __irq_dma2_channel4_5(void) {
- dispatch_handler(DMA2, DMA_CH4);
- dispatch_handler(DMA2, DMA_CH5);
-}
-#endif
diff --git a/libmaple/include/libmaple/dma.h b/libmaple/include/libmaple/dma.h
index 32e915c..0aed572 100644
--- a/libmaple/include/libmaple/dma.h
+++ b/libmaple/include/libmaple/dma.h
@@ -33,10 +33,6 @@
* @brief Direct Memory Access peripheral support
*/
-/*
- * See /notes/dma.txt for more information.
- */
-
#ifndef _LIBMAPLE_DMA_H_
#define _LIBMAPLE_DMA_H_
@@ -44,406 +40,50 @@
extern "C"{
#endif
-#include <libmaple/libmaple_types.h>
-#include <libmaple/rcc.h>
-#include <libmaple/nvic.h>
-
-/*
- * Register maps
- */
-
-/**
- * @brief DMA register map type.
+/* <series/dma.h> provides:
*
- * Note that DMA controller 2 (register map base pointer DMA2_BASE)
- * only supports channels 1--5.
+ * - Normal stuff: dma_reg_map and base pointers, register bit
+ * definitions, dma_dev pointer declarations, and any other
+ * convenience functions useful for that series.
*/
-typedef struct dma_reg_map {
- __io uint32 ISR; /**< Interrupt status register */
- __io uint32 IFCR; /**< Interrupt flag clear register */
- __io uint32 CCR1; /**< Channel 1 configuration register */
- __io uint32 CNDTR1; /**< Channel 1 number of data register */
- __io uint32 CPAR1; /**< Channel 1 peripheral address register */
- __io uint32 CMAR1; /**< Channel 1 memory address register */
- const uint32 RESERVED1; /**< Reserved. */
- __io uint32 CCR2; /**< Channel 2 configuration register */
- __io uint32 CNDTR2; /**< Channel 2 number of data register */
- __io uint32 CPAR2; /**< Channel 2 peripheral address register */
- __io uint32 CMAR2; /**< Channel 2 memory address register */
- const uint32 RESERVED2; /**< Reserved. */
- __io uint32 CCR3; /**< Channel 3 configuration register */
- __io uint32 CNDTR3; /**< Channel 3 number of data register */
- __io uint32 CPAR3; /**< Channel 3 peripheral address register */
- __io uint32 CMAR3; /**< Channel 3 memory address register */
- const uint32 RESERVED3; /**< Reserved. */
- __io uint32 CCR4; /**< Channel 4 configuration register */
- __io uint32 CNDTR4; /**< Channel 4 number of data register */
- __io uint32 CPAR4; /**< Channel 4 peripheral address register */
- __io uint32 CMAR4; /**< Channel 4 memory address register */
- const uint32 RESERVED4; /**< Reserved. */
- __io uint32 CCR5; /**< Channel 5 configuration register */
- __io uint32 CNDTR5; /**< Channel 5 number of data register */
- __io uint32 CPAR5; /**< Channel 5 peripheral address register */
- __io uint32 CMAR5; /**< Channel 5 memory address register */
- const uint32 RESERVED5; /**< Reserved. */
- __io uint32 CCR6; /**< Channel 6 configuration register */
- __io uint32 CNDTR6; /**< Channel 6 number of data register */
- __io uint32 CPAR6; /**< Channel 6 peripheral address register */
- __io uint32 CMAR6; /**< Channel 6 memory address register */
- const uint32 RESERVED6; /**< Reserved. */
- __io uint32 CCR7; /**< Channel 7 configuration register */
- __io uint32 CNDTR7; /**< Channel 7 number of data register */
- __io uint32 CPAR7; /**< Channel 7 peripheral address register */
- __io uint32 CMAR7; /**< Channel 7 memory address register */
- const uint32 RESERVED7; /**< Reserved. */
-} dma_reg_map;
-
-/** DMA controller 1 register map base pointer */
-#define DMA1_BASE ((struct dma_reg_map*)0x40020000)
-
-#ifdef STM32_HIGH_DENSITY
-/** DMA controller 2 register map base pointer */
-#define DMA2_BASE ((struct dma_reg_map*)0x40020400)
-#endif
-
-/*
- * Register bit definitions
- */
-
-/* Interrupt status register */
+#include <series/dma.h>
-#define DMA_ISR_TEIF7_BIT 27
-#define DMA_ISR_HTIF7_BIT 26
-#define DMA_ISR_TCIF7_BIT 25
-#define DMA_ISR_GIF7_BIT 24
-#define DMA_ISR_TEIF6_BIT 23
-#define DMA_ISR_HTIF6_BIT 22
-#define DMA_ISR_TCIF6_BIT 21
-#define DMA_ISR_GIF6_BIT 20
-#define DMA_ISR_TEIF5_BIT 19
-#define DMA_ISR_HTIF5_BIT 18
-#define DMA_ISR_TCIF5_BIT 17
-#define DMA_ISR_GIF5_BIT 16
-#define DMA_ISR_TEIF4_BIT 15
-#define DMA_ISR_HTIF4_BIT 14
-#define DMA_ISR_TCIF4_BIT 13
-#define DMA_ISR_GIF4_BIT 12
-#define DMA_ISR_TEIF3_BIT 11
-#define DMA_ISR_HTIF3_BIT 10
-#define DMA_ISR_TCIF3_BIT 9
-#define DMA_ISR_GIF3_BIT 8
-#define DMA_ISR_TEIF2_BIT 7
-#define DMA_ISR_HTIF2_BIT 6
-#define DMA_ISR_TCIF2_BIT 5
-#define DMA_ISR_GIF2_BIT 4
-#define DMA_ISR_TEIF1_BIT 3
-#define DMA_ISR_HTIF1_BIT 2
-#define DMA_ISR_TCIF1_BIT 1
-#define DMA_ISR_GIF1_BIT 0
-
-#define DMA_ISR_TEIF7 BIT(DMA_ISR_TEIF7_BIT)
-#define DMA_ISR_HTIF7 BIT(DMA_ISR_HTIF7_BIT)
-#define DMA_ISR_TCIF7 BIT(DMA_ISR_TCIF7_BIT)
-#define DMA_ISR_GIF7 BIT(DMA_ISR_GIF7_BIT)
-#define DMA_ISR_TEIF6 BIT(DMA_ISR_TEIF6_BIT)
-#define DMA_ISR_HTIF6 BIT(DMA_ISR_HTIF6_BIT)
-#define DMA_ISR_TCIF6 BIT(DMA_ISR_TCIF6_BIT)
-#define DMA_ISR_GIF6 BIT(DMA_ISR_GIF6_BIT)
-#define DMA_ISR_TEIF5 BIT(DMA_ISR_TEIF5_BIT)
-#define DMA_ISR_HTIF5 BIT(DMA_ISR_HTIF5_BIT)
-#define DMA_ISR_TCIF5 BIT(DMA_ISR_TCIF5_BIT)
-#define DMA_ISR_GIF5 BIT(DMA_ISR_GIF5_BIT)
-#define DMA_ISR_TEIF4 BIT(DMA_ISR_TEIF4_BIT)
-#define DMA_ISR_HTIF4 BIT(DMA_ISR_HTIF4_BIT)
-#define DMA_ISR_TCIF4 BIT(DMA_ISR_TCIF4_BIT)
-#define DMA_ISR_GIF4 BIT(DMA_ISR_GIF4_BIT)
-#define DMA_ISR_TEIF3 BIT(DMA_ISR_TEIF3_BIT)
-#define DMA_ISR_HTIF3 BIT(DMA_ISR_HTIF3_BIT)
-#define DMA_ISR_TCIF3 BIT(DMA_ISR_TCIF3_BIT)
-#define DMA_ISR_GIF3 BIT(DMA_ISR_GIF3_BIT)
-#define DMA_ISR_TEIF2 BIT(DMA_ISR_TEIF2_BIT)
-#define DMA_ISR_HTIF2 BIT(DMA_ISR_HTIF2_BIT)
-#define DMA_ISR_TCIF2 BIT(DMA_ISR_TCIF2_BIT)
-#define DMA_ISR_GIF2 BIT(DMA_ISR_GIF2_BIT)
-#define DMA_ISR_TEIF1 BIT(DMA_ISR_TEIF1_BIT)
-#define DMA_ISR_HTIF1 BIT(DMA_ISR_HTIF1_BIT)
-#define DMA_ISR_TCIF1 BIT(DMA_ISR_TCIF1_BIT)
-#define DMA_ISR_GIF1 BIT(DMA_ISR_GIF1_BIT)
-
-/* Interrupt flag clear register */
-
-#define DMA_IFCR_CTEIF7_BIT 27
-#define DMA_IFCR_CHTIF7_BIT 26
-#define DMA_IFCR_CTCIF7_BIT 25
-#define DMA_IFCR_CGIF7_BIT 24
-#define DMA_IFCR_CTEIF6_BIT 23
-#define DMA_IFCR_CHTIF6_BIT 22
-#define DMA_IFCR_CTCIF6_BIT 21
-#define DMA_IFCR_CGIF6_BIT 20
-#define DMA_IFCR_CTEIF5_BIT 19
-#define DMA_IFCR_CHTIF5_BIT 18
-#define DMA_IFCR_CTCIF5_BIT 17
-#define DMA_IFCR_CGIF5_BIT 16
-#define DMA_IFCR_CTEIF4_BIT 15
-#define DMA_IFCR_CHTIF4_BIT 14
-#define DMA_IFCR_CTCIF4_BIT 13
-#define DMA_IFCR_CGIF4_BIT 12
-#define DMA_IFCR_CTEIF3_BIT 11
-#define DMA_IFCR_CHTIF3_BIT 10
-#define DMA_IFCR_CTCIF3_BIT 9
-#define DMA_IFCR_CGIF3_BIT 8
-#define DMA_IFCR_CTEIF2_BIT 7
-#define DMA_IFCR_CHTIF2_BIT 6
-#define DMA_IFCR_CTCIF2_BIT 5
-#define DMA_IFCR_CGIF2_BIT 4
-#define DMA_IFCR_CTEIF1_BIT 3
-#define DMA_IFCR_CHTIF1_BIT 2
-#define DMA_IFCR_CTCIF1_BIT 1
-#define DMA_IFCR_CGIF1_BIT 0
-
-#define DMA_IFCR_CTEIF7 BIT(DMA_IFCR_CTEIF7_BIT)
-#define DMA_IFCR_CHTIF7 BIT(DMA_IFCR_CHTIF7_BIT)
-#define DMA_IFCR_CTCIF7 BIT(DMA_IFCR_CTCIF7_BIT)
-#define DMA_IFCR_CGIF7 BIT(DMA_IFCR_CGIF7_BIT)
-#define DMA_IFCR_CTEIF6 BIT(DMA_IFCR_CTEIF6_BIT)
-#define DMA_IFCR_CHTIF6 BIT(DMA_IFCR_CHTIF6_BIT)
-#define DMA_IFCR_CTCIF6 BIT(DMA_IFCR_CTCIF6_BIT)
-#define DMA_IFCR_CGIF6 BIT(DMA_IFCR_CGIF6_BIT)
-#define DMA_IFCR_CTEIF5 BIT(DMA_IFCR_CTEIF5_BIT)
-#define DMA_IFCR_CHTIF5 BIT(DMA_IFCR_CHTIF5_BIT)
-#define DMA_IFCR_CTCIF5 BIT(DMA_IFCR_CTCIF5_BIT)
-#define DMA_IFCR_CGIF5 BIT(DMA_IFCR_CGIF5_BIT)
-#define DMA_IFCR_CTEIF4 BIT(DMA_IFCR_CTEIF4_BIT)
-#define DMA_IFCR_CHTIF4 BIT(DMA_IFCR_CHTIF4_BIT)
-#define DMA_IFCR_CTCIF4 BIT(DMA_IFCR_CTCIF4_BIT)
-#define DMA_IFCR_CGIF4 BIT(DMA_IFCR_CGIF4_BIT)
-#define DMA_IFCR_CTEIF3 BIT(DMA_IFCR_CTEIF3_BIT)
-#define DMA_IFCR_CHTIF3 BIT(DMA_IFCR_CHTIF3_BIT)
-#define DMA_IFCR_CTCIF3 BIT(DMA_IFCR_CTCIF3_BIT)
-#define DMA_IFCR_CGIF3 BIT(DMA_IFCR_CGIF3_BIT)
-#define DMA_IFCR_CTEIF2 BIT(DMA_IFCR_CTEIF2_BIT)
-#define DMA_IFCR_CHTIF2 BIT(DMA_IFCR_CHTIF2_BIT)
-#define DMA_IFCR_CTCIF2 BIT(DMA_IFCR_CTCIF2_BIT)
-#define DMA_IFCR_CGIF2 BIT(DMA_IFCR_CGIF2_BIT)
-#define DMA_IFCR_CTEIF1 BIT(DMA_IFCR_CTEIF1_BIT)
-#define DMA_IFCR_CHTIF1 BIT(DMA_IFCR_CHTIF1_BIT)
-#define DMA_IFCR_CTCIF1 BIT(DMA_IFCR_CTCIF1_BIT)
-#define DMA_IFCR_CGIF1 BIT(DMA_IFCR_CGIF1_BIT)
-
-/* Channel configuration register */
-
-#define DMA_CCR_MEM2MEM_BIT 14
-#define DMA_CCR_MINC_BIT 7
-#define DMA_CCR_PINC_BIT 6
-#define DMA_CCR_CIRC_BIT 5
-#define DMA_CCR_DIR_BIT 4
-#define DMA_CCR_TEIE_BIT 3
-#define DMA_CCR_HTIE_BIT 2
-#define DMA_CCR_TCIE_BIT 1
-#define DMA_CCR_EN_BIT 0
-
-#define DMA_CCR_MEM2MEM BIT(DMA_CCR_MEM2MEM_BIT)
-#define DMA_CCR_PL (0x3 << 12)
-#define DMA_CCR_PL_LOW (0x0 << 12)
-#define DMA_CCR_PL_MEDIUM (0x1 << 12)
-#define DMA_CCR_PL_HIGH (0x2 << 12)
-#define DMA_CCR_PL_VERY_HIGH (0x3 << 12)
-#define DMA_CCR_MSIZE (0x3 << 10)
-#define DMA_CCR_MSIZE_8BITS (0x0 << 10)
-#define DMA_CCR_MSIZE_16BITS (0x1 << 10)
-#define DMA_CCR_MSIZE_32BITS (0x2 << 10)
-#define DMA_CCR_PSIZE (0x3 << 8)
-#define DMA_CCR_PSIZE_8BITS (0x0 << 8)
-#define DMA_CCR_PSIZE_16BITS (0x1 << 8)
-#define DMA_CCR_PSIZE_32BITS (0x2 << 8)
-#define DMA_CCR_MINC BIT(DMA_CCR_MINC_BIT)
-#define DMA_CCR_PINC BIT(DMA_CCR_PINC_BIT)
-#define DMA_CCR_CIRC BIT(DMA_CCR_CIRC_BIT)
-#define DMA_CCR_DIR BIT(DMA_CCR_DIR_BIT)
-#define DMA_CCR_TEIE BIT(DMA_CCR_TEIE_BIT)
-#define DMA_CCR_HTIE BIT(DMA_CCR_HTIE_BIT)
-#define DMA_CCR_TCIE BIT(DMA_CCR_TCIE_BIT)
-#define DMA_CCR_EN BIT(DMA_CCR_EN_BIT)
+#include <libmaple/libmaple_types.h>
+#include <libmaple/nvic.h>
+#include <libmaple/rcc.h>
/*
* Devices
*/
-/** Encapsulates state related to a DMA channel interrupt. */
+/* 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-specified channel interrupt
- handler */
- nvic_irq_num irq_line; /**< Channel's NVIC interrupt number */
+ void (*handler)(void); /* User handler */
+ nvic_irq_num irq_line; /* IRQ line for interrupt */
} dma_handler_config;
/** DMA device type */
typedef struct dma_dev {
- dma_reg_map *regs; /**< Register map */
- rcc_clk_id clk_id; /**< Clock ID */
- dma_handler_config handlers[]; /**<
- * @brief IRQ handlers and NVIC numbers.
- *
- * @see dma_attach_interrupt()
- * @see dma_detach_interrupt()
- */
+ dma_reg_map *regs; /**< Register map */
+ rcc_clk_id clk_id; /**< Clock ID */
+ struct dma_handler_config handlers[]; /**< For internal use */
} dma_dev;
-extern dma_dev *DMA1;
-#ifdef STM32_HIGH_DENSITY
-extern dma_dev *DMA2;
-#endif
-
/*
* Convenience functions
*/
void dma_init(dma_dev *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(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(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(dma_dev *dev,
- dma_channel channel,
- dma_priority priority);
-
-void dma_attach_interrupt(dma_dev *dev,
- dma_channel channel,
- void (*handler)(void));
-void dma_detach_interrupt(dma_dev *dev, dma_channel channel);
-
-/**
- * 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_irq_cause;
-
-dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel);
-
-void dma_enable(dma_dev *dev, dma_channel channel);
-void dma_disable(dma_dev *dev, dma_channel channel);
-
-void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *address);
-void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *address);
-
-/**
- * @brief DMA channel register map type.
- *
- * Provides access to an individual channel's registers.
- */
-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
-
-/**
- * @brief Obtain a pointer to an individual DMA channel's registers.
- *
- * For example, dma_channel_regs(DMA1, DMA_CH1)->CCR is DMA1_BASE->CCR1.
- *
- * @param dev DMA device
- * @param channel DMA channel whose channel register map to obtain.
- */
-static inline dma_channel_reg_map* dma_channel_regs(dma_dev *dev,
- dma_channel channel) {
- __io uint32 *ccr1 = &dev->regs->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.
- */
-static inline uint8 dma_is_channel_enabled(dma_dev *dev, dma_channel channel) {
- return (uint8)(dma_channel_regs(dev, channel)->CCR & DMA_CCR_EN);
-}
-
-/**
- * @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(dma_dev *dev, dma_channel channel) {
- uint8 shift = (channel - 1) * 4;
- return (dev->regs->ISR >> shift) & 0xF;
-}
-
-/**
- * @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()
+/*
+ * 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.
*/
-static inline void dma_clear_isr_bits(dma_dev *dev, dma_channel channel) {
- dev->regs->IFCR = BIT(4 * (channel - 1));
+static __always_inline dma_reg_map* _dma_dev_regs(dma_dev *dev) {
+ return dev->regs;
}
#ifdef __cplusplus
diff --git a/libmaple/rules.mk b/libmaple/rules.mk
index 93716d2..d6efb1f 100644
--- a/libmaple/rules.mk
+++ b/libmaple/rules.mk
@@ -12,6 +12,7 @@ CFLAGS_$(d) = $(LIBMAPLE_PRIVATE_INCLUDES) $(LIBMAPLE_INCLUDES) -Wall -Werror
# Local rules and targets
cSRCS_$(d) := adc.c
+cSRCS_$(d) += dma.c
cSRCS_$(d) += flash.c
cSRCS_$(d) += gpio.c
cSRCS_$(d) += iwdg.c
@@ -27,7 +28,6 @@ cSRCS_$(d) += usart_private.c
cSRCS_$(d) += util.c
# These still need to be ported to F2:
# cSRCS_$(d) += dac.c
-# cSRCS_$(d) += dma.c
# cSRCS_$(d) += exti.c
# cSRCS_$(d) += i2c.c
diff --git a/libmaple/stm32f1/dma.c b/libmaple/stm32f1/dma.c
new file mode 100644
index 0000000..14ac645
--- /dev/null
+++ b/libmaple/stm32f1/dma.c
@@ -0,0 +1,371 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2010 Michael Hope.
+ * 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/stm32f1/dma.c
+ * @author Marti Bolivar <mbolivar@leaflabs.com>;
+ * Original implementation by Michael Hope
+ * @brief STM32F1 DMA support.
+ */
+
+#include <libmaple/dma.h>
+#include <libmaple/bitband.h>
+
+/*
+ * Devices
+ */
+
+static dma_dev dma1 = {
+ .regs = DMA1_BASE,
+ .clk_id = RCC_DMA1,
+ .handlers = {{ .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 }},
+};
+/** DMA1 device */
+dma_dev *DMA1 = &dma1;
+
+#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
+static dma_dev dma2 = {
+ .regs = DMA2_BASE,
+ .clk_id = RCC_DMA2,
+ .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_CH1 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_CH2 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_CH3 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 },
+ { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }}, /* !@#$ */
+};
+/** DMA2 device */
+dma_dev *DMA2 = &dma2;
+#endif
+
+/*
+ * 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);
+
+ 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;
+}
+
+/**
+ * @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;
+
+ ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
+
+ channel_regs = dma_channel_regs(dev, channel);
+ channel_regs->CNDTR = num_transfers;
+}
+
+/**
+ * @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.
+ */
+void dma_set_priority(dma_dev *dev,
+ dma_channel channel,
+ dma_priority priority) {
+ dma_channel_reg_map *channel_regs;
+ uint32 ccr;
+
+ ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
+
+ channel_regs = dma_channel_regs(dev, channel);
+ ccr = channel_regs->CCR;
+ ccr &= ~DMA_CCR_PL;
+ ccr |= priority;
+ 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 (*handler)(void)) {
+ dev->handlers[channel - 1].handler = 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;
+}
+
+/**
+ * @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) {
+ uint8 status_bits = dma_get_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);
+
+ /* 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)) {
+ return DMA_TRANSFER_ERROR;
+ } else if (status_bits & BIT(1)) {
+ return DMA_TRANSFER_COMPLETE;
+ } else if (status_bits & BIT(2)) {
+ 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);
+}
+
+/**
+ * @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;
+
+ ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
+
+ chan_regs = dma_channel_regs(dev, channel);
+ 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;
+
+ ASSERT_FAULT(!dma_is_channel_enabled(dev, channel));
+
+ chan_regs = dma_channel_regs(dev, channel);
+ chan_regs->CPAR = (uint32)addr;
+}
+
+/*
+ * IRQ handlers
+ */
+
+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 */
+ }
+}
+
+void __irq_dma1_channel1(void) {
+ dispatch_handler(DMA1, DMA_CH1);
+}
+
+void __irq_dma1_channel2(void) {
+ dispatch_handler(DMA1, DMA_CH2);
+}
+
+void __irq_dma1_channel3(void) {
+ dispatch_handler(DMA1, DMA_CH3);
+}
+
+void __irq_dma1_channel4(void) {
+ dispatch_handler(DMA1, DMA_CH4);
+}
+
+void __irq_dma1_channel5(void) {
+ dispatch_handler(DMA1, DMA_CH5);
+}
+
+void __irq_dma1_channel6(void) {
+ dispatch_handler(DMA1, DMA_CH6);
+}
+
+void __irq_dma1_channel7(void) {
+ dispatch_handler(DMA1, DMA_CH7);
+}
+
+#ifdef STM32_HIGH_DENSITY
+void __irq_dma2_channel1(void) {
+ dispatch_handler(DMA2, DMA_CH1);
+}
+
+void __irq_dma2_channel2(void) {
+ dispatch_handler(DMA2, DMA_CH2);
+}
+
+void __irq_dma2_channel3(void) {
+ dispatch_handler(DMA2, DMA_CH3);
+}
+
+void __irq_dma2_channel4_5(void) {
+ dispatch_handler(DMA2, DMA_CH4);
+ dispatch_handler(DMA2, DMA_CH5);
+}
+#endif
diff --git a/libmaple/stm32f1/include/series/dma.h b/libmaple/stm32f1/include/series/dma.h
new file mode 100644
index 0000000..60582ea
--- /dev/null
+++ b/libmaple/stm32f1/include/series/dma.h
@@ -0,0 +1,438 @@
+/******************************************************************************
+ * The MIT License
+ *
+ * Copyright (c) 2010 Michael Hope.
+ * 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/stm32f1/include/series/dma.h
+ * @author Marti Bolivar <mbolivar@leaflabs.com>;
+ * Original implementation by Michael Hope
+ * @brief STM32F1 Direct Memory Access header
+ */
+
+/*
+ * See /notes/dma-stm32f1.txt for more information.
+ */
+
+#ifndef _LIBMAPLE_STM32F1_DMA_H_
+#define _LIBMAPLE_STM32F1_DMA_H_
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+#include <libmaple/libmaple_types.h>
+
+/*
+ * Register map and base pointers
+ */
+
+/**
+ * @brief DMA register map type.
+ *
+ * Note that DMA controller 2 (register map base pointer DMA2_BASE)
+ * only supports channels 1--5.
+ */
+typedef struct dma_reg_map {
+ __io uint32 ISR; /**< Interrupt status register */
+ __io uint32 IFCR; /**< Interrupt flag clear register */
+ __io uint32 CCR1; /**< Channel 1 configuration register */
+ __io uint32 CNDTR1; /**< Channel 1 number of data register */
+ __io uint32 CPAR1; /**< Channel 1 peripheral address register */
+ __io uint32 CMAR1; /**< Channel 1 memory address register */
+ const uint32 RESERVED1; /**< Reserved. */
+ __io uint32 CCR2; /**< Channel 2 configuration register */
+ __io uint32 CNDTR2; /**< Channel 2 number of data register */
+ __io uint32 CPAR2; /**< Channel 2 peripheral address register */
+ __io uint32 CMAR2; /**< Channel 2 memory address register */
+ const uint32 RESERVED2; /**< Reserved. */
+ __io uint32 CCR3; /**< Channel 3 configuration register */
+ __io uint32 CNDTR3; /**< Channel 3 number of data register */
+ __io uint32 CPAR3; /**< Channel 3 peripheral address register */
+ __io uint32 CMAR3; /**< Channel 3 memory address register */
+ const uint32 RESERVED3; /**< Reserved. */
+ __io uint32 CCR4; /**< Channel 4 configuration register */
+ __io uint32 CNDTR4; /**< Channel 4 number of data register */
+ __io uint32 CPAR4; /**< Channel 4 peripheral address register */
+ __io uint32 CMAR4; /**< Channel 4 memory address register */
+ const uint32 RESERVED4; /**< Reserved. */
+ __io uint32 CCR5; /**< Channel 5 configuration register */
+ __io uint32 CNDTR5; /**< Channel 5 number of data register */
+ __io uint32 CPAR5; /**< Channel 5 peripheral address register */
+ __io uint32 CMAR5; /**< Channel 5 memory address register */
+ const uint32 RESERVED5; /**< Reserved. */
+ __io uint32 CCR6; /**< Channel 6 configuration register */
+ __io uint32 CNDTR6; /**< Channel 6 number of data register */
+ __io uint32 CPAR6; /**< Channel 6 peripheral address register */
+ __io uint32 CMAR6; /**< Channel 6 memory address register */
+ const uint32 RESERVED6; /**< Reserved. */
+ __io uint32 CCR7; /**< Channel 7 configuration register */
+ __io uint32 CNDTR7; /**< Channel 7 number of data register */
+ __io uint32 CPAR7; /**< Channel 7 peripheral address register */
+ __io uint32 CMAR7; /**< Channel 7 memory address register */
+ const uint32 RESERVED7; /**< Reserved. */
+} dma_reg_map;
+
+/** DMA controller 1 register map base pointer */
+#define DMA1_BASE ((struct dma_reg_map*)0x40020000)
+/** DMA controller 2 register map base pointer */
+#define DMA2_BASE ((struct dma_reg_map*)0x40020400)
+
+/*
+ * Register bit definitions
+ */
+
+/* Interrupt status register */
+
+#define DMA_ISR_TEIF7_BIT 27
+#define DMA_ISR_HTIF7_BIT 26
+#define DMA_ISR_TCIF7_BIT 25
+#define DMA_ISR_GIF7_BIT 24
+#define DMA_ISR_TEIF6_BIT 23
+#define DMA_ISR_HTIF6_BIT 22
+#define DMA_ISR_TCIF6_BIT 21
+#define DMA_ISR_GIF6_BIT 20
+#define DMA_ISR_TEIF5_BIT 19
+#define DMA_ISR_HTIF5_BIT 18
+#define DMA_ISR_TCIF5_BIT 17
+#define DMA_ISR_GIF5_BIT 16
+#define DMA_ISR_TEIF4_BIT 15
+#define DMA_ISR_HTIF4_BIT 14
+#define DMA_ISR_TCIF4_BIT 13
+#define DMA_ISR_GIF4_BIT 12
+#define DMA_ISR_TEIF3_BIT 11
+#define DMA_ISR_HTIF3_BIT 10
+#define DMA_ISR_TCIF3_BIT 9
+#define DMA_ISR_GIF3_BIT 8
+#define DMA_ISR_TEIF2_BIT 7
+#define DMA_ISR_HTIF2_BIT 6
+#define DMA_ISR_TCIF2_BIT 5
+#define DMA_ISR_GIF2_BIT 4
+#define DMA_ISR_TEIF1_BIT 3
+#define DMA_ISR_HTIF1_BIT 2
+#define DMA_ISR_TCIF1_BIT 1
+#define DMA_ISR_GIF1_BIT 0
+
+#define DMA_ISR_TEIF7 (1U << DMA_ISR_TEIF7_BIT)
+#define DMA_ISR_HTIF7 (1U << DMA_ISR_HTIF7_BIT)
+#define DMA_ISR_TCIF7 (1U << DMA_ISR_TCIF7_BIT)
+#define DMA_ISR_GIF7 (1U << DMA_ISR_GIF7_BIT)
+#define DMA_ISR_TEIF6 (1U << DMA_ISR_TEIF6_BIT)
+#define DMA_ISR_HTIF6 (1U << DMA_ISR_HTIF6_BIT)
+#define DMA_ISR_TCIF6 (1U << DMA_ISR_TCIF6_BIT)
+#define DMA_ISR_GIF6 (1U << DMA_ISR_GIF6_BIT)
+#define DMA_ISR_TEIF5 (1U << DMA_ISR_TEIF5_BIT)
+#define DMA_ISR_HTIF5 (1U << DMA_ISR_HTIF5_BIT)
+#define DMA_ISR_TCIF5 (1U << DMA_ISR_TCIF5_BIT)
+#define DMA_ISR_GIF5 (1U << DMA_ISR_GIF5_BIT)
+#define DMA_ISR_TEIF4 (1U << DMA_ISR_TEIF4_BIT)
+#define DMA_ISR_HTIF4 (1U << DMA_ISR_HTIF4_BIT)
+#define DMA_ISR_TCIF4 (1U << DMA_ISR_TCIF4_BIT)
+#define DMA_ISR_GIF4 (1U << DMA_ISR_GIF4_BIT)
+#define DMA_ISR_TEIF3 (1U << DMA_ISR_TEIF3_BIT)
+#define DMA_ISR_HTIF3 (1U << DMA_ISR_HTIF3_BIT)
+#define DMA_ISR_TCIF3 (1U << DMA_ISR_TCIF3_BIT)
+#define DMA_ISR_GIF3 (1U << DMA_ISR_GIF3_BIT)
+#define DMA_ISR_TEIF2 (1U << DMA_ISR_TEIF2_BIT)
+#define DMA_ISR_HTIF2 (1U << DMA_ISR_HTIF2_BIT)
+#define DMA_ISR_TCIF2 (1U << DMA_ISR_TCIF2_BIT)
+#define DMA_ISR_GIF2 (1U << DMA_ISR_GIF2_BIT)
+#define DMA_ISR_TEIF1 (1U << DMA_ISR_TEIF1_BIT)
+#define DMA_ISR_HTIF1 (1U << DMA_ISR_HTIF1_BIT)
+#define DMA_ISR_TCIF1 (1U << DMA_ISR_TCIF1_BIT)
+#define DMA_ISR_GIF1 (1U << DMA_ISR_GIF1_BIT)
+
+/* Interrupt flag clear register */
+
+#define DMA_IFCR_CTEIF7_BIT 27
+#define DMA_IFCR_CHTIF7_BIT 26
+#define DMA_IFCR_CTCIF7_BIT 25
+#define DMA_IFCR_CGIF7_BIT 24
+#define DMA_IFCR_CTEIF6_BIT 23
+#define DMA_IFCR_CHTIF6_BIT 22
+#define DMA_IFCR_CTCIF6_BIT 21
+#define DMA_IFCR_CGIF6_BIT 20
+#define DMA_IFCR_CTEIF5_BIT 19
+#define DMA_IFCR_CHTIF5_BIT 18
+#define DMA_IFCR_CTCIF5_BIT 17
+#define DMA_IFCR_CGIF5_BIT 16
+#define DMA_IFCR_CTEIF4_BIT 15
+#define DMA_IFCR_CHTIF4_BIT 14
+#define DMA_IFCR_CTCIF4_BIT 13
+#define DMA_IFCR_CGIF4_BIT 12
+#define DMA_IFCR_CTEIF3_BIT 11
+#define DMA_IFCR_CHTIF3_BIT 10
+#define DMA_IFCR_CTCIF3_BIT 9
+#define DMA_IFCR_CGIF3_BIT 8
+#define DMA_IFCR_CTEIF2_BIT 7
+#define DMA_IFCR_CHTIF2_BIT 6
+#define DMA_IFCR_CTCIF2_BIT 5
+#define DMA_IFCR_CGIF2_BIT 4
+#define DMA_IFCR_CTEIF1_BIT 3
+#define DMA_IFCR_CHTIF1_BIT 2
+#define DMA_IFCR_CTCIF1_BIT 1
+#define DMA_IFCR_CGIF1_BIT 0
+
+#define DMA_IFCR_CTEIF7 (1U << DMA_IFCR_CTEIF7_BIT)
+#define DMA_IFCR_CHTIF7 (1U << DMA_IFCR_CHTIF7_BIT)
+#define DMA_IFCR_CTCIF7 (1U << DMA_IFCR_CTCIF7_BIT)
+#define DMA_IFCR_CGIF7 (1U << DMA_IFCR_CGIF7_BIT)
+#define DMA_IFCR_CTEIF6 (1U << DMA_IFCR_CTEIF6_BIT)
+#define DMA_IFCR_CHTIF6 (1U << DMA_IFCR_CHTIF6_BIT)
+#define DMA_IFCR_CTCIF6 (1U << DMA_IFCR_CTCIF6_BIT)
+#define DMA_IFCR_CGIF6 (1U << DMA_IFCR_CGIF6_BIT)
+#define DMA_IFCR_CTEIF5 (1U << DMA_IFCR_CTEIF5_BIT)
+#define DMA_IFCR_CHTIF5 (1U << DMA_IFCR_CHTIF5_BIT)
+#define DMA_IFCR_CTCIF5 (1U << DMA_IFCR_CTCIF5_BIT)
+#define DMA_IFCR_CGIF5 (1U << DMA_IFCR_CGIF5_BIT)
+#define DMA_IFCR_CTEIF4 (1U << DMA_IFCR_CTEIF4_BIT)
+#define DMA_IFCR_CHTIF4 (1U << DMA_IFCR_CHTIF4_BIT)
+#define DMA_IFCR_CTCIF4 (1U << DMA_IFCR_CTCIF4_BIT)
+#define DMA_IFCR_CGIF4 (1U << DMA_IFCR_CGIF4_BIT)
+#define DMA_IFCR_CTEIF3 (1U << DMA_IFCR_CTEIF3_BIT)
+#define DMA_IFCR_CHTIF3 (1U << DMA_IFCR_CHTIF3_BIT)
+#define DMA_IFCR_CTCIF3 (1U << DMA_IFCR_CTCIF3_BIT)
+#define DMA_IFCR_CGIF3 (1U << DMA_IFCR_CGIF3_BIT)
+#define DMA_IFCR_CTEIF2 (1U << DMA_IFCR_CTEIF2_BIT)
+#define DMA_IFCR_CHTIF2 (1U << DMA_IFCR_CHTIF2_BIT)
+#define DMA_IFCR_CTCIF2 (1U << DMA_IFCR_CTCIF2_BIT)
+#define DMA_IFCR_CGIF2 (1U << DMA_IFCR_CGIF2_BIT)
+#define DMA_IFCR_CTEIF1 (1U << DMA_IFCR_CTEIF1_BIT)
+#define DMA_IFCR_CHTIF1 (1U << DMA_IFCR_CHTIF1_BIT)
+#define DMA_IFCR_CTCIF1 (1U << DMA_IFCR_CTCIF1_BIT)
+#define DMA_IFCR_CGIF1 (1U << DMA_IFCR_CGIF1_BIT)
+
+/* Channel configuration register */
+
+#define DMA_CCR_MEM2MEM_BIT 14
+#define DMA_CCR_MINC_BIT 7
+#define DMA_CCR_PINC_BIT 6
+#define DMA_CCR_CIRC_BIT 5
+#define DMA_CCR_DIR_BIT 4
+#define DMA_CCR_TEIE_BIT 3
+#define DMA_CCR_HTIE_BIT 2
+#define DMA_CCR_TCIE_BIT 1
+#define DMA_CCR_EN_BIT 0
+
+#define DMA_CCR_MEM2MEM (1U << DMA_CCR_MEM2MEM_BIT)
+#define DMA_CCR_PL (0x3 << 12)
+#define DMA_CCR_PL_LOW (0x0 << 12)
+#define DMA_CCR_PL_MEDIUM (0x1 << 12)
+#define DMA_CCR_PL_HIGH (0x2 << 12)
+#define DMA_CCR_PL_VERY_HIGH (0x3 << 12)
+#define DMA_CCR_MSIZE (0x3 << 10)
+#define DMA_CCR_MSIZE_8BITS (0x0 << 10)
+#define DMA_CCR_MSIZE_16BITS (0x1 << 10)
+#define DMA_CCR_MSIZE_32BITS (0x2 << 10)
+#define DMA_CCR_PSIZE (0x3 << 8)
+#define DMA_CCR_PSIZE_8BITS (0x0 << 8)
+#define DMA_CCR_PSIZE_16BITS (0x1 << 8)
+#define DMA_CCR_PSIZE_32BITS (0x2 << 8)
+#define DMA_CCR_MINC (1U << DMA_CCR_MINC_BIT)
+#define DMA_CCR_PINC (1U << DMA_CCR_PINC_BIT)
+#define DMA_CCR_CIRC (1U << DMA_CCR_CIRC_BIT)
+#define DMA_CCR_DIR (1U << DMA_CCR_DIR_BIT)
+#define DMA_CCR_TEIE (1U << DMA_CCR_TEIE_BIT)
+#define DMA_CCR_HTIE (1U << DMA_CCR_HTIE_BIT)
+#define DMA_CCR_TCIE (1U << DMA_CCR_TCIE_BIT)
+#define DMA_CCR_EN (1U << DMA_CCR_EN_BIT)
+
+/*
+ * Devices
+ */
+
+struct dma_dev;
+extern struct dma_dev *DMA1;
+#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY)
+extern struct dma_dev *DMA2;
+#endif
+
+/*
+ * Convenience routines.
+ */
+
+/* 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()
+ */
+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);
+
+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 DMA channel register map type.
+ *
+ * Provides access to an individual channel's registers.
+ */
+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
+
+/**
+ * @brief Obtain a pointer to an individual DMA channel's registers.
+ *
+ * For example, dma_channel_regs(DMA1, DMA_CH1)->CCR is DMA1_BASE->CCR1.
+ *
+ * @param dev DMA device
+ * @param channel DMA channel whose channel register map to obtain.
+ */
+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.
+ */
+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);
+}
+
+/**
+ * @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 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()
+ */
+static inline void dma_clear_isr_bits(struct dma_dev *dev,
+ dma_channel channel) {
+ _dma_dev_regs(dev)->IFCR = (1U << (4 * (channel - 1)));
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
diff --git a/libmaple/stm32f1/rules.mk b/libmaple/stm32f1/rules.mk
index 52ceea6..b67f446 100644
--- a/libmaple/stm32f1/rules.mk
+++ b/libmaple/stm32f1/rules.mk
@@ -13,6 +13,7 @@ sSRCS_$(d) += vector_table_performance.S
cSRCS_$(d) := adc.c
cSRCS_$(d) += bkp.c
+cSRCS_$(d) += dma.c
cSRCS_$(d) += fsmc.c
cSRCS_$(d) += gpio.c
cSRCS_$(d) += rcc.c