diff options
author | Marti Bolivar <mbolivar@leaflabs.com> | 2012-04-26 17:33:56 -0400 |
---|---|---|
committer | Marti Bolivar <mbolivar@leaflabs.com> | 2012-05-03 14:09:05 -0400 |
commit | 1f67ed8f23048c45b0a3deec232b19047966bce7 (patch) | |
tree | da3aec665bda333922c249f5094942ee3da7eb46 /libmaple/stm32f1 | |
parent | a81a02257d69454412d4ef062b56a3cc5c758815 (diff) | |
download | librambutan-1f67ed8f23048c45b0a3deec232b19047966bce7.tar.gz librambutan-1f67ed8f23048c45b0a3deec232b19047966bce7.zip |
stm32f1: Resurrect DMA support. (sets up breaking change)
Breaking change set up: struct dma_handler_config is no longer part of
the public API in <libmaple/dma.h>. User code which was touching
these was always mistaken; it should be using dma_attach_interrupt()
or dma_detach_interrupt() instead.
Other than that, just move the nonportable bits in <libmaple/dma.h>
and libmaple/dma.c to the appropriate places under
libmaple/stm32f1/. (Ouch. This is almost everything.) Patch the
(new) STM32F1 <series/dma.h> here and there to make everything
compile; this is mostly limited to forward-declaring struct dma_dev
and providing a hack _dma_dev_regs() declaration so inline functions
in the series header can still access a device's registers.
Signed-off-by: Marti Bolivar <mbolivar@leaflabs.com>
Diffstat (limited to 'libmaple/stm32f1')
-rw-r--r-- | libmaple/stm32f1/dma.c | 371 | ||||
-rw-r--r-- | libmaple/stm32f1/include/series/dma.h | 438 | ||||
-rw-r--r-- | libmaple/stm32f1/rules.mk | 1 |
3 files changed, 810 insertions, 0 deletions
diff --git a/libmaple/stm32f1/dma.c b/libmaple/stm32f1/dma.c new file mode 100644 index 0000000..14ac645 --- /dev/null +++ b/libmaple/stm32f1/dma.c @@ -0,0 +1,371 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Michael Hope. + * Copyright (c) 2012 LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file libmaple/stm32f1/dma.c + * @author Marti Bolivar <mbolivar@leaflabs.com>; + * Original implementation by Michael Hope + * @brief STM32F1 DMA support. + */ + +#include <libmaple/dma.h> +#include <libmaple/bitband.h> + +/* + * Devices + */ + +static dma_dev dma1 = { + .regs = DMA1_BASE, + .clk_id = RCC_DMA1, + .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA_CH1 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH2 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH3 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH4 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH5 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH6 }, + { .handler = NULL, .irq_line = NVIC_DMA_CH7 }}, +}; +/** DMA1 device */ +dma_dev *DMA1 = &dma1; + +#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) +static dma_dev dma2 = { + .regs = DMA2_BASE, + .clk_id = RCC_DMA2, + .handlers = {{ .handler = NULL, .irq_line = NVIC_DMA2_CH1 }, + { .handler = NULL, .irq_line = NVIC_DMA2_CH2 }, + { .handler = NULL, .irq_line = NVIC_DMA2_CH3 }, + { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }, + { .handler = NULL, .irq_line = NVIC_DMA2_CH_4_5 }}, /* !@#$ */ +}; +/** DMA2 device */ +dma_dev *DMA2 = &dma2; +#endif + +/* + * Routines + */ + +/** + * @brief Set up a DMA transfer. + * + * The channel will be disabled before being reconfigured. The + * transfer will have low priority by default. You may choose another + * priority before the transfer begins using dma_set_priority(), as + * well as performing any other configuration you desire. When the + * channel is configured to your liking, enable it using dma_enable(). + * + * @param dev DMA device. + * @param channel DMA channel. + * @param peripheral_address Base address of peripheral data register + * involved in the transfer. + * @param peripheral_size Peripheral data transfer size. + * @param memory_address Base memory address involved in the transfer. + * @param memory_size Memory data transfer size. + * @param mode Logical OR of dma_mode_flags + * @sideeffect Disables the given DMA channel. + * @see dma_xfer_size + * @see dma_mode_flags + * @see dma_set_num_transfers() + * @see dma_set_priority() + * @see dma_attach_interrupt() + * @see dma_enable() + */ +void dma_setup_transfer(dma_dev *dev, + dma_channel channel, + __io void *peripheral_address, + dma_xfer_size peripheral_size, + __io void *memory_address, + dma_xfer_size memory_size, + uint32 mode) { + dma_channel_reg_map *channel_regs = dma_channel_regs(dev, channel); + + dma_disable(dev, channel); /* can't write to CMAR/CPAR otherwise */ + channel_regs->CCR = (memory_size << 10) | (peripheral_size << 8) | mode; + channel_regs->CMAR = (uint32)memory_address; + channel_regs->CPAR = (uint32)peripheral_address; +} + +/** + * @brief Set the number of data to be transferred on a DMA channel. + * + * You may not call this function while the channel is enabled. + * + * @param dev DMA device + * @param channel Channel through which the transfer occurs. + * @param num_transfers + */ +void dma_set_num_transfers(dma_dev *dev, + dma_channel channel, + uint16 num_transfers) { + dma_channel_reg_map *channel_regs; + + ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); + + channel_regs = dma_channel_regs(dev, channel); + channel_regs->CNDTR = num_transfers; +} + +/** + * @brief Set the priority of a DMA transfer. + * + * You may not call this function while the channel is enabled. + * + * @param dev DMA device + * @param channel DMA channel + * @param priority priority to set. + */ +void dma_set_priority(dma_dev *dev, + dma_channel channel, + dma_priority priority) { + dma_channel_reg_map *channel_regs; + uint32 ccr; + + ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); + + channel_regs = dma_channel_regs(dev, channel); + ccr = channel_regs->CCR; + ccr &= ~DMA_CCR_PL; + ccr |= priority; + channel_regs->CCR = ccr; +} + +/** + * @brief Attach an interrupt to a DMA transfer. + * + * Interrupts are enabled using appropriate mode flags in + * dma_setup_transfer(). + * + * @param dev DMA device + * @param channel Channel to attach handler to + * @param handler Interrupt handler to call when channel interrupt fires. + * @see dma_setup_transfer() + * @see dma_get_irq_cause() + * @see dma_detach_interrupt() + */ +void dma_attach_interrupt(dma_dev *dev, + dma_channel channel, + void (*handler)(void)) { + dev->handlers[channel - 1].handler = handler; + nvic_irq_enable(dev->handlers[channel - 1].irq_line); +} + +/** + * @brief Detach a DMA transfer interrupt handler. + * + * After calling this function, the given channel's interrupts will be + * disabled. + * + * @param dev DMA device + * @param channel Channel whose handler to detach + * @sideeffect Clears interrupt enable bits in the channel's CCR register. + * @see dma_attach_interrupt() + */ +void dma_detach_interrupt(dma_dev *dev, dma_channel channel) { + /* Don't use nvic_irq_disable()! Think about DMA2 channels 4 and 5. */ + dma_channel_regs(dev, channel)->CCR &= ~0xF; + dev->handlers[channel - 1].handler = NULL; +} + +/** + * @brief Discover the reason why a DMA interrupt was called. + * + * You may only call this function within an attached interrupt + * handler for the given channel. + * + * This function resets the internal DMA register state which encodes + * the cause of the interrupt; consequently, it can only be called + * once per interrupt handler invocation. + * + * @param dev DMA device + * @param channel Channel whose interrupt is being handled. + * @return Reason why the interrupt fired. + * @sideeffect Clears channel status flags in dev->regs->ISR. + * @see dma_attach_interrupt() + * @see dma_irq_cause + */ +dma_irq_cause dma_get_irq_cause(dma_dev *dev, dma_channel channel) { + uint8 status_bits = dma_get_isr_bits(dev, channel); + + /* If the channel global interrupt flag is cleared, then + * something's very wrong. */ + ASSERT(status_bits & BIT(0)); + + dma_clear_isr_bits(dev, channel); + + /* ISR flags get set even if the corresponding interrupt enable + * bits in the channel's configuration register are cleared, so we + * can't use a switch here. + * + * Don't change the order of these if statements. */ + if (status_bits & BIT(3)) { + return DMA_TRANSFER_ERROR; + } else if (status_bits & BIT(1)) { + return DMA_TRANSFER_COMPLETE; + } else if (status_bits & BIT(2)) { + return DMA_TRANSFER_HALF_COMPLETE; + } else if (status_bits & BIT(0)) { + /* Shouldn't happen (unless someone messed up an IFCR write). */ + throb(); + } +#if DEBUG_LEVEL < DEBUG_ALL + else { + /* We shouldn't have been called, but the debug level is too + * low for the above ASSERT() to have had any effect. In + * order to fail fast, mimic the DMA controller's behavior + * when an error occurs. */ + dma_disable(dev, channel); + } +#endif + return DMA_TRANSFER_ERROR; +} + +/** + * @brief Enable a DMA channel. + * @param dev DMA device + * @param channel Channel to enable + */ +void dma_enable(dma_dev *dev, dma_channel channel) { + dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel); + bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 1); +} + +/** + * @brief Disable a DMA channel. + * @param dev DMA device + * @param channel Channel to disable + */ +void dma_disable(dma_dev *dev, dma_channel channel) { + dma_channel_reg_map *chan_regs = dma_channel_regs(dev, channel); + bb_peri_set_bit(&chan_regs->CCR, DMA_CCR_EN_BIT, 0); +} + +/** + * @brief Set the base memory address where data will be read from or + * written to. + * + * You must not call this function while the channel is enabled. + * + * If the DMA memory size is 16 bits, the address is automatically + * aligned to a half-word. If the DMA memory size is 32 bits, the + * address is aligned to a word. + * + * @param dev DMA Device + * @param channel Channel whose base memory address to set. + * @param addr Memory base address to use. + */ +void dma_set_mem_addr(dma_dev *dev, dma_channel channel, __io void *addr) { + dma_channel_reg_map *chan_regs; + + ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); + + chan_regs = dma_channel_regs(dev, channel); + chan_regs->CMAR = (uint32)addr; +} + +/** + * @brief Set the base peripheral address where data will be read from + * or written to. + * + * You must not call this function while the channel is enabled. + * + * If the DMA peripheral size is 16 bits, the address is automatically + * aligned to a half-word. If the DMA peripheral size is 32 bits, the + * address is aligned to a word. + * + * @param dev DMA Device + * @param channel Channel whose peripheral data register base address to set. + * @param addr Peripheral memory base address to use. + */ +void dma_set_per_addr(dma_dev *dev, dma_channel channel, __io void *addr) { + dma_channel_reg_map *chan_regs; + + ASSERT_FAULT(!dma_is_channel_enabled(dev, channel)); + + chan_regs = dma_channel_regs(dev, channel); + chan_regs->CPAR = (uint32)addr; +} + +/* + * IRQ handlers + */ + +static __always_inline void dispatch_handler(dma_dev *dev, int channel) { + void (*handler)(void) = dev->handlers[channel - 1].handler; + if (handler) { + handler(); + dma_clear_isr_bits(dev, channel); /* in case handler doesn't */ + } +} + +void __irq_dma1_channel1(void) { + dispatch_handler(DMA1, DMA_CH1); +} + +void __irq_dma1_channel2(void) { + dispatch_handler(DMA1, DMA_CH2); +} + +void __irq_dma1_channel3(void) { + dispatch_handler(DMA1, DMA_CH3); +} + +void __irq_dma1_channel4(void) { + dispatch_handler(DMA1, DMA_CH4); +} + +void __irq_dma1_channel5(void) { + dispatch_handler(DMA1, DMA_CH5); +} + +void __irq_dma1_channel6(void) { + dispatch_handler(DMA1, DMA_CH6); +} + +void __irq_dma1_channel7(void) { + dispatch_handler(DMA1, DMA_CH7); +} + +#ifdef STM32_HIGH_DENSITY +void __irq_dma2_channel1(void) { + dispatch_handler(DMA2, DMA_CH1); +} + +void __irq_dma2_channel2(void) { + dispatch_handler(DMA2, DMA_CH2); +} + +void __irq_dma2_channel3(void) { + dispatch_handler(DMA2, DMA_CH3); +} + +void __irq_dma2_channel4_5(void) { + dispatch_handler(DMA2, DMA_CH4); + dispatch_handler(DMA2, DMA_CH5); +} +#endif diff --git a/libmaple/stm32f1/include/series/dma.h b/libmaple/stm32f1/include/series/dma.h new file mode 100644 index 0000000..60582ea --- /dev/null +++ b/libmaple/stm32f1/include/series/dma.h @@ -0,0 +1,438 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010 Michael Hope. + * Copyright (c) 2012 LeafLabs, LLC + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ + +/** + * @file libmaple/stm32f1/include/series/dma.h + * @author Marti Bolivar <mbolivar@leaflabs.com>; + * Original implementation by Michael Hope + * @brief STM32F1 Direct Memory Access header + */ + +/* + * See /notes/dma-stm32f1.txt for more information. + */ + +#ifndef _LIBMAPLE_STM32F1_DMA_H_ +#define _LIBMAPLE_STM32F1_DMA_H_ + +#ifdef __cplusplus +extern "C"{ +#endif + +#include <libmaple/libmaple_types.h> + +/* + * Register map and base pointers + */ + +/** + * @brief DMA register map type. + * + * Note that DMA controller 2 (register map base pointer DMA2_BASE) + * only supports channels 1--5. + */ +typedef struct dma_reg_map { + __io uint32 ISR; /**< Interrupt status register */ + __io uint32 IFCR; /**< Interrupt flag clear register */ + __io uint32 CCR1; /**< Channel 1 configuration register */ + __io uint32 CNDTR1; /**< Channel 1 number of data register */ + __io uint32 CPAR1; /**< Channel 1 peripheral address register */ + __io uint32 CMAR1; /**< Channel 1 memory address register */ + const uint32 RESERVED1; /**< Reserved. */ + __io uint32 CCR2; /**< Channel 2 configuration register */ + __io uint32 CNDTR2; /**< Channel 2 number of data register */ + __io uint32 CPAR2; /**< Channel 2 peripheral address register */ + __io uint32 CMAR2; /**< Channel 2 memory address register */ + const uint32 RESERVED2; /**< Reserved. */ + __io uint32 CCR3; /**< Channel 3 configuration register */ + __io uint32 CNDTR3; /**< Channel 3 number of data register */ + __io uint32 CPAR3; /**< Channel 3 peripheral address register */ + __io uint32 CMAR3; /**< Channel 3 memory address register */ + const uint32 RESERVED3; /**< Reserved. */ + __io uint32 CCR4; /**< Channel 4 configuration register */ + __io uint32 CNDTR4; /**< Channel 4 number of data register */ + __io uint32 CPAR4; /**< Channel 4 peripheral address register */ + __io uint32 CMAR4; /**< Channel 4 memory address register */ + const uint32 RESERVED4; /**< Reserved. */ + __io uint32 CCR5; /**< Channel 5 configuration register */ + __io uint32 CNDTR5; /**< Channel 5 number of data register */ + __io uint32 CPAR5; /**< Channel 5 peripheral address register */ + __io uint32 CMAR5; /**< Channel 5 memory address register */ + const uint32 RESERVED5; /**< Reserved. */ + __io uint32 CCR6; /**< Channel 6 configuration register */ + __io uint32 CNDTR6; /**< Channel 6 number of data register */ + __io uint32 CPAR6; /**< Channel 6 peripheral address register */ + __io uint32 CMAR6; /**< Channel 6 memory address register */ + const uint32 RESERVED6; /**< Reserved. */ + __io uint32 CCR7; /**< Channel 7 configuration register */ + __io uint32 CNDTR7; /**< Channel 7 number of data register */ + __io uint32 CPAR7; /**< Channel 7 peripheral address register */ + __io uint32 CMAR7; /**< Channel 7 memory address register */ + const uint32 RESERVED7; /**< Reserved. */ +} dma_reg_map; + +/** DMA controller 1 register map base pointer */ +#define DMA1_BASE ((struct dma_reg_map*)0x40020000) +/** DMA controller 2 register map base pointer */ +#define DMA2_BASE ((struct dma_reg_map*)0x40020400) + +/* + * Register bit definitions + */ + +/* Interrupt status register */ + +#define DMA_ISR_TEIF7_BIT 27 +#define DMA_ISR_HTIF7_BIT 26 +#define DMA_ISR_TCIF7_BIT 25 +#define DMA_ISR_GIF7_BIT 24 +#define DMA_ISR_TEIF6_BIT 23 +#define DMA_ISR_HTIF6_BIT 22 +#define DMA_ISR_TCIF6_BIT 21 +#define DMA_ISR_GIF6_BIT 20 +#define DMA_ISR_TEIF5_BIT 19 +#define DMA_ISR_HTIF5_BIT 18 +#define DMA_ISR_TCIF5_BIT 17 +#define DMA_ISR_GIF5_BIT 16 +#define DMA_ISR_TEIF4_BIT 15 +#define DMA_ISR_HTIF4_BIT 14 +#define DMA_ISR_TCIF4_BIT 13 +#define DMA_ISR_GIF4_BIT 12 +#define DMA_ISR_TEIF3_BIT 11 +#define DMA_ISR_HTIF3_BIT 10 +#define DMA_ISR_TCIF3_BIT 9 +#define DMA_ISR_GIF3_BIT 8 +#define DMA_ISR_TEIF2_BIT 7 +#define DMA_ISR_HTIF2_BIT 6 +#define DMA_ISR_TCIF2_BIT 5 +#define DMA_ISR_GIF2_BIT 4 +#define DMA_ISR_TEIF1_BIT 3 +#define DMA_ISR_HTIF1_BIT 2 +#define DMA_ISR_TCIF1_BIT 1 +#define DMA_ISR_GIF1_BIT 0 + +#define DMA_ISR_TEIF7 (1U << DMA_ISR_TEIF7_BIT) +#define DMA_ISR_HTIF7 (1U << DMA_ISR_HTIF7_BIT) +#define DMA_ISR_TCIF7 (1U << DMA_ISR_TCIF7_BIT) +#define DMA_ISR_GIF7 (1U << DMA_ISR_GIF7_BIT) +#define DMA_ISR_TEIF6 (1U << DMA_ISR_TEIF6_BIT) +#define DMA_ISR_HTIF6 (1U << DMA_ISR_HTIF6_BIT) +#define DMA_ISR_TCIF6 (1U << DMA_ISR_TCIF6_BIT) +#define DMA_ISR_GIF6 (1U << DMA_ISR_GIF6_BIT) +#define DMA_ISR_TEIF5 (1U << DMA_ISR_TEIF5_BIT) +#define DMA_ISR_HTIF5 (1U << DMA_ISR_HTIF5_BIT) +#define DMA_ISR_TCIF5 (1U << DMA_ISR_TCIF5_BIT) +#define DMA_ISR_GIF5 (1U << DMA_ISR_GIF5_BIT) +#define DMA_ISR_TEIF4 (1U << DMA_ISR_TEIF4_BIT) +#define DMA_ISR_HTIF4 (1U << DMA_ISR_HTIF4_BIT) +#define DMA_ISR_TCIF4 (1U << DMA_ISR_TCIF4_BIT) +#define DMA_ISR_GIF4 (1U << DMA_ISR_GIF4_BIT) +#define DMA_ISR_TEIF3 (1U << DMA_ISR_TEIF3_BIT) +#define DMA_ISR_HTIF3 (1U << DMA_ISR_HTIF3_BIT) +#define DMA_ISR_TCIF3 (1U << DMA_ISR_TCIF3_BIT) +#define DMA_ISR_GIF3 (1U << DMA_ISR_GIF3_BIT) +#define DMA_ISR_TEIF2 (1U << DMA_ISR_TEIF2_BIT) +#define DMA_ISR_HTIF2 (1U << DMA_ISR_HTIF2_BIT) +#define DMA_ISR_TCIF2 (1U << DMA_ISR_TCIF2_BIT) +#define DMA_ISR_GIF2 (1U << DMA_ISR_GIF2_BIT) +#define DMA_ISR_TEIF1 (1U << DMA_ISR_TEIF1_BIT) +#define DMA_ISR_HTIF1 (1U << DMA_ISR_HTIF1_BIT) +#define DMA_ISR_TCIF1 (1U << DMA_ISR_TCIF1_BIT) +#define DMA_ISR_GIF1 (1U << DMA_ISR_GIF1_BIT) + +/* Interrupt flag clear register */ + +#define DMA_IFCR_CTEIF7_BIT 27 +#define DMA_IFCR_CHTIF7_BIT 26 +#define DMA_IFCR_CTCIF7_BIT 25 +#define DMA_IFCR_CGIF7_BIT 24 +#define DMA_IFCR_CTEIF6_BIT 23 +#define DMA_IFCR_CHTIF6_BIT 22 +#define DMA_IFCR_CTCIF6_BIT 21 +#define DMA_IFCR_CGIF6_BIT 20 +#define DMA_IFCR_CTEIF5_BIT 19 +#define DMA_IFCR_CHTIF5_BIT 18 +#define DMA_IFCR_CTCIF5_BIT 17 +#define DMA_IFCR_CGIF5_BIT 16 +#define DMA_IFCR_CTEIF4_BIT 15 +#define DMA_IFCR_CHTIF4_BIT 14 +#define DMA_IFCR_CTCIF4_BIT 13 +#define DMA_IFCR_CGIF4_BIT 12 +#define DMA_IFCR_CTEIF3_BIT 11 +#define DMA_IFCR_CHTIF3_BIT 10 +#define DMA_IFCR_CTCIF3_BIT 9 +#define DMA_IFCR_CGIF3_BIT 8 +#define DMA_IFCR_CTEIF2_BIT 7 +#define DMA_IFCR_CHTIF2_BIT 6 +#define DMA_IFCR_CTCIF2_BIT 5 +#define DMA_IFCR_CGIF2_BIT 4 +#define DMA_IFCR_CTEIF1_BIT 3 +#define DMA_IFCR_CHTIF1_BIT 2 +#define DMA_IFCR_CTCIF1_BIT 1 +#define DMA_IFCR_CGIF1_BIT 0 + +#define DMA_IFCR_CTEIF7 (1U << DMA_IFCR_CTEIF7_BIT) +#define DMA_IFCR_CHTIF7 (1U << DMA_IFCR_CHTIF7_BIT) +#define DMA_IFCR_CTCIF7 (1U << DMA_IFCR_CTCIF7_BIT) +#define DMA_IFCR_CGIF7 (1U << DMA_IFCR_CGIF7_BIT) +#define DMA_IFCR_CTEIF6 (1U << DMA_IFCR_CTEIF6_BIT) +#define DMA_IFCR_CHTIF6 (1U << DMA_IFCR_CHTIF6_BIT) +#define DMA_IFCR_CTCIF6 (1U << DMA_IFCR_CTCIF6_BIT) +#define DMA_IFCR_CGIF6 (1U << DMA_IFCR_CGIF6_BIT) +#define DMA_IFCR_CTEIF5 (1U << DMA_IFCR_CTEIF5_BIT) +#define DMA_IFCR_CHTIF5 (1U << DMA_IFCR_CHTIF5_BIT) +#define DMA_IFCR_CTCIF5 (1U << DMA_IFCR_CTCIF5_BIT) +#define DMA_IFCR_CGIF5 (1U << DMA_IFCR_CGIF5_BIT) +#define DMA_IFCR_CTEIF4 (1U << DMA_IFCR_CTEIF4_BIT) +#define DMA_IFCR_CHTIF4 (1U << DMA_IFCR_CHTIF4_BIT) +#define DMA_IFCR_CTCIF4 (1U << DMA_IFCR_CTCIF4_BIT) +#define DMA_IFCR_CGIF4 (1U << DMA_IFCR_CGIF4_BIT) +#define DMA_IFCR_CTEIF3 (1U << DMA_IFCR_CTEIF3_BIT) +#define DMA_IFCR_CHTIF3 (1U << DMA_IFCR_CHTIF3_BIT) +#define DMA_IFCR_CTCIF3 (1U << DMA_IFCR_CTCIF3_BIT) +#define DMA_IFCR_CGIF3 (1U << DMA_IFCR_CGIF3_BIT) +#define DMA_IFCR_CTEIF2 (1U << DMA_IFCR_CTEIF2_BIT) +#define DMA_IFCR_CHTIF2 (1U << DMA_IFCR_CHTIF2_BIT) +#define DMA_IFCR_CTCIF2 (1U << DMA_IFCR_CTCIF2_BIT) +#define DMA_IFCR_CGIF2 (1U << DMA_IFCR_CGIF2_BIT) +#define DMA_IFCR_CTEIF1 (1U << DMA_IFCR_CTEIF1_BIT) +#define DMA_IFCR_CHTIF1 (1U << DMA_IFCR_CHTIF1_BIT) +#define DMA_IFCR_CTCIF1 (1U << DMA_IFCR_CTCIF1_BIT) +#define DMA_IFCR_CGIF1 (1U << DMA_IFCR_CGIF1_BIT) + +/* Channel configuration register */ + +#define DMA_CCR_MEM2MEM_BIT 14 +#define DMA_CCR_MINC_BIT 7 +#define DMA_CCR_PINC_BIT 6 +#define DMA_CCR_CIRC_BIT 5 +#define DMA_CCR_DIR_BIT 4 +#define DMA_CCR_TEIE_BIT 3 +#define DMA_CCR_HTIE_BIT 2 +#define DMA_CCR_TCIE_BIT 1 +#define DMA_CCR_EN_BIT 0 + +#define DMA_CCR_MEM2MEM (1U << DMA_CCR_MEM2MEM_BIT) +#define DMA_CCR_PL (0x3 << 12) +#define DMA_CCR_PL_LOW (0x0 << 12) +#define DMA_CCR_PL_MEDIUM (0x1 << 12) +#define DMA_CCR_PL_HIGH (0x2 << 12) +#define DMA_CCR_PL_VERY_HIGH (0x3 << 12) +#define DMA_CCR_MSIZE (0x3 << 10) +#define DMA_CCR_MSIZE_8BITS (0x0 << 10) +#define DMA_CCR_MSIZE_16BITS (0x1 << 10) +#define DMA_CCR_MSIZE_32BITS (0x2 << 10) +#define DMA_CCR_PSIZE (0x3 << 8) +#define DMA_CCR_PSIZE_8BITS (0x0 << 8) +#define DMA_CCR_PSIZE_16BITS (0x1 << 8) +#define DMA_CCR_PSIZE_32BITS (0x2 << 8) +#define DMA_CCR_MINC (1U << DMA_CCR_MINC_BIT) +#define DMA_CCR_PINC (1U << DMA_CCR_PINC_BIT) +#define DMA_CCR_CIRC (1U << DMA_CCR_CIRC_BIT) +#define DMA_CCR_DIR (1U << DMA_CCR_DIR_BIT) +#define DMA_CCR_TEIE (1U << DMA_CCR_TEIE_BIT) +#define DMA_CCR_HTIE (1U << DMA_CCR_HTIE_BIT) +#define DMA_CCR_TCIE (1U << DMA_CCR_TCIE_BIT) +#define DMA_CCR_EN (1U << DMA_CCR_EN_BIT) + +/* + * Devices + */ + +struct dma_dev; +extern struct dma_dev *DMA1; +#if defined(STM32_HIGH_DENSITY) || defined(STM32_XL_DENSITY) +extern struct dma_dev *DMA2; +#endif + +/* + * Convenience routines. + */ + +/* This hack is due to a circular dependency between us and + * <libmaple/dma.h>. */ +static __always_inline dma_reg_map* _dma_dev_regs(struct dma_dev*); + +/** Flags for DMA transfer configuration. */ +typedef enum dma_mode_flags { + DMA_MEM_2_MEM = 1 << 14, /**< Memory to memory mode */ + DMA_MINC_MODE = 1 << 7, /**< Auto-increment memory address */ + DMA_PINC_MODE = 1 << 6, /**< Auto-increment peripheral address */ + DMA_CIRC_MODE = 1 << 5, /**< Circular mode */ + DMA_FROM_MEM = 1 << 4, /**< Read from memory to peripheral */ + DMA_TRNS_ERR = 1 << 3, /**< Interrupt on transfer error */ + DMA_HALF_TRNS = 1 << 2, /**< Interrupt on half-transfer */ + DMA_TRNS_CMPLT = 1 << 1 /**< Interrupt on transfer completion */ +} dma_mode_flags; + +/** Source and destination transfer sizes. */ +typedef enum dma_xfer_size { + DMA_SIZE_8BITS = 0, /**< 8-bit transfers */ + DMA_SIZE_16BITS = 1, /**< 16-bit transfers */ + DMA_SIZE_32BITS = 2 /**< 32-bit transfers */ +} dma_xfer_size; + +/** DMA channel */ +typedef enum dma_channel { + DMA_CH1 = 1, /**< Channel 1 */ + DMA_CH2 = 2, /**< Channel 2 */ + DMA_CH3 = 3, /**< Channel 3 */ + DMA_CH4 = 4, /**< Channel 4 */ + DMA_CH5 = 5, /**< Channel 5 */ + DMA_CH6 = 6, /**< Channel 6 */ + DMA_CH7 = 7, /**< Channel 7 */ +} dma_channel; + +void dma_setup_transfer(struct dma_dev *dev, + dma_channel channel, + __io void *peripheral_address, + dma_xfer_size peripheral_size, + __io void *memory_address, + dma_xfer_size memory_size, + uint32 mode); + +void dma_set_num_transfers(struct dma_dev *dev, + dma_channel channel, + uint16 num_transfers); + +/** DMA transfer priority. */ +typedef enum dma_priority { + DMA_PRIORITY_LOW = DMA_CCR_PL_LOW, /**< Low priority */ + DMA_PRIORITY_MEDIUM = DMA_CCR_PL_MEDIUM, /**< Medium priority */ + DMA_PRIORITY_HIGH = DMA_CCR_PL_HIGH, /**< High priority */ + DMA_PRIORITY_VERY_HIGH = DMA_CCR_PL_VERY_HIGH /**< Very high priority */ +} dma_priority; + +void dma_set_priority(struct dma_dev *dev, + dma_channel channel, + dma_priority priority); + +void dma_attach_interrupt(struct dma_dev *dev, + dma_channel channel, + void (*handler)(void)); +void dma_detach_interrupt(struct dma_dev *dev, dma_channel channel); + +/** + * Encodes the reason why a DMA interrupt was called. + * @see dma_get_irq_cause() + */ +typedef enum dma_irq_cause { + DMA_TRANSFER_COMPLETE, /**< Transfer is complete. */ + DMA_TRANSFER_HALF_COMPLETE, /**< Transfer is half complete. */ + DMA_TRANSFER_ERROR, /**< Error occurred during transfer. */ +} dma_irq_cause; + +dma_irq_cause dma_get_irq_cause(struct dma_dev *dev, dma_channel channel); + +void dma_enable(struct dma_dev *dev, dma_channel channel); +void dma_disable(struct dma_dev *dev, dma_channel channel); + +void dma_set_mem_addr(struct dma_dev *dev, + dma_channel channel, + __io void *address); +void dma_set_per_addr(struct dma_dev *dev, + dma_channel channel, + __io void *address); + +/** + * @brief DMA channel register map type. + * + * Provides access to an individual channel's registers. + */ +typedef struct dma_channel_reg_map { + __io uint32 CCR; /**< Channel configuration register */ + __io uint32 CNDTR; /**< Channel number of data register */ + __io uint32 CPAR; /**< Channel peripheral address register */ + __io uint32 CMAR; /**< Channel memory address register */ +} dma_channel_reg_map; + +#define DMA_CHANNEL_NREGS 5 + +/** + * @brief Obtain a pointer to an individual DMA channel's registers. + * + * For example, dma_channel_regs(DMA1, DMA_CH1)->CCR is DMA1_BASE->CCR1. + * + * @param dev DMA device + * @param channel DMA channel whose channel register map to obtain. + */ +static inline dma_channel_reg_map* dma_channel_regs(struct dma_dev *dev, + dma_channel channel) { + __io uint32 *ccr1 = &_dma_dev_regs(dev)->CCR1; + return (dma_channel_reg_map*)(ccr1 + DMA_CHANNEL_NREGS * (channel - 1)); +} + +/** + * @brief Check if a DMA channel is enabled + * @param dev DMA device + * @param channel Channel whose enabled bit to check. + */ +static inline uint8 dma_is_channel_enabled(struct dma_dev *dev, + dma_channel channel) { + return (uint8)(dma_channel_regs(dev, channel)->CCR & DMA_CCR_EN); +} + +/** + * @brief Get the ISR status bits for a DMA channel. + * + * The bits are returned right-aligned, in the following order: + * transfer error flag, half-transfer flag, transfer complete flag, + * global interrupt flag. + * + * If you're attempting to figure out why a DMA interrupt fired; you + * may find dma_get_irq_cause() more convenient. + * + * @param dev DMA device + * @param channel Channel whose ISR bits to return. + * @see dma_get_irq_cause(). + */ +static inline uint8 dma_get_isr_bits(struct dma_dev *dev, + dma_channel channel) { + uint8 shift = (channel - 1) * 4; + return (_dma_dev_regs(dev)->ISR >> shift) & 0xF; +} + +/** + * @brief Clear the ISR status bits for a given DMA channel. + * + * If you're attempting to clean up after yourself in a DMA interrupt, + * you may find dma_get_irq_cause() more convenient. + * + * @param dev DMA device + * @param channel Channel whose ISR bits to clear. + * @see dma_get_irq_cause() + */ +static inline void dma_clear_isr_bits(struct dma_dev *dev, + dma_channel channel) { + _dma_dev_regs(dev)->IFCR = (1U << (4 * (channel - 1))); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif diff --git a/libmaple/stm32f1/rules.mk b/libmaple/stm32f1/rules.mk index 52ceea6..b67f446 100644 --- a/libmaple/stm32f1/rules.mk +++ b/libmaple/stm32f1/rules.mk @@ -13,6 +13,7 @@ sSRCS_$(d) += vector_table_performance.S cSRCS_$(d) := adc.c cSRCS_$(d) += bkp.c +cSRCS_$(d) += dma.c cSRCS_$(d) += fsmc.c cSRCS_$(d) += gpio.c cSRCS_$(d) += rcc.c |