Вопрос по ШИМу в проекте часов

Тема в разделе "Arduino & Shields", создана пользователем Eragon, 9 сен 2017.

  1. Eragon

    Eragon Нерд

    Всем доброго времени суток! Написал вот такой код, выводящий на 4-разрядный семисегментный индикатор время и температуру.
    Код (C++):
    #include <Wire.h>
    #include <BMP085.h>
    #include <DS3231.h>
    #define latchPin 3 // 12 защёлка
    #define clockPin 2 // 11 тактование
    #define dataPin  4 // 14 данные

    DS3231 clock;          
    RTCDateTime DateTime;
    BMP085 dps = BMP085();


    unsigned long gen1P = 0UL;
    unsigned long gen2P = 0UL;

    bool dot=0; // включение/отключение двоеточия
    bool switching=0; // переключение между температурой и временем

    long Temperature = 0; // хранение температуры

    byte Numbers_array[12] =
    {
      0x3F, 0x6, 0x5B, // 0, 1, 2
      0x4F, 0x66, 0x6D,// 3, 4, 5
      0x7D, 0x7, 0x7F, // 6, 7, 8
      0x6F, 0x39,0x63, // 9,C,*
    };

    byte clipboard[2][4]; // буфер обмена
    void setup()
    {
      //Serial.begin(9600);
      DDRD = B11111100; // 2, 3, 4, 5, 6, 7 - выход
      DDRB = B00001111; // 8,9,10,11 - выход
      PORTB |= B00001111; // выключаем все разряды
      clock.begin();
      Wire.begin();
      delay(1000);
      dps.init();  
      gen1P, gen2P = millis();
    }

    void loop()
    {
      DateTime = clock.getDateTime();
      uint8_t hour = uint8_t(DateTime.hour);
      uint8_t minute = uint8_t(DateTime.minute);
      uint8_t second = uint8_t(DateTime.second);
      blinkDot();
      getTime(hour, minute);
      getTemp(second);
      display_Of_data();
      if((second>=0 && second<=1) || (second>=6 && second<=7)) PORTB |= B00001111;
      if (second>=1 && second<=6 ) switching = 1; // с 1 по 6 секунду отображаем температуру
       else  switching = 0; // отображаем время
    }

    bool isTimer(unsigned long startTime, unsigned long period )
    {
       unsigned long currentTime;
       currentTime = millis();
       if (currentTime >= startTime) return (currentTime >= (startTime + period));
       else return (currentTime >= (4294967295 - startTime + period));
    }

    void display_Of_data()
    {
      static byte n = 0;
      if (isTimer (gen1P, 1))
       {
         gen1P = millis();
         n++;
         if (n >= 4)n = 0;
         PORTD &= ~(1<<PD3); // защёлка LOW
         shiftOut(dataPin, clockPin, MSBFIRST, clipboard[switching][n]); // передаём последовательность в регистр
         PORTD |= (1<<PD3); // защёлкиваем регистр
         PORTB |= B00001111; // выключаем все разряды
         PORTB &= ~(1<<n); // последовательно включаем каждый разряд
       }
    }

    void getTime(uint8_t hr, uint8_t mn)
    {
       clipboard[0][0] = Numbers_array[hr/10];
       clipboard[0][1] = Numbers_array[hr%10];
       clipboard[0][2] = Numbers_array[mn/10];
       clipboard[0][3] = Numbers_array[mn%10];
    }

    void getTemp(uint8_t sec)
    {
       if(sec==1) dps.getTemperature(&Temperature); // каждую минуту считываем показания с датчика
       uint8_t i = Temperature/10;
       clipboard[1][0] = Numbers_array[i/10];
       clipboard[1][1] = Numbers_array[i%10];
       clipboard[1][2] = Numbers_array[11];
       clipboard[1][3] = Numbers_array[10];
    }
     
     
    void blinkDot()
    {
      if(!switching)
       {
         if (isTimer (gen2P, 1000))
          {
            gen2P = millis();
            dot=!dot;
          }
            if (dot) PORTD |= (1<<PD5);
             else  PORTD &= ~(1<<PD5);
       }
       else PORTD &= ~(1<<PD5);
    }
    Код работает без нареканий)) Этого мне показалось мало и решил я добавить «плюшек» в виде возможности регулирования яркости разрядов и плавного включения/выключения двоеточия. Начал с двоеточия, и тут же начались проблемы.
    Дополнил существующую функцию управления двоеточием циклом for(пока только одним, в момент включения)
    Код (C++):
    void blinkDot()
    {
      if(!switching)
       {
         if (isTimer (gen2P, 1000))
          {
            gen2P = millis();
            dot=!dot;
          }
            if (dot) //PORTD |= (1<<PD5);
             {
               for (byte i=0; i<=255; i++)
                {
                  analogWrite(5,i);
                  delay(10);
                }
             }
           
             else  PORTD &= ~(1<<PD5);
       }
       else PORTD &= ~(1<<PD5);
    }
    Но после такого индикатор начал вести себя как одержимый дьяволом! То включится один сегмент, то второй… один горит ярче другой тускнее. Короче полная вакханалия)) Ради интереса попробовал сделать вот так
    Код (C++):
    void blinkDot()
    {
      if(!switching)
       {
         if (isTimer (gen2P, 1000))
          {
            gen2P = millis();
            dot=!dot;
          }
            if (dot) //PORTD |= (1<<PD5);
             {
               analogWrite(5,50);
             }
           
             else  PORTD &= ~(1<<PD5);
       }
       else PORTD &= ~(1<<PD5);
    }
    Теперь двоеточие горит постоянно (не отключается) с заданной яркостью.
    Подскажите пожалуйста, как можно добиться требуемого результата, сам я в ступоре ((
     
  2. NikitOS

    NikitOS Король шутов Администратор

    Полный код
     
  3. Eragon

    Eragon Нерд

    Полный код под первым спойлером)
     
  4. rkit

    rkit Гуру

    Либо работайте с функциями ардуино, либо с регистрами. А вперемешку будет работать абы как.
     
    NikitOS нравится это.
  5. Eragon

    Eragon Нерд

    Как уже писал выше, код работает отлично и в таком виде. Отказаться полностью от ардуиновских функций не могу, тупо из-за отсутствия достаточного количества знаний по данной теме.Вопрос в другом, почему когда я пытаюсь сделать ШИМ на одном 5 пине перестает работать вся конструкция...?
     
  6. qwone

    qwone Гик

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