Two different results from writing the same thing? - Pointer Crazyness

Kuba0040

Well-known member
Hello,
I’ve encountered some weird behavior when using pointers. My goal is to write the address in memory of an array to the DMA Source Adress register (SADDR). However, some weird things happen.

Here is my code:
Code:
int32_t TestChunkMemory[64]; //Array in memory
int32_t *TestChunk_pointer=TestChunkMemory; //Pointer to this array

void write_adress_to_DMA(int32_t *LocalAdress)
{
  Serial.print("Local adress: ");
  print64ln(LocalAdress);
  
  DMA_TCD0_SADDR=LocalAdress;
  Serial.print("What got written: ");
  print64ln(DMA_TCD0_SADDR);
}

Ok, first we create the array in memory and setup a pointer pointing to it.
Next, we have a function called “write_adress_to_DMA” which when called like so: write_adress_to_DMA(TestChunk_pointer);
will take the address the pointer is pointing to, (so the array address) and write it into the SADDR register. And it does all that correctly.
However, when I setup the rest of the DMA and start it, it doesn’t work. Also when I read the SADDR and DADDR registers I see there’s a lot of garbage in front of the value I’ve written to these registers in the write_adress_to_DMA function!

Example:
When we read SADDR after the write_adress_to_DMA function -> 0x20001888
When we read SADDR after setting up and starting the DMA -> 0x400E900020001888

However, if I write the array address like this, where I first store it in a uint32_t variable and then write it to SADDR like so:
Code:
int32_t TestChunkMemory[64]; //Array in memory
int32_t *TestChunk_pointer=TestChunkMemory; //Pointer to this array
uint32_t TestChunk_adress=&TestChunk_pointer; //Memory address

void write_adress_to_DMA(uint32_t LocalAdress)
{
  Serial.print("Local adress: ");
  print64ln(LocalAdress);
  
  DMA_TCD0_SADDR=LocalAdress;
  Serial.print("What got written: ");
  print64ln(DMA_TCD0_SADDR);
}

and run the function like so: write_adress_to_DMA(TestChunk_adress);

Then even though the same things get written to SADDR, the DMA magically works now, and we don’t see any garbage when we read the SADDR register (value after starting the DMA with the same configuration as before): 0x20000A90

What is going on? How can writing the same thing yield two different results? And lastly, I know it’s better to use DMAChannel.h for any DMA related stuff, but I really want to play with it in this bare metal way. Thank you for understanding.

I'm using a Teensy 4.0.

Thank you for the help.
 
Example:
When we read SADDR after the write_adress_to_DMA function -> 0x20001888
When we read SADDR after setting up and starting the DMA -> 0x400E900020001888
If you read from a 32 bit register and end up with extra garbage in a 64 bit value, the garbage didn't come from the 32 bit register.
 
Thank You. This has just made the issue even more confusing. Maybe the compiler is feaking out?
 
What is going on?

Most likely a bug in your print64ln() function, which you didn't show in your message (eg, see "Forum Rule" at the top of any forum page).


As I recall, weeks ago you started a thread to complain that Serial.print() doesn't support 64 bit integers. But it does. Or at least it has since Teensyduino 1.54. Since you're *still* not using Serial.print() for 64 bit integers after that answer, I have to wonder if you're intentionally staying with an old version of the core library?

There was also a recent thread, and I don't recall if it was you or someone else, about direct access to TCD registers not working. Indeed all versions before 1.56 had a bug with the defines for those registers on Teensy 4.x, which aren't normally accessed directly. If you didn't see that thread, the bottom line is you really should use DMAChannel to get a pointer to the TCD. You can then do everything with direct hardware access, so you really don't lose any capability, and DMAChannel assures the specific TCD you access won't conflict with any others when your code is combined or used libraries which access DMA.

So again, if you have an old version of the core library, direct access to the TCD registers just won't work.


but I really want to play with it in this bare metal way

You certainly can.

There aren't many places on the internet where you'll get good quality help with this sort of direct low-level hardware programming. This place is sort of special. And we do have some expectations beyond more casual conversation like you'd fine on social networking site. The main one is (when possible & within reason) showing complete code to reproduce your problem.

