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

BLE scanning on AC mains powered unit with dimming

I have an application that requires mains power for lighting with a BLE receiver that is permanently advertising and scanning and I need to be able to dim the lights.  My circuit uses zero-crossing and a triac to control the mains.  I'm using a Fanstel BT832 module which is just the NRF52832 chip.

My code is currently based on the ble_app_interactive from the ble_central_and_peripheral examples in nRF5_SDK_16.

I've built a prototype unit that works in every respect except the dimming. The dimming is fine if I turn off advertising and scanning, but flickers when these are running. I've tried using only GPIOTE to achieve the dimming (which flickered). I then found some example code that used PPI, with 2 timer instances monitoring phase shift and pulse length, and tried this. Again the dimming works perfectly until I start the scanning or advertising.

I’ve tried the code on the NRF52-dk, which gives the same results, but have also tried the code on a Decawave DWM1001 which uses the NRF52832 and this seems to work without any flickering.  I cannot see any reason for this.

I thought the lack of a crystal on the Fanstel chip may have been to blame, as the Decawave module has a crystal, but the NRF52-dk has a crystal and this still sees the flicker.

Can anyone help with how to overcome this?

const nrf_drv_timer_t phaseshift_timer  = NRF_DRV_TIMER_INSTANCE(1);
const nrf_drv_timer_t pulselength_timer = NRF_DRV_TIMER_INSTANCE(2);
static nrf_ppi_channel_t                m_zerocrossing_channel;

void zerocrossing_event_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {}
void trigger_event_handler(nrf_timer_event_t event_type, void* p_context){}

