DMA channel ISR

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

DMA channel ISR

5,491 Views
vale
Contributor I
Hallo,
would anyone give me an example of an ISR serving a DMA interrupt on completion of a transfer?
I need an example where the ISR reconfigures a new transfer with the same characteristics of the previous transfer.
 
Thank you
Valentina
Labels (1)
0 Kudos
11 Replies

1,274 Views
mccPaul
Contributor I
Hi Valentina,
 
There is an example below. This is taken from an application we have running on the mcf5282 that transfers data from a peripheral to the FEC. It works very reliably and we are able to use it to transfer data to the FEC at a rate of over 90Mbit/s.
 
The external peripheral generates an external IRQ (I have not show the intialialisation for this just the isr). It is important to make sure that the interrupt levels and priorities are set properly so that the peripheral does not interrupt the DMA completion ISR or to mask the external interrupt when the DMA data pump is running.
 
All code for 5282, but should be obvious how to change for 5223x.
 
Paul.
 
P.S. Excuse the source formatting, this forum seems to screw it up!
 
Code:
voidinit(void){  // Set up an interrupt handler for DMA0 mcf5282_interrupt_init(9,         MCF5282_INTC_ICR_IL(5)         | MCF5282_INTC_ICR_IP(3),         data_pump_isr);  // Clear DMA0 MCF5282_DMA0_DSR = MCF5282_DMA_DSR_DONE; }/**************************************************************************** * FUNCTION: start_copy_isr(void) *  * ISR servicing an external interrupt which starts the DMA data pump *  * RETURNS:  nothing *  * When the interrupt is asserted, the peripheral has one or more buffers full of * data. This ISR will start a DMA transfer for the first full buffer. The * DMA completion interrupt will start another DMA transfer if there is any * more data left to transfer. *  */__attribute__((interrupt_handler)) voidstart_copy_isr(){ // Some code removed here that just works out which // buffer to use as a destination - we have a double // buffering scheme in use here. SRC_BUFFER_BA(chnl) is // the address of the correct source buffer. // DST_BUFFER_BA(chnl) is the base address of the destination. // BUF_LEN is the buffer length. We make sure our // buffers are on 16-byte aligned addresses so that // we can fource line xfers.      // Configure a DMA transfer to current_buffer MCF5282_DMA0_SAR = (uint32)SRC_BUFFER_BA(chnl); MCF5282_DMA0_DAR = (uint32)DST_BUFFER_BA(chnl); MCF5282_DMA0_BCR = BUF_LEN << 16;  MCF5282_DMA0_DCR = 0 | MCF5282_DMA_DCR_INT    // Enable DMA interrupt       | MCF5282_DMA_DCR_SINC    // Inc src addr       | MCF5282_DMA_DCR_DINC    // Inc dst addr       | MCF5282_DMA_DCR_SSIZE_LINE  // 16 byte src xfer       | MCF5282_DMA_DCR_DSIZE_LINE;  // 16 byte dst xfer        MCF5282_DMA0_DCR |= MCF5282_DMA_DCR_START;    // Start DMA xfer // A bit more housekeeping deleted here. Mainly to mask the // external peripheral's interrupt. The DMA completion ISR will // check if the peripheral still has more data to transfer.}/**************************************************************************** * FUNCTION: data_pump_isr(void) *  * ISR for the data pump interrupt on DMA completion *  * RETURNS:  nothing *  * When a DMA transfer completes, this handler will be called. The source peripheral * status is examined and if another buffer is ready to go, then another * DMA transfer is initiated. If there is no more data, we reset our * state to wait for another buffer full of data. *  */__attribute__((interrupt_handler)) voiddata_pump_isr(void){ // Write DMA0(DONE) = 1 to clear interrupt MCF5282_DMA0_DSR = MCF5282_DMA_DSR_DONE; // The DMA transfer for the current buffer has completed. // Code removed here that will deal with the now filled // target buffer.  // Check if any other buffers need to be serviced if (SRC_BUF_FULL(chnl)) {   // Configure a DMA transfer to the relevant buffer   MCF5282_DMA0_SAR = (uint32)SRC_BUFFER_BA(chnl);   MCF5282_DMA0_DAR = (uint32)TGT_BUFFER_BA(chnl);   MCF5282_DMA0_BCR = BUF_LEN << 16;   MCF5282_DMA0_DCR = 0 | MCF5282_DMA_DCR_INT         | MCF5282_DMA_DCR_SINC         | MCF5282_DMA_DCR_DINC         | MCF5282_DMA_DCR_SSIZE_LINE         | MCF5282_DMA_DCR_DSIZE_LINE         | MCF5282_DMA_DCR_START;            // New DMA transfer is now started } else {  // No more data so put the peripheral in a state so that it  // can trigger a new transfer }}/* FUNCTION: mcf5282_interrupt_init() * * Initialise an interrupt handler for an interrupt source * for INTC0. If the handler is a NULL pointer, then mask * this interrupt. * * PARAM1: Interrupt source (1..62) * * PARAM2: Interrupt level and priority * * PARAM3: Interrupt handler * * RETURNS: none */voidmcf5282_interrupt_init(uint8 source, uint8 ipl, void (*handler)(void)){ // Only for user defined vectors in INTC0 if ((source > 0) && (source < 63)) {  // Interrupts should be disabled to avoid vector problems  // and to ensure that a spurious interrupt exception can't  // be generated.  uint8 sysint = asm_set_ipl(7);  if (handler)  {   // Set interrupt priority level   MCF5282_INTC0_ICR(source) = (ipl & 0x3F);   // Set vector   mcf5xxx_set_handler(source+64, (ADDRESS)handler);   // Clear mask for this handler   if (source < 32)    MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_INT(source)          | MCF5282_INTC_IMRL_MASKALL);   else   {    MCF5282_INTC0_IMRL &= ~(MCF5282_INTC_IMRL_MASKALL);    MCF5282_INTC0_IMRH &= ~(MCF5282_INTC_IMRH_INT(source));   }  }  else  {   // Set vector   mcf5xxx_set_handler(source+64, (ADDRESS)handler);   // Set mask for this handler   if (source < 32)   {    MCF5282_INTC0_IMRL |= MCF5282_INTC_IMRL_INT(source);   }   else   {    MCF5282_INTC0_IMRH |= MCF5282_INTC_IMRH_INT(source);   }  }  // As you were...  asm_set_ipl(sysint); }}

 
0 Kudos

1,274 Views
Joda
Contributor I
Hi Paul,
 
  I'm newbie about 52235 and I have a question for you. In my project I performed two DMA rx isr connecting DMA channel 0 with UART0 and DMA channel 1 with UART1. Channel 0 writes on Buff0 and channel 1 on Buff1.
 
I played with interrupts:
 
 
configuration :smileyinfo: : DMA channel0 operative alone
OK
 
configuration (ii) : DMA channel1 operative alone
OK
 
configuration (iii) : both operative (channel 0 is set IL=4 and channel 1 is set IL=5)
channel 0 is OK and channel 1 doesn't work
 
configuration (iv) : both operative (channel 0 is set IL=5 and channel 1 is set IL=4)
channel 0 doesn't work and channel 1 is OK
 
Do you know what's happening?
 
Thank you.
 
Joda
0 Kudos

1,274 Views
mccPaul
Contributor I
Well, without seeing any code, it is almost impossible to give you an idea why it isn't working! It could be anything from a simple coding mistake to a misunderstanding of how the interrupt controller/dma controller/uarts work.
 
Post some code and I'll take a look.
 
Paul.
0 Kudos

1,274 Views
Joda
Contributor I

OK, Paul. In the following lines you'll see the init procedure for the bridge (bridge_init), the init procedure for DMA (dma_uart_init), the Interrupt Enable procedure (EnableDMAinInterrupt) and the interrupt routine.

char bridge_init(unsigned char ucBridge, unsigned long int ulBaud_rateUART0, unsigned long int ulBaud_rateUART1){
  struct uartbridge_desc* uartbridge; 

  // initialize structure

 [...]
  // initialize buffers
 [...]
 // initialize pointers
  [...]

 

 // initialize UART0
 uartbridge->ucDevice_UART0=0;
 if (uart_init(uartbridge->ucDevice_UART0, ulBaud_rateUART0) != -1){
   uartbridge->ulSpeed_UART0=ulBaud_rateUART0;
    uartbridge->vucUART0_idle=1; 
  uartbridge->ucInitdone_UART0=1;
  printf("UART0 initialized\n\r");
 }
  
 // initialize UART1
 uartbridge->ucDevice_UART1=1;
 if (uart_init(uartbridge->ucDevice_UART1, ulBaud_rateUART1) != -1){
   uartbridge->ulSpeed_UART1=ulBaud_rateUART1;
  uartbridge->ucInitdone_UART1=1;
  printf("UART1 initialized\n\r");
 }
  
 // UART1 DMA1
 uartbridge->ucUART1_DMA_in=1;         // initialize input DMA ch1
 dma_uart_init(uartbridge->ucDevice_UART1, uartbridge->ucUART1_DMA_in, uartbridge->ucpUART1_RXBuf);
 EnableDMAinInterrupt(ucBridge,UART1_DMA_ch);         // unmask DMA1 interrupt
 MCF_UART_UCR(uartbridge->ucDevice_UART1)=MCF_UART_UCR_RESET_MR;   // reset UMR
 MCF_UART_UMR(uartbridge->ucDevice_UART1) = (   MCF_UART_UMR_PM_NONE  // no parity
                 | MCF_UART_UMR_SB(0x07)  // stop bit
                 | MCF_UART_UMR_CM_NORMAL // no loopback
                 | MCF_UART_UMR_BC(0x03)); // 8 bit

 

// UART0 DMA0
 uartbridge->uc_UART0_in=0;         // initialize input DMA ch0
 dma_uart_init(uartbridge->ucDevice_UART0, uartbridge->ucUART0_DMA_in, uartbridge->ucpUART0_RXBuf);
 EnableDMAinInterrupt(ucBridge, UART0_DMA_ch);        // unmask DMA0 interrupt
 
 MCF_UART_UCR(uartbridge->ucDevice_UART0)=MCF_UART_UCR_RESET_MR;   // reset UMR
 MCF_UART_UMR(uartbridge->ucDevice_UART0) = ( MCF_UART_UMR_PM_NONE  // no parity
                 | MCF_UART_UMR_SB(0x07)  // stop bit
                 | MCF_UART_UMR_CM_NORMAL // no loopback
                 | MCF_UART_UMR_BC(0x03)); // 8 bit

   return 0;
  
 }

char dma_uart_init(unsigned char ucDev, unsigned char ucChan, unsigned char* ucpBuffer){
  
 // route DMA channel 1 to UART1 module
 if ((ucDev==1) && (ucChan==1)){
  MCF_DMA_DMAREQC |= MCF_DMA_DMAREQC_DMAC1(0x9);   //route DMA Ch1 to UART1 RX
  MCF_DMA_SAR1=(volatile unsigned long)&MCF_UART1_URB; //DMA Ch1 source address
  MCF_DMA_DAR1=(volatile unsigned long) ucpBuffer;  //DMA Ch1 dest address
  MCF_DMA_BCR1=MCF_DMA_BCR_BCR(BLOCKSIZE);    //byte number
  
  MCF_DMA_DCR1 |= MCF_DMA_DCR_INT |    //enable int
            MCF_DMA_DCR_EEXT |                         //enable external request
            MCF_DMA_DCR_CS |                             //forces a single r/w cycle per request
            MCF_DMA_DCR_SSIZE(0x1) |                //1 byte size for source bus cycle
            MCF_DMA_DCR_DINC |                          //enable dest increment
            MCF_DMA_DCR_DSIZE(0x1);                //1 byte size for dest bus cycle
 
  MCF_SCM_RAMBAR |= MCF_SCM_RAMBAR_BDE;   //enable access to SRAM
  MCF_SCM_PACR2 |= MCF_SCM_PACR_UART1_RW;   //give r/w access to user/superuser
  printf("DMA1 initialized \n\r");
  return 0;
  }
 
 // route DMA channel 0 to UART0 module
 if ((ucDev==0) && (ucChan==0)){
  MCF_DMA_DMAREQC |= MCF_DMA_DMAREQC_DMAC0(0x8);   //route DMA Ch0 to UART0 RX
  MCF_DMA_SAR0=(volatile unsigned long)&MCF_UART0_URB; //DMA Ch0 source address
  MCF_DMA_DAR0=(volatile unsigned long) ucpBuffer;  //DMA Ch0 dest address
  MCF_DMA_BCR0=MCF_DMA_BCR_BCR(BLOCKSIZE);    //byte number
  
  MCF_DMA_DCR0 |= MCF_DMA_DCR_INT |  //enable int
        MCF_DMA_DCR_EEXT |                           //enable external request
        MCF_DMA_DCR_CS |                               //forces a single r/w cycle per request
        MCF_DMA_DCR_SSIZE(0x1) |                  //1 byte size for source bus cycle
        MCF_DMA_DCR_DINC |                            //enable dest increment
        MCF_DMA_DCR_DSIZE(0x1);                   //1 byte size for dest bus cycle
 
  MCF_SCM_RAMBAR |= MCF_SCM_RAMBAR_BDE;   //enable access to SRAM
  MCF_SCM_PACR2 |= ((MCF_SCM_PACR_UART0_RW)<<4); //give r/w access to user/superuser
  printf("DMA0 initialized \n\r");
  return 0;
  }
 
 return -1; 
}


 void EnableDMAinInterrupt(unsigned char ucBridge, unsigned char ucDMA_number){
  struct uartbridge_desc* uartbridge=&uartbridges[ucBridge];
  
  if (ucDMA_number==1){     //DMA1 for UART1
        MCF_INTC0_ICR10= DMA1_Int_setting;
        MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_MASK10 | MCF_INTC_IMRL_MASKALL);
  }

  
  else if (ucDMA_number==0){   //DMA0 for UART0
        MCF_INTC0_ICR9= DMA0_Int_setting;
        MCF_INTC0_IMRL &= ~(MCF_INTC_IMRL_MASK9 | MCF_INTC_IMRL_MASKALL);
  }
  
 else {
  printf("Enable DMA in int operation failed!");
 }
 }

