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
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?
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?
Solved by turning on the optimizations. Interesting.
Droidik, the hx711_sample() function needs to be compiled with optimization to work when PD_SCK frequency is 1MHz. I suspect it would not have been necessary if I had rewritten this line "m_sample.value |= (nrf_gpio_pin_read(DOUT) << (23 - i));" to something more efficient.
Vidar Berg, once again thank you for your amazing work and support.
Hi Vidar Berg, This example is amazing. However, I am having issues using this example. I modified the "ble_app_uart" example from Nordic SDK, and included your portion of the code (As you have done), upon compiling, the nRF52832 DK does not advertise nor does it perform any other function. When I comment out the function "hx711_init(INPUT_CH_A_128, hx711_callback)" , code starts running smoothly, advertising is ok too. So what do you think seems to be the issue. Please help me. I am using Keil MDK-ARM 5, SDKv15.0.0, nRF52832 DK. Thanks
So what debugging have you done?
When I comment out the function "hx711_init(INPUT_CH_A_128, hx711_callback)" , code starts running smoothly
So that's clearly an area to focus your debugging: is it getting stuck, or crashing in that function ... ?