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>