diff --git a/examples/hx711_demo/Makefile b/examples/hx711_demo/Makefile new file mode 100644 index 00000000..c8541702 --- /dev/null +++ b/examples/hx711_demo/Makefile @@ -0,0 +1,9 @@ +all : flash + +TARGET:=hx711_demo +TARGET_MCU?=CH32V003 + +include ../../ch32fun/ch32fun.mk + +flash : cv_flash +clean : cv_clean diff --git a/examples/hx711_demo/README.md b/examples/hx711_demo/README.md new file mode 100644 index 00000000..a6bd4f9d --- /dev/null +++ b/examples/hx711_demo/README.md @@ -0,0 +1,17 @@ +# HX711 Load Cell Amplifier Example + +This example shows how to interface a HX711 load cell amplifier. +The project provides a simple interface for calibration and reading weight values over the debug interface. + +## Hardware Connections + +- **HX711 Data Pin:** Connect to `PD4` (can be changed using define `HX711_DATA_PIN`). +- **HX711 Clock Pin:** Connect to `PD5` (can be changed using define `HX711_CLK_PIN`). + +## Usage + +1. Use `make` to upload firmware and `make monitor` to open the terminal +1. On startup, the device will tare (zero) the scale. +2. Enter commands: + - `C` + Enter: Starts calibration. Follow three prompts to place a known weight and enter its value. + - `V` + Enter: Reads and displays the current weight value. \ No newline at end of file diff --git a/examples/hx711_demo/funconfig.h b/examples/hx711_demo/funconfig.h new file mode 100644 index 00000000..aab11219 --- /dev/null +++ b/examples/hx711_demo/funconfig.h @@ -0,0 +1,10 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +// Place configuration items here, you can see a full list in ch32fun/ch32fun.h +// To reconfigure to a different processor, update TARGET_MCU in the Makefile + +#define FUNCONF_USE_DEBUGPRINTF 1 +#define FUNCONF_SYSTICK_USE_HCLK 1 + +#endif // _FUNCONFIG_H diff --git a/examples/hx711_demo/hx711.h b/examples/hx711_demo/hx711.h new file mode 100644 index 00000000..bee94f9c --- /dev/null +++ b/examples/hx711_demo/hx711.h @@ -0,0 +1,178 @@ +/** + * HX711 Library for ch32fun + * Derived from the Arduino HX711 library by Bogdan Necula + * (https://github.com/bogde/HX711) + * + * MIT License + * (c) 2025 Alexander Mandera + * (c) 2018 Bogdan Necula + */ + +#include "ch32fun.h" +#include + +// control pins +#ifndef HX711_DATA_PIN +#define HX711_DATA_PIN PD4 +#endif + +#ifndef HX711_CLK_PIN +#define HX711_CLK_PIN PD5 +#endif + +#ifdef HX711_GAIN + #if HX711_GAIN == 128 + #define HX711_GAIN 1 + #elif HX711_GAIN == 64 + #define HX711_GAIN 3 + #elif HX711_GAIN == 32 + #define HX711_GAIN 2 + #endif +#else + #define HX711_GAIN_VALUE 1 +#endif + +static uint32_t hx711_scale = 0; // Scale factor * 100 +static uint32_t hx711_offset = 0; + +uint8_t hx711_shift_in() __attribute__((section(".srodata"))) __attribute__((used)); +uint8_t hx711_shift_in() { + uint8_t value = 0; + uint8_t i; + + for (i = 0; i < 8; ++i) { + funDigitalWrite(HX711_CLK_PIN, FUN_HIGH); + Delay_Us(1); + value |= funDigitalRead(HX711_DATA_PIN) << (7 - i); + funDigitalWrite(HX711_CLK_PIN, FUN_LOW); + Delay_Us(1); + } + return (uint8_t)value; +} + +void hx711_init() { + // Clock pin as output + funPinMode(HX711_CLK_PIN, GPIO_Speed_10MHz | GPIO_CNF_OUT_PP); + + // Data pin as input with pullup + funPinMode(HX711_DATA_PIN, GPIO_CFGLR_IN_PUPD); + funDigitalWrite(HX711_DATA_PIN, FUN_HIGH); +} + +static inline void hx711_set_scale(uint32_t scale) { + hx711_scale = scale; +} + +static inline uint32_t hx711_get_scale(void) { + return hx711_scale; +} + +static inline void hx711_set_offset(uint32_t offset) { + hx711_offset = offset; +} + +static inline uint32_t hx711_get_offset(void) { + return hx711_offset; +} + +bool hx711_is_ready(void) { + return funDigitalRead(HX711_DATA_PIN) == FUN_LOW; +} + +void hx711_wait_ready(uint32_t delay) { + while(!hx711_is_ready()) { + Delay_Ms(delay); + } +} + +bool hx711_wait_ready_retry(uint8_t retries, uint32_t delay) { + uint8_t count = 0; + while(count < retries) { + if(hx711_is_ready()) { + return true; + } + Delay_Ms(delay); + count++; + } + return false; +} + +bool hx711_wait_ready_timeout(uint32_t timeout, uint32_t delay) { + uint64_t start = SysTick->CNT; + uint64_t timeout_ticks = timeout * DELAY_MS_TIME; + while((SysTick->CNT - start) < timeout_ticks) { + if(hx711_is_ready()) { + return true; + } + Delay_Ms(delay); + } + return false; +} + +uint32_t hx711_read(void) { + hx711_wait_ready(1); // Use 1ms delay + + uint32_t value = 0; + uint8_t data[3] = {0}; + + // Disable interrupts during reading + __disable_irq(); + + // Read 24 bits + for (uint8_t i = 0; i < 3; i++) { + data[2 - i] = hx711_shift_in(); + } + + // Set gain for next reading + for (uint8_t i = 0; i < HX711_GAIN_VALUE; i++) { + funDigitalWrite(HX711_CLK_PIN, 1); + Delay_Us(1); + funDigitalWrite(HX711_CLK_PIN, 0); + Delay_Us(1); + } + + __enable_irq(); + + // Combine bytes to 24-bit value + uint8_t filler = 0x00; + if(data[2] & 0x80) { + filler = 0xFF; + } + + value = ( (uint32_t)filler << 24 + | (uint32_t)data[2] << 16) + | ((uint32_t)data[1] << 8) + | data[0]; + + return value; +} + +uint32_t hx711_read_average(uint8_t times) { + uint64_t sum = 0; + for (uint8_t i = 0; i < times; i++) { + sum += hx711_read(); + } + return (uint32_t)(sum / times); +} + +static inline uint32_t hx711_get_value(uint8_t times) { + return hx711_read_average(times) - hx711_offset; +} + +static inline uint32_t hx711_get_units(uint8_t times) { + return hx711_get_value(times) * 100 / hx711_scale; +} + +static inline void hx711_tare(uint8_t times) { + uint32_t sum = hx711_read_average(times); + hx711_set_offset(sum); +} + +void hx711_power_down(void) { + funDigitalWrite(HX711_CLK_PIN, FUN_LOW); + funDigitalWrite(HX711_CLK_PIN, FUN_HIGH); +} + +void hx711_power_up(void) { + funDigitalWrite(HX711_CLK_PIN, FUN_LOW); +} \ No newline at end of file diff --git a/examples/hx711_demo/hx711_demo.c b/examples/hx711_demo/hx711_demo.c new file mode 100644 index 00000000..4ae68080 --- /dev/null +++ b/examples/hx711_demo/hx711_demo.c @@ -0,0 +1,118 @@ +#include "ch32fun.h" +#include + +#include "hx711.h" + +static char input_buffer[64]; +static int input_len = 0; + +void calibration( void ); + +// handle_debug_input: only buffer input +void handle_debug_input(int numbytes, uint8_t *data) +{ + for (int i = 0; i < numbytes && input_len < sizeof(input_buffer) - 1; i++) + { + char c = (char)data[i]; + input_buffer[input_len++] = c; + if (c == '\n' || c == '\r') + { + input_buffer[input_len] = '\0'; + } + } +} + +// Calibration routine +void calibration( void ) +{ + int target_weight_int = 100; // grams * 100 + int iterations = 3; + int divider_accum = 0; + + hx711_set_scale( 1.0f ); // Reset scale + hx711_tare( 1 ); + printf( "Ok\n" ); + + for ( int i = 0; i < iterations; i++ ) + { + printf( "Place known weight and enter its value (g, integer):\n" ); + // Wait for input + input_len = 0; + while ( 1 ) + { + poll_input(); + if ( input_len > 0 && ( input_buffer[input_len - 1] == '\n' || input_buffer[input_len - 1] == '\r' ) ) + { + input_buffer[input_len - 1] = '\0'; + // Convert string to integer + target_weight_int = 0; + for ( int j = 0; input_buffer[j] && j < 6; j++ ) + { + if ( input_buffer[j] >= '0' && input_buffer[j] <= '9' ) + { + target_weight_int = target_weight_int * 10 + ( input_buffer[j] - '0' ); + } + } + break; + } + } + uint32_t measured_weight = hx711_get_units( 10 ); + uint32_t divider = measured_weight / target_weight_int; + divider_accum += divider; + + printf( "%d grams - Ok\n", target_weight_int ); + input_len = 0; + } + + uint32_t divider_final = divider_accum / iterations; + + printf( "Calibration divider: %ld.%02ld\n", divider_final / 100, divider_final % 100 ); + hx711_set_scale( divider_final ); + printf( "Calibrated\n" ); +} + +// main loop: handle echo and command parsing +int main(void) +{ + SystemInit(); + while (!DebugPrintfBufferFree()); + + funGpioInitD(); + + printf("Start\n"); + hx711_init(); + hx711_tare(1); + printf("Tara done, give commands\n"); + + while (1) + { + poll_input(); + + // If a command is ready + if (input_len > 0 && (input_buffer[input_len - 1] == '\n' || input_buffer[input_len - 1] == '\r')) + { + // Echo back + for (int i = 0; i < input_len; i++) + putchar(input_buffer[i]); + input_buffer[input_len] = '\0'; + + printf("Received command: %s\n", input_buffer); + + // Parse command + int b = strncmp(input_buffer, "C", 1); + printf("Compare result: %d\n", b); + if (strncmp(input_buffer, "C", 1) == 0) + { + printf("Calibration\n"); + calibration(); + } + else if (strncmp(input_buffer, "V", 1) == 0) + { + printf("Value\n"); + uint32_t value = hx711_get_units(10); + printf("%ld\n", value); + } + input_len = 0; + } + } +} \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index f8d22bf9..b78fa745 100644 --- a/platformio.ini +++ b/platformio.ini @@ -129,6 +129,10 @@ build_src_filter = ${fun_base.build_src_filter} + extends = fun_base_003 build_src_filter = ${fun_base.build_src_filter} + +[env:hx711_demo] +extends = fun_base_003 +build_src_filter = ${fun_base.build_src_filter} + + [env:i2c_oled] extends = fun_base_003 build_src_filter = ${fun_base.build_src_filter} +