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

Всем доброго времени суток. Сегодня включил инет за 2 месяца первый раз и увидел отчет, переезд Ардуино на новый сервер – с новосельицем. Решил тоже предложить свой вклад в это дело своей свистулькой. Все что я задумывал реализовал: уменьшил потребление до 20мА с включенной подсветкой (без нее 14мА) – уменьшил рабочую частоту до 24мГц, питание STMдо 2,65 В. При емкости Акк 3800мА/ч хватает на 7-8 суток при условии: индикация активна только ночью. Решил вопрос с зарядкой – контролируя напряжение на Акк во время заряда и его отключение по достижении заданного напряжения и включения зарядки при падении напряжения до заданного уровня. Все это можно менять в программе. Это дало возможность продлить жизнь Акк. питать изделие от сети, (по возможности) через источник на контроллере Tny264, небольшим током 100-150мА. Он изначально создан работать день и ночь. Применил ЖК технологии IPS, это позволило улучшить контрастность на порядок и больше, угол обзора увеличился фантастически. Это позволило слегка подкрасить голубым циферблат круга, повеселей смотреться стало. Изменил положение экрана с 0 на 3. Улучшилась четкость прорисовки круга, диоды подсветки стоят со стороны шлейфа. В оригинале круг также был со стороны шлейфа, и это было не очень красиво, (положение 2) но экономически верно. Пока выкладываю только фото свистульки и скетч. Если будет интересно, выложу схему попозже к осени, дача все время отнимает. Хотя электронщику и по скетчу многое понятно. В качестве ключа отключающего минусовой провод зарядки от земли, использовал сборку 8205, 2шт в параллель, кварц 4мГц. В качестве источника питания применил любимую 63000, верхнее плечо делителя (ADJ) установить резистор 43кОм, нижнюю часть блочка, сточить до глины 01мкФ. Датчик положения свистульки, контролируется каждые 15 сек. и ВКЛ/ОТКЛ подсветку. А замер напряжения на Акк, происходит раз в минуту, и соответствующее действие. Цвета показаний напряжения: голубой - Акк заряжен, желтый - режим заряда подключен, оранжевый - во избежание выключения Акк, необходимо принять меры к зарядке. Часы при этом не остановятся, просто исчезнет подсветка.

// Библиотека Arduino_STM32-master.zip
#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, 0x39e7); // прямоугольник
  tft.fillCircle(120, 120, 116, 0xb4b4);         //круг
  tft.fillCircle(120, 120, 114, 0xff78); //стирание внутри круга
  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);   // с подтяжкой к +

   // минуты черточки
  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;
    // Draw minute markers
    tft.drawPixel(x00, y00, ILI9341_BLACK); //
  }
    //tft.setTextColor(0xcdcd);
    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); 

  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(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(150, 280, 30, 25, ILI9341_BLACK);
            tft.drawNumber(rtc.year() / 10, 288, 173, 4);
            tft.drawNumber(rtc.year() % 10, 304, 173, 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);  //
            // tft.fillRect( 0, 3, 45, 19, ILI9341_WHITE); //clear место на экране
            if (val > 3.65) tft.setTextColor(ILI9341_RED);  //по необходимости
                else if (val < 3.65 && val > 3.56) tft.setTextColor(ILI9341_NAVY);  //
                  else if (val < 3.56) 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", 39, 3, 2);  //
                     
                  if (val > 4.18) (GPIOB->regs->ODR &= ~(1 << 11)) ; //ВЫКЛючить заряд Акк- заряжен ~80%.
                  else if (val < 3.62) (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;
                }
              }   
                    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
                            } 

:grinning_face:


вот ты змей, к этому вопросов значит нет -

Serial.begin(4800);

А какие могут быть к этому вопросы? Озвучьте, пожалуйста.

а если расположить обычный 4.0 TFT SPI вертикально, то угол обзора будет без малого180 градусов ))) (без потери контрастности)

дошло наконец?
А сколько воплей-то было…

v.bay а что будет если температура будет отрицательной ? работать будет ?
а еще у вас нет мерцания экрана в начале каждой минуты ?)))
char c1[] = { 0 }; // то что вы тоже пытаетесь в пихнуть не в пихуемое, мне определенно нравится)))

в любом случае спс за код, если хотите обратите на эти моменты внимание

а еще не уверен что автор тестировал сборку долгое время…
но может мне и кажется)))

Криво, но будет (на первый взгляд).

а мне кажется будет показывать не понятно что))) но показывать будет!)))

Почему? Понятно что – неправильно округлённый до целого градуса результат вызова метода uRTCLib::temp(). Чего Вам там непонятного-то?

может и будет работать… там еще в библиотеку вникать надо… скорее всего не покажет,(из за чтения от -127 до 128 как 0 - 255) точнее покажет но температура 655.0 + но это все же не точно))) надо собирать и смотреть, а температура сейчас плюсовая)))