While the "Forum Rule" is short & kept simple, I hope you can understand there is an unwritten expectation that certain types of questions have a much stronger expectation that you will give a complete program which can be copied into Arduino and uploaded to the correct Teensy model to reproduce the problem. Especially this instance where you suggest "Maybe the compiler is feaking out?" is the sort of situation where we as a community, and I in particular, expect you will show a complete program.

Please, follow the forum rule. And make sure you have at least Teensyduino 1.56. And check with Serial.print(), which does indeed work with 64 bit integers.
 
Hello,
I have figured out what I was doing wrong by complete accident while preparing the source code for this post. It seems like I didn’t set the SOFF and DOFF registers properly (they were all 0). All works fine now.

I apologize for not providing the full source code before. I will provide the full working one here if anyone has similar issues. This code uses pointers for the addresses. The DMA setup is broken into separate functions, some of which are messy due to this still being an experimental program. However, I’ll explain a few weird things about my code.

1. In the case of some registers like the ATTR register when we use the configuration functions like “setupDMAChannel_0_Source” and “setupDMAChannel_0_Destination” the configuration work will first be saved into a uint32_t variable in memory and only later applied to the real ATTR register in “DMA_begin”. Why do that? All DMA registers start out undefined, so if we write to them using OR operations “|=” we don’t clear up the garbage that is in them when the CPU starts. So instead, we have a variable that starts out as 0, so we can write to it using ORs as much as we want, and then apply the finished result to ATTR.
Code:
uint32_t Memory_ATTR=0;
Memory_ATTR |= DMA_TCD_ATTR_SSIZE(DataSize);
DMA_TCD0_ATTR=Memory_ATTR;

2. The Minor Loop configuration also occurs with the use of uint32_t variables. This is because to configure the Minor Loop we must write to the registers in a very specific order (See my previous post about this). So we first store the user’s configuration into memory, and then in DMA_begin() we apply all the configuration work in the right order.
Code:
uint32_t Memory_BytesToTransfer=0;
int32_t Memory_MinorLoopOffset=0;
bool Memory_SourceOffsetEnable=false;
bool Memory_DestinationOffsetEnable=false;

//Clear the register as it's udnefined
DMA_TCD0_NBYTES_MLOFFNO=0;
 
if ((Memory_SourceOffsetEnable==true) || (Memory_DestinationOffsetEnable==true)) //Will we use the MinorLoop offset?
{
  if (Memory_SourceOffsetEnable==true) {DMA_TCD0_NBYTES_MLOFFNO |= DMA_TCD_NBYTES_SMLOE;}
  if (Memory_DestinationOffsetEnable==true) {DMA_TCD0_NBYTES_MLOFFNO |= DMA_TCD_NBYTES_DMLOE;}
  /*
   * If desired, enable the Minor Loop Offset for the Source/Destination Adress
   */
  
  //Now that the offsets are enabled we can use the Minor Offset register
  /*
   * This register behaves differently based on how we configure the DMA. To make it have the Minor Loop Offset we need to set some bits before we define
   * the Minor Loop Offset. Thats why the "ifs" are above the code below, and why we keep using different names for the same register. 
   */
   
  DMA_TCD0_NBYTES_MLOFFYES = Memory_BytesToTransfer | DMA_TCD_NBYTES_MLOFFYES_MLOFF(Memory_MinorLoopOffset);                    
  /*
   * NBYTES - How many bytes in total are we transfering per request? Think of it as the i<64 part in a for loop
   * MLOFF - As described before, each time a Minor Loop finishes (a DMA request finishes) we add a offset to the Source and Destination adress  
   * ONLY IF the enable bits are set
   */
}
else //No Minor Loop Offset
{
  DMA_TCD0_NBYTES_MLOFFNO = Memory_BytesToTransfer; //Only sets NBYTES
}

Lastly, I’d like to address a few things that I didn’t make clear in my previous post. I did update both Teensyduino and the Arduino IDE after my post on these issues. I am running version 1.56 of Teensyduino.
teensyduino.png
The print64ln function I mentioned can be seen defined at the top of my code. I got it from also my previous post on this topic. Why didn’t I use Serial.print()? Simply because the compiler kept throwing weird errors at me, so out of laziness I just used the macros as they worked with no issues, right out of the box.

Code:
#define print64ln(value) Serial.printf("0x%" PRIX64 "\n", value); //Prints a 64bit number in hex, then a new line
#define print64(value) Serial.printf("0x%" PRIX64, value); //Prints a 64bit number in hex.

