I am working on application where the nrf51 will read information from several sensors and send the data to a smartphone app via BLE. Among the sensors are one that sends a 23 byte message via UART at 9600 Baud about once a second and an Si7021 temp/humidity sensor over I2C. I am using the S130 Softdevice, and currently am prototyping using the nrf51 DK board.
I have a timer setup to trigger once a second, in order to prepare the sensor data and send it over BLE. This timeout handler routine seems like the natural place to put my blocking I2C code, but if I leave it here, then the application resets about every minute or so. If I dump a bunch of data into the UART, it resets immediately. If I comment out the I2C routine, then I can send the UART as much data as I want with no resets. I am currently circumventing the problem by running the I2C code from the UART handler routine only after having received a complete message, so I know more UART data won't be received while the I2C is running.
I don't like this workaround, and can not explain why I2C and UART read are not coexisting. I have tried to include the most relevant code snippets below; let me know if you need to see more. Thanks!
Addendum 1: I had to set the TWI to ".interrupt_priority = APP_IRQ_PRIORITY_HIGH" to call it from inside the timeout, or else it resets immediately. With APP_IRQ_PRIORITY_HIGH, it takes about a minute to reset with normal sensor input.
Addendum 2: I've spent some time with the debugger now, and determined that reset is triggered from inside the uart handler routine, and appears to be an over run of the 6 byte hardware UART receive buffer. It's ok in my application to miss a message from the sensor occasionally, so I've tried to implement error handling for this case:
case APP_UART_COMMUNICATION_ERROR:
if (p_event->data.error_communication == UART_ERRORSRC_OVERRUN_Msk)
{
// do I need more in here?
app_uart_flush();
index = 0;
uart_receive_state = WAIT_FOR_MSG;
printf("overrun!\r\n");
} else {
APP_ERROR_HANDLER(p_event->data.error_communication);
}
break;
This seems to work, but I wonder-
- Should I add anything to this to clear the error, the hardware buffer, or anything else?
- Seems strange to me that the nrf51 can't keep up with this at 9600 Baud with BLE and some other peripherals- is that surprising? This is a Commercial-Off-The-Shelf sensor with a UART interface, that like very many others, does NOT support hardware flow control.
Original code below:
static void uart_init(void)
{
uint32_t err_code;
const app_uart_comm_params_t comm_params =
{
//RX_PIN_NUMBER,
12,
TX_PIN_NUMBER,
RTS_PIN_NUMBER,
CTS_PIN_NUMBER,
APP_UART_FLOW_CONTROL_DISABLED,
false,
UART_BAUDRATE_BAUDRATE_Baud9600
};
APP_UART_FIFO_INIT( &comm_params,
UART_RX_BUF_SIZE,
UART_TX_BUF_SIZE,
uart_event_handle,
APP_IRQ_PRIORITY_LOW, // should this be high?
err_code);
APP_ERROR_CHECK(err_code);
}
void uart_event_handle(app_uart_evt_t * p_event)
{
switch (p_event->evt_type)
{
case APP_UART_DATA_READY:
UNUSED_VARIABLE(app_uart_get(&this_uart_char));
switch (uart_receive_state)
{
// parse uart output here and do this upon completion of message
Si7021_read();
}
break;
case APP_UART_COMMUNICATION_ERROR:
APP_ERROR_HANDLER(p_event->data.error_communication);
break;
case APP_UART_FIFO_ERROR:
APP_ERROR_HANDLER(p_event->data.error_code);
break;
default:
break;
}
}
void twi_init (void)
{
ret_code_t err_code;
const nrf_drv_twi_config_t twi_si_7021_config = {
.scl = SI7021_I2C_SCL_PIN,
.sda = SI7021_I2C_SDA_PIN,
.frequency = NRF_TWI_FREQ_100K,
.interrupt_priority = APP_IRQ_PRIORITY_LOW // was high
};
err_code = nrf_drv_twi_init(&m_twi_si_7021, &twi_si_7021_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
nrf_drv_twi_enable(&m_twi_si_7021);
}
void Si7021_read( void )
{
ret_code_t err_code;
static uint16_t m_sample;
uint8_t reg;
uint16_t this_sample;
reg = SI7021_HUMD_MEASURE_HOLD;
err_code = nrf_drv_twi_tx(&m_twi_si_7021, SI7021_ADDR, ®, sizeof(reg), true);
APP_ERROR_CHECK(err_code);
/* Read 2 bytes from the specified address. */
err_code = nrf_drv_twi_rx(&m_twi_si_7021, SI7021_ADDR, (uint8_t*)&m_sample, sizeof(m_sample));
APP_ERROR_CHECK(err_code);
this_sample = (m_sample>>8) | (m_sample<<8);
humidity = (float)this_sample * 125 / 65536.0 - 6;
reg = SI7021_TEMP_PREV;
err_code = nrf_drv_twi_tx(&m_twi_si_7021, SI7021_ADDR, ®, sizeof(reg), true);
APP_ERROR_CHECK(err_code);
/* Read 2 bytes from the specified address. */
err_code = nrf_drv_twi_rx(&m_twi_si_7021, SI7021_ADDR, (uint8_t*)&m_sample, sizeof(m_sample));
APP_ERROR_CHECK(err_code);
this_sample = (m_sample>>8) | (m_sample<<8);
temperature = (float)this_sample * 175.72 / 65536.0 - 46.85;
}
static void timers_init(void)
{
uint32_t err_code;
// Initialize timer module.
APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);
// Create timers.
err_code = app_timer_create(&m_sensor_timer_id,
APP_TIMER_MODE_REPEATED,
sensor_timeout_handler);
APP_ERROR_CHECK(err_code);
}
static void sensor_timeout_handler(void * p_context)
{
// if I do this here, the firmware resets if the UART is receiving much data
//Si7021_read();
// at this point, get data together from sensors and send over BLE ( S130 softdevice )
}