/** @brief Function for timer initialization, which will be started by zero-crossing using PPI.
*/
static void high_resolution_timer_init(void)
{

    uint32_t err_code = NRF_SUCCESS;
    nrf_drv_timer_config_t timer_config = NRF_DRV_TIMER_DEFAULT_CONFIG;

    timer_config.frequency = NRF_TIMER_FREQ_1MHz;
    timer_config.bit_width = NRF_TIMER_BIT_WIDTH_16;
//    timer_config.interrupt_priority = NRF_APP_PRIORITY_LOW;
    timer_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;

    timer_config.mode = NRF_TIMER_MODE_TIMER;
    timer_config.p_context = NULL;
    err_code = nrf_drv_timer_init(&phaseshift_timer, &timer_config, trigger_event_handler);
    APP_ERROR_CHECK(err_code);

    // 10000 cycles at 1Mhz makes 0.01 sec - the duration of one half wave at 50Hz mains AC frequency
    nrf_drv_timer_extended_compare(&phaseshift_timer, NRF_TIMER_CC_CHANNEL0, 0x2710, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    err_code = nrf_drv_timer_init(&pulselength_timer, &timer_config, trigger_event_handler);
    APP_ERROR_CHECK(err_code);
    // the length of the triac firing pulse is 0.001 sec
    nrf_drv_timer_extended_compare(&pulselength_timer, NRF_TIMER_CC_CHANNEL0, 0x03E8, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
}
/** @brief Function for initializing the PPI peripheral.
*/
#define SAJ_PIN_IN    16
#define SAJ_PIN_OUT   20

static void peripheral_init(void)
{
    uint32_t err_code = NRF_SUCCESS;
    nrf_ppi_channel_t ppi_channel2;
    nrf_ppi_channel_t ppi_channel3;
    nrf_ppi_channel_t ppi_channel4;
    nrf_ppi_channel_t ppi_channel5;
    nrf_ppi_channel_t ppi_channel6;
    uint32_t zerocrossing_addr;
    uint32_t load_addr;

    // init the phase-shift and pulse-length timer
    high_resolution_timer_init();

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    if (!nrf_drv_gpiote_is_init())
    {
        err_code = nrf_drv_gpiote_init();
        if(err_code != NRF_SUCCESS)
        {
            APP_ERROR_CHECK(err_code);
            return;
        }
    }
    
    // create the zero-crossing event
    nrf_drv_gpiote_in_config_t zero_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    zero_config.pull = NRF_GPIO_PIN_PULLUP;
    err_code = nrf_drv_gpiote_in_init(SAJ_PIN_IN, &zero_config, zerocrossing_event_handler);
    APP_ERROR_CHECK(err_code);
    zerocrossing_addr = nrf_drv_gpiote_in_event_addr_get(SAJ_PIN_IN);
    nrf_drv_gpiote_in_event_enable(SAJ_PIN_IN, true);
    // assign zero-crossing event to start of phase-shift timer
    // start timer on zero-crossing event
    err_code = nrf_drv_ppi_channel_alloc(&m_zerocrossing_channel);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(m_zerocrossing_channel,
                                          zerocrossing_addr,
                                          nrf_drv_timer_task_address_get(&phaseshift_timer, NRF_TIMER_TASK_START));
    APP_ERROR_CHECK(err_code);
    // create the load control task
    nrf_drv_gpiote_out_config_t load_config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(true);
    load_config.init_state = NRF_GPIOTE_INITIAL_VALUE_LOW;
    err_code = nrf_drv_gpiote_out_init(SAJ_PIN_OUT, &load_config);
    APP_ERROR_CHECK(err_code);
    load_addr = nrf_drv_gpiote_out_task_addr_get(SAJ_PIN_OUT);
    nrf_drv_gpiote_out_task_enable(SAJ_PIN_OUT);
    // assign the end of the phase-shift timer to the load control task
    // toggle load control if timer overruns
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel2);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(ppi_channel2,
                                          nrf_drv_timer_event_address_get(&phaseshift_timer, NRF_TIMER_EVENT_COMPARE0),
                                          load_addr);
    APP_ERROR_CHECK(err_code);
    // assign the end of the phase-shift timer to the start of the pulse-length timer
    // start pulse_length timer with the end of the phase-shift timer
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel3);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(ppi_channel3,
                                          nrf_drv_timer_event_address_get(&phaseshift_timer, NRF_TIMER_EVENT_COMPARE0),
                                          nrf_drv_timer_task_address_get(&pulselength_timer, NRF_TIMER_TASK_START));
    APP_ERROR_CHECK(err_code);
    // assign the end of the phase-shift timer to stop the phase-shift timer
    // end of phase-shift timer stops itself
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel4);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(ppi_channel4,
                                          nrf_drv_timer_event_address_get(&phaseshift_timer, NRF_TIMER_EVENT_COMPARE0),
                                          nrf_drv_timer_task_address_get(&phaseshift_timer, NRF_TIMER_TASK_STOP));
    APP_ERROR_CHECK(err_code);
    // assign end of pulse-length timer to the load control task
    // toggle load control with the end of pulse-length timer
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel5);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(ppi_channel5,
                                          nrf_drv_timer_event_address_get(&pulselength_timer, NRF_TIMER_EVENT_COMPARE0),
                                          load_addr);
    APP_ERROR_CHECK(err_code);
    // assign end of pulse-length timer to stop itself
    // end of pulse-length timer stops itself
    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel6);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_assign(ppi_channel6,
                                          nrf_drv_timer_event_address_get(&pulselength_timer, NRF_TIMER_EVENT_COMPARE0),
                                          nrf_drv_timer_task_address_get(&pulselength_timer, NRF_TIMER_TASK_STOP));
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(ppi_channel2);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(ppi_channel3);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(ppi_channel4);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(ppi_channel5);
    APP_ERROR_CHECK(err_code);
    err_code = nrf_drv_ppi_channel_enable(ppi_channel6);
    APP_ERROR_CHECK(err_code);
}