Example error when using Serial.print(DMA_TCD0_DADDR);
note: in expansion of macro 'DMA_TCD0_DADDR'
no matching function for call to 'print(volatile void* volatile&)'


Lastly, here is the complete, now working code using the pointers to get the address:

What both of the programs shown here do is copy 256 bytes (64 32bit values) from ReadData_array into WriteData_array using the DMA.

Code:
//Helpful macros
#define print64ln(value) Serial.printf("0x%" PRIX64 "\n", value); //Prints a 64bit number in hex, then a new line
#define print64(value) Serial.printf("0x%" PRIX64, value); //Prints a 64bit number in hex.

//Chunks
int32_t ReadData_array[64]; //In RAM
int32_t *ReadData=ReadData_array; //Pointer

int32_t WriteData_array[64]; //In RAM
int32_t *WriteData=WriteData_array; //Pointer

void fill_chunk(int32_t *myChunk) //Fills with data sequencially
{
  for (uint8_t i=0; i<64; i++)
  {
    *(myChunk+i)=i; 
  }
}

void show_chunk(int32_t *myChunk) //Shows whats stored in a chunk
{
  for (uint8_t i=0; i<64; i++)
  {
    Serial.println(*(myChunk+i));
  }
}


/*
 * Here we have temporary holds for all the DMA settings. 
 * Once DMA.begin() is ran, they all get applied in the
 * right order. While this uses up precious memory, it's not
 * really that much. And solves a lot of problems doing it like this.
 */

//All and I do mean ALL DMA registers have to be 0'ed out, as they start undefined.
//So any that use OR operations go into mememory first.
uint32_t Memory_CR=0;
uint32_t Memory_ATTR=0;

//The Minor Loop is funky so we have to construct it here:
uint32_t Memory_BytesToTransfer=0;
int32_t Memory_MinorLoopOffset=0;
bool Memory_SourceOffsetEnable=false;
bool Memory_DestinationOffsetEnable=false;

void DMA_begin() //Configures everything in the right order
{ 
  /*
   * Major Loop
   */
  DMA_CR=Memory_CR;

  /*
   * Stuff for both Source and Destination
   */
  DMA_TCD0_ATTR=Memory_ATTR;

  /*
   * Minor Loop - Now it's time to get funky
   */
  //Clear the register as it's udnefined
  DMA_TCD0_NBYTES_MLOFFNO=0;
  
  if ((Memory_SourceOffsetEnable==true) || (Memory_DestinationOffsetEnable==true)) //Will we use the MinorLoop offset?
  {
    if (Memory_SourceOffsetEnable==true) {DMA_TCD0_NBYTES_MLOFFNO |= DMA_TCD_NBYTES_SMLOE;}
    if (Memory_DestinationOffsetEnable==true) {DMA_TCD0_NBYTES_MLOFFNO |= DMA_TCD_NBYTES_DMLOE;}
    /*
     * If desired, enable the Minor Loop Offset for the Source/Destination Adress
     */
    
    //Now that the offsets are enabled we can use the Minor Offset register
    /*
     * This register behaves differently based on how we configure the DMA. To make it have the Minor Loop Offset we need to set some bits before we define
     * the Minor Loop Offset. Thats why the "ifs" are above the code below, and why we keep using different names for the same register
     */
     
    DMA_TCD0_NBYTES_MLOFFYES = Memory_BytesToTransfer | DMA_TCD_NBYTES_MLOFFYES_MLOFF(Memory_MinorLoopOffset);                    
    /*
     * NBYTES - How many bytes in total are we transfering per request? Think of it as the i<64 part in a for loop
     * MLOFF - As described before, each time a Minor Loop finishes (a DMA request finishes) we add a offset to the Source and Destination adress  
     * ONLY IF the enable bits are set
     */
  }
  else //No Minor Loop Offset
  {
    DMA_TCD0_NBYTES_MLOFFNO = Memory_BytesToTransfer; //Only sets NBYTES
  }

  //And with that our DMA channel should be all configured nicely!
}

