часы стрелочные (ремейк)

v.bay

✩✩✩✩✩✩✩
26 Окт 2026
1
0
Оформи код соответствующим тэгом, см. Правила
Ремейк

Всем доброго времени суток. На просторах Ардуино наткнулся на стрелочные часы на stm32F103. Честно говоря, собрал от безделия, посмотреть на реализацию графики. Все нормально получилось, потребление при 3,26В - 48 мА, об автономном питании можно забыть. Начал рыть, через год заказал плату у китайцев, дорого, правда, обошлась – почти 12 тыс. за 5 шт. Но уже удила закусил, свистулька не давала покоя. Еще год танцев с бубном, в результате реализовал: уменьшил потребление до 20мА с включенной подсветкой (без нее 14мА) – уменьшил рабочую частоту до 24мГц, при питании STM 2,65В через ЧИП 63000. При емкости Акк 3200мА/ч хватает на 6-7 суток при условии: индикация активна только ночью. Решил вопрос с зарядкой – контролируя напряжение на Акк во время заряда и его отключение по достижении заданного напряжения и включения зарядки при падении напряжения до заданного уровня. Все это можно менять в программе. Это дало возможность продлить жизнь Акк. питать изделие от сети, (по возможности) через источник на контроллере Tny264, небольшим током 100-150мА. Он изначально создан работать день и ночь. Применил ЖК 2,8” технологии IPS, это позволило улучшить контрастность на порядок, угол обзора увеличился фантастически. Это позволило слегка подкрасить голубым циферблат круга, повеселей смотреться стало. Изменил положение экрана с 0 на 3. В качестве ключа отключающего минусовой провод зарядки от земли, использовал сборку 8205, кварц 4мГц. В качестве источника питания применил 63000, верхнее плечо делителя (ADJ) резистор 43кОм, нижнюю часть блочка, сточил до глины 01мкФ. Датчик положения свистульки опрашивается каждые 15 сек. и ВКЛ/ОТКЛ подсветку. А замер напряжения на Акк, происходит раз в минуту, и соответствующее действие. Цвета показаний напряжения: голубой - Акк заряжен, желтый - режим заряда подключен, оранжевый - во избежание выключения Акк, необходимо принять меры к зарядке. Часы при этом не остановятся, просто исчезнет подсветка. Последнюю плату хотел использовать на stm32L151C8U6 купил, установил, пытаюсь конвертировать с F103. Процесс начал не так давно, обычно, начинаю в зиму, по завершению дачной страды, но пока результат нулевой. Попробовал на платформе CubeIde тоже мимо, при компиляции требуется 2 библиотеки в репозиторий, а ее держатели в страну больше не поставляют продукт. Я давно слежу за вашей работой, по урокам, немного, начал понимать структуру платформы Ардуино со дня основания получаю журнал с сайта РадиоЛоцман и там читал ваши статьи. И, как не странно, я все понимаю в ваших публикациях. Что у меня не всегда получается. Бывает из за сленга, а то и слишком замысловато пишут. Вот и решил обратиться к сообществу вашего сайта, может поможете с конвертацией с F103 на L151? Думаю, многие использовали бы этот ЧИП в своих свистульках. Я на Субе при 16мГц, в режиме Run, получил потребление 3,5 мА, а это дорогого стоит. Выкладываю вариант скетча, не ради хвастовства, а чтобы видно, что живой я, и мне это интересно очень.

