PWM Sequence from HAL to NRFX

Hello

I am trying to change the code from the HAL code up to NRFX in order to make it event-based instead of using infinite while loops. 

I am using 52840 on a custom PCA with goal in mind to create a half-duplex-half-UART communication.

Working with HAL drivers, the code works as intended, but it is not optimized and is very power hungry for our needs. 

The HAL Setup and sending of the message (Byte by byte, each byte 10 bits at a time (Start - 8xData - Stop)): 

TxSetup()
{
    NRF_P0->DIRSET = ((1UL) << PIN_NEG); // PIN_NEG as output
    NRF_P0->OUTCLR = ((1UL) << PIN_NEG); // initial LOW on PIN_NEG

    NRF_P0->DIRSET = ((1UL) << PIN_POS); // PIN_POS as output
    NRF_P0->OUTSET = ((1UL) << PIN_POS); // initial HIGH on PIN_POS

    CounterTopRegister = F_CLK / F_PWM; // F_CLK = 16000000
    cyclesPerBit = F_PWM / LL_BAUDRATE; // F_PWM = 32400 ; LL_BAUDRATE = 1200
    P_PWM_HIGH = CounterTopRegister;
    P_PWM_LOW = (int)(0.5 * CounterTopRegister);
    N_PWM_HIGH = P_PWM_HIGH + 0x8000;
    N_PWM_LOW = P_PWM_LOW + 0x8000;

    NRF_PWM2->PSEL.OUT[0] = (PIN_POS << PWM_PSEL_OUT_PIN_Pos) | (PIN_POS << PWM_PSEL_OUT_PORT_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    NRF_PWM2->PSEL.OUT[1] = (PIN_NEG << PWM_PSEL_OUT_PIN_Pos) | (PIN_NEG << PWM_PSEL_OUT_PORT_Pos) | (PWM_PSEL_OUT_CONNECT_Connected << PWM_PSEL_OUT_CONNECT_Pos);
    NRF_PWM2->MODE = (PWM_MODE_UPDOWN_Up << PWM_MODE_UPDOWN_Pos);
    NRF_PWM2->PRESCALER = (PWM_PRESCALER_PRESCALER_DIV_1 << PWM_PRESCALER_PRESCALER_Pos);
    NRF_PWM2->COUNTERTOP = (CounterTopRegister << PWM_COUNTERTOP_COUNTERTOP_Pos);
    NRF_PWM2->LOOP = (0 << PWM_LOOP_CNT_Pos);
    NRF_PWM2->DECODER = (PWM_DECODER_LOAD_Individual << PWM_DECODER_LOAD_Pos) | (PWM_DECODER_MODE_RefreshCount << PWM_DECODER_MODE_Pos);
    NRF_PWM2->SEQ[0].PTR = ((uint32_t)(initialSequence) << PWM_SEQ_PTR_PTR_Pos);

    NRF_PWM2->SEQ[0].CNT = 44;
    NRF_PWM2->SEQ[0].REFRESH = cyclesPerBit - 1;
    NRF_PWM2->SEQ[0].ENDDELAY = 0;
}

void TxWrite(uint8_t txVal)
{
    int i;
    // Generate the Startbit (LOW)
    initialSequence[0] = P_PWM_LOW;
    initialSequence[1] = N_PWM_LOW;
    initialSequence[2] = 0;
    initialSequence[3] = 0;
    // Generate the 8 databits
    for (i = 1; i < 9; i++)
    {
        if (txVal & 1)
        {
            // Bit is HIGH
            initialSequence[4 * i + 0] = P_PWM_HIGH;
            initialSequence[4 * i + 1] = N_PWM_HIGH;
        }
        else
        {
            // Bit is LOW
            initialSequence[4 * i + 0] = P_PWM_LOW;
            initialSequence[4 * i + 1] = N_PWM_LOW;
        }
        initialSequence[4 * i + 2] = 0;
        initialSequence[4 * i + 3] = 0;
        txVal = txVal >> 1;
    }

    // Generate the Stopbit (HIGH)
    initialSequence[36] = P_PWM_HIGH;
    initialSequence[37] = N_PWM_HIGH;
    initialSequence[38] = 0;
    initialSequence[39] = 0;
    initialSequence[40] = P_PWM_HIGH;
    initialSequence[41] = N_PWM_HIGH;
    initialSequence[42] = 0;
    initialSequence[43] = 0;

    NRF_PWM2->ENABLE = (PWM_ENABLE_ENABLE_Enabled << PWM_ENABLE_ENABLE_Pos);

    NRF_PWM2->TASKS_SEQSTART[0] = 1;
}

void TxWaitDone()
{

    while (NRF_PWM2->EVENTS_SEQEND[0] == 0)
        ;
    NRF_PWM2->EVENTS_SEQEND[0] = 0;
}

void TxStop()
{

    NRF_PWM2->TASKS_STOP = 1;

    while (NRF_PWM2->EVENTS_STOPPED == 0)
        ;
    NRF_PWM2->EVENTS_STOPPED = 0;

    NRF_PWM2->ENABLE = (PWM_ENABLE_ENABLE_Disabled << PWM_ENABLE_ENABLE_Pos);
}


WriteMessage(uint8_t *msg, uint8_t len)
{
    nrfx_lpcomp_disable();
    NRFX_IRQ_DISABLE(SAADC_IRQn);
    for (int i = 0; i < len; i++)
    {
        TxWrite(msg[i]);
        TxWaitDone();
        TxStop();
    }
    lpcomp_event_count = 0;
    is_carrier_found = 0;
    NRFX_IRQ_ENABLE(COMP_LPCOMP_IRQn);
    nrfx_lpcomp_enable();
    return 0;
}

The issue with this approach, again, is the while loops that, among other issues, can sometimes get stuck and hang the rest of the process. 

The actual issue rewriting this with NRFX drivers is the fact that I cannot, for the life of me, figure out how to write out my "initialSequence." I can get the PWM to toggle on or off, but not actually make it do that in sequence as I need it to. I tried following a few examples I found on Devzone, but nothing concrete enough to help me with my issue. 

The HAL Implementation repeats every 100 milliseconds, so the NRFX one should be able to match that. If anyone has an idea of how to write this using the NRFX drivers, or to point me in the direction of a set-up example, I'd be very grateful. 
To add to this: The idea is for this code to run along with a Zephyr based system that runs the rest of the functionality of the board (Thus why we're trying to make it event based as opposed to current implementation).

Thank you in advanced for response. If you need any more information, please ask. 

EDIT: Added the missing code that was calling the TX sequence in order. (Write, Wait, Stop)

  • Replies seem to have issues for some reason. 

    But regarding the issue, I think you misunderstood me, I have issue actually setting up the PWM ( I Think).

    I call "nrfx_pwm_simple_playback(&pwm, &pwm_sequence, 1, NRFX_PWM_FLAG_STOP);"
    And this is what drives my sequence. I no longer want to have direct registry access, unless absolutely needed. I wonder what, from the provided code, is wrong with the way I'm using NRFX API. And what do I need to modify in the code for it to execute the same function that my HAL code does. 
    The best I can achieve with NRFX is making PWM output a loop of repeating peak values at a defined duty cycle. 
    And it keeps being stuck on 1khz, where the HAL code works on 32Khz. 

  • Oh, maybe try changing the LOOPS (disguised as repeats):

        uint32_t repeats = (F_PWM / LL_BAUDRATE) - 1;
        uint32_t repeats = 0;

  • Still no Cigar. 

    I thought that LOOPS are actually the "playback Count" in the arguments of simple_playback. 
    And the Repeats was the same as SEQ[n].REFRESH? 

    My assumption is that something in the API is not properly configured, or I am not doign something in the right order. 

  • 1, not 0, for the repeats - sorry my typo. Maybe post the current code you are using, the earlier code is a little hard to follow with some unused code. Also include the main sequence if possible. I sometimes use the same api without any issues, though often use the bare metal drivers. A screen shot of the PWM registers might help in debug to confirm actual values loaded are not being obfuscated by the library code.

    REFRESH is a way of using more clock cycles per PWM value step, so typically set to 0.

  • Not sure the api drivers correctly handle the pins, but looking at the 'scope seems the pins aren't configured so try adding this after the init:

    // Boost differential pwm driver pins to high drive
    nrf_gpio_cfg(PIN_POS, 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(PIN_NEG, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
    

Related