void setupDMAChannel_0_Source(int32_t *ReadFrom, int32_t SourceIncrement, int32_t SourceBack, int8_t DataSize)
{
  DMA_TCD0_SADDR = ReadFrom;
  /*
   * SADDR - The source adress for the DMA to read from
   */

  DMA_TCD0_SOFF = SourceIncrement;
  /*
   * SOFF - With each read by how many bytes do we increment the source adress?
   */

  Memory_ATTR |= DMA_TCD_ATTR_SSIZE(DataSize);
  /*
   * SSIZE - How wide in bits (eg. 8, 16, 32) is the data we are transfering?
   * 
   * 0-> 8 bits 1-> 16 bits 2-> 32bits 5-> 32-byte burst (no idea what that is)
   */

  DMA_TCD0_SLAST = SourceBack; //This can be negative to go Back to the Future! Or... more like the adress we have started with before the transfer
  /*
   * SLAST - After a Major Loop completes, the value from this register will be added back into the SADDR regitser. 
   * We can use this to restore it to it's original state
   */
}

void setupDMAChannel_0_Destination(int32_t *WriteTo, int32_t DestinationIncrement, int32_t DestinationBack, int8_t DataSize)
{
  DMA_TCD0_DADDR = WriteTo;
  /*
   * DADDR - The destination adress for the DMA to write to
   */

  DMA_TCD0_DOFF = DestinationIncrement;
  /*
   * DOFF - With each write by how many bytes do we increment the source adress?
   */

  Memory_ATTR |= DMA_TCD_ATTR_DSIZE(DataSize);
  /*
   * DSIZE - How wide in bits (eg. 8, 16, 32) is the data we are transfering?
   * 
   * 0-> 8 bits 1-> 16 bits 2-> 32bits 5-> 32-byte burst (no idea what that is)
   */
  
  DMA_TCD0_DLASTSGA = DestinationBack;
  /*
   * DLASTSGA - After a Major Loop completes, the value from this register will be added back into the DADDR regitser. 
   * We can use this to restore it to it's original state.
   * 
   * It's more advanced then Source but I don't uderstand how that stuff works yet.
   */
}

void setupDMAChannel_0_MajorLoop(int32_t HowManyTransfers)
{
  Memory_CR = DMA_CR_GRP1PRI | DMA_CR_EMLM | DMA_CR_EDBG;
  /*
   * A GROUP PRIORITY MUST ALWAYS BE SET!
   * GRP1PRI - Group 1 Priority. Group 1 gets set as the most important. 
   * GRP0PRI - Group 0 Priority. Same thing as group 1
   * 
   * EMLM - Enable Minor Loop Mapping
   * Minor loops allow us to have offsets to the Source and Destination adress after each minor loop completes.
   * 
   * Minor loops are individual requests. So if I request to copy X bytes from A to B that action occurs inside a Minor Loop.
   * However, If I'd want to have that copy operation start with a diffrenet offset each time a request fires then that Copy Minor Loop will start working inside a larger Major Loop.
   * The Major Loop isn't a loop really, it's just waits uintil CITER reaches 0. At which point it finishes by applying it's own Major Loop offset.
   * 
   * EDBG - Enable Debug Mode. The DMA stalls when a debugger is plugged in
   */
  
  
  DMA_TCD0_BITER = HowManyTransfers;
  DMA_TCD0_CITER = HowManyTransfers;
  /*
   * CITER - Current Major Loop Interation Cout. It's a counter that starts at the number of transfers we want to do (BITER - Beggining Iteration Count)
   * and then decrements down until 0. When it reaches 0, we know that a transfer has finished and we can fire a INTMAJOR
   * interrupt for example.
   */

  
  //DMA_TCD0_CSR |= DMA_TCD_CSR_INTMAJOR;
  /*
   * BWC - Bandwith Control
   * If we want to slow the DMA down we can stall it every 4 or 8 CPU cycles. Writing a 0 here will have the DMA run at full speed.
   * 
   * INTMAJOR - Setting this to 1 will couse the DMA to fire a interrupt when it finishes the transfer
   */
}

void setupDMAChannel_0_MinorLoop(uint32_t BytesToTransfer, int32_t MinorLoopOffset, bool SourceOffsetEnable, bool DestinationOffsetEnable)
{ 
  /*
   * For explanations see DMA_begin()!
   */
  
  Memory_BytesToTransfer=BytesToTransfer;
  Memory_MinorLoopOffset=MinorLoopOffset;
  Memory_SourceOffsetEnable=SourceOffsetEnable;
  Memory_DestinationOffsetEnable=DestinationOffsetEnable;
}

