4.2 DMX Receiver with DMA

In the case of DMX receiver, the microcontroller will need to receive a range of data from the complete DMX packet. As mentioned earlier, the UART module is capable of filtering unwanted data bytes, but the firmware still needs to read the necessary bytes from the UxRXB register. Using the DMA module, the received data bytes can be transferred to a buffer in the RAM area without the involvement of the CPU.

The DMA module is setup to read the incoming byte from the UxRXB register and transfer it to a location in the RAM area. The destination address will increment in this case, but the source address remains static. The DMA operation can be triggered by the UART Receive Interrupt Flag (UxRXIF). When a byte is received into the buffer, the DMA is triggered to move it to the buffer. Note that the receive interrupt will set only for the valid address range defined in the UART configuration. The DMA module can be configured to stop or restart every time the destination counter reloads.

The example code below is for the DMA setup to receive 16 bytes of data starting from address 20. Note that the start code is always received and transferred into the RAM buffer. So the DMA count should be one more than the number of required bytes. Note that the UART configuration is still required, as mentioned in section Setup for DMX Receiver.

Code snippet for DMA setup to work with DMX Recevier

Note: This code example is written for PIC18F25K42. Minor edits might be needed to use it for other devices that feature the DMA module.

void DMA1_Initialize(void)
{
    DMA1SSA = &U1RXB;         //set source start address
    DMA1DSA = &RX_DATA;       //set destination start address
    DMA1CON1 = 0x60;          //DMODE=01|DSTP=1|SMR=00|SMODE=00|SSTP=0 
    DMA1SSZ = 0x0001;         //set source size = 1 byte
    DMA1DSZ = 0x0011;         //set destination size = 17 bytes
    DMA1SIRQ = 0x1B;          //set DMA Transfer Trigger Source = U1RX     
    DMA1AIRQ = 0x00;          //set DMA Transfer abort Source    
     
    PIR2bits.DMA1DCNTIF = 0;  //clear Destination Count Interrupt Flag      
    PIR2bits.DMA1SCNTIF = 0;  //clear Source Count Interrupt Flag 
    PIR2bits.DMA1AIF = 0;     //clear abort Interrupt Flag 
    PIR2bits.DMA1ORIF = 0;    //clear overrun Interrupt Flag 
    PIE2bits.DMA1DCNTIE = 0;  //disable Destination Count Interrupt 
    PIE2bits.DMA1SCNTIE = 0;  //disable Source Count Interrupt
    PIE2bits.DMA1AIE = 0;     //disable abort Interrupt 
    PIE2bits.DMA1ORIE = 0;    //disable overrun Interrupt 

    asm("BCF INTCON0,7");     //disable Global Interrupts
    asm ("BANKSEL PRLOCK");   //    
    asm ("MOVLW 0x55");       //    
    asm ("MOVWF PRLOCK");     //Arbiter Priority lock
    asm ("MOVLW 0xAA");       //sequence
    asm ("MOVWF PRLOCK");     //     
    asm ("BSF PRLOCK, 0");    //  
    asm("BSF INTCON0,7");     //enable Global Interrupts
    DMA1CON0 = 0x80;          //EN=1|SIRQEN=0|DGO=0|xx|AIRQEN=0|x|XIP=0 
 }
    

Note: User firmware can set the SIRQEN bit whenever the DMA transaction needs to start. This can be done by the following line of code.
DMA1CON0bits.SIRQEN = 1;