I am working with HX711 and I am trying to interface with nrf52832. Should I proceed with SAADC example or UART or Twi Sensor
I am working with HX711 and I am trying to interface with nrf52832. Should I proceed with SAADC example or UART or Twi Sensor
Hi,
Please refer to the datasheet for the hx711, it does not use a standardized serial interface such as UART or TWI, which means that it has to be implemented in software (i.e., bit banging).
We don't have an official reference example for this ADC, but I have played with it in my spare time, see attached code. Documentation for this code is lacking, and I haven't done any thorough testing of it, but hopefully it can serve as a starting point. You can find other examples as well if you do a google search for hx711 and nrf52/51. Just keep in mind that the implementation should account for higher priority interrupts such as radio events that may occur during ADC readout.
/******************************************************************************/ /* */ /******************************************************************************/ #include <stdint.h> #include <string.h> #include "hx711.h" #include "nrf_drv_gpiote.h" #include "app_util_platform.h" #include "boards.h" #include "nrf_delay.h" #define NRF_LOG_MODULE_NAME hx711 #if HX711_CONFIG_LOG_ENABLED #define NRF_LOG_LEVEL HX711_CONFIG_LOG_LEVEL #define NRF_LOG_INFO_COLOR HX711_CONFIG_INFO_COLOR #define NRF_LOG_DEBUG_COLOR HX711_CONFIG_DEBUG_COLOR #else // HX711_CONFIG_LOG_ENABLED #define NRF_LOG_LEVEL 0 #endif // HX711_CONFIG_LOG_ENABLED #include "nrf_log.h" NRF_LOG_MODULE_REGISTER(); /* Defines frequency and duty cycle for clock signal - default: 1 MHz 50%*/ #define TIMER_COUNTERTOP 32 #define TIMER_COMPARE 16 // Pinout #define PD_SCK 11 #define DOUT 7 #define VDD 13 // Note: Depending on design, GPIO may not be able to source enough current for hx711 and loadcell. /* HX711 ADC resolution in bits */ #define ADC_RES 24 typedef struct { uint32_t value; // buffer for ADC read uint8_t count; // number of bitshifts. Read complete when count == ADC_RES }hx711_sample_t; static volatile hx711_sample_t m_sample; static hx711_event_handler_t m_evt_handler; static uint32_t m_mode; static bool m_continous_sampling; static int convert_sample(uint32_t sample) { return (int)(sample << 8) >> 8; } /* Clocks out HX711 result - if readout fails consistently, try to increase the clock period and/or enable compiler optimization */ static void hx711_sample() { NRF_TIMER2->TASKS_CLEAR = 1; m_sample.count = 0; m_sample.value = 0; NRF_TIMER1->TASKS_START = 1; // Starts clock signal on PD_SCK for (uint32_t i=0; i < ADC_RES; i++) { do { /* NRF_TIMER->CC[1] contains number of clock cycles.*/ NRF_TIMER2->TASKS_CAPTURE[1] = 1; if (NRF_TIMER2->CC[1] >= ADC_RES) goto EXIT; // Readout not in sync with PD_CLK. Abort and notify error. } while(NRF_TIMER1->EVENTS_COMPARE[0] == 0); NRF_TIMER1->EVENTS_COMPARE[0] = 0; m_sample.value |= (nrf_gpio_pin_read(DOUT) << (23 - i)); m_sample.count++; } EXIT: if (!m_continous_sampling) { NRF_GPIOTE->TASKS_SET[1] = 1; // HX711 power-down } NRF_LOG_INFO("Number of bits : %d", m_sample.count); NRF_LOG_INFO("ADC val: 0x%x", m_sample.value); if (m_sample.count == ADC_RES) { // Send notification with raw ADC value m_evt_handler(DATA_READY, convert_sample(m_sample.value)); } else { // Notify readout error m_evt_handler(DATA_ERROR, m_sample.value); } } static void gpiote_evt_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { nrf_drv_gpiote_in_event_disable(DOUT); hx711_sample(); if (m_continous_sampling) { nrf_drv_gpiote_in_event_enable(DOUT, true); } } void hx711_init(hx711_mode_t mode, hx711_event_handler_t evt_handler) { ret_code_t ret_code; nrf_drv_gpiote_in_config_t gpiote_config = GPIOTE_CONFIG_IN_SENSE_HITOLO(false); m_evt_handler = evt_handler; m_mode = mode; nrf_gpio_cfg_output(PD_SCK); nrf_gpio_pin_set(PD_SCK); nrf_gpio_cfg(VDD, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_PULLUP, NRF_GPIO_PIN_S0H1, NRF_GPIO_PIN_NOSENSE); nrf_gpio_pin_set(VDD); nrf_gpio_cfg_input(DOUT, NRF_GPIO_PIN_NOPULL); if (!nrf_drv_gpiote_is_init()) { ret_code = nrf_drv_gpiote_init(); APP_ERROR_CHECK(ret_code); } ret_code = nrf_drv_gpiote_in_init(DOUT, &gpiote_config, gpiote_evt_handler); APP_ERROR_CHECK(ret_code); /* Set up timers, gpiote, and ppi for clock signal generation*/ NRF_TIMER1->CC[0] = 1; NRF_TIMER1->CC[1] = TIMER_COMPARE; NRF_TIMER1->CC[2] = TIMER_COUNTERTOP; NRF_TIMER1->SHORTS = (uint32_t) (1 << 2); //COMPARE2_CLEAR NRF_TIMER1->PRESCALER = 0; NRF_TIMER2->CC[0] = m_mode; NRF_TIMER2->MODE = 2; NRF_GPIOTE->CONFIG[1] = (uint32_t) (3 | (PD_SCK << 8) | (1 << 16) | (1 << 20)); NRF_PPI->CH[0].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[0]; NRF_PPI->CH[0].TEP = (uint32_t) &NRF_GPIOTE->TASKS_SET[1]; NRF_PPI->CH[1].EEP = (uint32_t) &NRF_TIMER1->EVENTS_COMPARE[1]; NRF_PPI->CH[1].TEP = (uint32_t) &NRF_GPIOTE->TASKS_CLR[1]; NRF_PPI->FORK[1].TEP = (uint32_t) &NRF_TIMER2->TASKS_COUNT; // Increment on falling edge NRF_PPI->CH[2].EEP = (uint32_t) &NRF_TIMER2->EVENTS_COMPARE[0]; NRF_PPI->CH[2].TEP = (uint32_t) &NRF_TIMER1->TASKS_SHUTDOWN; NRF_PPI->CHEN = 7; } void hx711_power_down() { nrf_gpio_cfg_default(VDD); nrf_gpio_cfg_default(PD_SCK); nrf_gpio_cfg_default(DOUT); } void hx711_start(bool single) { m_continous_sampling = !single; NRF_LOG_INFO("Start sampling"); NRF_GPIOTE->TASKS_CLR[1] = 1; // Generates interrupt when new sampling is available. nrf_drv_gpiote_in_event_enable(DOUT, true); } void hx711_stop() { nrf_drv_gpiote_in_event_disable(DOUT); NRF_GPIOTE->TASKS_SET[1] = 1; // Must be kept high for >60 us to power down HX711 }
Update 9/11-18:
I've included an example in this thread: https://devzone.nordicsemi.com/f/nordic-q-a/40271/timer-issue-with-hx711---stopped-by-vector-catch-error
Thanks a ton Sir , I will definitely go through the datasheet and would try to create own code with reference to your code .
I would comment down below if I face any hurdle to implement the same.
Hello Vidar Berg, thank you for this code! I'm trying to get it to work within KEIL5, however it does not compile.
Can you tell me which SDK version you wrote this for? It seems like NRF_GPIOTE is no longer available in the S130 soft device if I interpret the compilation errors correctly. Do you have any suggestions as to how to adjust this to a newer version? Please see the screenshot attached.
With kind regards!
Hello,
Unfortunately, the code was written for the nRF52 series. The same approach will not work on the nRF51 series as it doesn't have the PPI "fork" feature. Have you decided on using the nRF51? nRF52810 is a low cost alternative.
Vidar Berg
I just wanted to thank you for the provided sample code.
"Just keep in mind that the implementation should account for higher priority interrupts such as radio events that may occur during ADC readout."
I'm receiving DATA_ERROR in the callback every time. I think it's because of the BLE interrupts. What's the best solution to this problem in general?