void startDMAChannel_0()
{
  DMA_TCD0_CSR |= DMA_TCD_CSR_START; //Start a DMA request
}

void setup() 
{
  Serial.begin(2000000);

  delay(2000);
  
  //Fill the chunk with data
  fill_chunk(ReadData);

  //Show result
  Serial.println("Chunk filled with: "); 
  show_chunk(ReadData);

  //Setup DMA Channel 0
  Serial.print("Setting up DMA...");


  CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON);
  /*
   * Start DMA Clock, we have to do this before we do any writing to the DMA TCD or the CPU will crash.
   */
  
  setupDMAChannel_0_MajorLoop(1);
  /*
   * 1 - How many transfers in the Major Loop?
   */
  
  setupDMAChannel_0_Source(ReadData,4,-256,2);
  /*
   * ReadData - Source Adress
   * 4 - Adress Increment value
   * -256 - Once a Major loop finishes, go back by 256 bytes (return to start)
   * 2 - We are transfering 32 bit data
   */

  setupDMAChannel_0_Destination(WriteData,4,-256,2);
  /*
   * WriteData - Destination Adress
   * 4 - Adress Increment value
   * -256 - Once a Major loop finishes, go back by 256 bytes (return to start)
   * 2 - We are transfering 32 bit data
   */

  setupDMAChannel_0_MinorLoop(256,0,false,false);
  /*
   * 256 - How many bytes will we transfer with each Minor Loop?
   * 0 - Minor Loop Offset
   * true - Source Minor Loop Offset Enable
   * true - Destination Minor Loop Offset Enable
   */


  DMA_begin();
  /*
   * Some stuff, mainly the Minor Loop stuff has to be written in a very specific order.
   * Thats what DMA begin takes care of.
   */
   
  Serial.println(" Success!");

  //Start a DMA request
  Serial.print("Starting a DMA request...");

  startDMAChannel_0();
  
  Serial.println((DMA_TCD0_CSR>>6)&1); //Show if channel is active

  Serial.println(" Success!");

  //Show results
  Serial.println("Write Data contents: ");
  show_chunk(WriteData);
}

void loop() 
{
  
}

And here is the old version which used uint32_t addresses:
Note that this old version surely isn’t working as intended as even with the wrong SOFF and DOFF settings, it still copies the data. Also, if you do use the correct settings, it restarts the Teensy after a transfer is complete, so please use it as a example of what not to do.
Code:
//Helpful macros
#define print64ln(value) Serial.printf("0x%" PRIX64 "\n", value); //Prints a 64bit number in hex, then a new line
#define print64(value) Serial.printf("0x%" PRIX64, value); //Prints a 64bit number in hex.

//Chunks
int32_t ReadData_array[64]; //In RAM
int32_t *ReadData=ReadData_array; //Pointer
uint32_t ReadData_adress=&ReadData; //uint32_t address in memory

int32_t WriteData_array[64]; //In RAM
int32_t *WriteData=WriteData_array; //Pointer
uint32_t WriteData_adress=&WriteData; //uint32_t address in memory

void fill_chunk(int32_t *myChunk) //Fills with data sequencially
{
  for (uint8_t i=0; i<64; i++)
  {
    *(myChunk+i)=i; 
  }
}

void show_chunk(int32_t *myChunk) //Shows whats stored in a chunk
{
  for (uint8_t i=0; i<64; i++)
  {
    Serial.println(*(myChunk+i));
  }
}


/*
 * Here we have temporary holds for all the DMA settings. 
 * Once DMA.begin() is ran, they all get applied in the
 * right order. While this uses up precious memory, it's not
 * really that much. And solves a lot of problems doing it like this.
 */

//All and I do mean ALL DMA registers have to be 0'ed out, as they start undefined.
//So any that use OR operations go into mememory first.
uint32_t Memory_CR=0;
uint32_t Memory_ATTR=0;

//The Minor Loop is funky so we have to construct it here:
uint32_t Memory_BytesToTransfer=0;
int32_t Memory_MinorLoopOffset=0;
bool Memory_SourceOffsetEnable=false;
bool Memory_DestinationOffsetEnable=false;