The interrupt routine for DMA1 is:

__declspec(interrupt) void dma1_isr (void){
   unsigned char ucUART_usr,ucDMA_usr,ucUART1Buf_next;
   signed char scRoom;
   struct uartbridge_desc* uartbridge;
   unsigned char ucDevin;
   unsigned char ucDMA_in;
   int i;
 
   ulUART1_Int_DMAin_hit++;

   ucDevin=1;
   ucDMA_in=1;

  ucUART_usr=MCF_UART_USR(ucDevin);     //read input UART status
  MCF_UART_UCR(ucDevin)=MCF_UART_UCR_RESET_ERROR;  //reset input UART error status
  ucDMA_usr=MCF_DMA_DSR(ucDMA_in);     //read DMA status

   if (ucDMA_usr & MCF_DMA_DSR_DONE){   //check the interrupt source
     ulUART1_Int_DMAin_done_hit++;
   
    // input UART error check
    if (ucUART_usr & (MCF_UART_USR_OE |  //overrun error
             MCF_UART_USR_PE |  //parity error
             MCF_UART_USR_FE |  //framing error
             MCF_UART_USR_RB )) //received break
     { 
       printf("Input UART1 error.");
       ulUART1_in_err_counter++;
    }

    // input DMA error check
    if (ucDMA_usr & (MCF_DMA_DSR_CE | //configuration error
             MCF_DMA_DSR_BES | //source error
            MCF_DMA_DSR_BED | //dest error
            MCF_DMA_DSR_REQ //pending req
          )){
      printf("Input DMA1 error.");
      ulUART1_DMAin_err_counter++;
     
     MCF_DMA_DSR(ucDMA_in) |= MCF_DMA_DSR_DONE;
     MCF_DMA_DMAREQC=0;  //remove routing between UART and DMA
    }
    else {
   if ((ucUART1Buf_next=uartbridge->vucUART1Buf_in+BLOCKSIZE)>=UARTRTXBUFSIZE)
      ucUART1Buf_next-=UARTRTXBUFSIZE;
  
     // RX buf full check
     scRoom=(uartbridge->vucUART1Buf_out - ucUART1Buf_next);  //room left
   if (scRoom < 0) scRoom+=UARTRTXBUFSIZE;  
     if (scRoom < BLOCKSIZE) ulUART1_DMAbytein_drop++;  //no room left: drop one frame, don't update
      else {
      uartbridge->vucUART1Buf_in=ucUART1Buf_next;      //there is room left: update vucRx_in
      uartbridge->vucUART1_idle=0;
   }
      
      MCF_DMA_DAR(ucDMA_in)=(volatile unsigned long) (uartbridge->ucpUART1_RXBuf + uartbridge->vucUART1Buf_in); //update the DMA pointer
     
     MCF_DMA_DMAREQC=0;        //stop routing DMA channel to UART
     MCF_DMA_DSR(ucDMA_in) |= MCF_DMA_DSR_DONE;  //clear DMA interrupt and error bits 
       MCF_DMA_BCR(ucDMA_in) |= MCF_DMA_BCR_BCR(BLOCKSIZE);
      MCF_DMA_DMAREQC |= MCF_DMA_DMAREQC_DMAC1(0x9); //restore routing DMA channel to UART
     }
   }
    else ulUART1_Int_DMAin_diff_hit++;
 }

 

