Помгите пожалуйста запустить дисплей со сдвиговым регистром shd0032

Тема в разделе "Arduino & Shields", создана пользователем gifrom, 13 окт 2015.

  1. gifrom

    gifrom Нуб

    имеется дисплей shd0032 (описание http://www.chipdip.ru/product/shd0032g/ )и arduino Mega2560. С программированием и электроникой слабо знаком, это мои первые попытки. Хочу для начала научиться выводить на дисплей нужные символы. Но за последние несколько недель так ничего и не получилось. Пытался использовать вот такой код с этого примера http://ardushop.ru/kak-podklyuchit-sdvigoviy-registr-k-arduino/
    , но адаптировать под мой дисплей его не вышло. Если смотреть по диаграмме в описании, то начинает загораться S27, затем S11, если S24, то следом S8 и т.д. В примере 16 бит, а у меня ведь 32, непонятно как эти строки под 32бита переделать
    // 16 бит необходимо разделить на два байта:
    // И записать каждый байт в соответствующий регистр
    byte register1 = highByte(bitsToSend);
    byte register2 = lowByte(bitsToSend);

    // Последовательная передача данных на пин DS
    shiftOut(DS, SH_CP, MSBFIRST, register2);
    shiftOut(DS, SH_CP, MSBFIRST, register1);
     
  2. Megakoteyka

    Megakoteyka Оракул Модератор

    http://lib.chipdip.ru/042/DOC001042788.pdf Читали?
    Вам нужно каждой выводимой цифре поставить в соответствие код.
    Код (C++):
    byte digits[10] = {
      00000000b,
      00000110b,
      ...
    };
    И уже этот код выталкивать.
    Допустим, есть число x = 1234. Сперва нужно его разделить на 4 отдельных десятичных числа, для этого есть операция % (остаток от деления).
    int d1 = x % 10 // 4
    int d2 = x % 100 // 3
    ...

    Чтобы получить из массива код нужной цифры, нужно эту цифру использовать в качестве индекса массива.
    Код (C++):
    byte code = digits[d1];
    А потом разряды полученных четырех кодов нужно последовательно запихать в регистр.
    Понимаете принцип?
     
    Последнее редактирование: 14 окт 2015
  3. gifrom

    gifrom Нуб

    Код (C++):
    unsigned char chars[BYTES] ={0,0,0,0}; //Массив разрядов
    #define SYMBOLS_ARRAY_LENGTH 12 //Длина массива символов
    unsigned char Symbols[SYMBOLS_ARRAY_LENGTH]={ // Массив символов индикатора
      252, // 0
      96, // 1
      218, // 2
      242, // 3
      102, // 4
      182, // 5
      190, // 6
      224, // 7
      254, // 8
      246, // 9
      1, // .
      2 // -
    };
    Что сегменты сопоставляются символам я понял, думал что-то типа этого сделать, но у меня не получается зажечь те сегменты которые хочу. Если пытаешься зажечь один сегмент запросто могут два загореться и при том не в том разряде и т.д. Для простоты я хотел сперва не числа выводить а зажигать те сегменты которые хочу. Сегменты - это ведь те же светодиоды из примера, который я выкладывал выше
     
    Последнее редактирование: 15 окт 2015
  4. Megakoteyka

    Megakoteyka Оракул Модератор

    Если во все сегменты записать единицы, то все должны загореться. Так происходит? Если нет, то проблема в формировании сигналов.
    Попробуйте сформировать сигналы не с помощью библиотеки, а самостоятельно.
     
  5. gifrom

    gifrom Нуб

    Все загорелись. Наверняка что-то до ужаса элементарное не так делаю или немогу понять
     
  6. Megakoteyka

    Megakoteyka Оракул Модератор

    Давайте посмотрим на текущую версию кода.
     
  7. gifrom

    gifrom Нуб

    Код (C++):
    //Пин SH_CP
    int SH_CP = 9; //Тактовые импульсы
    //Пин ST_CP
    int ST_CP = 8; //Импульс записи в выходной регистр
    //Пин DS
    int DS = 10; //Вход последовательных данных
    int E = 7; //Разрешающий сигнал включения светодиодов
    void setup() {
       // Настраиваем выходы SH_CP, ST_CP, DS
       pinMode(SH_CP, OUTPUT);
       pinMode(ST_CP, OUTPUT);
       pinMode(DS, OUTPUT);
       pinMode(E, OUTPUT);
    }
    void loop() {
      // Цикл обхода 32 светодиодов
      for (int i = 0; i < 32; i++) {
        // Запись в регистр
        registerWrite(i, HIGH);
        // Задержка 0.5 с.
        delay(500);
        // Отключение предыдущего светодиода
        if (i > 0) {
           registerWrite(i - 1, LOW);
        }
        // Отключение последнего светодиода
        // ("предыдущий" для первого"
        else {
           registerWrite(31, LOW);
        }
      }
    }
    // Метод для отсылки данных в регистры
    void registerWrite(int num, int state) {
    // Для хранения 32 битов используется unsigned int
    unsigned int bitsToSend = 0;
    // 0b0000000000000000000000000000000
    // Инициализируем начало приема данных
    digitalWrite(ST_CP, LOW);
    // Устанавливаем 1 в соответствующий бит
    bitWrite(bitsToSend, num, state);
    // 32 бит необходимо разделить на четыре байта:
    // И записать каждый байт в соответствующий регистр
    byte register1 = highByte(bitsToSend);
    byte register2 = lowByte(bitsToSend);
    byte register3 = highByte(bitsToSend);
    byte register4 = lowByte(bitsToSend);
    // Последовательная передача данных на пин DS
    shiftOut(DS, SH_CP, MSBFIRST, register2);
    shiftOut(DS, SH_CP, MSBFIRST, register1);
    shiftOut(DS, SH_CP, MSBFIRST, register4);
    shiftOut(DS, SH_CP, MSBFIRST, register3);
    // Инициализируем окончание передачи данных.
    // Регистры подадут напряжение на указанные выходы
    digitalWrite(ST_CP, HIGH);
    delay(5000);
    }
     
  8. Megakoteyka

    Megakoteyka Оракул Модератор

    Ну так вот. Для начала вместо unsigned int (16 бит) будем использовать unsigned long (32 бита). Потом смотрим на картинку: SHD0032G.PNG
    Тут очень хорошо видно последовательность формирования сигналов для регистра.
    Перед записью в регистр сигнал LATCH должен стоять в нуле, а ENABLE в единице.

    Попробуйте вот такой код, он должен на все сегменты выводить счетчик от 0 до 9:
    Код (C++):

    #define PIN_E 7 // enable
    #define PIN_L 8 // latch
    #define PIN_C 9 // clock
    #define PIN_D 10// data

    #define SYMBOLS_ARRAY_LENGTH 12 //Длина массива символов
    unsigned char Symbols[SYMBOLS_ARRAY_LENGTH]={ // Массив символов индикатора
      252, // 0
      96,  // 1
      218, // 2
      242, // 3
      102, // 4
      182, // 5
      190, // 6
      224, // 7
      254, // 8
      246, // 9
      1,   // .
      2    // -
    };

    void setup()
    {
      pinMode(PIN_E, OUTPUT);
      pinMode(PIN_L, OUTPUT);
      pinMode(PIN_C, OUTPUT);
      pinMode(PIN_D, OUTPUT);
      digitalWrite(PIN_L, LOW); // сигнал защелки вниз
      digitalWrite(PIN_E, LOW); // включаем сегменты
      digitalWrite(PIN_C, LOW); // тактовый сигнал вниз
    }

    void loop()
    {
      // показываем на дисплее счетчик
      for(int i = 0; i < 10; i++)
      {
        unsigned long value = GetValue(i, i, i, i);
        WriteReg(value);
      }
    }

    // пишем в регистр сразу 32 разряда
    void WriteReg(unsigned long value)
    {
      for(int i = 0; i < 32; i++)      // перебираем разряды
      {
        int data = (value >> i) & 0x1; // берем очередной разряд
        digitalWrite(PIN_C, HIGH);     // тактовый сигнал вверх
        digitalWrite(PIN_D, data);     // устанавливаем сигнал данных
        digitalWrite(PIN_C, LOW);      // тактовый сигнал вниз
      }
      digitalWrite(PIN_L, HIGH);       // защелку вверх
      digitalWrite(PIN_L, LOW);        // защелку вниз
    }

    // передаем числа, получаем на выходе код для их отображения
    unsigned long GetValue(byte s1, byte s2, byte s3, byte s4)
    {
      return
        (unsigned long)(Symbols[s1] << 0) |
        (unsigned long)(Symbols[s2] << 8) |
        (unsigned long)(Symbols[s3] << 16) |
        (unsigned long)(Symbols[s4] << 24);
    }
     
    Последнее редактирование: 15 окт 2015
  9. Megakoteyka

    Megakoteyka Оракул Модератор

    Попробуйте и расскажите, что из этого вышло.
     
  10. gifrom

    gifrom Нуб

    Вышло статическое изображение " 8.8.0.0."
     
  11. Megakoteyka

    Megakoteyka Оракул Модератор

    Попробуйте задержек навтыкать.
     
  12. gifrom

    gifrom Нуб

    Вставил задержку после
    Но та же проблема что и у меня была, цифры выводятся попарно и при этом иероглифами

     
  13. Megakoteyka

    Megakoteyka Оракул Модератор

    Вставьте задержку несколько мкс между обращениями к пинам.
     
  14. gifrom

    gifrom Нуб

    Теперь только после выходных проверить смогу. Плата на работе осталась.
     
  15. gifrom

    gifrom Нуб

    Чуть время выполнения увеличивается, но изображения все как на видео. Нашёл код с которым дисплей нормально работает, но не могу управление дисплеем вычленить, для меня там слишком сложно
    Код (C++):
    // Детектор нуля идет на 0-е прерывание (pin 2)
    // Прерывание от валкодера (B pin валкодера) на 1 прерывание (pin 3)
    // Второй пин валкодера (A) подключен к pin 4
    // Управление симистором для фазового регулятора реализовано через pin 5
    // Нажатие кнопки валкодера (S) подключено к pin 6 (низкий уровень при нажатии)
    // Вход ENABLE SND0032 дисплея подключен на pin 7 (активный уровень низкий)
    // Вход LATCH SND0032 дисплея подключен на pin 8
    // Вход CLOCK SND0032 дисплея подключен на pin 9
    // Вход SERIAL-IN SND0032 дисплея подключен на pin 10
    // АЦП от трансформатора подключен на pin А0

    #include <EEPROM.h>

    #define DEBUG  // Режим отладки
    #define Serial1 Serial
    char m_version[]="v0.2";

    #ifndef cbi
    #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
    #endif

    #ifndef sbi
    #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
    #endif

    #define MAX_INDEX_INPUT 255  // Выравниваем по границе байта, тк увеличиваем считывание анального входа. (было 70)
    unsigned int data_adc[MAX_INDEX_INPUT+1];
    unsigned int tic_adc[MAX_INDEX_INPUT+1];
    volatile uint16_t deltaArray[16];      // массив для вычисления средней дельты отставания реального нуля напряжения
    volatile uint8_t  deltaIndex=0;        // текущий индекс массива средней дельты отставания реального нуля напряжения
    volatile uint16_t deltaZ;              // Дельта в тиках прерывания нуля и начала возрастания напряжения.
    volatile unsigned int VolVal;          // Считанное с АЦП значение напряжения
    volatile int MaxVolts, MaxVoltsOut;    // Пиковое напряжение
    volatile unsigned char StateVolts=0;   // Состояние вычисление среднеквадратичного
    //0 - надо вычислить - запускается старт, 1 - вычисляется, 2 - вычислено, 3 - идет обработка рузультата
    // 4 - Результат обработан, можно вычислять следующий
    unsigned int ticIndex=0;            // Текущий индекс массива дла расчета среднеквадратичного

    // Serial
    #define RX_BUFFER_SIZE 70
    #define TX_BUFFER_SIZE 70
    char tbuffer[TX_BUFFER_SIZE];
    char FlUsart=0; // Признак того, что надо вывести информацию на СОМ-порт
    char RBuffer[RX_BUFFER_SIZE];
    int PosRx = 0;


    #define PIN_TRIAC  5    // Управление симистором реализовано через PIN 5
    #define PIN_VOLC_B 4    // Определение пина B валкодера
    #define PIN_VOLC_S 6    // Определение пина S валкодера

    #define D_IN         10 //Определение бита IN
    #define D_CLOCK      9 //Определение бита CLOCK
    #define D_LATCH      8 //Определение бита LATCH
    #define D_ENABLE     7 //Определение бита ENABLE
    #define BYTES 4 //Количество разрядов
    unsigned char chars[BYTES] ={0,0,0,0}; //Массив разрядов
    #define SYMBOLS_ARRAY_LENGTH 12 //Длина массива символов
    unsigned char Symbols[SYMBOLS_ARRAY_LENGTH]={ // Массив символов индикатора
        252, // 0
        96, // 1
        218, // 2
        242, // 3
        102, // 4
        182, // 5
        190, // 6
        224, // 7
        254, // 8
        246, // 9
        1, // .
        2 // -
    };
    char CountKeys;       // Переменная для защиты от дребезга
    char reDisplayData;   // Флаг обновления информации на дисплее/

    // Переменные второго счетчика
    volatile int Counter1=0;
    volatile unsigned long Sec, LSec;   // Текущее состояние времени с начала процесса (не беззнаковое, потому, что есть проверки на <0);
    volatile unsigned long pChangeSec;  // Временная отметка последних изменений в параметрах
    char paramChanged = 0;              // Флаг, что требуется перезаписать eeprom



    unsigned long TekPower;
    unsigned int OpenTriacArray[16];
    unsigned int OpenTriacIndex=0;
    unsigned int TimeOpenTriac=0;
    unsigned int TicSqNapr; // Количество тиков таймера от нуля до нуля, на основании которого рассчитано среднеквадратичное

    // Установка мощности
    char         PowerEnabled = 1;  // Флаг включения нагрузки
    unsigned int Power=2000;       // Номинальная мощность ТЭНов
    unsigned int UstPower=1000;     // Нужно получить мощность от регулятора (текущая мощность)

    /*
    * Режимы работы
    */

    #define MODE_UST_POWER   0               // Режим отображения/установки текущей мощности
    #define MIN_MODE         MODE_UST_POWER  // < Минимальный режим
    #define MODE_DESP_MAXU   1               // Режим отображения максимального напряжения
    #define MODE_MAX_POWER   2               // Режим отображения/установки максимальной мощности
    #define MAX_MODE         MODE_MAX_POWER  // < Максимальный режим

    unsigned char StateMachine = MIN_MODE;


    #define PR_REWRITE_EEPROM 19  // Константа, которая содержит признак необходимости перезаписи энергонезависимой памяти (1-254).
    // При запуске программы, значение 0-го байта ЕЕПРОМ сравнивается с этим знначением,
    // и если они не совпадают, тогда энергонезависимая памиять переписывается текущими значениями переменных
    // То есть для перезаписи контанты при первом запуске, ее значение надо поменять
    unsigned int ee_addr; // Переменная для хранения текущего адреса чтения/записи в EEPROM


    unsigned char eeReadChar()
    {
      unsigned char lByte = EEPROM.read(ee_addr);
      ee_addr++;
      return lByte;
    }

    void eeWriteChar(char p_value)
    {
      if (EEPROM.read(ee_addr)!=p_value)
        EEPROM.write(ee_addr, p_value);
      ee_addr++;
    }

    void eeWriteInt(int p_value)
    {
      unsigned char lByte = (unsigned char) (p_value);
      unsigned char hByte =(unsigned char) (p_value >> 8);

      // Для экономии ресурса памяти, сначала проверяем ее содержимое и запись производим только если значение отличается.
      eeWriteChar(lByte);
      eeWriteChar(hByte);
    }

    //This function will read a 2 byte integer from the eeprom at the specified address and address + 1
    unsigned int eeReadInt()
    {
      unsigned char lByte = eeReadChar();
      unsigned char hByte = eeReadChar();
      return (unsigned int) (lByte) | (unsigned int)  (hByte << 8);
    }

    // Сохранение всех полезных настроек в eeprom
    void writeEEPROM()
    {
      int i;
      ee_addr=0;
      eeWriteChar(PR_REWRITE_EEPROM);
      eeWriteInt(Power);
      eeWriteInt(UstPower);
      eeWriteChar(StateMachine);
    }

    // Чтение настроек из eeprom
    void readEEPROM()
    {
      int i;
      ee_addr=0;
      eeReadChar();  // Пропуск первого байта
      Power=eeReadInt();
      UstPower=eeReadInt();
      StateMachine = eeReadChar();
    }



    //Отправить 1 байт в SERIAL_IN дисплея
    void push_byte(unsigned char _byte)
    {
      unsigned char j;
      for(j=0;j<8;j++){
        digitalWrite(D_CLOCK, LOW);
        digitalWrite(D_IN, 1 & _byte);
        _byte = _byte>>1;
        digitalWrite(D_CLOCK, HIGH);  
      }
    }

    //Показать текущие значения в массиве разрядов
    void dispData(void)
    {
      unsigned char i;
      unsigned char dot = (Sec & 1);

      switch (StateMachine) {
      case MODE_UST_POWER:
        // Режим отображения текущей мощности
        i = UstPower/1000;
        if (i<SYMBOLS_ARRAY_LENGTH) chars[3] = Symbols[i]+dot;
        i = (UstPower%1000)/100;
        if (i<SYMBOLS_ARRAY_LENGTH) chars[2] = Symbols[i];
        i = (UstPower%100)/10;
        if (i<SYMBOLS_ARRAY_LENGTH) chars[1] = Symbols[i];
        i = (UstPower%10);
        if (i<SYMBOLS_ARRAY_LENGTH) chars[0] = Symbols[i];
        break;
     
       case MODE_DESP_MAXU:
        // Режим отображения максимального напряжения
        if (i<SYMBOLS_ARRAY_LENGTH) chars[3] = 16; // Это код символа нижнего подчеркивания.
        i = (MaxVoltsOut%1000)/100;
        if (i<SYMBOLS_ARRAY_LENGTH) chars[2] = Symbols[i];
        i = (MaxVoltsOut%100)/10;
        if (i<SYMBOLS_ARRAY_LENGTH) chars[1] = Symbols[i];
        i = (MaxVoltsOut%10);
        if (i<SYMBOLS_ARRAY_LENGTH) chars[0] = Symbols[i];
        break;
     
      case MODE_MAX_POWER:
        // Режим отображения максимальной мощности
        i = Power/1000;
        if (i<SYMBOLS_ARRAY_LENGTH) chars[3] = Symbols[i]+dot;
        i = (Power%1000)/100;
        if (i<SYMBOLS_ARRAY_LENGTH) chars[2] = Symbols[i]+dot;
        i = (Power%100)/10;
        if (i<SYMBOLS_ARRAY_LENGTH) chars[1] = Symbols[i]+dot;
        i = (Power%10);
        if (i<SYMBOLS_ARRAY_LENGTH) chars[0] = Symbols[i]+dot;
        break;
      }


      for (i=0;i<BYTES;i++) {
        push_byte(chars[i]);
      }
      digitalWrite(D_LATCH, HIGH);
      digitalWrite(D_LATCH, LOW);
      reDisplayData = 0;
    }

    void setup()
    {
      uint8_t analog_reference = DEFAULT;
      Serial1.begin(9600);

      // Настройка ADC
      sbi(ADCSRA,ADPS2);
      cbi(ADCSRA,ADPS1);
      cbi(ADCSRA,ADPS0);
      ADMUX = (analog_reference << 6) | (0 & 0x07);
      sbi(ADCSRA,ACIE);
      sbi(ADCSRA,ADSC);
      sbi(ADCSRA,ADATE);

      // Читаем ранее сохраненные значения из энергонезависимой памяти.
      if (EEPROM.read(0)!=PR_REWRITE_EEPROM) {
        writeEEPROM();
      }

      pinMode(PIN_TRIAC,OUTPUT);
      digitalWrite(PIN_TRIAC,LOW);
     
      pinMode(PIN_VOLC_B, INPUT);
      pinMode(PIN_VOLC_S, INPUT);
      digitalWrite(PIN_VOLC_S,HIGH);
     
      pinMode(D_IN, OUTPUT);
      pinMode(D_CLOCK, OUTPUT);
      pinMode(D_LATCH, OUTPUT);
      pinMode(D_ENABLE, OUTPUT);

      attachInterrupt(1, VOLC_ISR, RISING);
      attachInterrupt(0, zero_crosss_int, RISING);  // Устанавливаем на 0-е прерывание функцию (это pin 2) обработку от детектора нулюя сетевого напряжения

      // Таймер 2 используется для подсчета числа секунд и запуска расчета чреднеквадратичного напряжения на входе
      // Прерываение по этому таймеру будет вызываться 125 раз в секунду.
      TCCR2A = (1<<WGM21);    // Режим CTC (сброс по совпадению)
      TCCR2A=0x00;
      TCCR2B=0x07;       //
      TCNT2=0x00;
      OCR2A=0x7D;
      OCR2B=0x00;
      TIMSK2 = (1<<OCIE2A);   // Разрешить прерывание по совпадению


      // Таймер 1 - управление открытием симистора
      TCCR1A=0x00;
      TCCR1B=0x03;
      TCNT1H=0x00;
      TCNT1L=0x00;
      ICR1H=0x00;
      ICR1L=0x00;
      OCR1AH=0x00;
      OCR1AL=0x02;
      OCR1BH=0x00;
      OCR1BL=0x00;
      TIMSK1 = (1<<OCIE1A);   // Разрешить прерывание по совпадению

      readEEPROM();           // Читаем все из eeprom

      // Обновляем данные на дисплее и включаем его
      dispData();
      digitalWrite(D_ENABLE, LOW);
     
      StateVolts=0;
      sei();                 // Глобально разрешить прерывания
    }
     
     
    Последнее редактирование: 19 окт 2015
  16. gifrom

    gifrom Нуб

    и продолжение
    Код (C++):



    /**
    * Функция обработки прерывания от линии B валкодера
    * В зависимости от состояния линии A валкодера, определяем направление вращения.
    */

    void VOLC_ISR()
    {
      char v = digitalRead(PIN_VOLC_B);
      switch (StateMachine) {
      case MODE_UST_POWER:
        // Установка текущей мощности
        if (v) {
          UstPower++;
          if (UstPower > Power) UstPower=Power;
        } else {
          UstPower--;
          if (UstPower < 0) UstPower=0;
        }
        paramChanged = 1; // Отмечаем, что требуется запись в eeprom
        pChangeSec = Sec; // Отметка времени последнего изменения
        reDisplayData++;
        break;
      case MODE_MAX_POWER:
        // Установка максимальной мощности
        if (v) {
          Power++;
          if (UstPower > Power) UstPower=Power;
        } else {
          Power--;
          if (Power < 0) Power=0;
        }
        paramChanged = 1; // Отмечаем, что требуется запись в eeprom
        pChangeSec = Sec; // Отметка времени последнего изменения
        reDisplayData++;
        break;
      }
    }


    // Прерывание управления симистором.
    ISR(TIMER1_COMPA_vect)
    {
      // Включаем симистор
      if (UstPower>0 && PowerEnabled) digitalWrite(PIN_TRIAC,HIGH);
    }


    ISR(ADC_vect)
    {
      static unsigned int prev_tic = 0;
      static char pad = 0;
      unsigned int tic;

      VolVal = ADCL|(ADCH << 8);

      // Пропускаем каждые 4 отсчета
      if (++pad<5) return;
      pad = 0;

      tic = (uint16_t) TCNT1L | (uint16_t) (TCNT1H<<8); // Расчитывааем текущее значение таймера

      // Обходим сброс таймера по прерыванию нуля
      if (prev_tic < tic) prev_tic = tic;
      else tic = prev_tic + tic;

     
     
      if (VolVal < 2) {
       
        // Если дана команда рассчитать среднеквадратичное, или среднеквадратичное рассчитывается, но 0 встретился раньше, чем положено, то запускаем расчет заново.      
        if (StateVolts==0 || (StateVolts==1 && ticIndex<=20)) {
            ticIndex=0;
            StateVolts=1;
            tic=0;
            MaxVolts=0;
            prev_tic = 0;
          }
        }

        // Если ноль встретился и процесс расчета идет, тогда ставим флаг окончания процесса.
        if (StateVolts==1 && ticIndex > 80) {
          if (MaxVolts>100) {
            // Если максимальное напряжение менее 100 вольт - дропаем результаты и начинаем заного
            data_adc[ticIndex] = VolVal;
            tic_adc[ticIndex] = tic;
            ticIndex++;
            StateVolts=2;    
            MaxVoltsOut=MaxVolts;
            prev_tic = 0;
          } else {
            StateVolts = 0;
          }                
      }

      // Если процесси идет, тогда просто накапливаем значения измерений
      if (StateVolts==1) {
        data_adc[ticIndex]=VolVal;
        tic_adc[ticIndex]=tic;
       
        if (ticIndex==1) {
          // при первом ненулевом результате фиксируем дельту
          deltaArray[deltaIndex++] = tic;
          if (deltaIndex>=16) deltaIndex=0;
        }
       
        ticIndex++;
        if (VolVal>MaxVolts) MaxVolts=VolVal;
      }

      // Если превышен размер массива, то считаем эту выборку несостоятельной (Вдруг глюк АЦП или помеха в сети), начинаем снова.
      if (ticIndex>=MAX_INDEX_INPUT)  StateVolts=0;
    }

    // 2 Прерывание вызывается 125 раз в секунду
    ISR(TIMER2_COMPA_vect)
    {
      if (Counter1 % 20 ==0) {
        if (StateVolts==4)  {
          StateVolts=0; // Раз в 1/5 секунды запускаем измерение среднеквадратичного (в дальнейшем можно будет настроить,
                        // чтобы оно делалось так часто, насколько позволяют возможности контроллера).
        }
      }

      // Следим за защитой от дребезга клавиш
      if (Counter1 % 20 ==0) {            
        if (CountKeys>0) CountKeys--;
      }
     
      Counter1++;
      if (Counter1>=125) {
        reDisplayData++; // обновляем дисплей
        Sec++;
        Counter1=0;
       }
     
      // Сброс таймера
      TCNT2=0x00;
    }


    // Функция вызывается по прерыванию нуля от нуля сети 220 вольт
    void zero_crosss_int()  // function to be fired at the zero crossing to dim the light
    {
      unsigned int TimeOpenTriacFact=0;
      // Сбрасываем таймер.
      TCNT1H=0x00;
      TCNT1L=0x00;
      if (UstPower>=Power) {
        // Если мощность больше или равна номинальной, включаем триак всегда, при этом прерывание на закрытие никогда не выполнится.
        TimeOpenTriac=5000;
      } else {
        digitalWrite(PIN_TRIAC,LOW);
      }

      // время открытие + ошибка прохождения нуля
      TimeOpenTriacFact = TimeOpenTriac + deltaZ;
       
      // В прерывании нуля настраиваем, чтобы прерывание на 1-м таймере выдалось через то время, которое мы ранее рассчитали.
      OCR1AH=(char)(TimeOpenTriacFact>>8);
      OCR1AL=(char)TimeOpenTriacFact;
    }


    void loop()
    {
      char *Command = NULL, *ptr, *ptr2;
      unsigned int Val;
      int i,j, s;
      unsigned long SqNapr=0,SqNaprPrev=0,FindPower;
      unsigned int TimeOpenTriac1=0;
      char s_rx;

      // Опрос нажатия кнопки валкодера (S)
      if (!CountKeys) {
        if (LOW == digitalRead(PIN_VOLC_S)) {
          StateMachine++;
          if (StateMachine>MAX_MODE) StateMachine = MIN_MODE;
          paramChanged = 1; // Отмечаем, что требуется запись в eeprom
          pChangeSec = Sec; // Отметка времени последнего изменения
          reDisplayData++;
          CountKeys=2; // защита от дребезга
        }
      }
     
      // Проверка необходимости записа изменений в eeprom, если небыло дальнейших изменений
      // в течении 5 секунд. Это сделано, чтобы валкодером не протереть в ней дырку
      if (paramChanged>0 && pChangeSec>0 && Sec > pChangeSec+5) {
        writeEEPROM();
        paramChanged = 0;
        pChangeSec = 0;
      }

      // Обработка принудительного обновления информации на дисплее
      if (reDisplayData) dispData();
     
      if (LSec != Sec) {
        LSec = Sec;
        sprintf(tbuffer,"DATA %u %u %u %u %u", PowerEnabled, Power, UstPower, Sec, MaxVoltsOut*707/1000);
        Serial1.println(tbuffer);
      }

     
      if (Serial.available() > 0) {
        //если есть доступные данные считываем байт
        s_rx=Serial.read();
        if (s_rx == '\r') {


          RBuffer[PosRx]=0;
          PosRx=0;

    #ifdef DEBUG    
          /* Отладка - возврат строки */
          Serial.print("dbg: ");
          Serial.println(RBuffer);
    #endif /* DEBUG */

          // передаем данные в следующем формате:
          // сколькоприбавитьсекунд,Темпереатура1,Температура2, Температура3
          ptr = RBuffer;
          ptr2=strchr(ptr,',');
          if (ptr2) {
            *ptr2++ = 0;
            Command = ptr;
            Val = atoi(ptr2);
            if(!strcmp(Command,"MAXP")) {
              /* Установка номинальной мощности тэнов */
              Power=Val;
              sprintf(tbuffer,"MAXP: %u", Power);
              FlUsart++;
            } else if(!strcmp(Command,"ENABLE")) {
              /* Включение иил выключение напряжения на нагрузке */
              PowerEnabled = Val;
              sprintf(tbuffer,"ENABLE: %u", Val);
              FlUsart++;
            } else if(!strcmp(Command,"POWER")) {
              /* Установка мощности на нагрузке */
              UstPower = Val;
              if (UstPower > Power) UstPower = Power;
              sprintf(tbuffer,"POWER: %u", UstPower);
              FlUsart++;
            }
          }
        } else {
          /* не символ возврата коретки - помещаем в буфер */
          if (PosRx>=RX_BUFFER_SIZE) PosRx=0;
          RBuffer[PosRx++]=s_rx;
        }
      }
      // Окончание анализа асинхронного порта

      if (FlUsart>0) {
          // Признак того, что надо выдать информацию в ком-порт (есл ее вдруг невозможно сразу выдать, например, из прерывания).
          Serial1.println(tbuffer);
          FlUsart=0;  
      }      
     
      // Начало расчета среднеквадратичного
      if (StateVolts==2){
        // Расчитываем  среднее смещение прерывания нуля и реального нуля
        uint16_t deltaSum=0;
        for(i=0;i<16;i++) deltaSum += deltaArray[i];
        deltaZ = deltaSum >> 4; // деление на 16
        StateVolts=3;
       
    #ifdef DEBUG
        Serial1.println("BEGIN SQNAPR");
    #endif /* DEBUG */


        // поскольку тут идет преобразование и умножение 32 разрядных чисел, делаем это по необходимости, а не каждый раз
        TekPower=(unsigned long) UstPower*220*220/ Power;
        SqNapr= SqNaprPrev= TimeOpenTriac1=0;
        FindPower=TekPower*ticIndex;

        if (UstPower < Power) {
          for(i=ticIndex-1; i>=0; i--) {
            SqNapr += (unsigned long) data_adc[i]* (unsigned long) data_adc[i];
            if (SqNapr>=FindPower)  {
              // Рассчитали среднеквадратичное
              OpenTriacArray[OpenTriacIndex++] = tic_adc[i] + (SqNapr-FindPower)*(tic_adc[i+1]-tic_adc[i])/(SqNapr-SqNaprPrev);
              if (OpenTriacIndex>=16) OpenTriacIndex=0;
             
              for(j=0; j<16; j++) TimeOpenTriac1 += OpenTriacArray[j];
              TimeOpenTriac = TimeOpenTriac1 >>4; // деление на 16

              // Присвоили текущие значения
              TicSqNapr=tic_adc[ticIndex-1]; // Количство тиков таймера, на основании которого расчитано среденеквадратичное.
              break;
            }
            SqNaprPrev=SqNapr;
          }
        }    
    static int jj=0;
    #ifdef DEBUG
        Serial1.println("VOLTS");
     
        sprintf(tbuffer,"Zr=%4u %u",TimeOpenTriac,OpenTriacIndex);
        Serial1.println(tbuffer);

        sprintf(tbuffer,"Pm/P=%u %u", Power,(long) UstPower);
        Serial1.println(tbuffer);

        sprintf(tbuffer,"Zr=%4u U=%3u",TimeOpenTriac,(long) MaxVoltsOut*707/1000);
        Serial1.println(tbuffer);

        sprintf(tbuffer,"COUNT=%u",ticIndex);
        Serial1.println(tbuffer);
    /*
        for(i=0;i<ticIndex;i++){
          // Данные для построения графика напряжения
          sprintf(tbuffer,"$t[%u][", jj);
          Serial1.print(tbuffer);
          sprintf(tbuffer,"%u]=",tic_adc[i]);    
          Serial1.print(tbuffer);
          Serial1.print(data_adc[i]);
          Serial1.println(";");
        }
        jj++;
    */

        sprintf(tbuffer,"deltaZ=%u %u", deltaZ, TicSqNapr);
        Serial1.println(tbuffer);    
        Serial1.println("ENDVOLTS");
             
       
    #endif /* DEBUG */
        StateVolts=4;
      } // окончание  расчета среднеквадратичного

    }
     
  17. Limoney

    Limoney Гик

    Попробуйте
     
  18. Megakoteyka

    Megakoteyka Оракул Модератор

    Ага, догнал. Сам неправильно на картинку посмотрел, проглядел начало первого такта. В моем примере исправьте функцию WriteReg. Должно быть:
    Код (C++):
    // пишем в регистр сразу 32 разряда
    void WriteReg(unsigned long value)
    {
      for(int i = 0; i < 32; i++)      // перебираем разряды
      {
        int data = (value >> i) & 0x1; // берем очередной разряд
        digitalWrite(PIN_C, LOW);     // тактовый сигнал вверх
        digitalWrite(PIN_D, data);     // устанавливаем сигнал данных
        digitalWrite(PIN_C, HIGH);      // тактовый сигнал вниз
      }
      digitalWrite(PIN_L, HIGH);       // защелку вверх
      digitalWrite(PIN_L, LOW);        // защелку вниз
    }
    Стало лучше?
     
  19. gifrom

    gifrom Нуб

    С вашим кодом всегда горит 8.8.77
    С учётом исправлений ваш код сейчас выглядит так
    Код (C++):
    #define PIN_E 7 // enable
    #define PIN_L 8 // latch
    #define PIN_C 9 // clock
    #define PIN_D 10// data

    #define SYMBOLS_ARRAY_LENGTH 12 //Длина массива символов
    unsigned char Symbols[SYMBOLS_ARRAY_LENGTH]={ // Массив символов индикатора
      252, // 0
      96,  // 1
      218, // 2
      242, // 3
      102, // 4
      182, // 5
      190, // 6
      224, // 7
      254, // 8
      246, // 9
      1,   // .
      2    // -
    };

    void setup()
    {
      pinMode(PIN_E, OUTPUT);
      pinMode(PIN_L, OUTPUT);
      pinMode(PIN_C, OUTPUT);
      pinMode(PIN_D, OUTPUT);
      digitalWrite(PIN_L, LOW); // сигнал защелки вниз
      digitalWrite(PIN_E, LOW); // включаем сегменты
      digitalWrite(PIN_C, LOW); // тактовый сигнал вниз
    }

    void loop()
    {
      // показываем на дисплее счетчик
      for(int i = 0; i < 10; i++)
      {
        unsigned long value = GetValue(i, i, i, i);
        WriteReg(value);
      }
    }

    // пишем в регистр сразу 32 разряда
    // пишем в регистр сразу 32 разряда
    void WriteReg(unsigned long value)
    {
      for(int i = 0; i < 32; i++)      // перебираем разряды
      {
        int data = (value >> i) & 0x1; // берем очередной разряд
        digitalWrite(PIN_C, LOW);     // тактовый сигнал вверх
        digitalWrite(PIN_D, data);     // устанавливаем сигнал данных
        digitalWrite(PIN_C, HIGH);      // тактовый сигнал вниз
      }
      digitalWrite(PIN_L, HIGH);       // защелку вверх
      digitalWrite(PIN_L, LOW);        // защелку вниз
      delay(500);
    }

    // передаем числа, получаем на выходе код для их отображения
    unsigned long GetValue(byte s1, byte s2, byte s3, byte s4)
    {
      return
        (unsigned long)(Symbols[s1] << 0) |
        (unsigned long)(Symbols[s2] << 8) |
        (unsigned long)(Symbols[s3] << 16) |
        (unsigned long)(Symbols[s4] << 24);
    }
    и выдаёт


    Так что вроде стало лучше, хотябы читаемые цифры появились.
    Спасибо вам обоим за помощь
     
    Последнее редактирование: 20 окт 2015
  20. Sunsai

    Sunsai Нуб

    Ну раз не хватает типа данных для запихивания всех 4х сегментов сразу, нужно просто по одному слать.

    Код (C++):
    #define PIN_E 7 // enable
    #define PIN_L 8 // latch
    #define PIN_C 9 // clock
    #define PIN_D 10// data

    #define SYMBOLS_ARRAY_LENGTH 18 //Длина массива символов
    unsigned char Symbols[SYMBOLS_ARRAY_LENGTH]={ // Массив символов индикатора
      252, // 0
      96,  // 1
      218, // 2
      242, // 3
      102, // 4
      182, // 5
      190, // 6
      224, // 7
      254, // 8
      246, // 9
      238, // A
      62,  // B
      156, // C
      122, // D
      158, // E
      142, // F
      1,   // .
      2    // -
    };

    void setup()
    {
      pinMode(PIN_E, OUTPUT);
      pinMode(PIN_L, OUTPUT);
      pinMode(PIN_C, OUTPUT);
      pinMode(PIN_D, OUTPUT);
      digitalWrite(PIN_L, LOW); // сигнал защелки вниз
      digitalWrite(PIN_E, LOW); // включаем сегменты
      digitalWrite(PIN_C, LOW); // тактовый сигнал вниз
      Serial.begin(9600);
    }

    void loop()
    {
      // показываем на дисплее счетчик
      for(int i = 0; i < SYMBOLS_ARRAY_LENGTH; i++)
      {
        WriteReg(i, i, i, i);
        WriteLatch();
        Serial.println(i);
      }
    }

    // пишем в регистр 8 разрядов
    void WriteReg(byte s1, byte s2, byte s3, byte s4)
    {
      byte s[4] = {Symbols[s1], Symbols[s2], Symbols[s3], Symbols[s4]};
      for (int b = 0; b < 4; b++)
      {
        byte value = s[b];
        for(int i = 0; i < 8; i++)      // перебираем разряды
        {
          int data = (value >> i) & 0x1; // берем очередной разряд
          digitalWrite(PIN_C, LOW);     // тактовый сигнал вверх
          digitalWrite(PIN_D, data);     // устанавливаем сигнал данных
          digitalWrite(PIN_C, HIGH);      // тактовый сигнал вниз
        }
      }
    }

    // пишем защелку
    void WriteLatch()
    {
      digitalWrite(PIN_L, HIGH);       // защелку вверх
      digitalWrite(PIN_L, LOW);        // защелку вниз
      delay(1000);
    }
     
    Последнее редактирование: 8 ноя 2015