Хабрахабр

[Из песочницы] Как подключить АЦП HX711 к NRF52832

1. Введение

На повестке дня стояла задача разработать протокол общения микроконтролера nrf52832 с двумя полумостовыми китайскими тензодатчиками.

Задача оказалась не простой, так как столкнулся с отсутствием какой — либо внятной информации. Вероятнее, что «корень зла» находится в самом SDK от Nordic Semiconductor — это постоянное обновления версий, некоторая избыточность и запутанность функционала. Пришлось писать все с нуля.

Я думаю эта тема довольно актуальна исходя из того, что данный чип обладает BLE стеком и целым набором “вкусняшек” режима энергосбережения. Но в техническую часть я сильно углубляться не буду, так как на эту тему написано немало статей.

2. Описание проекта

image

Железо:

  • 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)

Настраиваем линию данных 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

image

Выводы

Спасибо всем за внимание, надеюсь эта статья будет полезной и сократит драгоценное время на поиск решения для разработчиков. Хочу сказать, что технический подход который использует Nordic в своих платформах довольно интересен с точки зрения энергоэффективности.

P.S.

Проект еще в процессе разработки, поэтому если будет интересна эта тема в следующей статье я постараюсь описать алгоритм калибровки датчиков веса, а также подключения BLE стека.

Материалы

Показать больше

Похожие публикации

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»