Encoding NRZ bitstream

I'm in need of some thoughts:  I'm using nRF52840 and SDK15.3 to do this but this is mostly a hardware thing.

I need to put out a long (like 7K bits) bitstring with each bit encoded in an NRZ format.  Bit length is 1.25uS so this isn't all that fast. Each bit tho looks like:

Where T0H is 400nS and T1H is 800 nS each +- 150nS

The bit stream is changed from time to time in firmware so it needs to be in RAM.

I'm thinking the PWM system can do this somehow through some slight of hand and DMA assist...  Has anyone got  a suggestion of exactly how to do it?

  • Manchester Encoding gives pretty much what you are looking for; I posted some code here pwm-sequence which gives a PWM implementation for a specific packet example  as well as other posts. Worth a search on "Manchester Encoding" in the dev zone for vaious other solutions.

    // PWM Half-Duplex UART - nrfx drivers
    // ===================================
    // Requirements
    //  10 bit bytes (Start - 8xData - Stop)
    //  single char or burst (packet) transmission
    //  Differential output using 2 pins
    //  No level change on pins outside transmitted data to avoid spurious character detection
    #include "nrf_pwm.h"
    
    #define LL_BAUDRATE    800000
    #define F_CLK        16000000              // nRF52832/nRF52840 fixed at 16MHz
    #define F_PRESCALER  (PWM_PRESCALER_PRESCALER_DIV_1)  // Divide by 1, 16MHz clock
    #define COUNTER_TOP  ((F_CLK+(LL_BAUDRATE/2)) / LL_BAUDRATE)
    STATIC_ASSERT(COUNTER_TOP < 32768, "COUNTER_TOP value too large for 15-bit register");
    STATIC_ASSERT(COUNTER_TOP >= 3, "COUNTER_TOP value too small for correct operation");
    //#define BIT_LOW               0         // Normal encoding
    //#define BIT_HIGH    (COUNTER_TOP)       // Normal encoding
    #define BIT_LOW   ((2*COUNTER_TOP)/3)     // Manchester encoding 2/3 bit
    #define BIT_HIGH  (COUNTER_TOP/3)         // Manchester encoding 1/3 bit
    #define PWM_NEG (PIN_FEATHER_D6)          // Low level outside transmission
    #define PWM_POS (PIN_FEATHER_D9)          // High level outside transmission
    
    typedef struct TX_BYTE_T {
        nrf_pwm_values_grouped_t StartBit;
        nrf_pwm_values_grouped_t DataBits[8];
        nrf_pwm_values_grouped_t StopBit;
    } TxByte_t;
    
    TxByte_t TxPacket[4];
    const uint16_t TxPacketSize = (sizeof(TxPacket)/sizeof(uint16_t));
    
    void encodeByte(TxByte_t* pBuffer, const uint8_t ch)
    {
        // Encode byte in little-endian format with start and stop bits
        pBuffer->StartBit.group_0 = pBuffer->StartBit.group_1 = (BIT_LOW);  pBuffer->StartBit.group_0 |= 0x8000; // 0 Start bit
        pBuffer->StopBit.group_0  = pBuffer->StopBit.group_1  = (BIT_HIGH); pBuffer->StopBit.group_0  |= 0x8000; // 0 Stop bit
        for(uint32_t i=0; i<8; i++)
        {
            pBuffer->DataBits[i].group_0 = pBuffer->DataBits[i].group_1 = (((ch>>i) & 0x01) ? BIT_HIGH : BIT_LOW);
            pBuffer->DataBits[i].group_0 |= 0x8000; // inverted pin
        }
    }
    
    buildMessage(void)
    {
        TxByte_t *pTx = TxPacket;
        uint8_t STX         = 0x82;
        uint8_t signal_id   = 0x64 | 0x80;
        uint8_t data_byte_0 = 0x03 | 0x80;
        uint8_t checksum    = (signal_id) + (data_byte_0);
        checksum &= 0x7F;
        encodeByte(pTx++, STX);
        encodeByte(pTx++, signal_id);
        encodeByte(pTx++, data_byte_0);
        encodeByte(pTx++, checksum);
    }
    
    void TestPWM_Uart(void)
    {
        static nrfx_pwm_t m_pwm0 = NRFX_PWM_INSTANCE(0);
        uint32_t err_code;
        // Declare a configuration structure and use a macro to instantiate it with default parameters.
        nrfx_pwm_config_t pwm_config = NRFX_PWM_DEFAULT_CONFIG;
        //    .irq_priority = NRFX_PWM_DEFAULT_CONFIG_IRQ_PRIORITY,                  \
        //    .base_clock   = (nrf_pwm_clk_t)NRFX_PWM_DEFAULT_CONFIG_BASE_CLOCK,     \
        //    .count_mode   = (nrf_pwm_mode_t)NRFX_PWM_DEFAULT_CONFIG_COUNT_MODE,    \
        //    .top_value    = NRFX_PWM_DEFAULT_CONFIG_TOP_VALUE,                     \
        //    .load_mode    = (nrf_pwm_dec_load_t)NRFX_PWM_DEFAULT_CONFIG_LOAD_MODE, \
        //    .step_mode    = (nrf_pwm_dec_step_t)NRFX_PWM_DEFAULT_CONFIG_STEP_MODE
       static nrf_pwm_sequence_t pwm_sequence;
    
        // Override some of the default parameters:
        pwm_config.output_pins[0] = PWM_NEG | NRFX_PWM_PIN_INVERTED;
        pwm_config.output_pins[1] = NRFX_PWM_PIN_NOT_USED;
        pwm_config.output_pins[2] = PWM_POS;
        pwm_config.output_pins[3] = NRFX_PWM_PIN_NOT_USED;
    
        pwm_config.load_mode      = NRF_PWM_LOAD_GROUPED;   // 1 == 1st half word (16-bit) used in channels 0 and 1; 2nd word in channels 2 and 3
        pwm_config.base_clock     = F_PRESCALER;            // 0 == divide by 1 for 16MHz clock
        pwm_config.step_mode      = NRFX_PWM_DEFAULT_CONFIG_STEP_MODE; // 0 == Count Up
        pwm_config.top_value      = COUNTER_TOP;
    
        // Pass config structure into driver init() function
        err_code = nrfx_pwm_init(&m_pwm0, &pwm_config, NULL);
        APP_ERROR_CHECK(err_code);
        // Boost differential pwm driver pins to high drive - this is optional
        nrf_gpio_cfg(PWM_NEG, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        nrf_gpio_cfg(PWM_POS, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        // Encode the 4-byte test message
        buildMessage();
        pwm_sequence.values.p_grouped = (uint32_t)TxPacket;
        pwm_sequence.length           = TxPacketSize;
        pwm_sequence.repeats          = 0;
        nrfx_pwm_simple_playback(&m_pwm0, &pwm_sequence, 1, NRFX_PWM_FLAG_STOP); // Send once
        while (1)
        {
            __WFE();
        }
    }

  • Hello,

    I thought I might add:

    https://docs.zephyrproject.org/latest/samples/drivers/led_strip/README.html

    Your figure looked familiar, so in case this is what you are trying to do, this may also be a starting point.

Related