/* main.c - STM32 Nucleo L0 + HX711 + SSD1306(OLED) example (LED removed)
Pins used (only GPIOA):
HX711 DOUT -> PA6
HX711 SCK -> PA7
TARE button -> PA0
Buzzer -> PA1
OLED SDA -> PA4 (bit-bang)
OLED SCL -> PA5 (bit-bang)
*/
#include "main.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
UART_HandleTypeDef huart2;
/* Pin defines */
#define HX711_DOUT_PORT GPIOA
#define HX711_DOUT_PIN GPIO_PIN_6
#define HX711_SCK_PORT GPIOA
#define HX711_SCK_PIN GPIO_PIN_7
#define TARE_BTN_PORT GPIOA
#define TARE_BTN_PIN GPIO_PIN_0
#define BUZZER_PORT GPIOA
#define BUZZER_PIN GPIO_PIN_1
#define OLED_SDA_PORT GPIOA
#define OLED_SDA_PIN GPIO_PIN_4
#define OLED_SCL_PORT GPIOA
#define OLED_SCL_PIN GPIO_PIN_5
/* Calibration */
volatile long long offset = 0;
float calibrationFactor = 2300.0f; // 初始估计,请用砝码校准
/* OLED buffer */
#define OLED_WIDTH 128
#define OLED_PAGES 4
static uint8_t oled_buf[OLED_WIDTH * OLED_PAGES];
/* small digit font and glyphs (same as earlier) */
static const uint8_t font_digits[][5] = {
{0x3E,0x51,0x49,0x45,0x3E},{0x00,0x42,0x7F,0x40,0x00},
{0x42,0x61,0x51,0x49,0x46},{0x21,0x41,0x45,0x4B,0x31},
{0x18,0x14,0x12,0x7F,0x10},{0x27,0x45,0x45,0x45,0x39},
{0x3C,0x4A,0x49,0x49,0x30},{0x01,0x71,0x09,0x05,0x03},
{0x36,0x49,0x49,0x49,0x36},{0x06,0x49,0x49,0x29,0x1E}
};
static const uint8_t glyph_dot[1] = {0x60};
static const uint8_t glyph_g[5] = {0x3A,0x45,0x45,0x45,0x3F};
/* printf -> UART */
int _write(int file, char *ptr, int len) {
HAL_UART_Transmit(&huart2, (uint8_t*)ptr, len, HAL_MAX_DELAY);
return len;
}
/* HX711 low-level */
int32_t HX711_read_raw(void) {
while (HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) == GPIO_PIN_SET) HAL_Delay(1);
uint32_t count = 0;
for (int i = 0; i < 24; ++i) {
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET);
HAL_Delay(1);
count <<= 1;
if (HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) == GPIO_PIN_SET) count++;
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET);
HAL_Delay(1);
}
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET);
HAL_Delay(1);
HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET);
HAL_Delay(1);
if (count & 0x800000) count |= 0xFF000000;
return (int32_t)count;
}
long long HX711_read_average(uint8_t times) {
long long sum = 0;
for (uint8_t i = 0; i < times; ++i) {
sum += HX711_read_raw();
HAL_Delay(10);
}
return sum / times;
}
void HX711_tare(uint8_t times) { offset = HX711_read_average(times); }
float HX711_get_units(uint8_t times) {
long long raw = HX711_read_average(times);
long long value = raw - offset;
return (float)value / calibrationFactor;
}
/* software i2c bitbang for SSD1306 (same handlers as before) */
static void sda_high(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = OLED_SDA_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(OLED_SDA_PORT, &GPIO_InitStruct); }
static void sda_low(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = OLED_SDA_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(OLED_SDA_PORT, &GPIO_InitStruct); HAL_GPIO_WritePin(OLED_SDA_PORT, OLED_SDA_PIN, GPIO_PIN_RESET); }
static void scl_high(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = OLED_SCL_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(OLED_SCL_PORT, &GPIO_InitStruct); }
static void scl_low(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = OLED_SCL_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(OLED_SCL_PORT, &GPIO_InitStruct); HAL_GPIO_WritePin(OLED_SCL_PORT, OLED_SCL_PIN, GPIO_PIN_RESET); }
static void i2c_delay(void) { asm("nop"); HAL_Delay(0); }
static void i2c_start(void) { sda_high(); scl_high(); i2c_delay(); sda_low(); i2c_delay(); scl_low(); i2c_delay(); }
static void i2c_stop(void) { sda_low(); i2c_delay(); scl_high(); i2c_delay(); sda_high(); i2c_delay(); }
static uint8_t i2c_write_byte(uint8_t data) {
for (int i=0;i<8;i++) {
if (data & 0x80) sda_high(); else sda_low();
i2c_delay(); scl_high(); i2c_delay(); scl_low(); i2c_delay();
data <<= 1;
}
sda_high(); i2c_delay(); scl_high(); i2c_delay();
uint8_t ack = (HAL_GPIO_ReadPin(OLED_SDA_PORT, OLED_SDA_PIN) == GPIO_PIN_SET);
scl_low(); i2c_delay();
return ack;
}
static void ssd1306_write_cmd(uint8_t cmd) { i2c_start(); i2c_write_byte(0x3C<<1); i2c_write_byte(0x00); i2c_write_byte(cmd); i2c_stop(); }
static void ssd1306_write_data_block(uint8_t* data, uint16_t len) { i2c_start(); i2c_write_byte(0x3C<<1); i2c_write_byte(0x40); for (uint16_t i=0;i<len;i++) i2c_write_byte(data[i]); i2c_stop(); }
void ssd1306_init(void) {
sda_high(); scl_high(); HAL_Delay(10);
ssd1306_write_cmd(0xAE); ssd1306_write_cmd(0x20); ssd1306_write_cmd(0x00);
ssd1306_write_cmd(0xB0); ssd1306_write_cmd(0xC8); ssd1306_write_cmd(0x00); ssd1306_write_cmd(0x10);
ssd1306_write_cmd(0x40); ssd1306_write_cmd(0x81); ssd1306_write_cmd(0x7F); ssd1306_write_cmd(0xA1);
ssd1306_write_cmd(0xA6); ssd1306_write_cmd(0xA8); ssd1306_write_cmd(0x1F); ssd1306_write_cmd(0xA4);
ssd1306_write_cmd(0xD3); ssd1306_write_cmd(0x00); ssd1306_write_cmd(0xD5); ssd1306_write_cmd(0xF0);
ssd1306_write_cmd(0xD9); ssd1306_write_cmd(0x22); ssd1306_write_cmd(0xDA); ssd1306_write_cmd(0x02);
ssd1306_write_cmd(0xDB); ssd1306_write_cmd(0x20); ssd1306_write_cmd(0x8D); ssd1306_write_cmd(0x14);
ssd1306_write_cmd(0xAF);
memset(oled_buf,0,sizeof(oled_buf));
}
void ssd1306_update(void) {
for (uint8_t page=0; page<OLED_PAGES; ++page) {
ssd1306_write_cmd(0xB0 + page); ssd1306_write_cmd(0x00); ssd1306_write_cmd(0x10);
ssd1306_write_data_block(&oled_buf[page*OLED_WIDTH], OLED_WIDTH);
}
}
/* small drawing helpers (same as previous) */
void oled_draw_glyph_at(uint8_t page, uint8_t col, const uint8_t *glyph, uint8_t width) {
if (col + width >= OLED_WIDTH) return;
uint8_t *p = &oled_buf[page*OLED_WIDTH + col];
for (uint8_t i=0;i<width;i++) p[i] = glyph[i];
}
uint8_t oled_put_char_small(uint8_t page, uint8_t col, char c) {
if (c >= '0' && c <= '9') { oled_draw_glyph_at(page,col,font_digits[c-'0'],5); if (col+5 < OLED_WIDTH) oled_buf[page*OLED_WIDTH + col + 5]=0x00; return 6; }
else if (c == '.') { oled_draw_glyph_at(page,col,glyph_dot,1); if (col+1 < OLED_WIDTH) oled_buf[page*OLED_WIDTH + col + 1]=0x00; return 2; }
else if (c == 'g') { oled_draw_glyph_at(page,col,glyph_g,5); if (col+5 < OLED_WIDTH) oled_buf[page*OLED_WIDTH + col + 5]=0x00; return 6; }
else { if (col+1 < OLED_WIDTH) oled_buf[page*OLED_WIDTH + col]=0x00; return 2; }
}
void oled_print_small(uint8_t page, uint8_t col, const char *s) {
uint8_t x = col;
while (*s && x < (OLED_WIDTH - 6)) x += oled_put_char_small(page, x, *s++);
}
void buzzerBeep(uint8_t times, uint16_t ms) {
for (uint8_t i=0;i<times;i++) {
HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_SET);
HAL_Delay(ms);
HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_RESET);
HAL_Delay(80);
}
}
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_USART2_UART_Init();
printf("STM32 HX711 + SSD1306 (no LED) start\r\n");
ssd1306_init();
HAL_Delay(200);
printf("Taring... leave pan empty\r\n");
HAL_Delay(300);
HX711_tare(5);
printf("Tare done offset=%lld\r\n", offset);
char inbuf[64]; uint8_t inpos = 0;
while (1) {
float weight = HX711_get_units(3);
if (weight < 0) weight = 0.0f;
printf("Weight(g): %.2f\r\n", weight);
// update oled top two pages
for (int p=0;p<2;p++) for (int c=0;c<OLED_WIDTH;c++) oled_buf[p*OLED_WIDTH + c]=0x00;
oled_print_small(0,0,"Wt:");
char wstr[32]; snprintf(wstr,sizeof(wstr),"%.2f",weight);
oled_print_small(1,0,wstr);
uint8_t col_after=0; for (const char *p=wstr;*p;++p) { if (*p=='.') col_after+=2; else col_after+=6; }
oled_print_small(1,col_after+1,"g");
ssd1306_update();
// TARE button
if (HAL_GPIO_ReadPin(TARE_BTN_PORT, TARE_BTN_PIN) == GPIO_PIN_RESET) {
HAL_Delay(40);
if (HAL_GPIO_ReadPin(TARE_BTN_PORT, TARE_BTN_PIN) == GPIO_PIN_RESET) {
HX711_tare(5);
printf("TARE pressed. offset=%lld\r\n", offset);
buzzerBeep(1,120);
while (HAL_GPIO_ReadPin(TARE_BTN_PORT, TARE_BTN_PIN) == GPIO_PIN_RESET) HAL_Delay(20);
}
}
// serial input to set calibrationFactor
if (HAL_UART_Receive(&huart2, (uint8_t *)&inbuf[inpos], 1, 0) == HAL_OK) {
char ch = inbuf[inpos];
if (ch == '\r' || ch == '\n') {
inbuf[inpos] = 0; inpos = 0;
if (strlen(inbuf) > 0) {
char *endptr; float v = strtof(inbuf, &endptr);
if (endptr != inbuf && v > 0) { calibrationFactor = v; printf("New cal=%.2f\r\n", calibrationFactor); }
else printf("Invalid calibration input\r\n");
}
memset(inbuf,0,sizeof(inbuf));
} else { if (inpos < sizeof(inbuf)-1) inbuf[inpos++] = ch; }
}
HAL_Delay(300);
}
}
/* SystemClock_Config, MX_USART2_UART_Init and MX_GPIO_Init below (same as earlier but no GPIOD) */
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0);
}
static void MX_USART2_UART_Init(void) {
huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart2);
}
static void MX_GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* HX711 SCK PA7 output */
GPIO_InitStruct.Pin = HX711_SCK_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(HX711_SCK_PORT, &GPIO_InitStruct); HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET);
/* HX711 DOUT PA6 input */
GPIO_InitStruct.Pin = HX711_DOUT_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(HX711_DOUT_PORT, &GPIO_InitStruct);
/* TARE button PA0 input pullup */
GPIO_InitStruct.Pin = TARE_BTN_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(TARE_BTN_PORT, &GPIO_InitStruct);
/* Buzzer PA1 output */
GPIO_InitStruct.Pin = BUZZER_PIN; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(BUZZER_PORT, &GPIO_InitStruct); HAL_GPIO_WritePin(BUZZER_PORT, BUZZER_PIN, GPIO_PIN_RESET);
/* OLED SDA/SCL default input pull-up */
GPIO_InitStruct.Pin = OLED_SDA_PIN | OLED_SCL_PIN; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(OLED_SDA_PORT, &GPIO_InitStruct);
}