1. ўвядзенне
На парадку дня стаяла задача распрацаваць пратакол зносін мікракантролера nrf52832 з двума паўмаставымі кітайскімі тэнзадатчыкамі.
Задача аказалася не простай, бо сутыкнуўся з адсутнасцю якой-небудзь выразнай інфармацыі. Верагодней, што «корань зла» знаходзіцца ў самым SDK ад Nordic Semiconductor – гэта пастаяннае абнаўлення версій, некаторая надмернасць і заблытанасць функцыяналу. Прыйшлося пісаць усё з нуля.
Я думаю гэтая тэма даволі актуальная зыходзячы з таго, што дадзены чып валодае BLE стэкам і цэлым наборам "смакаў" рэжыму энергазберажэння. Але ў тэхнічную частку я моцна паглыбляцца не буду, бо на гэтую тэму напісана нямала артыкулаў.
2. Апісанне праекта
жалеза:
- Adafruit Feather nRF52 Bluefruit LE (тое, што апынулася пад рукой)
- АЛП HX711
- Кітайскія тэнзадатчыкі 2 шт. (50х2 кг)
- Праграматар ST-LINK V2
софт:
- IDE VSCODE
- NRF SDK 16
- OpenOCD
- Праграматар ST-LINK V2
Усё знаходзіцца ў адным праекце, прыйдзецца толькі падшаманіць Makefile (пазначыць размяшчэнне вашага SDK).
3. Апісанне кода
Будзем выкарыстоўваць GPIOTE модуль для працы з перыферыяй зыходзячы з прывязкі задач і падзей, а таксама PPI модуль для перадачы даных з адной перыферыі ў іншую без удзелу працэсара.
ret_code_t err_code;
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config);//настраеваем на выход
nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false);//будем передергивать пин для импульса
err_code = nrf_drv_gpiote_out_init(PD_SCK, &config);//настраеваем на выход
Наладжваем лінію сінхранізацыі PD_SCL на вынахад для генерацыі імпульсаў працягласцю 10 мкс.
nrf_drv_gpiote_in_config_t gpiote_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false);// переход уровня с высокого на низкий
nrf_gpio_cfg_input(DOUT, NRF_GPIO_PIN_NOPULL);// на вход без подтяжки
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);//отключаем прерывание
nrf_drv_timer_enable(&m_timer0);//включаем таймер
}
Наладжваем лінію дадзеных DOUT для счытвання стану гатовасці HX711, пры наяўнасці нізкага ўзроўня спрацоўвае апрацоўшчык у якім адключаны перапыненне і запускаем таймер для генерацыі сінхранізавалых імпульсаў на вынахадзе 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));// подключаем таймер к выходу
APP_ERROR_CHECK(err_code);
err_code = nrf_drv_ppi_channel_enable(m_ppi_channel1);// включаем канал
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_out_task_enable(PD_SCK);
// уключаем gpiote
Пасля чаго ініцыялізуем PPI модуль і камутуем наш таймер да выхаду PD_SCL, для генеравання імпульсаў працягласць 10мкс пры наступ падзеі параўнання, а таксама ўключаем GPIOTE модуль.
nrf_drv_timer_config_t timer_cfg = NRF_DRV_TIMER_DEFAULT_CONFIG;// по умолчанию
timer_cfg.frequency = NRF_TIMER_FREQ_1MHz;// тактируем на частоте 1Мгц
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);// срабатывает по сравнению
Ініцыялізуем нулявы таймер і яго апрацоўшчык.
if(m_counter%2 != 0 && m_counter<=48){
buffer <<= 1;// переменная считанных даных
c_counter++;// счетчик положительных импульсов
if(nrf_gpio_pin_read(DOUT))buffer++;//считываем состояние входа
}
Самае цікавае адбываецца ў апрацоўшчыку таймера. Перыяд імпульсаў складае 20 мкс. Нас цікавяць няцотныя імпульсы (па ўзыходзячым фронце) і пры ўмове, што іх колькасць не больш за 24, а падзей - 48. Пры кожнай няцотнай падзеі адбываецца счытванне DOUT
З дататыту варта, што колькасць імпульсаў павінна быць не меней 25, што адпавядае каэфіцыенту ўзмацнення 128 (у кодзе я выкарыстаў 25 імпульсаў), гэта эквівалентна 50 падзеям таймера, што паказвае на канчатак фрэйма дадзеных.
++m_counter;// счетчик событий
if(m_counter==50){
nrf_drv_timer_disable(&m_timer0);// отключаем таймер
m_simple_timer_state = SIMPLE_TIMER_STATE_STOPPED;//
buffer = buffer ^ 0x800000;
hx711_stop();//jотключаем hx711
}
Пасля гэтага адключаем таймер і апрацоўваем дадзеныя (па датажыту) і перакладаем HX711 у рэжым нізкага энергаспажывання.
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();// включаем hx711
nrf_drv_gpiote_out_toggle(LED_1);
m_simple_timer_state = SIMPLE_TIMER_STATE_STARTED;
}
}
/**@brief Create timers.
*/
static void create_timers()
{
ret_code_t err_code;
// Create timers
err_code = app_timer_create(&m_repeated_timer_id,
APP_TIMER_MODE_REPEATED,
repeated_timer_handler);
APP_ERROR_CHECK(err_code);
}
Чакаем падзеі ад RTC таймера з інтэрвалам у 10 з (гэтую ўжо на ваша меркаванне) у апрацоўшчыку запускаем HX711, выклікаючы перапыненне па лініі DOUT.
Ёсць яшчэ адзін момант, логі выводзяцца праз UART (baud rate 115200, TX – 6 пін, RX – 8 пін) усе налады знаходзяцца ў sdk_config.h
Высновы
Дзякуй усім за ўвагу, спадзяюся гэты артыкул будзе карыснай і скароціць каштоўны час на пошук рашэння для распрацоўшчыкаў. Жадаю сказаць, што тэхнічны падыход які выкарыстоўвае Nordic у сваіх платформах даволі цікавы з пункта гледжання энергаэфектыўнасці.
PS
Праект яшчэ ў працэсе распрацоўкі, таму калі будзе цікавая гэтая тэма ў наступным артыкуле я пастараюся апісаць алгарытм каліброўкі датчыкаў вагі, а таксама падлучэння BLE стэка.
Матэрыялы
Крыніца: habr.com