- Засыпание в режиме FU2 работает через раз, у меня во всяком случае. На старом форуме даже была тема похожая. Соотвественно и на передатчике и на приемнике я отказался от сна, повесил HC-12 на LowPower UART , приемник просыпается как приходит пакет по UART, передатчик просыпается по таймеру. Потребление всей схемы (на приемнике еще и ЖК дисплей постоянно включенный) порядка 100 мкА.
- модули из другой партии
хотел взять предыдущего продавца - уже нет их.
- конечно я допускаю что в коде проблема. Но он полная копия прошлогоднего, убран режим сна HC-12, добавлен другой дисплей + настройки STM32 для другого UART.
/**
******************************************************************************
* @file : main.c
* @author : Auto-generated by STM32CubeIDE
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/*
* Очередной удаленный термометр на STM32g031f8p6 DS18b20 и ЖК экраном на приемнике
*/
#include <stdint.h>
#include "system_stm32g0xx.h"
#include "g0base.h"
#include "g0uart.h"
#include <stdio.h>
#include "stm32g0xx.h"
#include "g0st7567.h"
#include "g0ds18b20.h"
#include "g0oneware.h"
#include <string.h>
#include <stdlib.h>
#include "g0hc12.h"
#if !defined(__SOFT_FP__) && defined(__ARM_FP)
#warning "FPU is not initialized, but the project is compiling for an FPU. Please initialize the FPU before use."
#endif
extern "C" int __io_putchar(int ch) {
g0uartWrite((const unsigned char) ch);
return ch;
}
constexpr unsigned long periodSleepSec = 30UL*60UL; // период просыпания и отправки данных в секундах -> 30UL*60UL = 30 минут
constexpr unsigned long periodRebootSec = 15UL*24UL*60UL*60UL; // период принудительной перезагрузки 15 дней
// по результатам тестирования, через несколько дней беспрерывной работы перестает передаваться температура
// 27.03.2026 добавлен таймер переинициализации премопередатчика каждые почти три дня
constexpr unsigned long periodReinitSec = 3UL*23UL*60UL*60UL;
int main(void) {
g0getIWDGreboot();
g0initIWDG();
g0resetIWDG();
SystemInit();
g0byHSI();
SystemCoreClockUpdate();
g0initPA4led();
g0setPA4led(true);
g0initPA7modeKEYS();
g0onLSI();
g0initLPTIM1wakeup();
g0initUART1();
printf("CLK = %d\r\n", (int)SystemCoreClock);
g0initHC12();
g0initDS18B20();
if (g0readROMds18b20()) {
g0saveTHTLCFG();
printf("DS18B20 ready.\r\n");
g0convertDS18B20();
delay_ms(750);
g0readRAMds18b20();
t_ram_ds18b20 * ddd = getRAMds18B20();
printf("local temp = %d\r\n", g0dsRAMtoTempInt(ddd));
g0sendHC12tempDS18B20(ddd, true);
}
if (getDeviceRecieveMode()) { // reciever mode
printf("reciever mode\r\n");
initST7567bySPI1af0();
st7567a_PrintString((const unsigned char *)"STM32G031F8P6", 0, 0);
st7567a_PrintString((const unsigned char *)"ST7567A SPI1", 0, 1);
st7567a_PrintString((const unsigned char *)"HC-12 LPUART1", 0, 2);
st7567a_PrintString((const unsigned char *)"outdoor", 0, 3);
st7567a_PrintString((const unsigned char *)"thermometer", 4, 4);
if (g0lastIWDGreboot()) st7567a_PrintString((const unsigned char *)"IWDG start", 0, 5);
else st7567a_PrintString((const unsigned char *)"normal start", 0, 5);
spiOFFST7567();
}
if (g0lastIWDGreboot()) printf("IWDG start\r\n");
else printf("normal start\r\n");
while (g0uartLoopTX()) {};
g0setPA4led(false);
g0disableUART1();
/* Loop forever */
while (1) {
if (getDeviceRecieveMode()) { // reciever mode
// -- to sleep --
PWR->CR1 &= ~PWR_CR1_LPMS; // 000: Stop 0 mode
PWR->CR1 |= PWR_CR1_LPMS_0; // 001: Stop 1 mode
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Set SLEEPDEEP bit
LPUART1->CR1 |= USART_CR1_UESM; // 1: LPUART able to wake up the MCU from low-power mode. When this function is active, the clock source for the LPUART must be HSI or LSE (see RCC chapter)
__WFI();
// -- wakeup --
LPUART1->CR1 &= ~USART_CR1_UESM; // 0: LPUART not able to wake up the MCU from low-power mode
PWR->SCR |= PWR_SCR_CWUF; // Clear wake-up flags
g0resetIWDG();
//g0testIWDGsec40();
static unsigned long currentTimerSleep = 0;
static unsigned long lastTimeGetLocalTempSec = 0UL;
static unsigned long periodLastGetTemp = 0UL;
static int extTemp = 0;
t_ram_ds18b20 extRAM;
t_ram_ds18b20 * eee = &extRAM;
delay_ms(50UL); // после просыпания ждем пока по uart прилетят все байты
if (((seconds() - currentTimerSleep) >= (periodSleepSec - 9UL*60UL)) || (g0getHC12data(eee))) { // приемник просыпается на 9 минут чаще передатчика
currentTimerSleep = seconds();
// internal / local sensor ds18b20
g0initDS18B20();
initST7567bySPI1af0();
if (g0readROMds18b20()) {
g0saveTHTLCFG();
g0convertDS18B20();
delay_ms(750);
g0readRAMds18b20();
st7567a_PrintString((const unsigned char *)"T local DS18B20", 0, 0);
int localTemp = g0dsRAMtoTempInt(getRAMds18B20());
unsigned char lT[16];
lT[0] = '\0';
DS18B20intToString((const int)localTemp, (const unsigned char *)&lT[0]);
st7567a_PrintString((const unsigned char *)&lT[0], 5, 1);
lastTimeGetLocalTempSec = seconds();
} else {
st7567a_PrintString((const unsigned char *)"no local DS18B20", 0, 0);
if (lastTimeGetLocalTempSec) {
unsigned char oS[16];
oS[0] = '\0';
strcpy((char *) &oS[0], (const char *) "last ");
unsigned long wasPeriod = (seconds() - lastTimeGetLocalTempSec) / 60UL;
unsigned char lT[16];
lT[0] = '\0';
itoa((int)wasPeriod, (char *)&lT[0], 10);
strcat((char *) &oS[0], (const char *)&lT[0]);
strcat((char *) &oS[0], (const char *)"m.ago");
st7567a_PrintString((const unsigned char *)&oS[0], 0, 1);
} else {
st7567a_PrintString((const unsigned char *)"data is missing", 0, 1);
}
}
// show uptime
unsigned char uS[17];
uS[0] = '\0';
unsigned long uPeriod = seconds() / 60UL; // minutes
sprintf((char *)&uS[0], "%dd.%02dh.%02dm.", (int)(uPeriod/60UL/24UL), (int)((uPeriod%1440UL)/60UL), (int)(uPeriod%60UL));
st7567a_PrintString((const unsigned char *)&uS[0], 0, 3);
// outdoor / external sensor ds18b20
if (g0getHC12last()) {
periodLastGetTemp = seconds();
extTemp = g0dsRAMtoTempInt(eee);
unsigned char oS[16];
oS[0] = '\0';
DS18B20intToString((const int)extTemp, (const unsigned char *)&oS[0]);
st7567a_PrintString((const unsigned char *)&oS[0], 1, 5, 1);
} else if (periodLastGetTemp) { // приходили данные // отображаем температуру и время прошедшее с последнего прихода
unsigned char oS[16];
oS[0] = '\0';
DS18B20intToString((const int)extTemp, (const unsigned char *)&oS[0]);
st7567a_PrintString((const unsigned char *)&oS[0], 1, 5, 1);
oS[0] = '\0';
strcpy((char *) &oS[0], (const char *) "last ");
unsigned long wasPeriod = (seconds() - periodLastGetTemp) / 60UL;
unsigned char lT[16];
lT[0] = '\0';
itoa((int)wasPeriod, (char *)&lT[0], 10);
strcat((char *) &oS[0], (const char *)&lT[0]);
strcat((char *) &oS[0], (const char *)"m.ago");
st7567a_PrintString((const unsigned char *)&oS[0], 0, 7);
}
// end show display
spiOFFST7567();
}
} else { // transmitter mode
// -- to sleep --
PWR->CR1 &= ~PWR_CR1_LPMS; // 000: Stop 0 mode
PWR->CR1 |= PWR_CR1_LPMS_0; // 001: Stop 1 mode
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Set SLEEPDEEP bit
__WFI();
// -- wakeup --
PWR->SCR |= PWR_SCR_CWUF; // Clear wake-up flags
g0resetIWDG();
static unsigned long transmitTimerSleep = 0UL;
if ((seconds() - transmitTimerSleep) >= periodSleepSec) {
transmitTimerSleep = seconds();
g0initDS18B20();
if (g0readROMds18b20()) {
g0saveTHTLCFG();
g0convertDS18B20();
delay_ms(750);
g0readRAMds18b20();
g0sendHC12tempDS18B20(getRAMds18B20());
}
} else {
g0setPA4led(true);
delay_ms(80);
g0setPA4led(false);
}
}
static unsigned long currentRebootTimer = 0UL;
if ((seconds() - currentRebootTimer) >= periodRebootSec) {
currentRebootTimer = seconds();
NVIC_SystemReset();
}
static unsigned long currentReinitTimer = 0UL;
if ((seconds() - currentReinitTimer) >= periodReinitSec) {
currentReinitTimer = seconds();
g0initHC12();
}
}
}
/*
* g0hc12.cpp
*
* Created on: Apr 1, 2025
* Author: seleznev_a
*/
#include "g0hc12.h"
#include "stm32g0xx.h"
#include "g0base.h"
#include <stdio.h>
#include "g0uart.h"
#include <string.h>
#include <ctype.h>
unsigned char lpfoBufTXlog[max_buf_lpuart]; // буфер отправки
unsigned char lpfoBufRXlog[max_buf_lpuart]; // буфер приема
unsigned short lpfoPosTXlog; // текущая абсолютная позиция отправки
unsigned short lpfoPosRXlog; // аналогично для приема
unsigned short lpfoCountTXlog; // количество байт на отправку
unsigned short lpfoCountRXlog; // количество не прочитанных байт
bool lastOKextTemp = false;
bool g0getHC12last(void) {
bool extLast = lastOKextTemp;
lastOKextTemp = false;
return extLast;
}
void g0hc12toSetMode(void) {
GPIOA->BSRR |= GPIO_BSRR_BR5; // LOW
delay_ms(50);
}
void g0hc12toWorkMode(void) {
GPIOA->BSRR |= GPIO_BSRR_BS5; // HIGH
delay_ms(50);
}
void g0initPinPA5(void) {
RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // GPIO A port ON
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD5; // No pull-up, pull-down
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED5; // 00: Very low speed
GPIOA->OTYPER |= GPIO_OTYPER_OT5; // 1: Output open-drain
GPIOA->MODER |= GPIO_MODER_MODE5_0; // 01: Output mode
GPIOA->MODER &= ~GPIO_MODER_MODE5_1; // 01: Output mode
}
extern "C" void USART3_USART4_LPUART1_IRQHandler(void) {
if (LPUART1->ISR & USART_ISR_RXNE_RXFNE) { //Следим за состоянием данного флага 1 - данные пришли, 0- пусто
while (g0uartLoopTX()) {};
lpfoBufRXlog[lpfoPosRXlog] = (unsigned char)LPUART1->RDR; // вносим его в массив // считываем байт из регистра МК
++lpfoCountRXlog; // увеличиваем счетчики
++lpfoPosRXlog; // увеличиваем счетчики
if (lpfoPosRXlog >= max_buf_lpuart) lpfoPosRXlog = 0; // если позиция превысила размер - обнуляем
}
}
void g0initLPUART1(void) {
lpfoPosTXlog = 0; lpfoPosRXlog = 0; lpfoCountTXlog = 0; lpfoCountRXlog = 0; // сбрасываем все переменные
RCC->IOPENR |= RCC_IOPENR_GPIOAEN; // GPIO A port ON
// PA2 Настраиваем порт Tx
GPIOA->MODER |= GPIO_MODER_MODE2_1; // 10 — режим альтернативной функции.
GPIOA->MODER &= ~GPIO_MODER_MODE2_0; // 10 — режим альтернативной функции.
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED2; // 00: Very low speed
GPIOA->OTYPER &= ~GPIO_OTYPER_OT2; // 0 - двухтактный выход или push-pull сокращено PP (после сброса)
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD2_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->PUPDR |= GPIO_PUPDR_PUPD2_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->AFR[0] &= ~GPIO_AFRL_AFSEL2; // 0000: AF0 >> port2
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL2_2; // 0110: AF6 >> port2
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL2_1; // 0110: AF6 >> port2
// PA3 Настаиваем Rx
GPIOA->MODER |= GPIO_MODER_MODE3_1; // 10 — режим альтернативной функции.
GPIOA->MODER &= ~GPIO_MODER_MODE3_0; // 10 — режим альтернативной функции.
GPIOA->OSPEEDR &= ~GPIO_OSPEEDR_OSPEED3; // 00: Very low speed
GPIOA->PUPDR &= ~GPIO_PUPDR_PUPD3_1; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->PUPDR |= GPIO_PUPDR_PUPD3_0; // 01 — подтяжка к плюсу питания или pull-up сокращено PU
GPIOA->AFR[0] &= ~GPIO_AFRL_AFSEL3; // 0000: AF0 >> port3
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL3_2; // 0110: AF6 >> port3
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL3_1; // 0110: AF6 >> port3
// Настраиваем LPUART1 by HSI16
RCC->APBENR1 &= ~RCC_APBENR1_LPUART1EN; // LPUART1 clock OFF
RCC->CCIPR &= ~RCC_CCIPR_LPUART1SEL; // 00: LPUART1 clock by PCLK
RCC->CCIPR |= RCC_CCIPR_LPUART1SEL_1; // 10: HSI16
RCC->APBENR1 |= RCC_APBENR1_LPUART1EN; // LPUART1 clock ON
LPUART1->PRESC = 2; // 0010: input clock divided by 4
LPUART1->BRR = 0x1A0AB; // 16000000 * 256 / 4 / 9600 = 106666 -> 0x1A0AA
// start LPUART1
LPUART1->ISR = 0;
LPUART1->CR1 |= USART_CR1_RXNEIE_RXFNEIE; // 1: USART interrupt generated whenever ORE = 1 or RXNE = 1 in the USART_ISR register
NVIC_EnableIRQ(LPUART1_IRQn);
LPUART1->CR1 |= USART_CR1_UE|USART_CR1_TE|USART_CR1_RE;
}
void g0setLPUARTto4800(void) {
LPUART1->CR1 &= ~(USART_CR1_UE|USART_CR1_TE|USART_CR1_RE);
LPUART1->BRR = 0x34155; // 16000000 * 256 / 4 / 4800 = 213333 -> 0x34155
LPUART1->CR1 |= USART_CR1_UE|USART_CR1_TE|USART_CR1_RE;
}
void g0lprtWrite(const unsigned char inByte) { // кладет байт в отправляемый кольцевой буфер
if (lpfoCountTXlog < max_buf_lpuart) { // если в кольцевом буфере есть место
lpfoBufTXlog[lpfoPosTXlog] = inByte; // кладем байт в буфер
++lpfoCountTXlog; //увеличиваем счетчик
++lpfoPosTXlog; //увеличиваем счетчик
if (lpfoPosTXlog >= max_buf_lpuart) lpfoPosTXlog = 0; // если позиция перепрыгрула за границу - обнуляем
}
}
bool g0lprtLoopTX(void) {
if (lpfoCountTXlog) { // если есть что отправлять
if (LPUART1->ISR & USART_ISR_TXE_TXFNF) { // в сдвиговом регистре пусто
if (lpfoCountTXlog <= lpfoPosTXlog) { // если позиция отправки больше количества отправляемых байт
LPUART1->TDR = (unsigned char)lpfoBufTXlog[lpfoPosTXlog-lpfoCountTXlog]; // берем первый вошедший байт // кладем байт в регистр МК для непосредственно отправки
} else { // если хвост кольцевого буфера выходит за границы размеров
LPUART1->TDR = (unsigned char)lpfoBufTXlog[max_buf_lpuart-(lpfoCountTXlog-lpfoPosTXlog)]; // берем байт по другой логике // кладем байт в регистр МК для непосредственно отправки
}
--lpfoCountTXlog; // уменьшаем счетчик не отправленных байт
}
return true;
}
return false;
}
unsigned short g0lprtAvailable(void) { // возвращает количество байт в приемном буфере лога
return lpfoCountRXlog;
}
unsigned char g0lprtRead(void) { // читаем входящий байт из кольцевого буфера
unsigned char currOut; // Переменная куда читать
if (lpfoCountRXlog <= lpfoPosRXlog) { // если позиция чтения больше остатка
currOut = lpfoBufRXlog[lpfoPosRXlog-lpfoCountRXlog]; // читаем
} else { // если хвост чтения за границей буфера
currOut = lpfoBufRXlog[max_buf_lpuart-(lpfoCountRXlog-lpfoPosRXlog)]; // читаем по другой формуле
}
--lpfoCountRXlog; // уменьшаем счетчик количества
return currOut; // возвращаем прочитанный байт
}
void g0lprtPrint(const unsigned char * inString) {
unsigned short i = 0;
while (*(inString+i)) {
g0lprtWrite(*(inString+i));
++i;
}
}
bool g0waitHC12responseOKRN(bool showResp) {
unsigned char responseStr[] = "OK\r\n";
constexpr unsigned char lenResponse = 4;
bool outResult = false;
delay_ms(200); // wait for get response from HC-12
unsigned char findPos = 0;
while (g0lprtAvailable()) {
unsigned char inB = g0lprtRead();
if (inB) {
if (showResp) {
g0uartWrite(inB);
while (g0uartLoopTX()) {};
}
if (inB == responseStr[findPos]) {
if ((++findPos) >= lenResponse) {
findPos = 0;
outResult = true;
break;
}
} else {
findPos = 0;
break;
}
}
}
return outResult;
}
void g0justWaitHC12response(bool showResp) {
delay_ms(200); // wait for get response from HC-12
while (g0lprtAvailable()) {
unsigned char inB = g0lprtRead();
if ((inB) && (showResp)) {
g0uartWrite(inB);
while (g0uartLoopTX()) {};
}
}
}
void g0sendHC12(const unsigned char * inCMD, bool showLog, bool needWaitResp = false) {
g0lprtPrint(inCMD);
while (g0lprtLoopTX()) {};
if (showLog) {
g0uartPrint(inCMD);
g0uartWrite('\n');
while (g0uartLoopTX()) {};
}
if (needWaitResp) g0justWaitHC12response(showLog);
}
void g0testSpeedHC12(void) {
unsigned long cntRepiter = 4UL;
unsigned char strOK[] = "OK\r\n";
unsigned long lenStr = strlen((const char *)strOK);
constexpr unsigned long getStr = 8UL;
unsigned char inStr[getStr];
while (cntRepiter--) {
unsigned long posIn = 0UL;
g0sendHC12((const unsigned char *)"AT\r", true, false);
delay_ms(200);
while (g0lprtAvailable() && (posIn < getStr)) {
unsigned char inB = g0lprtRead();
if (inB) {
g0uartWrite(inB);
while (g0uartLoopTX()) {};
inStr[posIn++] = inB;
if (posIn == lenStr) {
if (!memcmp((const void *)&strOK[0], (const void *)&inStr[0], lenStr)) return;
}
}
}
if (cntRepiter == 2UL) g0setLPUARTto4800();
}
}
void g0initHC12(void) {
g0initLPUART1();
g0initPinPA5();
g0hc12toSetMode();
g0testSpeedHC12();
g0sendHC12((const unsigned char *)"AT+DEFAULT\r", true, true);
g0sendHC12((const unsigned char *)"AT+C056\r", true, true); // channel 001...127
g0sendHC12((const unsigned char *)"AT+FU2\r", true, true); // 4800 baudrate
g0sendHC12((const unsigned char *)"AT+RX\r", true, true);
g0hc12toWorkMode();
g0setLPUARTto4800();
}
unsigned char * addByteToStringAs2char(const unsigned char * inS, const unsigned char inByte) {
unsigned char * pStr = (unsigned char *)inS;
char sN[3] = {'\0', '\0', '\0'};
sprintf((char*)&sN[0], "%02X", inByte);
strcat((char *)pStr, (const char *)&sN[0]);
return pStr + 2;
}
void g0sendHC12tempDS18B20(t_ram_ds18b20 * outData, bool needShowLog) {
unsigned char * pData = (unsigned char *)outData;
constexpr unsigned char lenData = sizeof(t_ram_ds18b20);
unsigned char outStr[lenData*2+1];
unsigned char * pStr = (unsigned char *) &outStr[0];
memset(pStr, '\0', lenData*2+1);
for (unsigned char i = 0; i < lenData; ++i) {
pStr = addByteToStringAs2char(pStr, *(pData+i));
}
g0sendHC12((const unsigned char *)&outStr[0], needShowLog, true);
}
unsigned char getByteByChar(const unsigned char * inPos) { // преобразования двух текстовых байт в число
unsigned char outByte = 0;
unsigned char c1 = *inPos;
if (isdigit(c1)) outByte = c1 - '0'; else outByte = c1 - 55;
outByte *= 16;
c1 = *(inPos + 1);
if (isdigit(c1)) outByte += c1 - '0'; else outByte += c1 - 55;
return outByte;
}
bool g0getHC12data(t_ram_ds18b20 * pData) {
constexpr unsigned char lenData = sizeof(t_ram_ds18b20);
constexpr unsigned char lenStr = lenData*2;
if (g0lprtAvailable() != lenStr) {
while (g0lprtAvailable()) __attribute__((unused)) unsigned char y = g0lprtRead();
return false;
}
unsigned char inStr[lenStr+1];
inStr[lenStr] = '\0';
unsigned char posStr = 0;
while (g0lprtAvailable()) {
unsigned char iB = g0lprtRead();
if (iB) {
if ((((iB>='A') && (iB<='F')) || ((iB>='0') && (iB<='9'))) && (posStr < lenStr)) {
inStr[posStr++] = iB;
} else {
posStr = 0;
}
}
}
if (posStr == lenStr) {
unsigned char * ta = (unsigned char *)pData;
unsigned char * as = (unsigned char *)&inStr[0];
for(unsigned char i = 0; i <= lenData; ++i) {
*(ta++) = getByteByChar(as);
as += 2;
}
lastOKextTemp = true;
return true;
} else {
return false;
}
}