1. Introducción
El objetivo era desarrollar un protocolo de comunicación entre el microcontrolador nrf52832 y dos transductores de carga de media puente de origen chino.
La tarea resultó ser compleja, ya que me encontré con la falta de información clara. Es probable que "el origen del problema" resida en el propio SDK de Nordic Semiconductor: las constantes actualizaciones de versiones, cierta redundancia y confusión en la funcionalidad. Tuve que escribirlo todo desde cero.
Creo que este tema es bastante relevante, considerando que este chip cuenta con una pila BLE y un conjunto completo de "mejoras" en el modo de ahorro de energía. Pero no profundizaré demasiado en la parte técnica, ya que se han escrito bastantes artículos sobre este tema.
2. Descripción del proyecto

Hardware:
- Adafruit Feather nRF52 Bluefruit LE (lo que tenía a mano)
- ADC HX711
- Transductores de carga chinos 2 pcs (50x2 kg)
- Programador ST-LINK V2
Software:
- IDE VSCODE
- NRF SDK 16
- OpenOCD
- Programador ST-LINK V2
Todo se encuentra en un solo proyecto, solo tendré que ajustar el Makefile (especificar la ubicación de su SDK).
3. Descripción del código
Usaremos el módulo GPIOTE para trabajar con la periferia, basándonos en la asignación de tareas y eventos, así como el módulo PPI para transferir datos de una periferia a otra sin la participación del procesador.
ret_code_t err_code;
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config); // configuramos como salida
nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false); // alternaremos el pin para el pulso
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config); // configuramos como salida
Configuramos la línea de sincronización PD_SCL como salida para generar pulsos de 10 µs de duración.
nrf_drv_gpiote_in_config_t gpiote_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false); // transición del nivel alto a bajo
nrf_gpio_cfg_input(DOUT, NRF_GPIO_PIN_NOPULL); // a la entrada sin tirador
err_code = nrf_drv_gpiote_in_init(DOUT, &gpiote_config, gpiote_evt_handler); static void gpiote_evt_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
nrf_drv_gpiote_in_event_disable(DOUT); // desactivamos la interrupción
nrf_drv_timer_enable(&m_timer0); // activamos el temporizador
}
Configuramos la línea de datos DOUT para leer el estado de preparación del HX711, al detectar un nivel bajo, se activa un controlador en el que desactivamos la interrupción y comenzamos el temporizador para generar pulsos sincronizados en la salida PD_SCL.
err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel1);
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_assign(m_ppi_channel1, nrf_drv_timer_event_address_get(&m_timer0, NRF_TIMER_EVENT_COMPARE0), nrf_drv_gpiote_out_task_addr_get(PD_SCK)); // conectamos el temporizador a la salida
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1); // habilitamos el canal
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_task_enable(PD_SCK); // включаем gpiote
Luego inicializamos el módulo PPI y conectamos nuestro temporizador a la salida PD_SCL, para generar pulsos de 10 µs al ocurrir el evento de comparación, y también habilitamos el módulo GPIOTE.
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG; // por defecto
timer_cfg.frequency = NRF_TIMER_FREQ_1MHz; // temporizamos a una frecuencia de 1 MHz
ret_code_t err_code = nrf_drv_timer_init(&m_timer0, &timer_cfg, timer0_event_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_timer_extended_compare(&m_timer0,
NRF_TIMER_CC_CHANNEL0,
nrf_drv_timer_us_to_ticks(&m_timer0,
10),
NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK,
true); // se activa al compararInicializamos el temporizador cero y su manejador.
if(m_counter%2 != 0 && m_counter<=48){
buffer <<= 1; // variable de datos leídos
c_counter++; // contador de pulsos positivos
if(nrf_gpio_pin_read(DOUT)) buffer++; // leemos el estado de la entrada
}
Lo más interesante sucede en el manejador del temporizador. El período de los pulsos es de 20 µs. Nos interesan los pulsos impares (en el flanco ascendente) y bajo la condición de que su número no exceda 24 y los eventos sean 48. En cada evento impar se realiza la lectura de DOUT.
Según la hoja de datos, el número de pulsos debe ser al menos 25, lo que corresponde a un factor de ganancia de 128 (en el código utilicé 25 pulsos), esto equivale a 50 eventos del temporizador, lo que indica el final del marco de datos.
++m_counter; // contador de eventos
if(m_counter==50){
nrf_drv_timer_disable(&m_timer0); // deshabilitamos el temporizador
m_simple_timer_state = SIMPLE_TIMER_STATE_STOPPED; //
buffer = buffer ^ 0x800000;
hx711_stop(); // desconectamos hx711
}
Después de esto, deshabilitamos el temporizador y procesamos los datos (según la hoja de datos) y ponemos el HX711 en modo de bajo consumo.
static void repeated_timer_handler(void * p_context)
{
nrf_drv_gpiote_out_toggle(LED_2);
if(m_simple_timer_state == SIMPLE_TIMER_STATE_STOPPED){
hx711_start(); // encendemos hx711
nrf_drv_gpiote_out_toggle(LED_1);
m_simple_timer_state = SIMPLE_TIMER_STATE_STARTED;
}
}
/**@brief Crea temporizadores.
*\/
static void create_timers()
{
ret_code_t err_code;
// Crear temporizadores
err_code = app_timer_create(&m_repeated_timer_id,
APP_TIMER_MODE_REPEATED,
repeated_timer_handler);
APP_ERROR_CHECK(err_code);
}
Esperamos un evento del temporizador RTC con un intervalo de 10 s (esto ya depende de usted) en el manejador iniciamos el HX711, llamando a la interrupción en la línea DOUT.
Hay otro punto, los registros se emiten a través de UART (baud rate 115200, TX — pin 6, RX — pin 8) todas las configuraciones están en sdk_config.h.

Conclusiones
Gracias a todos por su atención, espero que este artículo sea útil y ahorre tiempo valioso en la búsqueda de soluciones para los desarrolladores. Quiero decir que el enfoque técnico que utiliza Nordic en sus plataformas es bastante interesante desde el punto de vista de la eficiencia energética.
P.D.
El proyecto aún está en desarrollo, por lo que si este tema es de interés, en el próximo artículo trataré de describir el algoritmo de calibración de sensores de peso, así como la conexión de la pila BLE.
Materiales
Fuente: habr.com