The routine for DMA0 is dual. In the file mcf52235_vectors.s I modified the following:

//vector49: .long _irq_handler
vector49: .long _dma0_isr /* DMA0 ISR */
//vector4A: .long _irq_handler
vector4A: .long _dma1_isr /* DMA1 ISR */
vector4B: .long _irq_handler

 

 

Please, try to give me some hint.

Thank you in advance.

0 Kudos

1,274 Views
Joda
Contributor I
I found!
 
In ISR, when I stop routing DMA channel with
   MCF_DMA_DMAREQC=0;  //remove routing between UART and DMA
I stopped also the other channel! What a dumb!
 
The right commands are:
   MCF_DMA_DMAREQC &= ~(MCF_DMA_DMAREQC_DMAC1(0xF));  //stop routing DMA1 to UART1
   MCF_DMA_DMAREQC &= ~(MCF_DMA_DMAREQC_DMAC0(0xF));  //stop routing DMA0 to UART0
 
 
Joda
0 Kudos

1,274 Views
mccPaul
Contributor I
Well done!
 
I was going to have to go through your code with the reference manual at my side, but I have had too much work to find the time yet.
 
Cheers,
 
Paul.
0 Kudos

1,274 Views
Kremer
Contributor I
 Hi Paul
 
 I just didn´t understand why you did this:
 
  MCF5282_DMA0_BCR = BUF_LEN << 16;
 
