1. Úvod
Na pořadu dne byla úloha vyvinout komunikační protokol pro mikrořadič nrf52832 s dvěma polovodičovými čínskými tenzometry.
Úkol se ukázal jako složitý, protože jsem se setkal s nedostatkem jakýchkoli jasných informací. Je pravděpodobné, že "kořen zla" spočívá ve vlastním SDK od Nordic Semiconductor — neustálé aktualizace verzí, jistá nadbytečnost a zmatenost funkcionality. Bylo nutné psát vše od začátku.
Myslím, že toto téma je docela aktuální, vzhledem k tomu, že tento čip disponuje BLE stackem a celou řadou "lahůdek" režimu úspory energie. Ale do technické části se příliš nebudu ponořovat, protože na toto téma bylo napsáno mnoho článků.
2. Popis projektu

Hardware:
- Adafruit Feather nRF52 Bluefruit LE (to, co bylo po ruce)
- ADC HX711
- Čínské tenzometry 2 kusy (50x2 kg)
- Programátor ST-LINK V2
Software:
- IDE VSCODE
- NRF SDK 16
- OpenOCD
- Programátor ST-LINK V2
Vše je v jednom projektu, bude potřeba jen upravit Makefile (uvést umístění vašeho SDK).
3. Popis kódu
Budeme používat modul GPIOTE pro práci s periferiemi na základě vazby úloh a událostí, a také modul PPI pro přenos dat z jedné periferie na druhou bez účasti mikroprocesoru.
ret_code_t err_code;
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config); // nastavujeme na výstup
nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false); // budeme přepínat pin pro impuls
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config); // nastavujeme na výstup
Nastavujeme synchronizační linku PD_SCL na výstup pro generování impulsů o délce 10 µs.
nrf_drv_gpiote_in_config_t gpiote_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false); // přechod úrovně z vysoké na nízkou
nrf_gpio_cfg_input(DOUT, NRF_GPIO_PIN_NOPULL); // na vstup bez pull-up
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); // vypínáme přerušení
nrf_drv_timer_enable(&m_timer0); // zapínáme časovač
}
Nastavujeme datovou linku DOUT pro načítání stavu připravenosti HX711, při nízké úrovni se spouští obslužná rutina, ve které vypínáme přerušení a spouštíme časovač pro generování synchronizačních impulsů na výstupu 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)); // připojujeme časovač k výstupu
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1); // zapínáme kanál
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_task_enable(PD_SCK); // включаем gpiote
Poté inicializujeme modul PPI a připojíme náš časovač k výstupu PD_SCL pro generování pulzů o délce 10 μs při události porovnání, a také zapneme modul GPIOTE.
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG; // výchozí nastavení
timer_cfg.frequency = NRF_TIMER_FREQ_1MHz; // taktujeme na frekvenci 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); // vyvolá se při porovnáníInicializujeme nulový časovač a jeho obslužnou rutinu.
if(m_counter % 2 != 0 && m_counter <= 48) {
buffer <<= 1; // proměnná přečtených dat
c_counter++; // čítač pozitivních pulzů
if(nrf_gpio_pin_read(DOUT)) buffer++; // čteme stav vstupu
}
Nejzajímavější se děje v obslužné rutině časovače. Délka pulzů činí 20 μs. Zajímáme se o liché pulzy (na vzestupné hraně) a za podmínky, že jich je maximálně 24, a událostí 48. Při každé liché události dochází ke čtení DOUT.
Z datasheetu vyplývá, že počet pulzů musí být minimálně 25, což odpovídá zesílení 128 (v kódu jsem použil 25 pulzů), to odpovídá 50 událostem časovače, což naznačuje konec datového rámce.
++m_counter; // čítač událostí
if(m_counter == 50) {
nrf_drv_timer_disable(&m_timer0); // vypínáme časovač
m_simple_timer_state = SIMPLE_TIMER_STATE_STOPPED; //
buffer = buffer ^ 0x800000;
hx711_stop(); // vypínáme hx711
}
Poté vypneme časovač a zpracujeme data (podle datasheetu) a přepneme HX711 do režimu nízké spotřeby.
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(); // zapneme hx711
nrf_drv_gpiote_out_toggle(LED_1);
m_simple_timer_state = SIMPLE_TIMER_STATE_STARTED;
}
}
/**@brief Vytvořte časovače.
*/*
static void create_timers()
{
ret_code_t err_code;
// Vytvořte časovače
err_code = app_timer_create(&m_repeated_timer_id,
APP_TIMER_MODE_REPEATED,
repeated_timer_handler);
APP_ERROR_CHECK(err_code);
}
Očekáváme událost od RTC časovače s intervalem 10 s (to již závisí na vás), v obslužné rutině spustíme HX711 voláním přerušení na lince DOUT.
Ještě jedna věc, protokoly se vyvádějí přes UART (baud rate 115200, TX — pin 6, RX — pin 8), všechny nastavení jsou v sdk_config.h.

Závěry
Děkuji všem za pozornost, doufám, že tento článek bude užitečný a ušetří vzácný čas při hledání řešení pro vývojáře. Chci říct, že technický přístup, který Nordic používá ve svých platformách, je docela zajímavý z hlediska energetické efektivity.
P.S.
Projekt je ještě ve fázi vývoje, proto pokud vás toto téma zajímá, v příštím článku se pokusím popsat algoritmus kalibrování váhových senzorů, stejně jako připojení BLE stacku.
Materiály
Zdroj: habr.com
