This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Receiving DMX512 data stream

Parts of this discussion started on this thread: i-am-trying-to-do-dmx512-protocol

I am using SDK 15.2.0 on an nRF52810.  My test board is based off of a Laird BL651 which contains a real (not simulated) nRF52810.  I'm using the Segger IDE.  My code is based off of the ble_app_uart_pca10040e_s112 example.  I made a copy of app_uart_fifo.c and modified to use the nrfx_uarte drivers directly.

I am trying to receive a DMX512 data stream. The DMX controller sends a packet of up to 513 bytes of data at 250KBaud and precedes each packet with a UART BREAK (holds the line low for 90us).  In my test case the controller is only sending 65 bytes of data.

I initiate the read using: nrfx_uarte_rx(&app_uart_inst, dmx_rx_buffer, BUF_READ_SIZE );  where BUF_READ_SIZE  is 513.  Here is my event handler:

static void uart_event_handler(const nrfx_uarte_event_t * p_event, void* p_context) 
{
    app_uart_evt_t app_uart_event; 
    uint32_t err_code;
 
    switch (p_event->type)
    {
        case NRFX_UARTE_EVT_RX_DONE:
            {
                //never reached??
                NRF_LOG_DEBUG("DMX data in:");
                NRF_LOG_HEXDUMP_DEBUG(dmx_rx_buffer, p_event->data.rxtx.bytes);
                (void)nrfx_uarte_rx(&app_uart_inst, dmx_rx_buffer, BUF_READ_SIZE);
            }
            break;

        case NRFX_UARTE_EVT_ERROR:
            app_uart_event.evt_type                 = APP_UART_COMMUNICATION_ERROR;
            app_uart_event.data.error_communication = p_event->data.error.error_mask;

            if (app_uart_event.data.error_communication & UARTE_ERRORSRC_BREAK_Msk)
            {
                size_t amount = nrf_uarte_rx_amount_get(app_uart_inst.p_reg);
                NRF_LOG_DEBUG("DMX err: %d %d %d %d", amount, p_event->data.rxtx.bytes,  dmx_rx_buffer[1],  dmx_rx_buffer[57]);
                (void)nrfx_uarte_rx(&app_uart_inst, dmx_rx_buffer, BUF_READ_SIZE);
            }
            break;
            
        default:
            break;
    }
}

The good news is I'm capturing the data reliably.  The bad news is I can't tell how much data was received.  The EVT_RX_DONE handler never gets called unless I reduce BUF_READ_SIZE from 513 to 65 or less.  The EVT_ERROR handler is correctly detecting BREAK.  I checked it with a scope and the handler gets called consistently 60us after the start of the BREAK which is good.  The handler is also seeing a FRAMING error which is expected.  It doesn't appear to be missing any of the BREAK events.

Once in the EVT_ERROR handler I can't find the amount of data received before the error occurred. 

Also,  if I stop the code and check the 513 byte buffer contents, I can see the correct 65 bytes of DMX data,  but the 65 bytes of data gets replicated throughout the 513 byte buffer, so I see my data at locations 0-64, but also at 65-129, 130-195, ... .

is there a way to find the number of bytes received before the BREAK and can I stop the data from filling the rest of the buffer after the BREAK is received?

  • I tried a stoprx followed by a flushrx and I get the same result.

    I set up the PPI to find the break event, checked it with a scope and it looks like I want.  However, I'm still not seeing the expected rx count in the uarte handler.  Here is my PPI setup, maybe you can spot a problem or a better way?

        // Break begins on High to low transition, reset and start timer
        NRF_PPI->CH[0].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[0];
        NRF_PPI->CH[0].TEP = (uint32_t)&NRF_TIMER1->TASKS_START;
        NRF_PPI->FORK[0].TEP = (uint32_t)&NRF_TIMER1-> TASKS_CLEAR;
        
        // Stop count on low to high transition, only count when line is low
        NRF_PPI->CH[1].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[1];
        NRF_PPI->CH[1].TEP = (uint32_t)&NRF_TIMER1->TASKS_CAPTURE[0];
        NRF_PPI->FORK[1].TEP = (uint32_t)&NRF_TIMER1->TASKS_STOP;
        
        // StopRX when count matches compare
        NRF_PPI->CH[2].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[1]; 
        NRF_PPI->CH[2].TEP = (uint32_t)&NRF_UARTE0->TASKS_STOPRX;
        NRF_PPI->FORK[2].TEP = (uint32_t)&NRF_GPIOTE->TASKS_OUT[2];  // for scope
    //    NRF_PPI->FORK[2].TEP = (uint32_t)&NRF_UARTE0->TASKS_FLUSHRX;
        
        // Timer 1, sends event when count reaches 50us
        NRF_TIMER1->MODE = (uint32_t)TIMER_MODE_MODE_Timer<<TIMER_MODE_MODE_Pos; // Timer mode
        NRF_TIMER1->BITMODE = (uint32_t)TIMER_BITMODE_BITMODE_24Bit<<TIMER_BITMODE_BITMODE_Pos; // 32 bits resolution
        NRF_TIMER1->PRESCALER = (uint32_t)0; // Prescaler = 0
        NRF_TIMER1->CC[1] =  (uint32_t)800;
    
        // Event on GPIO high to low
        NRF_GPIOTE->CONFIG[0] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos | 
                                             YELLOWWIRE << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos;
    
        // Event on GPIO low to high
        NRF_GPIOTE->CONFIG[1] = GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos | 
                                             YELLOWWIRE << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos;
    
    #if 1 // For scoping, toggle gpio on break detected
        NRF_GPIOTE->CONFIG[2] = GPIOTE_CONFIG_MODE_Task << GPIOTE_CONFIG_MODE_Pos | 
                                             GPIOTE_CONFIG_POLARITY_Toggle << GPIOTE_CONFIG_POLARITY_Pos | 
                                             ORANGEWIRE << GPIOTE_CONFIG_PSEL_Pos | 
                                             GPIOTE_CONFIG_OUTINIT_Low << GPIOTE_CONFIG_OUTINIT_Pos;
    #endif
    
                                                 
    
        NRF_TIMER1->TASKS_START = (uint32_t)1; // start the timer
    
        NRF_PPI->CHENSET = (uint32_t)(1<<0); //Enable PPI channel0
        NRF_PPI->CHENSET = (uint32_t)(1<<1); //Enable PPI channel1
        NRF_PPI->CHENSET = (uint32_t)(1<<2); //Enable PPI channel2
    

    I tied the uart rx line to a gpio to use with the PPI.

    I'm expecting when I tell the uart to receive 513 bytes and then send it 65 bytes followed by a break that the uart handler will get called with an NRFX_UARTE_EVT_RX_DONE event and have a count of 65.  But that isn't happening.

Related