mDMA implements DMA APIs for mbed. It is inspired by modDMA and simpleDMA. Compared with other mbed DMA implementations, mDMA has new features like 1) support LLI 2) support more than 4KB data transfer 3) support vectorized transfer. 4) support burst transfer. 5) Improved memory-memory transfer. It could beat memcpy 6) The library implementation fit the code structure of mbed sdk. Currently only support LPC1768 but could be extended to other platforms.

Dependents:   test_mDMA

Committer:
steniu01
Date:
Mon Mar 09 21:47:24 2015 +0000
Revision:
1:9421d79fb372
Parent:
0:8e50c5fd42f6
improved the coding style

Who changed what in which revision?

UserRevisionLine numberNew contents of line
steniu01 0:8e50c5fd42f6 1 #include "dma_api.h"
steniu01 0:8e50c5fd42f6 2 #include <assert.h>
steniu01 0:8e50c5fd42f6 3
steniu01 0:8e50c5fd42f6 4
steniu01 0:8e50c5fd42f6 5 int _channel_num = 8;
steniu01 0:8e50c5fd42f6 6
steniu01 0:8e50c5fd42f6 7 //Define LPC1768 DMA IRQ. Different platforms might have different name
steniu01 0:8e50c5fd42f6 8 IRQn_Type _DMA_IRQ = DMA_IRQn;
steniu01 0:8e50c5fd42f6 9
steniu01 0:8e50c5fd42f6 10 //DMA channel control register bit set
steniu01 0:8e50c5fd42f6 11 #define DMA_CCxControl_TransferSize_Pos 0
steniu01 0:8e50c5fd42f6 12 #define DMA_CCxControl_SBSize_Pos 12
steniu01 0:8e50c5fd42f6 13 #define DMA_CCxControl_DBSize_Pos 15
steniu01 0:8e50c5fd42f6 14 #define DMA_CCxControl_SWidth_Pos 18
steniu01 0:8e50c5fd42f6 15 #define DMA_CCxControl_DWidth_Pos 21
steniu01 0:8e50c5fd42f6 16 #define DMA_CCxControl_SI_Pos 26
steniu01 0:8e50c5fd42f6 17 #define DMA_CCxControl_DI_Pos 27
steniu01 0:8e50c5fd42f6 18 #define DMA_CCxControl_I_Pos 31
steniu01 0:8e50c5fd42f6 19
steniu01 0:8e50c5fd42f6 20 //DMA Channel config register bit set
steniu01 0:8e50c5fd42f6 21 #define DMA_CCxConfig_E_Pos 0
steniu01 0:8e50c5fd42f6 22 #define DMA_CCxConfig_SrcPeripheral_Pos 1
steniu01 0:8e50c5fd42f6 23 #define DMA_CCxConfig_DestPeripheral_Pos 6
steniu01 0:8e50c5fd42f6 24 #define DMA_CCxConfig_TransferType_Pos 11
steniu01 0:8e50c5fd42f6 25 #define DMA_CCxConfig_IE_Pos 14
steniu01 0:8e50c5fd42f6 26 #define DMA_CCxConfig_ITC_Pos 15
steniu01 0:8e50c5fd42f6 27 #define DMA_CCxConfig_L_Pos 16
steniu01 0:8e50c5fd42f6 28 #define DMA_CCxConfig_A_Pos 17
steniu01 0:8e50c5fd42f6 29 #define DMA_CCxConfig_H_Pos 18
steniu01 0:8e50c5fd42f6 30
steniu01 0:8e50c5fd42f6 31 // DMA interrupt callback
steniu01 0:8e50c5fd42f6 32 func_ptr dma_irq_finish[8];
steniu01 0:8e50c5fd42f6 33 func_ptr dma_irq_error[8];
steniu01 0:8e50c5fd42f6 34
steniu01 0:8e50c5fd42f6 35
steniu01 0:8e50c5fd42f6 36
steniu01 0:8e50c5fd42f6 37 // Optimisation 1. Use burst mode
steniu01 0:8e50c5fd42f6 38 #define BURST_ENABLED 1
steniu01 0:8e50c5fd42f6 39 // Optimisation 2. Pack bytes/half to word
steniu01 0:8e50c5fd42f6 40 #define WORD_TRANSFER 1
steniu01 0:8e50c5fd42f6 41 // define whether transfer large size data supported
steniu01 0:8e50c5fd42f6 42 //#define LARGE_DATA_TRANSFER 1
steniu01 0:8e50c5fd42f6 43
steniu01 0:8e50c5fd42f6 44
steniu01 0:8e50c5fd42f6 45 // map the trigger index
steniu01 0:8e50c5fd42f6 46 int trigger_value [33] = {
steniu01 0:8e50c5fd42f6 47 -1, // ALWAYS ,
steniu01 0:8e50c5fd42f6 48 0, // _SSP0_TX,
steniu01 0:8e50c5fd42f6 49 1, // _SSP0_RX,
steniu01 0:8e50c5fd42f6 50 2, // _SSP1_TX,
steniu01 0:8e50c5fd42f6 51 3, // _SSP1_RX,
steniu01 0:8e50c5fd42f6 52 4, // _ADC,
steniu01 0:8e50c5fd42f6 53 5, // _I2S0,
steniu01 0:8e50c5fd42f6 54 6, // _I2S1,
steniu01 0:8e50c5fd42f6 55 7, // _DAC,
steniu01 0:8e50c5fd42f6 56 8, // _UART0_TX,
steniu01 0:8e50c5fd42f6 57 9, // _UART0_RX,
steniu01 0:8e50c5fd42f6 58 10, // _UART1_TX,
steniu01 0:8e50c5fd42f6 59 11, // _UART1_RX,
steniu01 0:8e50c5fd42f6 60 12, // _UART2_TX,
steniu01 0:8e50c5fd42f6 61 13, // _UART2_RX,
steniu01 0:8e50c5fd42f6 62 14, // _UART3_TX,
steniu01 0:8e50c5fd42f6 63 15, // _UART3_RX,
steniu01 0:8e50c5fd42f6 64 24, // _MATCH0_0,
steniu01 0:8e50c5fd42f6 65 25, // _MATCH0_1,
steniu01 0:8e50c5fd42f6 66 26, // _MATCH1_0,
steniu01 0:8e50c5fd42f6 67 27, // _MATCH1_1,
steniu01 0:8e50c5fd42f6 68 28, // _MATCH2_0,
steniu01 0:8e50c5fd42f6 69 29, // _MATCH2_1,
steniu01 0:8e50c5fd42f6 70 30, // _MATCH3_0,
steniu01 0:8e50c5fd42f6 71 31 // _MATCH3_1,
steniu01 0:8e50c5fd42f6 72 };
steniu01 0:8e50c5fd42f6 73
steniu01 0:8e50c5fd42f6 74
steniu01 0:8e50c5fd42f6 75
steniu01 0:8e50c5fd42f6 76 typedef struct DMA_InitTypeDef{
steniu01 0:8e50c5fd42f6 77 uint32_t DMA_DstAddr; //Specifies the destination base address for DMAy Channelx.
steniu01 0:8e50c5fd42f6 78 uint32_t DMA_SrcAddr; // Specifies the source base address for DMAy Channelx.
steniu01 0:8e50c5fd42f6 79 uint32_t DMA_TransferSize;// Specifies the source transfer size
steniu01 0:8e50c5fd42f6 80 uint32_t DMA_SrcBurst; // Specifies the source burst size
steniu01 0:8e50c5fd42f6 81 uint32_t DMA_DestBurst; // Specifies the destination burst size
steniu01 0:8e50c5fd42f6 82 uint32_t DMA_SrcWidth; //Specifies the source transfer width
steniu01 0:8e50c5fd42f6 83 uint32_t DMA_DestWidth; // Specifies the destination transfer width
steniu01 0:8e50c5fd42f6 84 uint32_t DMA_LLI; // Specifies the next link address
steniu01 0:8e50c5fd42f6 85 LLI* LLI_list[32]; // Declare an array to store up to 32 linked lists. We probably won't need that much
steniu01 0:8e50c5fd42f6 86 unsigned int LLI_count;
steniu01 0:8e50c5fd42f6 87 bool DMA_SrcInc; // Specifies whether the source is incremented or not
steniu01 0:8e50c5fd42f6 88 bool DMA_DestInc; // Specifies whether the destination is incremented or not
steniu01 0:8e50c5fd42f6 89 bool DMA_TermInt; // Specifies whether the terminal count interrupt enabled or not
steniu01 0:8e50c5fd42f6 90 uint32_t DMA_TriggerSource; // Specifies the source trigger
steniu01 0:8e50c5fd42f6 91 uint32_t DMA_TriggerDestination; // Specifies the destination trigger
steniu01 0:8e50c5fd42f6 92 TransferType DMA_TransferType;
steniu01 0:8e50c5fd42f6 93 }DMA_InitTypeDef ;
steniu01 0:8e50c5fd42f6 94
steniu01 0:8e50c5fd42f6 95
steniu01 0:8e50c5fd42f6 96
steniu01 0:8e50c5fd42f6 97 /**
steniu01 0:8e50c5fd42f6 98 * @brief Check whether the address is with memory ranage.
steniu01 0:8e50c5fd42f6 99 * @param src: the physical address
steniu01 0:8e50c5fd42f6 100 * @retval 0 or 1
steniu01 0:8e50c5fd42f6 101 */
steniu01 0:8e50c5fd42f6 102 inline static bool is_memory(uint32_t addr);
steniu01 0:8e50c5fd42f6 103
steniu01 0:8e50c5fd42f6 104 /**
steniu01 0:8e50c5fd42f6 105 * @brief Static function. Automatice cacluate the transfer type according to the source and destination address
steniu01 0:8e50c5fd42f6 106 * @param src_addr. Source starting address.
steniu01 0:8e50c5fd42f6 107 * @param dst_addr. Destination starting address.
steniu01 0:8e50c5fd42f6 108 * @retval Transfer type. It can be M2M,M2P,P2M or P2P
steniu01 0:8e50c5fd42f6 109 */
steniu01 0:8e50c5fd42f6 110 static TransferType get_transfer_type(uint32_t src_addr, uint32_t dst_addr );
steniu01 0:8e50c5fd42f6 111
steniu01 0:8e50c5fd42f6 112 /**
steniu01 0:8e50c5fd42f6 113 * @brief Static function. Get the channel address according to the channel number
steniu01 0:8e50c5fd42f6 114 * @param channel. The chosen channel number
steniu01 0:8e50c5fd42f6 115 * @retval Chosen channel base address
steniu01 0:8e50c5fd42f6 116 */
steniu01 0:8e50c5fd42f6 117
steniu01 0:8e50c5fd42f6 118 inline static LPC_GPDMACH_TypeDef* return_channel(int channel)
steniu01 0:8e50c5fd42f6 119 {
steniu01 0:8e50c5fd42f6 120 return (LPC_GPDMACH_TypeDef*)(LPC_GPDMACH0_BASE + 0x20*channel);
steniu01 0:8e50c5fd42f6 121 }
steniu01 0:8e50c5fd42f6 122
steniu01 0:8e50c5fd42f6 123
steniu01 0:8e50c5fd42f6 124 /**
steniu01 0:8e50c5fd42f6 125 * @brief Enable DMA Burst mode for optimisation
steniu01 0:8e50c5fd42f6 126 */
steniu01 0:8e50c5fd42f6 127
steniu01 0:8e50c5fd42f6 128
steniu01 0:8e50c5fd42f6 129 void DMA_destination(DMA_InitTypeDef* DMA_InitStruct, uint32_t dst, unsigned int width, bool inc)
steniu01 0:8e50c5fd42f6 130 {
steniu01 0:8e50c5fd42f6 131 DMA_InitStruct->DMA_DstAddr = dst;
steniu01 0:8e50c5fd42f6 132 DMA_InitStruct->DMA_SrcBurst = 0x00; // 1 byte To be done
steniu01 0:8e50c5fd42f6 133 DMA_InitStruct->DMA_DestWidth = (width >>4);
steniu01 0:8e50c5fd42f6 134 DMA_InitStruct->DMA_DestInc = inc;
steniu01 0:8e50c5fd42f6 135 }
steniu01 0:8e50c5fd42f6 136
steniu01 0:8e50c5fd42f6 137
steniu01 0:8e50c5fd42f6 138 void DMA_source(DMA_InitTypeDef* DMA_InitStruct, uint32_t src, unsigned int width, bool inc)
steniu01 0:8e50c5fd42f6 139 {
steniu01 0:8e50c5fd42f6 140 DMA_InitStruct->DMA_SrcAddr = src;
steniu01 0:8e50c5fd42f6 141 DMA_InitStruct->DMA_SrcBurst = 0x00; // 1 byte
steniu01 0:8e50c5fd42f6 142 DMA_InitStruct->DMA_SrcInc = inc;
steniu01 0:8e50c5fd42f6 143 DMA_InitStruct->DMA_SrcWidth = (width >> 4);
steniu01 0:8e50c5fd42f6 144 }
steniu01 0:8e50c5fd42f6 145
steniu01 0:8e50c5fd42f6 146
steniu01 0:8e50c5fd42f6 147 void DMA_trigger_source(DMA_InitTypeDef* DMA_InitStruct, TriggerType trig)
steniu01 0:8e50c5fd42f6 148 {
steniu01 0:8e50c5fd42f6 149 DMA_InitStruct->DMA_TriggerSource = trigger_value[trig];
steniu01 0:8e50c5fd42f6 150 }
steniu01 0:8e50c5fd42f6 151
steniu01 0:8e50c5fd42f6 152
steniu01 0:8e50c5fd42f6 153 void DMA_trigger_destination(DMA_InitTypeDef* DMA_InitStruct, TriggerType trig)
steniu01 0:8e50c5fd42f6 154 {
steniu01 0:8e50c5fd42f6 155 DMA_InitStruct->DMA_TriggerDestination = trigger_value[trig];
steniu01 0:8e50c5fd42f6 156 }
steniu01 0:8e50c5fd42f6 157 // Currently, not support more than 4K transfer in LLI
steniu01 0:8e50c5fd42f6 158 void DMA_next(DMA_InitTypeDef* DMA_InitStruct, const uint32_t src, const uint32_t dst, uint32_t size)
steniu01 0:8e50c5fd42f6 159 {
steniu01 0:8e50c5fd42f6 160 //static int count = 0;
steniu01 0:8e50c5fd42f6 161 DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count] = malloc(sizeof(LLI));
steniu01 0:8e50c5fd42f6 162 DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count]->LLI_dst = dst;
steniu01 0:8e50c5fd42f6 163 DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count]->LLI_src = src;
steniu01 0:8e50c5fd42f6 164 DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count]->LLI_next = 0; // 0 not NULL as the programmer manual says LLI_next should be 0 if it is the last of the lists
steniu01 0:8e50c5fd42f6 165 DMA_InitStruct->LLI_list[DMA_InitStruct->LLI_count]->LLI_control = size & 0xFFF;
steniu01 0:8e50c5fd42f6 166 DMA_InitStruct->LLI_count++;
steniu01 0:8e50c5fd42f6 167 // TBD: need to add assert to check the input size;
steniu01 0:8e50c5fd42f6 168 }
steniu01 0:8e50c5fd42f6 169
steniu01 0:8e50c5fd42f6 170
steniu01 0:8e50c5fd42f6 171 /*
steniu01 0:8e50c5fd42f6 172 void DMA_next(DMA_InitTypeDef* DMA_InitStruct, LLI* list)
steniu01 0:8e50c5fd42f6 173 {
steniu01 0:8e50c5fd42f6 174 DMA_InitStruct->DMA_LLI = (uint32_t)list;
steniu01 0:8e50c5fd42f6 175 }
steniu01 0:8e50c5fd42f6 176 */
steniu01 0:8e50c5fd42f6 177
steniu01 0:8e50c5fd42f6 178
steniu01 0:8e50c5fd42f6 179 void DMA_init(int channel, DMA_InitTypeDef* DMA_InitStruct)
steniu01 0:8e50c5fd42f6 180 {
steniu01 0:8e50c5fd42f6 181
steniu01 0:8e50c5fd42f6 182 LPC_GPDMACH_TypeDef* GPDMA_channel;
steniu01 0:8e50c5fd42f6 183 // Get DMA channel address
steniu01 0:8e50c5fd42f6 184 GPDMA_channel = return_channel(channel);
steniu01 0:8e50c5fd42f6 185 // Calcuate transfer type according to source address and destination address
steniu01 0:8e50c5fd42f6 186 DMA_InitStruct->DMA_TransferType = get_transfer_type(DMA_InitStruct->DMA_SrcAddr, DMA_InitStruct->DMA_DstAddr);
steniu01 0:8e50c5fd42f6 187 // Reset interrupt pending bits for DMA Channel
steniu01 0:8e50c5fd42f6 188 LPC_GPDMA->DMACIntTCClear = 1<<channel;
steniu01 0:8e50c5fd42f6 189 LPC_GPDMA->DMACIntErrClr = 1<<channel;
steniu01 0:8e50c5fd42f6 190
steniu01 0:8e50c5fd42f6 191
steniu01 0:8e50c5fd42f6 192 /*--------------------------- DMAy channelx request select register ----------------*/
steniu01 0:8e50c5fd42f6 193 // Choose UART or Timer DMA request for DMA inputs 8 through 15
steniu01 0:8e50c5fd42f6 194 if ((DMA_InitStruct->DMA_TriggerSource & 0x10) || (DMA_InitStruct->DMA_TriggerDestination & 0x10))
steniu01 0:8e50c5fd42f6 195 LPC_SC->DMAREQSEL |= 1 << ((DMA_InitStruct->DMA_TriggerSource || DMA_InitStruct->DMA_TriggerSource) - 24); // I don't know why. The user manual says DMAREQSET is GPDMA register, but in mbed, it is LPC_SC register
steniu01 0:8e50c5fd42f6 196 else
steniu01 0:8e50c5fd42f6 197 LPC_SC->DMAREQSEL &= ~(1 << ((DMA_InitStruct->DMA_TriggerSource - 24)&& (DMA_InitStruct->DMA_TriggerDestination - 24)));
steniu01 0:8e50c5fd42f6 198
steniu01 0:8e50c5fd42f6 199
steniu01 0:8e50c5fd42f6 200 /*--------------------------- DMAy channelx source and destination ----------------*/
steniu01 0:8e50c5fd42f6 201 // Write to DMA channel source address
steniu01 0:8e50c5fd42f6 202 GPDMA_channel->DMACCSrcAddr = DMA_InitStruct->DMA_SrcAddr;
steniu01 0:8e50c5fd42f6 203 // Write to DMA channel destination address
steniu01 0:8e50c5fd42f6 204 GPDMA_channel->DMACCDestAddr = DMA_InitStruct->DMA_DstAddr;
steniu01 0:8e50c5fd42f6 205
steniu01 0:8e50c5fd42f6 206 #ifdef BURST_ENABLED
steniu01 0:8e50c5fd42f6 207 if (DMA_InitStruct->DMA_TransferType == M2M)
steniu01 0:8e50c5fd42f6 208 {
steniu01 0:8e50c5fd42f6 209 /*always set burst size as 256 as the performance is same no matter what the burst size is. Need to double confirm, it seems the
steniu01 0:8e50c5fd42f6 210 interconnect would wrap the burst size as 4 no matter what the DMA AHB has set. This is to avoid DMA AHB bus take over too many bus bandwidth*/
steniu01 0:8e50c5fd42f6 211 DMA_InitStruct->DMA_SrcBurst = 7; //
steniu01 0:8e50c5fd42f6 212 DMA_InitStruct->DMA_DestBurst = 7;
steniu01 0:8e50c5fd42f6 213 }
steniu01 0:8e50c5fd42f6 214 #endif
steniu01 0:8e50c5fd42f6 215
steniu01 0:8e50c5fd42f6 216 /*--------------------------- DMAy channelx control register ----------------*/
steniu01 0:8e50c5fd42f6 217 // Set TransferSize
steniu01 0:8e50c5fd42f6 218 // Set source burst size
steniu01 0:8e50c5fd42f6 219 // Set destination burst size
steniu01 0:8e50c5fd42f6 220 // Set source width size
steniu01 0:8e50c5fd42f6 221 // Set destination width size
steniu01 0:8e50c5fd42f6 222 // Set source increment
steniu01 0:8e50c5fd42f6 223 // Set destination increment
steniu01 0:8e50c5fd42f6 224 // Set destination increment
steniu01 0:8e50c5fd42f6 225 // Enable terminal count interrupt
steniu01 0:8e50c5fd42f6 226 GPDMA_channel->DMACCControl |= (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos) |
steniu01 0:8e50c5fd42f6 227 (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
steniu01 0:8e50c5fd42f6 228 (DMA_InitStruct->DMA_SrcWidth << DMA_CCxControl_SWidth_Pos) |
steniu01 0:8e50c5fd42f6 229 (DMA_InitStruct->DMA_DestWidth << DMA_CCxControl_DWidth_Pos) |
steniu01 0:8e50c5fd42f6 230 (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos) |
steniu01 0:8e50c5fd42f6 231 (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos) |
steniu01 0:8e50c5fd42f6 232 (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos );
steniu01 0:8e50c5fd42f6 233
steniu01 0:8e50c5fd42f6 234 /*--------------------------- DMAy channelx configuration register ----------------*/
steniu01 0:8e50c5fd42f6 235 // Set source periheral trigger. If the source is memory, this bit is ignored. Set according to low 4 bit of DMA_Trigger
steniu01 0:8e50c5fd42f6 236 // Set destination periheral trigger. If the destination is memory, this bit is ignored. Set according low 4 bit of DMA_Trigger
steniu01 0:8e50c5fd42f6 237 // Set transfer type: M2M, M2P, P2M, M2M
steniu01 0:8e50c5fd42f6 238 GPDMA_channel->DMACCConfig |= ((DMA_InitStruct->DMA_TriggerSource & 0x0f) << DMA_CCxConfig_SrcPeripheral_Pos)| //set SrcPeripheral according low 4 bit of DMA_Trigger
steniu01 0:8e50c5fd42f6 239 ((DMA_InitStruct->DMA_TriggerDestination & 0x0f) << DMA_CCxConfig_DestPeripheral_Pos)| //set DstPeripheral according low 4 bit of DMA_Trigger
steniu01 0:8e50c5fd42f6 240 (DMA_InitStruct->DMA_TransferType << DMA_CCxConfig_TransferType_Pos);
steniu01 0:8e50c5fd42f6 241
steniu01 0:8e50c5fd42f6 242 /* set link list items register*/
steniu01 0:8e50c5fd42f6 243 if(DMA_InitStruct->LLI_count != 0)
steniu01 0:8e50c5fd42f6 244 {
steniu01 0:8e50c5fd42f6 245 int j;
steniu01 0:8e50c5fd42f6 246 for (j=0; j < DMA_InitStruct->LLI_count; j++)
steniu01 0:8e50c5fd42f6 247 {
steniu01 0:8e50c5fd42f6 248 DMA_InitStruct->LLI_list[j]->LLI_control |= GPDMA_channel->DMACCControl;
steniu01 0:8e50c5fd42f6 249 if (j< DMA_InitStruct->LLI_count - 1)
steniu01 0:8e50c5fd42f6 250 DMA_InitStruct->LLI_list[j]->LLI_next = (uint32_t)DMA_InitStruct->LLI_list[j+1];
steniu01 0:8e50c5fd42f6 251 }
steniu01 0:8e50c5fd42f6 252 GPDMA_channel->DMACCLLI = (uint32_t)DMA_InitStruct->LLI_list[0];
steniu01 0:8e50c5fd42f6 253 }
steniu01 0:8e50c5fd42f6 254 //LPC_GPDMA->DMACSync =0x1;
steniu01 0:8e50c5fd42f6 255 }//end of DMA_init
steniu01 0:8e50c5fd42f6 256
steniu01 0:8e50c5fd42f6 257
steniu01 0:8e50c5fd42f6 258 void DMA_start(int channel, DMA_InitTypeDef* DMA_InitStruct, unsigned int length)
steniu01 0:8e50c5fd42f6 259 {
steniu01 0:8e50c5fd42f6 260 LPC_GPDMACH_TypeDef* volatile GPDMA_channel;
steniu01 0:8e50c5fd42f6 261 // Get DMA channel address
steniu01 0:8e50c5fd42f6 262 GPDMA_channel = return_channel(channel);
steniu01 0:8e50c5fd42f6 263
steniu01 0:8e50c5fd42f6 264 // Set the transfer size
steniu01 0:8e50c5fd42f6 265 // Put it here rather than in DMA_Init. So that one could send new transfer data without reinit other registers
steniu01 0:8e50c5fd42f6 266
steniu01 0:8e50c5fd42f6 267 int length_low;
steniu01 0:8e50c5fd42f6 268 int length_high;
steniu01 0:8e50c5fd42f6 269 int length_unalligned;
steniu01 0:8e50c5fd42f6 270 int length_alligned;
steniu01 0:8e50c5fd42f6 271
steniu01 0:8e50c5fd42f6 272 if ((DMA_InitStruct->DMA_DstAddr % sizeof(long) == 0) && (DMA_InitStruct->DMA_SrcAddr % sizeof(long) == 0) && // Only use word aligned when starting addresss is at 4 bytes boundary
steniu01 0:8e50c5fd42f6 273 (length_low >=4) && // Only use word aligned when length is no less than 4
steniu01 0:8e50c5fd42f6 274 (DMA_InitStruct->DMA_SrcInc == 1) && // Only use word aligned when the source is incretmental
steniu01 0:8e50c5fd42f6 275 (DMA_InitStruct->DMA_DestInc == 1) && // Only use word aligned when the address is incretmental
steniu01 0:8e50c5fd42f6 276 (DMA_InitStruct->DMA_SrcWidth < 0x2)) // Only use word aligned when the source width is less than 4 bytes
steniu01 0:8e50c5fd42f6 277 {
steniu01 0:8e50c5fd42f6 278
steniu01 0:8e50c5fd42f6 279 unsigned int old_SrcWidth = DMA_InitStruct->DMA_SrcWidth;
steniu01 0:8e50c5fd42f6 280 unsigned int old_DstWidth = DMA_InitStruct->DMA_DestWidth;
steniu01 0:8e50c5fd42f6 281 unsigned int new_width = 2; // New width will be word
steniu01 0:8e50c5fd42f6 282
steniu01 0:8e50c5fd42f6 283
steniu01 0:8e50c5fd42f6 284 length_unalligned = (length & 0x3) >> DMA_InitStruct->DMA_SrcWidth;
steniu01 0:8e50c5fd42f6 285 length_alligned = length - length_unalligned;
steniu01 0:8e50c5fd42f6 286
steniu01 0:8e50c5fd42f6 287 int length_new = length_alligned << DMA_InitStruct->DMA_SrcWidth >> new_width;
steniu01 0:8e50c5fd42f6 288
steniu01 0:8e50c5fd42f6 289 GPDMA_channel->DMACCConfig &= ~(1ul << DMA_CCxConfig_ITC_Pos); // mask DMA terminate count IRQ
steniu01 0:8e50c5fd42f6 290 GPDMA_channel->DMACCControl |= 1ul << DMA_CCxControl_I_Pos;
steniu01 0:8e50c5fd42f6 291 // Unmask IE
steniu01 0:8e50c5fd42f6 292 GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_IE_Pos; // unmask DMA Errr IRQ
steniu01 0:8e50c5fd42f6 293
steniu01 0:8e50c5fd42f6 294 while (length_new > 0xfff)
steniu01 0:8e50c5fd42f6 295 {
steniu01 0:8e50c5fd42f6 296
steniu01 0:8e50c5fd42f6 297 GPDMA_channel->DMACCControl = 0xfff << DMA_CCxControl_TransferSize_Pos |
steniu01 0:8e50c5fd42f6 298 (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos) |
steniu01 0:8e50c5fd42f6 299 (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
steniu01 0:8e50c5fd42f6 300 (2 << DMA_CCxControl_SWidth_Pos) |
steniu01 0:8e50c5fd42f6 301 (2 << DMA_CCxControl_DWidth_Pos) |
steniu01 0:8e50c5fd42f6 302 (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos) |
steniu01 0:8e50c5fd42f6 303 (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos) |
steniu01 0:8e50c5fd42f6 304 (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos);
steniu01 0:8e50c5fd42f6 305
steniu01 0:8e50c5fd42f6 306
steniu01 0:8e50c5fd42f6 307
steniu01 0:8e50c5fd42f6 308 LPC_GPDMA->DMACConfig = 0x01;
steniu01 0:8e50c5fd42f6 309 GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_E_Pos;
steniu01 0:8e50c5fd42f6 310 length_new -= 0xfff;
steniu01 0:8e50c5fd42f6 311
steniu01 0:8e50c5fd42f6 312 // DMACCSrcAddr currently hold the end address of first part
steniu01 0:8e50c5fd42f6 313 GPDMA_channel->DMACCSrcAddr = GPDMA_channel->DMACCSrcAddr + 1;
steniu01 0:8e50c5fd42f6 314 GPDMA_channel->DMACCDestAddr = GPDMA_channel->DMACCDestAddr + 1;
steniu01 0:8e50c5fd42f6 315 while (DMA_channel_active(channel));
steniu01 0:8e50c5fd42f6 316 }
steniu01 0:8e50c5fd42f6 317
steniu01 0:8e50c5fd42f6 318 GPDMA_channel->DMACCControl = length_new << DMA_CCxControl_TransferSize_Pos |
steniu01 0:8e50c5fd42f6 319 (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos) |
steniu01 0:8e50c5fd42f6 320 (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
steniu01 0:8e50c5fd42f6 321 (2 << DMA_CCxControl_SWidth_Pos) |
steniu01 0:8e50c5fd42f6 322 (2 << DMA_CCxControl_DWidth_Pos) |
steniu01 0:8e50c5fd42f6 323 (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos) |
steniu01 0:8e50c5fd42f6 324 (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos) |
steniu01 0:8e50c5fd42f6 325 (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos);
steniu01 0:8e50c5fd42f6 326
steniu01 0:8e50c5fd42f6 327 LPC_GPDMA->DMACConfig = 0x01;
steniu01 0:8e50c5fd42f6 328 GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_E_Pos;
steniu01 0:8e50c5fd42f6 329 while (DMA_channel_active(channel));
steniu01 0:8e50c5fd42f6 330
steniu01 0:8e50c5fd42f6 331 GPDMA_channel->DMACCSrcAddr = GPDMA_channel->DMACCSrcAddr + 4;
steniu01 0:8e50c5fd42f6 332 GPDMA_channel->DMACCDestAddr = GPDMA_channel->DMACCDestAddr + 4;
steniu01 0:8e50c5fd42f6 333
steniu01 0:8e50c5fd42f6 334 DMA_InitStruct->DMA_TransferSize = length_unalligned;
steniu01 0:8e50c5fd42f6 335 GPDMA_channel->DMACCControl = length_unalligned << DMA_CCxControl_TransferSize_Pos |
steniu01 0:8e50c5fd42f6 336 (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos) |
steniu01 0:8e50c5fd42f6 337 (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
steniu01 0:8e50c5fd42f6 338 (DMA_InitStruct->DMA_SrcWidth << DMA_CCxControl_SWidth_Pos) |
steniu01 0:8e50c5fd42f6 339 (DMA_InitStruct->DMA_DestWidth << DMA_CCxControl_DWidth_Pos) |
steniu01 0:8e50c5fd42f6 340 (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos) |
steniu01 0:8e50c5fd42f6 341 (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos) |
steniu01 0:8e50c5fd42f6 342 (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos);
steniu01 0:8e50c5fd42f6 343 }
steniu01 0:8e50c5fd42f6 344
steniu01 0:8e50c5fd42f6 345
steniu01 0:8e50c5fd42f6 346 else
steniu01 0:8e50c5fd42f6 347 {
steniu01 0:8e50c5fd42f6 348 GPDMA_channel->DMACCConfig &= ~(1ul << DMA_CCxConfig_ITC_Pos); // mask DMA terminate count IRQ
steniu01 0:8e50c5fd42f6 349 GPDMA_channel->DMACCControl |= 1ul << DMA_CCxControl_I_Pos;
steniu01 0:8e50c5fd42f6 350 // Unmask IE
steniu01 0:8e50c5fd42f6 351 GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_IE_Pos; // unmask DMA Errr IRQ
steniu01 0:8e50c5fd42f6 352
steniu01 0:8e50c5fd42f6 353 while (length > 0xfff)
steniu01 0:8e50c5fd42f6 354 {
steniu01 0:8e50c5fd42f6 355 GPDMA_channel->DMACCControl = 0xfff << DMA_CCxControl_TransferSize_Pos |
steniu01 0:8e50c5fd42f6 356 (DMA_InitStruct->DMA_SrcBurst << DMA_CCxControl_SBSize_Pos) |
steniu01 0:8e50c5fd42f6 357 (DMA_InitStruct->DMA_DestBurst << DMA_CCxControl_DBSize_Pos) |
steniu01 0:8e50c5fd42f6 358 (DMA_InitStruct->DMA_SrcWidth << DMA_CCxControl_SWidth_Pos) |
steniu01 0:8e50c5fd42f6 359 (DMA_InitStruct->DMA_DestWidth << DMA_CCxControl_DWidth_Pos) |
steniu01 0:8e50c5fd42f6 360 (DMA_InitStruct->DMA_SrcInc << DMA_CCxControl_SI_Pos) |
steniu01 0:8e50c5fd42f6 361 (DMA_InitStruct->DMA_DestInc << DMA_CCxControl_DI_Pos) |
steniu01 0:8e50c5fd42f6 362 (DMA_InitStruct->DMA_TermInt << DMA_CCxControl_I_Pos);
steniu01 0:8e50c5fd42f6 363
steniu01 0:8e50c5fd42f6 364
steniu01 0:8e50c5fd42f6 365
steniu01 0:8e50c5fd42f6 366 LPC_GPDMA->DMACConfig = 0x01;
steniu01 0:8e50c5fd42f6 367 GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_E_Pos;
steniu01 0:8e50c5fd42f6 368 length -= 0xfff;
steniu01 0:8e50c5fd42f6 369
steniu01 0:8e50c5fd42f6 370 // DMACCSrcAddr currently hold the end address of first part
steniu01 0:8e50c5fd42f6 371 GPDMA_channel->DMACCSrcAddr = GPDMA_channel->DMACCSrcAddr + 1;
steniu01 0:8e50c5fd42f6 372 GPDMA_channel->DMACCDestAddr = GPDMA_channel->DMACCDestAddr + 1;
steniu01 0:8e50c5fd42f6 373 while (DMA_channel_active(channel));
steniu01 0:8e50c5fd42f6 374 }
steniu01 0:8e50c5fd42f6 375
steniu01 0:8e50c5fd42f6 376
steniu01 0:8e50c5fd42f6 377
steniu01 0:8e50c5fd42f6 378 GPDMA_channel->DMACCSrcAddr = GPDMA_channel->DMACCSrcAddr + 1;
steniu01 0:8e50c5fd42f6 379 GPDMA_channel->DMACCDestAddr = GPDMA_channel->DMACCDestAddr + 1;
steniu01 0:8e50c5fd42f6 380
steniu01 0:8e50c5fd42f6 381 DMA_InitStruct->DMA_TransferSize = length;
steniu01 0:8e50c5fd42f6 382 }
steniu01 0:8e50c5fd42f6 383
steniu01 0:8e50c5fd42f6 384
steniu01 0:8e50c5fd42f6 385 // Enable Interrupt
steniu01 0:8e50c5fd42f6 386 // Unmask ITC
steniu01 0:8e50c5fd42f6 387 GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_ITC_Pos;
steniu01 0:8e50c5fd42f6 388
steniu01 0:8e50c5fd42f6 389
steniu01 0:8e50c5fd42f6 390 GPDMA_channel->DMACCControl |= DMA_InitStruct->DMA_TransferSize << DMA_CCxControl_TransferSize_Pos;
steniu01 0:8e50c5fd42f6 391
steniu01 0:8e50c5fd42f6 392 DMA_InitStruct->LLI_count = 0; // Clear the LLI lists count so that we could reuse this channel next time
steniu01 0:8e50c5fd42f6 393 // Enable DMA
steniu01 0:8e50c5fd42f6 394 LPC_GPDMA->DMACConfig = 0x01;
steniu01 0:8e50c5fd42f6 395 GPDMA_channel->DMACCConfig |= 1ul << DMA_CCxConfig_E_Pos;
steniu01 0:8e50c5fd42f6 396 }
steniu01 0:8e50c5fd42f6 397
steniu01 0:8e50c5fd42f6 398
steniu01 0:8e50c5fd42f6 399 bool DMA_channel_active(int channel)
steniu01 0:8e50c5fd42f6 400 {
steniu01 0:8e50c5fd42f6 401 LPC_SC->PCONP |= (1 << 29);
steniu01 0:8e50c5fd42f6 402 return (LPC_GPDMA->DMACEnbldChns && (1<<channel));
steniu01 0:8e50c5fd42f6 403 }
steniu01 0:8e50c5fd42f6 404
steniu01 0:8e50c5fd42f6 405
steniu01 0:8e50c5fd42f6 406
steniu01 0:8e50c5fd42f6 407 void DMA_reset(int channel)
steniu01 0:8e50c5fd42f6 408 {
steniu01 0:8e50c5fd42f6 409 assert (channel <= _channel_num && channel>=0);
steniu01 0:8e50c5fd42f6 410 LPC_SC->PCONP |= (1 << 29);
steniu01 0:8e50c5fd42f6 411 LPC_GPDMACH_TypeDef* GPDMA_channel;
steniu01 0:8e50c5fd42f6 412 //Get DMA channel address
steniu01 0:8e50c5fd42f6 413 GPDMA_channel = return_channel(channel);
steniu01 0:8e50c5fd42f6 414 GPDMA_channel->DMACCConfig = 0;
steniu01 0:8e50c5fd42f6 415 GPDMA_channel->DMACCControl = 0;
steniu01 0:8e50c5fd42f6 416 GPDMA_channel->DMACCLLI = 0;
steniu01 0:8e50c5fd42f6 417 GPDMA_channel->DMACCSrcAddr = 0;
steniu01 0:8e50c5fd42f6 418 LPC_GPDMA->DMACIntErrClr |= 1 << channel; // clear the interrupt
steniu01 0:8e50c5fd42f6 419 LPC_GPDMA->DMACIntTCClear |= 1 << channel;
steniu01 0:8e50c5fd42f6 420 dma_irq_error [channel] = 0;
steniu01 0:8e50c5fd42f6 421 dma_irq_finish[channel] = 0;
steniu01 0:8e50c5fd42f6 422 }
steniu01 0:8e50c5fd42f6 423
steniu01 0:8e50c5fd42f6 424
steniu01 0:8e50c5fd42f6 425 DMA_InitTypeDef* DMA_struct_create(void)
steniu01 0:8e50c5fd42f6 426 {
steniu01 0:8e50c5fd42f6 427 DMA_InitTypeDef* DMA_InitStruct = (DMA_InitTypeDef*) malloc(sizeof(DMA_InitTypeDef));
steniu01 0:8e50c5fd42f6 428 /*-------------- Reset DMA init structure parameters values ------------------*/
steniu01 0:8e50c5fd42f6 429 DMA_InitStruct->DMA_DstAddr = 0;
steniu01 0:8e50c5fd42f6 430 DMA_InitStruct->DMA_SrcAddr = 0;
steniu01 0:8e50c5fd42f6 431 DMA_InitStruct->DMA_TransferSize = 0;
steniu01 0:8e50c5fd42f6 432 DMA_InitStruct->DMA_SrcBurst = 0;
steniu01 0:8e50c5fd42f6 433 DMA_InitStruct->DMA_DestBurst = 0;
steniu01 0:8e50c5fd42f6 434 DMA_InitStruct->DMA_SrcWidth = 0;
steniu01 0:8e50c5fd42f6 435 DMA_InitStruct->DMA_DestWidth = 0;
steniu01 0:8e50c5fd42f6 436 DMA_InitStruct->DMA_LLI = 0;
steniu01 0:8e50c5fd42f6 437 DMA_InitStruct->LLI_count = 0 ;
steniu01 0:8e50c5fd42f6 438 DMA_InitStruct->DMA_SrcInc = 0;
steniu01 0:8e50c5fd42f6 439 DMA_InitStruct->DMA_DestInc = 0;
steniu01 0:8e50c5fd42f6 440 DMA_InitStruct->DMA_TermInt = 1; // always enable terminal count interrupt in default
steniu01 0:8e50c5fd42f6 441 DMA_InitStruct->DMA_TriggerSource = trigger_value[ALWAYS];
steniu01 0:8e50c5fd42f6 442 DMA_InitStruct->DMA_TriggerDestination = trigger_value[ALWAYS];
steniu01 0:8e50c5fd42f6 443 DMA_InitStruct->DMA_TransferType = M2M;
steniu01 0:8e50c5fd42f6 444 return DMA_InitStruct;
steniu01 0:8e50c5fd42f6 445 } //end of DMA_StructInit
steniu01 0:8e50c5fd42f6 446
steniu01 0:8e50c5fd42f6 447 void DMA_struct_delete(DMA_InitTypeDef* ptr)
steniu01 0:8e50c5fd42f6 448 {
steniu01 0:8e50c5fd42f6 449 free(ptr);
steniu01 0:8e50c5fd42f6 450 }
steniu01 0:8e50c5fd42f6 451
steniu01 0:8e50c5fd42f6 452 inline static bool is_memory(uint32_t addr)
steniu01 0:8e50c5fd42f6 453 {
steniu01 0:8e50c5fd42f6 454 if ((addr >> 28) == 0 || (addr >> 28)== 1)
steniu01 0:8e50c5fd42f6 455 return 1;
steniu01 0:8e50c5fd42f6 456 else
steniu01 0:8e50c5fd42f6 457 return 0;
steniu01 0:8e50c5fd42f6 458 }
steniu01 0:8e50c5fd42f6 459
steniu01 0:8e50c5fd42f6 460
steniu01 0:8e50c5fd42f6 461 void DMA_IRQ_handler(void)
steniu01 0:8e50c5fd42f6 462 {
steniu01 0:8e50c5fd42f6 463 //only call the attached function when certain interrupt happened on the right channel
steniu01 0:8e50c5fd42f6 464 unsigned i ;
steniu01 0:8e50c5fd42f6 465 uint32_t FinishStatus=0;
steniu01 0:8e50c5fd42f6 466 uint32_t ErrStatus=0;
steniu01 0:8e50c5fd42f6 467 FinishStatus = LPC_GPDMA->DMACIntTCStat & 0xFF;
steniu01 0:8e50c5fd42f6 468 ErrStatus = LPC_GPDMA->DMACIntErrStat & 0xFF;
steniu01 0:8e50c5fd42f6 469
steniu01 0:8e50c5fd42f6 470
steniu01 0:8e50c5fd42f6 471 if (FinishStatus !=0) //only checking when there is interrupt happened
steniu01 0:8e50c5fd42f6 472 for (i =0; i < 8; i++) // roundrobin checking how many channels get interrupts
steniu01 0:8e50c5fd42f6 473 {
steniu01 0:8e50c5fd42f6 474 if (FinishStatus & 1 << i)
steniu01 0:8e50c5fd42f6 475 dma_irq_finish[i]();
steniu01 0:8e50c5fd42f6 476 }
steniu01 0:8e50c5fd42f6 477 if (ErrStatus !=0)
steniu01 0:8e50c5fd42f6 478 for (i =0; i < 8; i++)
steniu01 0:8e50c5fd42f6 479 {
steniu01 0:8e50c5fd42f6 480 if (ErrStatus & 1 << i)
steniu01 0:8e50c5fd42f6 481 dma_irq_error[i]();
steniu01 0:8e50c5fd42f6 482 }
steniu01 0:8e50c5fd42f6 483
steniu01 0:8e50c5fd42f6 484 LPC_GPDMA->DMACIntTCClear |= FinishStatus;
steniu01 0:8e50c5fd42f6 485 LPC_GPDMA->DMACIntErrClr |= ErrStatus;
steniu01 0:8e50c5fd42f6 486 }
steniu01 0:8e50c5fd42f6 487
steniu01 0:8e50c5fd42f6 488
steniu01 0:8e50c5fd42f6 489 void DMA_IRQ_attach(int channel, int status, func_ptr ptr)
steniu01 0:8e50c5fd42f6 490 {
steniu01 0:8e50c5fd42f6 491 assert(channel <= _channel_num && channel >= 0);
steniu01 0:8e50c5fd42f6 492 assert(status == ERR || status == FINISH);
steniu01 1:9421d79fb372 493
steniu01 0:8e50c5fd42f6 494 if (status == ERR)
steniu01 0:8e50c5fd42f6 495 dma_irq_error[channel] = ptr;
steniu01 0:8e50c5fd42f6 496 else if (status == FINISH)
steniu01 0:8e50c5fd42f6 497 dma_irq_finish[channel] = ptr;
steniu01 0:8e50c5fd42f6 498 }
steniu01 0:8e50c5fd42f6 499
steniu01 0:8e50c5fd42f6 500
steniu01 0:8e50c5fd42f6 501
steniu01 0:8e50c5fd42f6 502 void DMA_IRQ_detach(int channel)
steniu01 0:8e50c5fd42f6 503 {
steniu01 0:8e50c5fd42f6 504 dma_irq_error[channel] = 0;
steniu01 0:8e50c5fd42f6 505 dma_irq_finish[channel] = 0;
steniu01 0:8e50c5fd42f6 506 }
steniu01 0:8e50c5fd42f6 507
steniu01 0:8e50c5fd42f6 508
steniu01 0:8e50c5fd42f6 509
steniu01 0:8e50c5fd42f6 510 static TransferType get_transfer_type(uint32_t src_addr, uint32_t dst_addr)
steniu01 0:8e50c5fd42f6 511 {
steniu01 0:8e50c5fd42f6 512 if (is_memory(src_addr)) { //if source is memory
steniu01 0:8e50c5fd42f6 513 if(is_memory(dst_addr)) //if destination is memory
steniu01 0:8e50c5fd42f6 514 return M2M; //return M2M if source is memory and destination is memory.
steniu01 0:8e50c5fd42f6 515 else //if destination is peripheral
steniu01 0:8e50c5fd42f6 516 return M2P; //return M2P if source is memory and destination is peripheral
steniu01 0:8e50c5fd42f6 517 } else { //if source is peripheral
steniu01 0:8e50c5fd42f6 518 if(is_memory(dst_addr)) //if destination is memory
steniu01 0:8e50c5fd42f6 519 return P2M; //return P2M if source is peripheral and destination is memory
steniu01 0:8e50c5fd42f6 520 else //if destination is peripheral
steniu01 0:8e50c5fd42f6 521 return P2P; //return P2P if source is peripheral and destination is peripheral
steniu01 0:8e50c5fd42f6 522 }
steniu01 0:8e50c5fd42f6 523 }
steniu01 0:8e50c5fd42f6 524