Basically because i don´t know how BUF_LEN is declared but can you explain this to me please?
I understand you are doing a line transfer but shifting a value like 0x00000001 16 bits to left would turn it into a 64K or 0x00010000. I just didn´t understand that.
 
 Thank you
 
0 Kudos

1,274 Views
mccPaul
Contributor I
Hi Kremer,
 


Kremer wrote:
 
 I just didn´t understand why you did this:
 
  MCF5282_DMA0_BCR = BUF_LEN << 16;
 

You can configure the byte count registers to be a 16-bit or a 24-bit count. If the count is 16-bit (flag BCR24BIT = 0), then it is encoded in bits 31-16 of the byte count register BCR. If it is 24-bit, then the count is encoded in bits 23-0.
 
I am using 16-bit counts and I define BUF_LEN to be the actual length of the buffer, therefore, I have to shift BUF_LEN 16 bits to the left to store it in the correct place in the 32-bit wide byte count register. The lowest 16 bits of BCR are reserved and set to 0 on reset so the assignment won't changed them.
 
Cheers,
 
Paul.
 
 
0 Kudos

1,274 Views
Kremer
Contributor I
 Hi Paul
 
 Thank you for your reply. I´m not questioning if your aplication works, just because it must be working very fine. That´s why people like us exist!!! I´m just trying to understand that DMA scheme.
 Indeed, looking into the system control module we have the MPARK register wich let us choose between 24 bits BCR or 16 bits.
 As you explained, when working with 16 bits counter config, you must use the upper word of the register, so that´s why you shift the BUF_LEN 16 positions left. But my question resides now on where DSRn will be when you are using the 16 bit counter mode?
 On MCF52235RM Rev 4 page 20-6 we have that this 32 bit register is divided into 2 DMA registers. The first 24 bits are reserved for the BCRn and the others 8 bits are the DSRn (Status register). So when using this mode, where the DSRn will be?
 Maybe there are differences between the 5223x and the processor you are using regarding this DMA scheme.
 
 Thank you
 Regards
 
0 Kudos

1,274 Views
mccPaul
Contributor I
Hi Kremer,
 
There is a small difference between the 5223x and the 528x parts. On the 528x the BCR and DSR are separate and BCR needs to be configured as per my previous post. For the 5223x parts DSR and BCR are combined as you pointed out.
 
To port the example above you would need to remove the left shift from the assignment to the BCR:
 
Code:
MCF5223x_DMA0_BCR = BUF_LEN;

There are small differences like this between parts all over the place - presumably to make sure that there is plenty of work for developers!
 
Cheers,
 
Paul.
0 Kudos

1,274 Views
Kremer
Contributor I
 Yeah, right! Now everything is clear.
Taking a look into the MCF5282UM i can see that it has a different memory mapped registers comparing to MCF5223x, at least for the DMA registers. So that 16 shift you did to use the upper word of the BCRn shall not be done in the 5223x case or you´ll be writing things to the status register. Well, nice to know.
 
 Thank you again
 Cheers
 
 Kremer
0 Kudos