Sometimes you just need to ask for help...

Status
Not open for further replies.

Brooks

Well-known member
I'm working on a DMA-based AX-12 servo driver. This implements a half-duplex async interface to the servos.

I've gotten the TX side working. Well, almost. when I look at the data stream with a o-scope I can see that the second byte of the Tx buffer is not being sent. Transmission goes straight from the 1st byte to the 3rd byte.

I suspect the uart's fifo's are immediately requesting the 2nd byte, it's supplied by the eDMA, but the uart is not seeing it. I'm thinking of just manually pushing data to the uart until it says it doesn't need anymore right now, and then DMA'ing the rest.

Any thoughts? Thanks in advance!


Code:
//*****************************************************************************
//  Changes I had to make:
//  1) I had to remove the like-named routine from Serial3 !!!
//     void uart2_status_isr(void) {
//*****************************************************************************
#include <kinetis.h>

enum {
    cPin_OnTeensyLed      = 13,
    cPin_TxDriveEnable    = 24,
    cPin_RxDriveEnable    = 25,
    cPin_UartTx           =  8,
    cPin_UartRx           =  7
};

#define cBaudRate       10000

#define cIRQ_PRIORITY     64

int volatile transmitting;
uint32_t volatile     mDmaErr;
int volatile          mTxCycleCompleted;
uint32_t              mLedTimer;


    char cMessage [] = "\x3C\xFF\xF0\xFF\x00\xFF\xFF\xFF\xFF\xFF\x00\xFF\x00\xFF\x00\xFF\x00";

//*****************************************************************************
void dma_ch10_isr(void) {
    mDmaErr |= DMA_ES;
    DMA_CINT = DMA_CINT_CINT(10);    // Clear interrupt

    UART2_C5 = 0;   // Clear C5[TDMAS]
    UART2_C2 = UART_C2_TE | UART_C2_TCIE;   // | UART_C2_TIE;
}

//*****************************************************************************
void uart2_status_isr(void) {
    if (UART2_S1 & UART_S1_TC) {
        transmitting = 0;                 // Report our completion; // TODO: Make better!
        mTxCycleCompleted = 1;
        digitalWrite(cPin_TxDriveEnable, HIGH);
        UART2_C2 = UART_C2_TE;
    }
}

//*****************************************************************************
void startTx(void * iTxAddress, uint32_t iTxLength) {
    DMAMUX0_CHCFG10 = 0;
    DMA_CERQ = DMA_CERQ_CERQ(10);

    digitalWrite(cPin_TxDriveEnable, LOW);

    transmitting = 1;

    DMA_TCD10_CITER_ELINKNO = 
      DMA_TCD10_BITER_ELINKNO = iTxLength;                          // BITER & CITER, which must match!
        
    DMA_TCD10_NBYTES_MLNO     = DMA_TCD_NBYTES_SMLOE |              // Minor Loop Offset --> Src  ENABLED
                                                                    // Minor Loop Offset --> Dest DISABLED
                                DMA_TCD_NBYTES_MLOFFYES_MLOFF(1) |  // Increment Src addr by 1
                                DMA_TCD_NBYTES_MLOFFYES_NBYTES(1);  // One byte at a time!


    
    DMA_TCD10_SADDR           = iTxAddress;   // TCD Source Addr
    DMA_TCD10_SOFF            = 0L;           // TCD Signed Source Address Offset
    DMA_TCD10_ATTR            = 0L;           // TCD Transfer Attributes
    DMA_TCD10_SLAST           = 0L;           // TCD Last Source Addr Adj.
    DMA_TCD10_DADDR           = &UART2_D;     // TCD Destination Address
    DMA_TCD10_DOFF            = 0L;           // TCD Signed Dest Address Offset
    DMA_TCD10_DLASTSGA        = 0L;           // TCD Last Destination Addr Adj

    DMA_SERQ = DMA_SERQ_SERQ(10);

    DMA_SERQ = DMA_SERQ_SERQ(10);
    DMAMUX0_CHCFG10 = DMAMUX_ENABLE | DMAMUX_SOURCE_UART2_TX; 

    UART2_C5 = UART_C5_TDMAS | UART_C5_RDMAS;   // DMA enablements
    UART2_C2 = UART_C2_TE | UART_C2_TIE;        // Tx enabled, IEs (interrupts) needed for DMA

    DMA_TCD10_CSR = DMA_TCD_CSR_INTMAJOR;
    DMA_TCD10_CSR = (DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_START);
}