Вот, Вы сначала посмотрите, а потом уж “бредьте” :slight_smile:

Ну, думаю, началось. Я . целую зиму пытался понять, что же это за ардуино. К сожалению, много за машиной не могу сидеть, единственный глаз не позволяют 2-3 часа и все. И все же немного прояснилось. По поводу скорости шины 4800, это строка вообще не работает. Можете ее закоментить ничего не измениться. Скорость так и останется 11800. Она, как я понял, определена в Arduino_STM32-master. Т.е. в системе, блоках кодов, которые описывают, привязывают адреса внутреннего фарша, понятные компиллеру, к идентификаторам понятным программисту. Там же и созданы, откомпилированы библиотеки и они также не изменяются. Я не могу это утверждать, пусть старшие товарищи меня поправят. Далее по температуре = -50С. Сам не проверял, поэтому ничего утверждать не могу, но наверняка, уже при -10С , Акк загнется. МММ уважаемый, не понял, в чем отличие прежнего блока кода от теперешнего? то что поменял маску с 1 на 0. Это связано с тем что, несколько инцидентов наблюдал, береш часы в руки, а они сбоят, или вообще улетают в некуда. Я не ставил подтяжку по входу. Пришел к выводу, что статика, корпус из ПВХ, при обработке стружка как на магните.. После танцев с бубном, поставил глину 047, прямо на выводы датчика, а за одно и уровень сменил.Но суть работы не изменилась. Еще, раньше этот код был в вертушке и каждый оборот проверялся на фальш/истина. Теперь это функция и вызывается по необходимости. Но этого ты не заметил.

Меня больше интересовала функция // asm(“wfi”); //остановка Loop, а вернее всего макрос. Кто, что думает по поводу его. Здесь есть известный и очень разбирающийся Александр, вот это ему под силу. Я, много почерпнул читая его статьи. Но он вряд ли посещает эту ветку.

так лениво, по этому и спросил тут

я лишь уверен на 80% что не будет работать

Ну, пребывайте в такой уверенности.

Как говаривал поэт:

Да я вижу, что вы не понимаете. Как 2 года не понимали, так и сейчас. Так что просто поверьте - разница между 1 и 0 в данном случае принципиальна.

Это кто такой? Уж не Гайвер ли? Если да, то вы ошиблись дверью. Гайвера отсюда выгнали в начале его карьеры и он к нам не заходит. У него свой форум.

Енто про админа поди, с уроками :slightly_smiling_face:

ну если вы говорите наверное оно так… наверное есть этому причины…
а так очень вероятно что при -1 у него уже выведет не понятно что…
будет у него время, может проверит)))

МММ, я последний раз, попытаюсь обьяснить, как это работает. Выражение GPIOC->regs->IDR &(1 << 13) означает: что из порта С пин 13 считывается значение датчика 0 или 1, далее эта информация накрывается маской “1” по логике “И” потому что амперсанд. Пусть с пин 13 считался '“1”. операция 1 & 1 = 1 надеюсь понимаешь. Затем вторая часть функции 1 << 13)) == 0) результирующая “1” сравнивается с “0” - результат фальш - значит работаем по строке else GPIOB->regs->ODR |= (1 << 10); //{=1 ВЫКЛ подсветка if PC13=1. в выходной регистр В пин 10, заносим 1, В блоке Set этот регистр настроен как выход с открытым стоком, т.е. работает только нижний n- channel MOSFET полумоста, если на стоке 1, то транзистор в режиме отсечки (висит исток в воздухе). Он соединен с затвором ключа включающего подсветку, p-channel и его затвор соединен через R=47k, с + Акк и на нем от 3 до 4,1В и ключ тоже переходит в режим отсечки, в каком бы состоянии не находился. Если с пин 13 рег. B считывается “0” то 0 & 1 = 0. Сравниваем с “0” совпадает, работаем по строчке GPIOB->regs->ODR &= ~(1 << 10); // ВКЛ подсветка if PC13= 0 т.е. рег.B пин 10 переходит в 0, нижний ключ переходит в насыщение, открывается, затвор р-канального ключа соединяясь с GND. Он тоже переходит в насыщение. По моему понятно даже школьнику из начальной школы. как видишь сравнение с 0 или 1 “'это главное” это просто ваш бред, и напрасно влезли снова в эту свадьбу. Как говорят: молчи громче и будешь белым и пушистым.

По поводу изгнали кого то там, я не знаю тот это или нет, но если мы оба об одном человеке, то вы сделали напрасно это. Знаю только что он подписывается Александр на латинице. С нового года он стал публиковать статьи в журнале, я его по электронке получаю, просто здорово. Он так все разжевывает, что даже мне все понятно. Но мне нравятся не только он, есть DiMax, тоже классный программист и, по моему, он и электронщик. Но я не всегда понимаю его сленг.