#include <Wire.h>
#include <uRTCLib.h> //https://github.com/Naguissa/uRTCLib.git
#include <Adafruit_GFX_AS.h>
#include <Adafruit_ILI9341_STM.h> //_STM
#include <SPI.h>
uRTCLib rtc(0x68);
Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(PA1, PA2, PA3); //cs, dc, rst, Mosi - PA7, SCK, - PA5
float sx = 0, sy = 1, mx = 1, my = 0, hx = -1, hy = 0;
float sdeg = 0, mdeg = 0, hdeg = 0;
uint16_t osx = 120, osy = 120, omx = 120, omy = 120, ohx = 120, ohy = 120;
uint16_t x00 = 0, x11 = 0, y00 = 0, y11 = 0;
uint32_t targetTime = 0;
uint32_t hh, mm, ss;
uint8_t Week;
boolean initial = 1, www = 1, vvv = 1;
void setup(void)
{
Serial.begin(4800);
Wire.begin();
// rtc.set(15, 22, 19, 5, 19, 3, 25);
// uRTCLib::set(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year)
tft.begin();
tft.setRotation(3); // расположение экрана
tft.fillScreen(ILI9341_WHITE); //стирание всего экрана
tft.drawRect (0, 0, 320, 240, 0x59e9); // прямоугольник 59e9
tft.fillCircle(120, 120, 116, 0x8374); //круг 0x8494
tft.fillCircle(120, 120, 114, 0xff78); //стирание внутри круга
// tft.drawFastHLine (278, 14, 15, 0x59e9); //подчеркивание
pinMode(PA6, INPUT_ANALOG); //выбираем канал АЦП6
// устанавливаем порты управления
GPIOB->regs->CRH = 0x6600; //PB11,PB10,CNF[1:0]= 01-вых.с откр.исток.MODE[1:0]= 10, f-инхр.порта 2Мгц.
GPIOB->regs->ODR |= (1 << 11); // ВКЛючить Акк уст.лог.1(n-chan)
GPIOB->regs->ODR &= ~(1 << 10); //лог.0 на вых.PB10-ВКЛ подсветки(P-chan)
GPIOC->regs->CRH = (0x800000); // Pc13 CNF[1:0]= 10,MODE[1:0]= 00
GPIOC->regs->ODR |= (1 << 13); // с подтяжкой к +
m_s_markers(); // черточки
hh_Date(); // рим цифры
targetTime = millis() + 498; //секунда
}
void loop() {
rtc.refresh(); // включить загрузку с rtc
if (targetTime < millis()) {
hh = rtc.hour(); // грузим из rtc в рег-ы
mm = rtc.minute();
ss = rtc.second();
Week = rtc.dayOfWeek();
tft.setTextColor(ILI9341_BLACK);
targetTime = millis() + 498; //заполнение секунды
ss++;
if (ss == 60) {
ss = 0;
mm++;
if (mm > 59) {
mm = 0;
hh++;
if (hh > 23) {
hh = 0;
Week++;
}
}
}
sdeg = ss * 6;
mdeg = mm * 6 + sdeg * 0.01666667;
hdeg = hh * 30 + mdeg * 0.0833333;
hx = cos((hdeg - 90) * 0.0174532925);
hy = sin((hdeg - 90) * 0.0174532925);
mx = cos((mdeg - 90) * 0.0174532925);
my = sin((mdeg - 90) * 0.0174532925);
sx = cos((sdeg - 90) * 0.0174532925);
sy = sin((sdeg - 90) * 0.0174532925);
if (ss == 0 || initial) {
initial = 0;
tft.drawLine(ohx, ohy, 120, 121, 0xff78); //стирание старых показаний графики
ohx = hx * 62 + 121;
ohy = hy * 62 + 121;
tft.drawLine(omx, omy, 120, 121, 0xff78); //стирание циферблата
omx = mx * 84 + 120;
omy = my * 84 + 120;
}
tft.drawLine(osx, osy, 120, 120, 0xff78); //стирание старых показаний графики
osx = sx * 90 + 120;
osy = sy * 90 + 120;
tft.drawLine(osx, osy, 120, 120, ILI9341_CYAN); //вывод новых оказаний графики
tft.drawLine(ohx, ohy, 120, 121, ILI9341_BLACK);
tft.drawLine(omx, omy, 120, 121, ILI9341_BLACK);
tft.drawLine(osx, osy, 120, 120, ILI9341_CYAN);
tft.fillCircle(120, 121, 3, ILI9341_CYAN);
if (ss == 0 || www == 1) { //каждую минуту
www = 0;
tft.fillRect(245, 55, 30, 140, ILI9341_WHITE);
tft.setTextColor(ILI9341_BLACK); //?
tft.drawNumber(hh / 10, 245, 55, 4);
tft.drawNumber(hh % 10, 260, 55, 4);
tft.drawCentreString(":", 260, 84, 4);
tft.drawNumber(mm / 10, 245, 113, 4);
tft.drawNumber(mm % 10, 260, 113, 4);
tft.drawCentreString(":", 260, 142, 4);
tft.fillRect(1, 220, 40, 19, ILI9341_WHITE); //температура
tft.drawNumber(rtc.temp() / 100, 6, 220, 2); ///
tft.drawCentreString(" C", 25, 219, 2); //
int value = 0; //
value = analogRead(PA6); //читаем данные АЦП
float val = value * (5.50 / 4095.00); //
if (val > 3.60) tft.setTextColor(ILI9341_RED); //по необходимости
else if (val < 3.60 && val > 3.50) tft.setTextColor(ILI9341_NAVY); //
else if (val < 3.50) tft.setTextColor(ILI9341_DARKCYAN); //
char c1[] = { 0 }; //создаем масив
dtostrf(val, 5, 2, c1); //конвертируем float -> char
tft.fillRect( 1, 3, 45, 19, ILI9341_WHITE); //clear место на экране
tft.drawCentreString( c1, 5, 3, 2); // выводим
tft.drawCentreString("V", 40, 3, 2); //

if (val > 4.18) (GPIOB->regs->ODR &= ~(1 << 11)) ; //ВЫКЛючить заряд Акк- заряжен ~80%.
else if (val < 3.63) (GPIOB->regs->ODR |= (1 << 11)); //ВКЛючить возможность заряда Акк
}
if (hh == 0 && mm == 0 && ss < 2 || vvv == 1) { //каждые сутки
vvv = 0;
tft.fillRect(253, 10, 55, 25, ILI9341_WHITE);
if (Week > 7) Week = 1;
switch (Week) {
case 1:
tft.setTextColor(ILI9341_BLACK); //белый
tft.drawCentreString("Mon", 280, 12, 4); break;
case 2:
tft.setTextColor(ILI9341_BLACK);
tft.drawCentreString("Tue", 280, 12, 4); break;
case 3:
tft.setTextColor(ILI9341_BLACK);
tft.drawCentreString("Wed", 280, 12, 4); break;
case 4:
tft.setTextColor(ILI9341_BLACK);
tft.drawCentreString("Thu", 280, 12, 4); break;
case 5:
tft.setTextColor(ILI9341_BLACK);
tft.drawCentreString("Fri", 280, 12, 4); break;
case 6:
tft.setTextColor(ILI9341_RED); //голубой
tft.drawCentreString("Sat", 280, 12, 4); break;
case 7:
tft.setTextColor(ILI9341_DARKCYAN ); //рыжий
tft.drawCentreString("Sun", 280, 12, 4); break;
}
calendar();
}
tft.fillRect(245, 170, 30, 25, ILI9341_WHITE); //sec
tft.drawNumber(ss / 10, 245, 173, 4);
tft.drawNumber(ss % 10, 260, 173, 4);
tft.setTextColor(ILI9341_NAVY); //восстанов римскихж
switch (ss) {
case 16:
tft.drawCentreString("III", 210, 112, 2);
LEDPower(); break;
case 31:
tft.drawCentreString("VI", 122, 200, 2);
LEDPower(); break;
case 46:
tft.drawCentreString("IX", 32, 112, 2);
LEDPower(); break;
case 1:
tft.drawCentreString("XII", 120, 25, 2);
LEDPower(); break;
}
}
asm("wfi"); //остановка Loop
}
void LEDPower() {
if ((GPIOC->regs->IDR &(1 << 13)) == 0) //{счит. уровня с рег.датчика
GPIOB->regs->ODR &= ~(1 << 10); // ВКЛ подсветка if PC13= 0
else GPIOB->regs->ODR |= (1 << 10); //{=1 ВЫКЛ подсветка if PC13=1
}
void m_s_markers(){
for (int i = 0; i < 360; i += 30) {
sx = cos((i - 90) * 0.0174532925);
sy = sin((i - 90) * 0.0174532925);
x00 = sx * 113 + 120;
y00 = sy * 113 + 120;
x11 = sx * 100 + 120;
y11 = sy * 100 + 120;
tft.drawLine(x00, y00, x11, y11, ILI9341_BLACK); //
}
for (int i = 0; i < 360; i += 6) {
sx = cos((i - 90) * 0.0174532925);
sy = sin((i - 90) * 0.0174532925);
x00 = sx * 102 + 120;
y00 = sy * 102 + 120;
tft.drawPixel(x00, y00, ILI9341_BLACK);
}
}
void hh_Date(){
tft.setTextColor(ILI9341_NAVY);
tft.drawCentreString("XII", 120, 25, 2);
tft.drawCentreString("IX", 32, 112, 2);
tft.drawCentreString("VI", 122, 200, 2);
tft.drawCentreString("III", 210, 112, 2);
tft.fillCircle(120, 121, 3, ILI9341_BLACK);
}
void calendar(){
tft.fillRect(288, 55, 30, 140, ILI9341_WHITE); //чистим место календарь
tft.setTextColor(ILI9341_BLACK); //
tft.drawNumber(rtc.day() / 10, 288, 55, 4);
tft.drawNumber(rtc.day() % 10, 304, 55, 4);
tft.drawCentreString("/", 304, 84, 4);
tft.drawNumber(rtc.month() / 10, 288, 113, 4);
tft.drawNumber(rtc.month() % 10, 304, 113, 4);
tft.drawCentreString("/", 304, 142, 4);
// tft.fillRect(288, 173, 30, 25, ILI9341_BLACK);
tft.drawNumber(rtc.year() / 10, 288, 173, 4);
tft.drawNumber(rtc.year() % 10, 304, 173, 4);
}
1793097244964.png