Hi,
I am trying to build a Dali driver for the nRF52 with nRF Connect SDK. In the current approach, the Dali signal is send on the bus with a bit-banging approach using Zephyr's GPIO library and an nRFx timer. To summarize Dali, the data is sent with a baud rate of 1200 bps, and the data is manchester encoded (the data 1's and 0's are encoded in rising and falling edges). Dali specifies that a half-bit should be between 400µs and 433.3 µs (typically 416.7µs), and a double half-bit between 800µs and 866.7µs (typically 833.3µs).
However, I have trouble getting the timing right. Most edges are correct. However, sometimes an edge deviates and is sent outside the window (see image below), and with quite a big amount (up to 30µs). But which edge deviates (if any), is random. To ensure nothing is interrupted

The sample from the image is measured with a logic analyzer sampling at 1MHz, connected directly to the tx of the nRF52833DK. I verified with an oscilloscope that slope of the signal is correct.
In the background, Bluetooth mesh is running, but in this test it is not actively used (I also tried disabling Bluetooth, but no difference).
Timer library:
static struct dali_timer_data dali_timer_data =
{
.p_instance = NRFX_TIMER_INSTANCE(CONFIG_DALI_TIMER_INSTANCE),
.cfg = {
.frequency = NRF_TIMER_FREQ_16MHz,
.mode = NRF_TIMER_MODE_TIMER,
.bit_width = NRF_TIMER_BIT_WIDTH_32,
.interrupt_priority = 0
}
};
static void timer_handler(nrf_timer_event_t event_type, void *p_context)
{
struct dali_timer *timer = (struct dali_timer *)p_context;
struct dali_timer_data *data = (struct dali_timer_data *)timer->timer_data;
if(event_type == NRF_TIMER_EVENT_COMPARE0)
{
timer->callback(timer, timer->user_data);
}
}
int dali_timer_init(struct dali_timer *timer, dali_timer_cb_t cb)
{
timer->callback = cb;
timer->timer_data = (void *) &dali_timer_data;
dali_timer_data.cfg.p_context = (void *)timer;
nrfx_timer_init(&dali_timer_data.p_instance, &dali_timer_data.cfg, timer_handler);
IRQ_CONNECT(TIMER2_IRQn, IRQ_ZERO_LATENCY, nrfx_timer_2_irq_handler, NULL, 0);
return 0;
}
int dali_timer_start(struct dali_timer *timer, dali_timeout_t period)
{
struct dali_timer_data * timer_data = timer->timer_data;
nrfx_timer_extended_compare(&timer_data->p_instance,
NRF_TIMER_CC_CHANNEL0,
period.ticks,
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true);
nrfx_timer_enable(&timer_data->p_instance);
return 0;
}
int dali_timer_stop(struct dali_timer *timer)
{
struct dali_timer_data * timer_data = timer->timer_data;
nrfx_timer_disable(&timer_data->p_instance);
nrfx_timer_clear(&timer_data->p_instance);
return 0;
}
Dali timer handler:
static void dali_timer_handler(struct dali_timer *timer, void * user_data)
{
struct dali_gpio_data *data = (struct dali_gpio_data *) user_data;
struct dali_gpio_cfg const * cfg = data->dev->config;
gpio_pin_set_dt(&cfg->tx, BIT_READ64(data->tx_buff.edge_data, data->edge_count));
data->edge_count++;
if(data->tx_buff.n_edges == data->edge_count)
{
dali_timer_stop(timer);
tx_settle_time_schedule(data);
k_event_set(&data->dali_drv_event, 0);
user_callback(data->dev, &(struct dali_drv_evt_data){
.type = DALI_EVT_TX_SEND,
.tx_send = {
.data = data->tx_buff.data
}
});
}
}To ensure nothing interrupts the timer, I used `IRQ_ZERO_LATENCY` for this test. But it surprises me that despite this, some edges deviate from the timer while most are spot-on.
Does anyone know if I did something wrong here?
Would it help to use nrfx GPIO(TE) instead?
Would it be possible (and efficient) using nrfx GPIOTE with PPI and nrfx timer for this?
Or would it be a good option to use SPI at 2400bps and leave all pins except for MOSI unconnected? For this the concern is that as part of the multi-master specification, the device must read back the value it set on the bus to detect if there is a collision.
Thanks in advance.
