From f36fae273ec84ee2c53a33caa2dddea2d79db0da Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Tue, 15 Nov 2011 12:45:43 -0500 Subject: Move public headers to include directories; related cleanups. Move libmaple/*.h to (new) libmaple/include/libmaple/. The new accepted way to include a libmaple header foo.h is with: #include This is more polite in terms of the include namespace. It also allows us to e.g. implement the Arduino SPI library at all (which has header SPI.h; providing it was previously impossible on case-insensitive filesystems due to libmaple's spi.h). Similarly for Wirish. The old include style (#include "header.h") is now deprecated. libmaple/*.h: - Change include guard #defines from _FOO_H_ to _LIBMAPLE_FOO_H_. - Add license headers where they're missing - Add conditional extern "C" { ... } blocks where they're missing (they aren't always necessary, but we might was well do it against the future, while we're at it.). - Change includes from #include "foo.h" to #include . - Move includes after extern "C". - Remove extra trailing newlines Note that this doesn't include the headers under libmaple/usb/ or libmaple/usb/usb_lib. These will get fixed later. libmaple/*.c: - Change includes from #include "foo.h" to #include . Makefile: - Add I$(LIBMAPLE_PATH)/include/libmaple to GLOBAL_FLAGS. This allows for users (including Wirish) to migrate their code, but should go away ASAP, since it slows down compilation. Wirish: - Move wirish/**/*.h to (new) wirish/include/wirish/. This ignores the USB headers, which, as usual, are getting handled after everything else. - Similarly generify wirish/boards/ structure. For each supported board "foo", move wirish/boards/foo.h and wirish/boards/foo.cpp to wirish/boards/foo/include/board/board.h and wirish/boards/foo/board.cpp, respectively. Also remove the #ifdef hacks around the .cpp files. - wirish/rules.mk: put wirish/boards/foo/include in the include path (and add wirish/boards/foo/board.cpp to the list of sources to be compiled). This allows saying: #include instead of the hack currently in place. We can allow the user to override this setting later to make adding custom board definitions easier. - Disable -Werror in libmaple/rules.mk, as the current USB warnings don't let the olimex_stm32_h103 board compile. We can re-enable -Werror once we've moved the board-specific bits out of libmaple proper. libraries, examples: - Update includes accordingly. - Miscellaneous cosmetic fixups. Signed-off-by: Marti Bolivar --- libmaple/dma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'libmaple/dma.c') diff --git a/libmaple/dma.c b/libmaple/dma.c index 60f4d47..f20613b 100644 --- a/libmaple/dma.c +++ b/libmaple/dma.c @@ -31,9 +31,9 @@ * @brief Direct Memory Access peripheral support */ -#include "dma.h" -#include "bitband.h" -#include "util.h" +#include +#include +#include /* * Devices -- cgit v1.2.3 From 1f67ed8f23048c45b0a3deec232b19047966bce7 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Thu, 26 Apr 2012 17:33:56 -0400 Subject: 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 . 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 and libmaple/dma.c to the appropriate places under libmaple/stm32f1/. (Ouch. This is almost everything.) Patch the (new) STM32F1 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 --- libmaple/dma.c | 338 +------------------------- libmaple/include/libmaple/dma.h | 404 ++----------------------------- libmaple/rules.mk | 2 +- libmaple/stm32f1/dma.c | 371 ++++++++++++++++++++++++++++ libmaple/stm32f1/include/series/dma.h | 438 ++++++++++++++++++++++++++++++++++ libmaple/stm32f1/rules.mk | 1 + 6 files changed, 836 insertions(+), 718 deletions(-) create mode 100644 libmaple/stm32f1/dma.c create mode 100644 libmaple/stm32f1/include/series/dma.h (limited to 'libmaple/dma.c') 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 ; * Original implementation by Michael Hope - * @brief Direct Memory Access peripheral support + * @brief Portable DMA routines. */ #include -#include -#include - -/* - * 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 -#include -#include - -/* - * Register maps - */ - -/** - * @brief DMA register map type. +/* 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 -#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 +#include +#include /* * 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 ; + * Original implementation by Michael Hope + * @brief STM32F1 DMA support. + */ + +#include +#include + +/* + * 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 ; + * 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 + +/* + * 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 + * . */ +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 -- cgit v1.2.3 From e51c60e03661cc924420eba757e15044016b7c1f Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Tue, 12 Jun 2012 17:01:12 -0400 Subject: DMA: prep for F2 with new "tube" API. To prepare for STM32F2/F4 DMA support, introduce a new libmaple DMA API, and move some code around to make priority level and interrupt handling more generic. The new API is based on a new set of types (dma_tube, struct dma_tube_reg_map, enum dma_request_src, enum dma_cfg_flags, and struct dma_tube_config). The central abstraction is the dma_tube type. STM32F2/F4 use DMA streams to control dataflow, and STM32F1 uses channels. dma_tube stands for whichever is appropriate for the current target. Dealing with tubes allows for configuring and using DMA with opaque tube values in the same source, instead of (as with ST's firmware) requiring two separate codebases. The new API is also more user-friendly, as it doesn't require knowing which DMA address registers to set and which configuration register flags go along with them. It now suffices to specify the source and destination for the DMA transfer, along with their sizes. This avoids confusion (e.g. for memory-to-memory transfers, data flows from the peripheral address register to the memory register, which might be surprising on F2, which has two memory address registers). The old API (based on enum dma_mode_flags and dma_setup_transfer()) is still available on F1, but deprecate it. Signed-off-by: Marti Bolivar --- libmaple/dma.c | 35 +++ libmaple/dma_private.h | 61 +++++ libmaple/include/libmaple/dma.h | 402 +++++++++++++++++++++++++++++--- libmaple/include/libmaple/dma_common.h | 114 +++++++++ libmaple/stm32f1/dma.c | 381 +++++++++++++++--------------- libmaple/stm32f1/include/series/dma.h | 411 +++++++++++++++++++++------------ 6 files changed, 1038 insertions(+), 366 deletions(-) create mode 100644 libmaple/dma_private.h create mode 100644 libmaple/include/libmaple/dma_common.h (limited to 'libmaple/dma.c') 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 +#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 +#include + +/* + * 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 @@ -41,50 +41,400 @@ extern "C"{ #endif /* 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 - +/* buys us dma_dev and other necessities. */ +#include #include -#include -#include /* - * 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 + * @brief Common DMA sub-header for and . + * + * 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 instead. + */ + +/* + * There's a fair amount of common DMA functionality needed by each + * and . 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 +#include +#include + +/* + * 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 #include +/* 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 ; * Original implementation by Michael Hope - * @brief STM32F1 Direct Memory Access header + * @brief STM32F1 DMA series header. */ /* @@ -44,13 +44,14 @@ extern "C"{ #endif #include +#include /* - * 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, . */ -/* This hack is due to a circular dependency between us and - * . */ -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" -- cgit v1.2.3