shiftOut() и последовательное соединение регистров 74HC595

Тема в разделе "Arduino & Shields", создана пользователем Recoshet, 6 янв 2014.

  1. Recoshet

    Recoshet Нерд

    Здравствуйте, у меня есть рабочая конструкция, при которой работает один сдвиговый регистр:
    Код (Text):

    //Порт подключенный к ST_CP 74HC595
    int latchPin = 9;
    //Порт подключенный к SH_CP  74HC595
    int clockPin = 8;
    //Порт подключенный к DS 74HC595
    int dataPin = 10;

    int i = 0;
    int time = 250;
    long tmpTime = 0;

    void setup() {
      //устанавливаем режим порта выхода
      pinMode(latchPin, OUTPUT);
      pinMode(clockPin, OUTPUT);
      pinMode(dataPin, OUTPUT);
    }

    void writeShift(int data){
        //устанавливаем LOW на latchPin пока не окончена передача байта
        digitalWrite(latchPin, LOW);
        shiftOut(dataPin, clockPin, LSBFIRST, data);  
        //устанавливаем HIGH на latchPin, чтобы проинформировать регистр, что передача окончена.
        digitalWrite(latchPin, HIGH);
    }

    void loop(){
      if (millis() >= tmpTime + time){
        tmpTime = millis();
        writeShift(i++);
      }
    }
     
    Но если я подключаю второй регистр последовательно вот по такой схеме:
    http://robocraft.ru/files/spi/16bit_shreg_schematics_preview.jpg

    То второй регистр ведёт себя не правильно, выводит предыдущее значение первого регистра (до меня только что дошло что он просто туда "выталкивает" своё предыдущее значение, когда получает новые данные). Можно ли как то без костылей с SPI отправлять 2х байтные данные через shiftOut или какую нить другую функцию/библиотеку.

    P.S. В будущем планирую подключить так последовательно ещё парочку регистров
     
    Последнее редактирование: 6 янв 2014
  2. Recoshet

    Recoshet Нерд

    Короче разобрался, кому интересно вот видео с результатом работы


    Скетч получился такой: http://codepad.org/WBAyvbda
     
  3. NR55RU

    NR55RU Гик

    А в чем заключается проблема ?
    Подключив два 8 битовых сдвиговых регистра вы по сути получили один 16-и битовый, и регистр ведет себя совершенно верно, как только получается 9-й бит то 1-й проталкивает в следующий регистр.
    Итог, работаем с двумя 8 битовыми реистрами буд то у нас один 16 битовый и все :)
    Я не знаю как работает shifOut() но для работы с таким регистром достаточно простерших побитовых операций и одной функции.

    Например на вскидку можно сделать так:
    Код (Text):
        int val = 255;
        int i,shift;
        for(i = 0; i < 16; i++)
        {
            shift = val & (0x8000 >> i);
            if(shift > 0)
            {
                writeToRegister(1);
            }
            else
            {
                writeToRegister(0);
            }
        }
    Функция writeToRegister() осуществляет физическую запись значения в регистр.
    У меня под рукой нет даташита на регистр, не помню на память какие там сигналы требуется и на какие пины что бы подкинуть вам функцию фактической записи в регистр, но работать с ним крайне просто, я с их помощью управлял светодиодной матрицей.
    Вот кстати давешняя темка я там как раз разбирался с матрицей.

    P.s. Дам личный совет, с такими простыми вещами как сдвиговые регистры лучше разобраться без всяких библиотек, дают очень хороший опыт и знания. научитесь им управлять используя лишь побитовые операции и функцию digitalWrite() очень много интересного узнаете :)
     
    Последнее редактирование: 6 янв 2014
  4. Recoshet

    Recoshet Нерд

    Да, получается всё так) Только я не особо умею в языке Си оперировать с двоичными и шестнадцатеричными данными. Идея всё организовать вручную без библиотек меня тоже посещала, но я обычно сначала всё запускаю, вижу что работает, а потом разбираюсь как это устроено. Буду пробовать, спасибо!
     
  5. NR55RU

    NR55RU Гик

    На самом деле ни одно цифровое устройство не знает ни десятичной ни шестнадцатеричной системы счисления, они знаю ТОЛЬКО двоичную, в остальные системы программы для нас переводят дабы нам было удобнее :)
    Почитайте эту статью, сегодня как раз ее выложил, возможно она вам поможет. :)

    А так к примеру смотрите.
    Например вы имеет 4 битовый сдвиговый регистр и желаете зажечь светодиоды через один.
    В итоге вам надо записать в регистры двоичное число 0101
    И того в регистр вам надо передать 4 сигнала, 2 низких и 2 высоких.
    Проблема в том как узнать значения отдельных битов так того числа что вы желаете передать, вот так и приходит на помощь побитовые операции.
    Прежде чем читать далее, надеюсь вы ознакомились со статьей по ссылке :)

    Например берем такую операцию 0101 & 0001 = 0001
    Так мы узнала что в первом разряде единица.

    Далее
    0101 & 0010 = 0000
    Так мы узнали что во втором разряде ноль.

    Далее:
    0101 & 0100 = 0100
    Так мы узнали что в третьем разряде единица. Хотя по факту это получается не единица, но число больше нуля и этого более чем достаточно.
    Ибо нас интересует лишь факт будет ли после этой операции полученное значение нулем или больше чем нуль и все.

    А как нам получить последовательно числа 0001, 0010, 0100 и тд и тп.
    А тут приходит на помощь простой сдвиг влево, например 0001 << 1 = 0010, 0001 << 2 = 0100

    В итоге определяя значения битов можно передать любое двоичное число в регистр, хоть 4 бита хоть 32 :)
     
    acos, Recoshet, Unixon и ещё 1-му нравится это.
  6. Unixon

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

    Более того, оно и двоичную то вспоминает только в хорошем расположении духа, а так и вовсе работает с аналоговыми сигналами. Убедиться в этом можно, например, перестав соблюдать в цифровой схеме рекомендованные параметры цифрового сигнала, т.е. проигнорировав логические уровни и временные задержки.
     
  7. Recoshet

    Recoshet Нерд

    Спасибо за такой подробный ответ, спасибо за статью. Но как это работает я понял сразу, когда мне удалось записать в регистр цифру 3 и у меня загорелись первые 2 светодиода, и я понял что туда попали данные в виде 0000 0011 что равно 3 в десятичной системе.
    У меня есть трудности с синтаксисом в языке Си при работе с битами.
    Почитал мануальчики и опробовал следующие трюки:
    Отправил в регистр 0b00000011 << 1 после чего светодиоды "переместились" левее на один.
    Теперь хочу запустить такой алгоритм:
    Есть переменная array[0] = 0b00010000;
    Если я выполню array[0] << 4, то я потеряю эту единичку. А как можно отловить, что при следующем сдвиге будет "переполнение" и то что уходит за границы этого байта нужно вписать в array[1].
    В итоге хочу понять как реализовать такой результат:

    array[0] = 0b01001011
    выполняю array[0] << 6
    и хочу что бы в результате получился массив со следующим данными:
    array[0] // 0b11000000
    array[1] // 0b00010010

    UPD: Можно конечно парсить строку, но это совсем не интересно :)
     
    Последнее редактирование: 10 янв 2014
  8. acos

    acos Официальный гик Администратор

    Используй unsigned long))
    Типа такого, суть та же, что и у NR55RU, хоть и на светодиоде)
    Правда чтоб сделать сдвиговый регистр еще надо SCK и RCK в этом цикле дергать

    Код (Text):
    /*
      Blink
      Turns on an LED on for one second, then off for one second, repeatedly.

      This example code is in the public domain.
    */

    // Pin 13 has an LED connected on most Arduino boards.
    // give it a name:
    int led = 13;

    // the setup routine runs once when you press reset:
    void setup() {
      // initialize the digital pin as an output.
      pinMode(led, OUTPUT);
    }

    // the loop routine runs over and over again forever:
    void loop() {
          unsigned long val = 0b10101011100010101100;
        int i,shift;
        for(i = 0; i < 20; i++)
        {
            shift = (val & 1);
            if(shift > 0)
            {
                digitalWrite(led, HIGH);  // turn the LED on (HIGH is the voltage level)
            }
            else
            {
                digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
            }
            val= val >> 1;
            delay(1000);              // wait for a second
        }
    }
     
  9. Recoshet

    Recoshet Нерд

    acos, как работать и записывать данные в регистры я понял. А вот как правильно обрабатывать биты, не загромождая операвную память, хочется выяснть.
    Вы предложили вариант с переменной unsigned long, но она тоже не "безразмерная".
    Если я применю к 0b10101011100010101100 << 1 то поеряю единицу, а мне бы хотелось эту единицу отправить в другую переменную. Взгляните на калькулятор в windows. Если его перевести в режим ввода двоичных чисел, то он при наборе разбивает строку на каждые 4 бита визуально.
     
  10. Unixon

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

    А зачем именно ловить переполнение? Вы же знаете размер буфера в битах. Просто считаете биты и выбираете следующий (или предыдущий) элемент буфера, когда текущий закончится. Ну и порядок следования бит выбираете таким, чтобы их удобно было в буфер складывать без пробелов или лишних сдвигов, например, от младших к старшим. Либо смешанным порядком: для байтов (слов, и т.п. элементов буфера) от младших к старшим (little endian), а для бит внутри байта - наоборот - от старших к младшим (big endian), так и волки сыты будут и овцы целы. Хотя endianness к битам и не относится напрямую, но аналогия, полагаю, понятна.
     
  11. geher

    geher Гуру

    Если. нужно, чтобы старшие биты при сдвиге "переползали" в соседний элемент массива, то это можно попробовать реализовать примерно так:
    Код (Text):
     
    // размер массива
    #define x 100
    unsigned char array[x];
    unsigned char n; //сдвиг от 0 до 8
    ...
      for (int i=x-1;i>0;i--){
      array[i]=(((*((unsigned short*) (&(array[i-1])))) << n) & 0xFF00) >>8;
      }
      array[0]=array[0] << n;
     
     
  12. acos

    acos Официальный гик Администратор

    Я извиняюсь. Просто думал, что вам нужно лишь подключить второй регистр) unsigned long хватит на 4 таких регистра. Если б речь шла о асме, я б предложил ловить переполнение, но тут можно положится на возможности среды, которая сама будет заботится о переполнениях в каждом байте этой самой четырехбайтной ерунды, тем более что для задачи этого достаточно. Считайте, что это у вас такой четырехбайтный массив с перескакиванием флага переполнения в следующий байт.
    Чтобы не терять единицу, копируйте данные в переменную, а её уже крутите на сдвигах.
     
  13. myfriend

    myfriend Нуб

    дд! возник вопрос 64 бита как протолкнуть, и как по 3 провода использовать не 1 линию а хотя бы 2, нужно 220 светодиодов каждым управлять отдельно, нигде никто не дал простой пример, все обсуждают что это возможно, есть ли кто знает ответ?
     
  14. mcureenab

    mcureenab Гуру

    Две - три цепочки регистров подключить? Подключайте каждую цепочку на свою пару пинов (dataPin, clockPin), что вас смущает? Но функция shiftOut умеет работать одновременно только с одной цепочкой сдвиговых регистров. latchPin может быть общим для всех регистров.

    В принципе можно сделать функцию которая раскладывает биты на несколько dataPin (каждый бит для своей цепочки), а затем выдает импульс на один общий clockPin.

    Прозвучал вопрос, как побитово двигать массив... Может проще двигать биты непосредственно в регистрах? Один импульс на clockPin сдвигает данные в регистрах на 1 бит. Бит который выталкивается из последнего регистра можно поймать, поменять, и снова отправить в регистры. И так циклически прокручивать каждый раз меняя некоторые биты.

    Освоите работу с регистрами МК, можно будет сразу по 8 бит отправлять в 8 цепочек регистров.
     
  15. myfriend

    myfriend Нуб

     
  16. mcureenab

    mcureenab Гуру

    Проверьте внимательно схему подключения регистров.
     
  17. mcureenab

    mcureenab Гуру

    В библиотеке вроде как правильно. Но используется Serial. Видимо автор не удалил весь отладочный код.

    Код (C++):
    void Shift595::writeRegisters(void)
    {
        digitalWrite(_latchPin, LOW);           // unlatch from the 595 to update registers  
        for(int i = _numOfPins-1; i>=0; i--)
        {
            digitalWrite(_clockPin, LOW);       //  bring the clock pin LOW, allowing us to shift new
                                                //  data onto the register
           
            boolean data = _registers[i];        //  shift the new data onto the register
            digitalWrite(_dataPin, data);  
           
            digitalWrite(_clockPin, HIGH);        // bring the clock pin HIGH to complete shift
        }  
        digitalWrite(_latchPin, HIGH);          // re-latch to the 595 to output changes
       
        Serial.println();
    }
     
     
  18. mcureenab

    mcureenab Гуру

    И тут. Если нужно поменять несколько пинов, придется ждать прокрутки после изменения каждого пина.

    Код (C++):
    void Shift595::setRegisterPin(int index, boolean data)
    {  
        _registers[index] = data;
        writeRegisters();
    }
     
     
  19. myfriend

    myfriend Нуб

    Код (C++):
    void Shift595::setRegisterPin(int index, boolean data)
    {
        _registers[index] = data;
        writeRegisters();
    }
    спасибо за ответ, подскажите меняя index удастся назначить разные пины?

    удалось подключить абсолютно отдельно каждый регистр не паровозом и удалось успешно управлять отдельными ножками сразу 2х регистров вот код доделал сам сэкономит многим много дней размышлений

    Код (C++):
    #include <Shift595.h>

    #define   dataPin          11      // SINII (расцветка пинов регистра на картинке) все понятно data
    #define   latchPin          4      // ZELENY  latch
    #define   clockPin          6      // ORANJEVY   clock и помните GND обязательно 8+13 и 5в обязательно 10+16 // пины иначе регистр не работает и многие коды только в 1.6.0 компилируются, а в 1.6.10 тот же код ошибочный что еще много дней сэкономит решения вопроса

    #define   numOfRegisters    1      // number of shift registers present так и оставил пока 1 шт. проще вышло
    //их каждый по 3м проводам управлять чем пытатья 64 бит на каждую ножку управлять 4 регистрами

    Shift595 Shifter(dataPin, latchPin, clockPin, numOfRegisters);
    //************************************************************************************
    #define   dataPindva          12      // SINII
    #define   latchPindva          5      // ZELENY
    #define   clockPindva          7     // ORANJEVY

    #define   numOfRegistersdva    1      // number of shift registers present

    Shift595 Shifterdva(dataPindva, latchPindva, clockPindva, numOfRegistersdva);

    void setup(){}

    void loop()
    {
      Shifter.setRegisterPin(0, HIGH);
      Shifter.setRegisterPin(1, HIGH);
      Shifter.setRegisterPin(2, HIGH);
      Shifter.setRegisterPin(3, HIGH);
      Shifter.setRegisterPin(4, HIGH);
      Shifter.setRegisterPin(5, HIGH);
      Shifter.setRegisterPin(6, HIGH);
      Shifter.setRegisterPin(7, HIGH);

       delay(1000);

       Shifter.setRegisterPin(0, LOW);
     
    Shifter.setRegisterPin(1, LOW);
      Shifter.setRegisterPin(2, LOW);
    Shifter.setRegisterPin(3, LOW);
       Shifter.setRegisterPin(4, LOW);
       Shifter.setRegisterPin(5, LOW);
    Shifter.setRegisterPin(6, LOW);
      Shifter.setRegisterPin(7, LOW);

       delay(1000);
    //************************************************************************************
      Shifterdva.setRegisterPin(0, HIGH);
      Shifterdva.setRegisterPin(1, HIGH);
      Shifterdva.setRegisterPin(2, HIGH);
      Shifterdva.setRegisterPin(3, HIGH);
      Shifterdva.setRegisterPin(4, HIGH);
      Shifterdva.setRegisterPin(5, HIGH);
      Shifterdva.setRegisterPin(6, HIGH);
      Shifterdva.setRegisterPin(7, HIGH);

       delay(1000);

    Shifterdva.setRegisterPin(0, LOW);
    Shifterdva.setRegisterPin(1, LOW);
    Shifterdva.setRegisterPin(2, LOW);
    Shifterdva.setRegisterPin(3, LOW);
    Shifterdva.setRegisterPin(4, LOW);
    Shifterdva.setRegisterPin(5, LOW);
    Shifterdva.setRegisterPin(6, LOW);
    Shifterdva.setRegisterPin(7, LOW);

       delay(1000);

    }
    так удастся с каждых 3 пинов получить 8 пинов, используем 30 получаем 80, которыми каждым удастся управлять нажимая отдельно. ардуино mega2560 даст примерно 120 светодиодов или выходов. по вышеприведенной схеме.
    при 16 битном подключении даст 240 светодиодов примерно, подскажите если кто знает, как управлять каждым светодиодом отдельно 16 бит. при данном способе подключения, если разберусь напишу вам также.
     
    Последнее редактирование: 1 фев 2017
  20. mcureenab

    mcureenab Гуру

    Вы как раз этим занимаетесь. Первый аргумент функции
    setRegisterPin(0, LOW);
    это index внутреннего массива объекта Shifter и номер пина на цепочке регистров. 0 - 7, 8 - 15, 16 - 23, ...

    В заголовочном файле есть ограничение на количество регистров в одной цепочке - 8. Всего 8x8 = 64 пина. Объект статически размещает массив boolean[64].

    #include <Shift595.h>

    #define max595s 8

    Цепочки регистров должны работать, если они исправны и правильно собраны. Думаю, вы второй регистр неправильно подключили, поэтому в него данные из первого регистра не поступают.

    А. Библиотеку я отсюда тиснул https://sites.google.com/site/shift595arduino/