void DMA_begin() //Configures everything in the right order
{ 
  /*
   * Major Loop
   */
  DMA_CR=Memory_CR;

  /*
   * Stuff for both Source and Destination
   */
  DMA_TCD0_ATTR=Memory_ATTR;

  /*
   * Minor Loop - Now it's time to get funky
   */
  //Clear the register as it's udnefined
  DMA_TCD0_NBYTES_MLOFFNO=0;
  
  if ((Memory_SourceOffsetEnable==true) || (Memory_DestinationOffsetEnable==true)) //Will we use the MinorLoop offset?
  {
    if (Memory_SourceOffsetEnable==true) {DMA_TCD0_NBYTES_MLOFFNO |= DMA_TCD_NBYTES_SMLOE;}
    if (Memory_DestinationOffsetEnable==true) {DMA_TCD0_NBYTES_MLOFFNO |= DMA_TCD_NBYTES_DMLOE;}
    /*
     * If desired, enable the Minor Loop Offset for the Source/Destination Adress
     */
    
    //Now that the offsets are enabled we can use the Minor Offset register
    /*
     * This register behaves differently based on how we configure the DMA. To make it have the Minor Loop Offset we need to set some bits before we define
     * the Minor Loop Offset. Thats why the "ifs" are above the code below, and why we keep using different names for the same register. 
     */
     
    DMA_TCD0_NBYTES_MLOFFYES = Memory_BytesToTransfer | DMA_TCD_NBYTES_MLOFFYES_MLOFF(Memory_MinorLoopOffset);                    
    /*
     * NBYTES - How many bytes in total are we transfering per request? Think of it as the i<64 part in a for loop
     * MLOFF - As described before, each time a Minor Loop finishes (a DMA request finishes) we add a offset to the Source and Destination adress  
     * ONLY IF the enable bits are set
     */
  }
  else //No Minor Loop Offset
  {
    DMA_TCD0_NBYTES_MLOFFNO = Memory_BytesToTransfer; //Only sets NBYTES
  }

  //And with that our DMA channel should be all configured nicely!
}

void setupDMAChannel_0_Source(uint32_t ReadFrom, int32_t SourceIncrement, int32_t SourceBack, int8_t DataSize)
{
  DMA_TCD0_SADDR = ReadFrom;
  /*
   * SADDR - The source adress for the DMA to read from
   */

  DMA_TCD0_SOFF = SourceIncrement;
  /*
   * SOFF - With each read by how many bytes do we increment the source adress?
   */

  Memory_ATTR |= DMA_TCD_ATTR_SSIZE(DataSize);
  /*
   * SSIZE - How wide in bits (eg. 8, 16, 32) is the data we are transfering?
   * 
   * 0-> 8 bits 1-> 16 bits 2-> 32bits 5-> 32-byte burst (no idea what that is)
   */

  DMA_TCD0_SLAST = SourceBack; //This can be negative to go Back to the Future! Or... more like the adress we have started with before the transfer
  /*
   * SLAST - After a Major Loop completes, the value from this register will be added back into the SADDR regitser. 
   * We can use this to restore it to it's original state
   */
}

void setupDMAChannel_0_Destination(uint32_t WriteTo, int32_t DestinationIncrement, int32_t DestinationBack, int8_t DataSize)
{
  DMA_TCD0_DADDR = WriteTo;
  /*
   * DADDR - The destination adress for the DMA to write to
   */

  DMA_TCD0_DOFF = DestinationIncrement;
  /*
   * DOFF - With each write by how many bytes do we increment the source adress?
   */

  Memory_ATTR |= DMA_TCD_ATTR_DSIZE(DataSize);
  /*
   * DSIZE - How wide in bits (eg. 8, 16, 32) is the data we are transfering?
   * 
   * 0-> 8 bits 1-> 16 bits 2-> 32bits 5-> 32-byte burst (no idea what that is)
   */
  
  DMA_TCD0_DLASTSGA = DestinationBack;
  /*
   * DLASTSGA - After a Major Loop completes, the value from this register will be added back into the DADDR regitser. 
   * We can use this to restore it to it's original state.
   * 
   * It's more advanced then Source but I don't uderstand how that stuff works yet.
   */
}