//*****************************************************************************
void setupUartAndDma() {
    SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
    SIM_SCGC7 |= SIM_SCGC7_DMA;

    // We're assuming our TCD is inactive!
    DMAMUX0_CHCFG10 = 0;  // Tx
    DMAMUX0_CHCFG11 = 0;  // Rx

    DMA_CR    = DMA_CR_EMLM | DMA_CR_ERCA;
    DMA_CERQ  = DMA_CERQ_CERQ(10);
    DMA_CEEI  = DMA_CEEI_CEEI(10);
    DMA_CINT  = DMA_CINT_CINT(10);

    if (DMA_ERR & DMA_ERR_ERR10) {
        DMA_CERR |= DMA_CERR_CERR(10);
    }

    transmitting = 0;
    SIM_SCGC4 |= SIM_SCGC4_UART2;
    UART2_C2 = 0;   // ONLY after clock is on! Don't ask me how I know...

    CORE_PIN7_CONFIG = PORT_PCR_PE  | PORT_PCR_PS  | PORT_PCR_PFE | PORT_PCR_MUX(3);  // Rx
    CORE_PIN8_CONFIG = PORT_PCR_DSE | PORT_PCR_SRE | PORT_PCR_MUX(3);                 // Tx

    uint32_t divisor = (((F_BUS * 2) + ((cBaudRate) >> 1)) / (cBaudRate));

    UART2_BDH = (divisor >> 13) & 0x1F;
    UART2_BDL = (divisor >> 5) & 0xFF;
    UART2_C4  = divisor & 0x1F;

    UART2_C1     = 0;
    UART2_S2    &= ~0x10;       // No Rx invert
    UART2_C3    &= ~0x10;       // No Tx invert
    UART2_PFIFO  = 0;

    NVIC_SET_PRIORITY(IRQ_DMA_CH10, cIRQ_PRIORITY);
    NVIC_ENABLE_IRQ(IRQ_DMA_CH10);

    NVIC_SET_PRIORITY(IRQ_UART2_STATUS, cIRQ_PRIORITY);
    NVIC_ENABLE_IRQ(IRQ_UART2_STATUS);
}

//*****************************************************************************
void setup() {
mDmaErr = 0L;
mTxCycleCompleted = 0L;
mLedTimer = 0;
    pinMode(cPin_OnTeensyLed,   OUTPUT);
    digitalWrite(cPin_OnTeensyLed, LOW);

    pinMode(cPin_TxDriveEnable, OUTPUT);
    pinMode(cPin_RxDriveEnable, OUTPUT);

    setupUartAndDma();
}

//*****************************************************************************
void loop() {
    if ( ! transmitting) {
        delay(20);
        startTx(cMessage, 17);
    }
}
 
Maybe try using SOFF to increment the source address, rather than NBYTES_SMLOE?

Also, you can install your ISR function to the vector table with attachInterruptVector(), which avoids the need to delete the one inside the core library.
 
Vector: Done! I recently uncovered the forum conversation of a few years ago about this, and it was on my To-Do list

NBYTES_SMLOE --> SOFF: Done! No change - still missing the 2nd byte of the message. Sigh...

I can probably paper over it by sending an extra servo-msg prefix character (0xFF), but it bothers me to not know why.

Thanks for your thoughts. Let me know if anything else occurs to you!
 
Figured it out, with some clues from a much earlier post.

Drops a byte: Set up DMA, enable UART Tx, start DMA.

Works perfectly: Set up DMA, start DMA, enable UART Tx.
 
Status
Not open for further replies.
Back
Top