steven niu / mDMA Featured

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