1. Introducere
Pe agenda se afla sarcina de a dezvolta un protocol de comunicare între microcontrolerul nrf52832 și două senzori de tensiune chinezești cu punte semi.
Sarcina s-a dovedit a fi complicată, deoarece m-am confruntat cu lipsa unor informații clare. Cel mai probabil, „rădăcina răului” se află în însăși SDK-ul de la Nordic Semiconductor — aceste actualizări constante ale versiunilor, oarecare redundanță și confuzia funcționalității. A fost nevoie să scriu totul de la zero.
Cred că această temă este destul de relevantă, având în vedere că acest cip dispune de un stivă BLE și un întreg set de „delicii” privind modul de economisire a energiei. Dar nu mă voi aprofunda foarte mult în partea tehnică, deoarece s-au scris deja multe articole pe această temă.
2. Descrierea proiectului

Hardware:
- Adafruit Feather nRF52 Bluefruit LE (ceea ce s-a dovedit a fi la îndemână)
- ADC HX711
- Senzori de tensiune chinezești 2 buc. (50x2 kg)
- Programator ST-LINK V2
Software:
- IDE VSCODE
- NRF SDK 16
- OpenOCD
- Programator ST-LINK V2
Totul se află într-un singur proiect, va trebui doar să modifici Makefile-ul (să specifici locația SDK-ului tău).
3. Descrierea codului
Vom folosi modulul GPIOTE pentru a lucra cu perifericele, pe baza legăturilor de sarcini și evenimente, precum și modulul PPI pentru a transfera date dintr-o periferie în alta fără a implica procesorul.
ret_code_t err_code;
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config); // setăm pe ieșire
nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false); // vom comuta pinul pentru impuls
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config); // setăm pe ieșire
Configurăm linia de sincronizare PD_SCL pe ieșire pentru a genera impulsuri de 10 μs.
nrf_drv_gpiote_in_config_t gpiote_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false); // trecerea nivelului de la înalt la jos
nrf_gpio_cfg_input(DOUT, NRF_GPIO_PIN_NOPULL); // pe intrare fără 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); // dezactivăm întreruperea
nrf_drv_timer_enable(&m_timer0); // activăm timerul
}
Configurăm linia de date DOUT pentru a citi starea de pregătire HX711, în cazul în care se detectează un nivel scăzut, se activează handlerul în care dezactivăm întreruperea și pornim timerul pentru a genera impulsuri de sincronizare pe ieșirea 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)); // conectăm timerul la ieșire
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1); // activăm canalul
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_task_enable(PD_SCK); // включаем gpiote
Ulterior, inițializăm modulul PPI și conectăm timerul nostru la ieșirea PD_SCL pentru a genera impulsuri de 10μs la apariția evenimentului de comparație și, de asemenea, activăm modulul GPIOTE.
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG; // implicit
timer_cfg.frequency = NRF_TIMER_FREQ_1MHz; // generăm la frecvența de 1MHz
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 declanșează la comparațieInițializăm timerul zero și handlerul său.
if(m_counter%2 != 0 && m_counter<=48){
buffer <<= 1; // variabila datelor citite
c_counter++; // contorul impulsurilor pozitive
if(nrf_gpio_pin_read(DOUT))buffer++; // citim starea intrării
}
Cele mai interesante lucruri se întâmplă în handlerul timerului. Perioada impulsurilor este de 20μs. Ne interesează impulsurile impare (pe frontul ascendent) și cu condiția ca numărul lor să nu depășească 24, iar evenimentele – 48. La fiecare eveniment impar, se citește DOUT.
Din fișa tehnică reiese că numărul de impulsuri trebuie să fie de cel puțin 25, ceea ce corespunde unui coeficient de amplificare de 128 (în cod am folosit 25 impulsuri), ceea ce este echivalent cu 50 de evenimente ale timerului, ceea ce indică terminarea cadrelor de date.
++m_counter; // contorul evenimentelor
if(m_counter==50){
nrf_drv_timer_disable(&m_timer0); // dezactivăm timerul
m_simple_timer_state = SIMPLE_TIMER_STATE_STOPPED; //
buffer = buffer ^ 0x800000;
hx711_stop(); // deconectăm hx711
}
După aceasta, deconectăm timerul și procesăm datele (conform fișei tehnice) și punem HX711 în modul de consum redus de energie.
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(); // activăm hx711
nrf_drv_gpiote_out_toggle(LED_1);
m_simple_timer_state = SIMPLE_TIMER_STATE_STARTED;
}
}
/**@brief Crează temporizatoare.
*\/
static void create_timers()
{
ret_code_t err_code;
// Crează temporizatoare
err_code = app_timer_create(&m_repeated_timer_id,
APP_TIMER_MODE_REPEATED,
repeated_timer_handler);
APP_ERROR_CHECK(err_code);
}
Așteptăm un eveniment de la temporizatorul RTC la un interval de 10 sec (aceasta depinde deja de opțiunea dumneavoastră) în handler activăm HX711, apelând o întrerupere pe linia DOUT.
Există încă un moment, logurile sunt transmise prin UART (viteză de baud 115200, TX — pin 6, RX — pin 8) toate setările se află în sdk_config.h

Conclusions
Mulțumesc tuturor pentru atenție, sper ca acest articol să fie util și să reducă timpul prețios în căutarea soluțiilor pentru dezvoltatori. Vreau să spun că abordarea tehnică folosită de Nordic în platformele sale este destul de interesantă din punct de vedere al eficienței energetice.
P.S.
Proiectul este încă în dezvoltare, așa că dacă această temă vă interesează în articolul următor voi încerca să descriu algoritmul de calibrare a senzorilor de greutate, precum și conectarea stivei BLE.
Materiale
Sursa: habr.com