void setupDMAChannel_0_MajorLoop(int32_t HowManyTransfers)
{
  Memory_CR = DMA_CR_GRP1PRI | DMA_CR_EMLM | DMA_CR_EDBG;
  /*
   * A GROUP PRIORITY MUST ALWAYS BE SET!
   * GRP1PRI - Group 1 Priority. Group 1 gets set as the most important. 
   * GRP0PRI - Group 0 Priority. Same thing as group 1
   * 
   * EMLM - Enable Minor Loop Mapping
   * Minor loops allow us to have offsets to the Source and Destination adress after each minor loop completes.
   * 
   * Minor loops are individual requests. So if I request to copy X bytes from A to B that action occurs inside a Minor Loop.
   * However, If I'd want to have that copy operation start with a diffrenet offset each time a request fires then that Copy Minor Loop will start working inside a larger Major Loop.
   * The Major Loop isn't a loop really, it's just waits uintil CITER reaches 0. At which point it finishes by applying it's own Major Loop offset.
   * 
   * EDBG - Enable Debug Mode. The DMA stalls when a debugger is plugged in
   */
  
  
  DMA_TCD0_BITER = HowManyTransfers;
  DMA_TCD0_CITER = HowManyTransfers;
  /*
   * CITER - Current Major Loop Interation Cout. It's a counter that starts at the number of transfers we want to do (BITER - Beggining Iteration Count)
   * and then decrements down until 0. When it reaches 0, we know that a transfer has finished and we can fire a INTMAJOR
   * interrupt for example.
   */

  
  //DMA_TCD0_CSR |= DMA_TCD_CSR_INTMAJOR;
  /*
   * BWC - Bandwith Control
   * If we want to slow the DMA down we can stall it every 4 or 8 CPU cycles. Writing a 0 here will have the DMA run at full speed.
   * 
   * INTMAJOR - Setting this to 1 will couse the DMA to fire a interrupt when it finishes the transfer
   */
}

void setupDMAChannel_0_MinorLoop(uint32_t BytesToTransfer, int32_t MinorLoopOffset, bool SourceOffsetEnable, bool DestinationOffsetEnable)
{ 
  /*
   * For explanations see DMA_begin()!
   */
  
  Memory_BytesToTransfer=BytesToTransfer;
  Memory_MinorLoopOffset=MinorLoopOffset;
  Memory_SourceOffsetEnable=SourceOffsetEnable;
  Memory_DestinationOffsetEnable=DestinationOffsetEnable;
}

void startDMAChannel_0()
{
  DMA_TCD0_CSR |= DMA_TCD_CSR_START; //Start a DMA request
}

void setup() 
{
  Serial.begin(2000000);

  delay(2000);
  
  //Fill the chunk with data
  fill_chunk(ReadData);

  //Show result
  Serial.println("Chunk filled with: "); 
  show_chunk(ReadData);

  //Setup DMA Channel 0
  Serial.print("Setting up DMA...");


  CCM_CCGR5 |= CCM_CCGR5_DMA(CCM_CCGR_ON);
  /*
   * Start DMA Clock, we have to do this before we do any writing to the DMA TCD or the CPU will crash.
   */
  
  setupDMAChannel_0_MajorLoop(1);
  /*
   * 1 - How many transfers in the Major Loop?
   */
  
  setupDMAChannel_0_Source(ReadData_adress,0,0,2);
  /*
   * ReadData - Source Adress
   * 4 - Adress Increment value
   * -256 - Once a Major loop finishes, go back by 256 bytes (return to start)
   * 2 - We are transfering 32 bit data
   */

  setupDMAChannel_0_Destination(WriteData_adress,0,0,2);
  /*
   * WriteData - Destination Adress
   * 4 - Adress Increment value
   * -256 - Once a Major loop finishes, go back by 256 bytes (return to start)
   * 2 - We are transfering 32 bit data
   */

  setupDMAChannel_0_MinorLoop(256,0,false,false);
  /*
   * 256 - How many bytes will we transfer with each Minor Loop?
   * 0 - Minor Loop Offset
   * true - Source Minor Loop Offset Enable
   * true - Destination Minor Loop Offset Enable
   */


  DMA_begin();
  /*
   * Some stuff, mainly the Minor Loop stuff has to be written in a very specific order.
   * Thats what DMA begin takes care of.
   */
   
  Serial.println(" Success!");

  //Start a DMA request
  Serial.print("Starting a DMA request...");

  startDMAChannel_0();
  
  Serial.println((DMA_TCD0_CSR>>6)&1); //Show if channel is active

  Serial.println(" Success!");

  //Show results
  Serial.println("Write Data contents: ");
  show_chunk(WriteData);
}

void loop() 
{
  
}
 
Back
Top