void set_brightness(uint16_t brightness)
{
    uint32_t err_code = NRF_SUCCESS;
    if(brightness > 0) {

        NRF_LOG_RAW_INFO("Setting brightness %u\r\n", brightness);

        // enable zero-crossing event
        err_code = nrf_drv_ppi_channel_enable(m_zerocrossing_channel);
        APP_ERROR_CHECK(err_code);
        uint16_t counter;
        counter = 10000 - 100*brightness;
        // the min ticks is 4 - represents full cycle load
        nrf_drv_timer_extended_compare(&phaseshift_timer, NRF_TIMER_CC_CHANNEL0, counter+4, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, false);
    }
    else
    {
        // disable zero-crossing event
        err_code = nrf_drv_ppi_channel_disable(m_zerocrossing_channel);
        APP_ERROR_CHECK(err_code);
    }
}

int main(void)
{
    log_init();
//    timer_init();
 
    ble_m_init();
    peer_manager_init();
//    nfc_pairing_init(); // SAJ: not using NFC pairing

    NRF_LOG_RAW_INFO("App started.\r\n");

    scan_start();
    adv_start();

    peripheral_init();
    set_brightness(25);

    for (;;)
    {
        idle_state_handler();
    }
}
// <h> Clock - SoftDevice clock configuration

// ***** SAJ DIMMING: IMPORTANT!!! ***** amendments made to this are to allow the code to run on the Fanstel chip
// as the code that ran OK on the dev kit would not run on the Fanstel without these changes


//==========================================================
// <o> NRF_SDH_CLOCK_LF_SRC  - SoftDevice clock source.
 
// <0=> NRF_CLOCK_LF_SRC_RC 
// <1=> NRF_CLOCK_LF_SRC_XTAL 
// <2=> NRF_CLOCK_LF_SRC_SYNTH 

#ifndef NRF_SDH_CLOCK_LF_SRC
#define NRF_SDH_CLOCK_LF_SRC 0 // SAJ: was 1
#endif

// <o> NRF_SDH_CLOCK_LF_RC_CTIV - SoftDevice calibration timer interval. 
#ifndef NRF_SDH_CLOCK_LF_RC_CTIV
#define NRF_SDH_CLOCK_LF_RC_CTIV 16 // SAJ: was 0
#endif

// <o> NRF_SDH_CLOCK_LF_RC_TEMP_CTIV - SoftDevice calibration timer interval under constant temperature. 
// <i> How often (in number of calibration intervals) the RC oscillator shall be calibrated
// <i>  if the temperature has not changed.

#ifndef NRF_SDH_CLOCK_LF_RC_TEMP_CTIV
#define NRF_SDH_CLOCK_LF_RC_TEMP_CTIV 2 // SAJ: was 0
#endif

// <o> NRF_SDH_CLOCK_LF_ACCURACY  - External clock accuracy used in the LL to compute timing.
 
// <0=> NRF_CLOCK_LF_ACCURACY_250_PPM 
// <1=> NRF_CLOCK_LF_ACCURACY_500_PPM 
// <2=> NRF_CLOCK_LF_ACCURACY_150_PPM 
// <3=> NRF_CLOCK_LF_ACCURACY_100_PPM 
// <4=> NRF_CLOCK_LF_ACCURACY_75_PPM 
// <5=> NRF_CLOCK_LF_ACCURACY_50_PPM 
// <6=> NRF_CLOCK_LF_ACCURACY_30_PPM 
// <7=> NRF_CLOCK_LF_ACCURACY_20_PPM 
// <8=> NRF_CLOCK_LF_ACCURACY_10_PPM 
// <9=> NRF_CLOCK_LF_ACCURACY_5_PPM 
// <10=> NRF_CLOCK_LF_ACCURACY_2_PPM 
// <11=> NRF_CLOCK_LF_ACCURACY_1_PPM 

#ifndef NRF_SDH_CLOCK_LF_ACCURACY
#define NRF_SDH_CLOCK_LF_ACCURACY 1 // SAJ: was 7
#endif

// </h> 

Parents Reply Children
No Data
Related