Циклический сдвиг битов

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

  1. Eragon

    Eragon Нерд

    Всем доброго времени суток! Интересен такой вопрос, как сделать простую (если это можно так назвать) бегущую строку на 8 светодиодах. Просто хочется понять смысл)) Т.е имеем 8 светодиодов, подключенных с 0 по 7 пины. При включении горят первые 3 (0,1,2 пины) затем начинают двигаться с заданным интервалов и когда 1 из 3 светодиодов дойдёт до конца (7 пин) должен загореться 0 пин ну и т.д. В общем получается принцип бегущей строки. Подскажите пожалуйста каким образом это реализовывается.
     
  2. Larymar

    Larymar Нуб

    Во-первых, никогда не вяжите ничего на 0 и 1 пин,
    во вторых, в вашем случае будет простой вариант смотреть остатки от деления, если непонятно - накидаю код
     
  3. ostrov

    ostrov Гуру

    Можно массив из 8 элементов использовать. Можно байт, что то же массив на 8 бит. Его и сдвигать проще.
     
  4. Larymar

    Larymar Нуб

    я подразумевал совокупность
    что-то
    while(true){
    i++;
    array[i%8] -включить
    }
     
  5. Eragon

    Eragon Нерд

    Я понимаю, что нужен циклический сдвиг массива, смотрел несколько уроков по С++ на эту тему, но как это к ардуине и светодиодом применить не доходит:(. Так то мне это нужно для реализации моей, наверно бредовой задумки))
    Написал вот такой скетч для отображения температуры и времени на семисегментном индикаторе.
    Код (C++):
    #include <Wire.h>
    #include <BMP085.h>
    #include <DS3231.h>  // Библиотека для работы с модулем DS3231
    #define latchPin 3 // 12 вывод(защёлка)
    #define clockPin 2 // 11 вывод (вход для тактовых импульсов)
    #define dataPin  4 // 14 вывод (вход для последовательных данных)

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

    uint32_t gen1P = 0UL;
    uint32_t gen2P = 0UL;
    bool trig;

    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 displayBuffer[2][4];

    void setup()
    {
      //Serial.begin(9600);
      DDRD = B10111100; // 2, 3, 4, 5 - выход
      DDRB = B00001111; // 8,9,10,11 - выход
      PORTB |= B00001111; // выключаем все разряды
      clock.begin();
      Wire.begin();
      delay(1000);
      dps.init();
    }

    void loop()
    {
      byte switchTime = getTime();
      blinkDot();
      getTime();
      getTemp(switchTime);
      displayData(trig);
      if (switchTime==0) PORTB |= B00001111; // выключаем все разряды на момент чтения показаний с датчика
      if ( switchTime>=1 && switchTime<=6 )  trig = 1;  // с 1 по 6 секунду отображаем температуру
      else trig = 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));
    }

    byte getTime()
    {
       DateTime = clock.getDateTime();    
       byte hour = byte(DateTime.hour);
       byte minute = byte(DateTime.minute);
       byte second = byte(DateTime.second);
       displayBuffer[0][0] = hour/10;
       displayBuffer[0][1] = hour%10;
       displayBuffer[0][2] = minute/10;
       displayBuffer[0][3] = minute%10;
       return second;
    }

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

    void blinkDot()
    {
      static bool x;
      if ( isTimer (gen2P,1000) ) // мигаем двоеточием раз в секунду
       {
         gen2P = millis();
         x = !x;
         PORTD |= (x << 5);
       }
       else PORTD &= (x << 5);
       if (trig == 1) PORTD &= ~(1 << 5); // если включено отображение температуры, отключаем двоеточие

    }
    void getTemp(byte sec)
    {
        static long Temperature;
        if (sec==0)dps.getTemperature(&Temperature);
        int i = Temperature/10;
        displayBuffer[1][0] = i/10;
        displayBuffer[1][1] = i%10;
        displayBuffer[1][2] = 10;
        displayBuffer[1][3] = 11;
    }
     
    И вот пришла идея сделать переход между отображением времени и температуры наподобие бегущей строки, например:
    имеем время 12:00 и температуру 25С*
    сначала отображается время 12:00
    когда настало время переключения начинаем сдвигать время влево, на индикаторе должно быть 12 02
    затем 12 25 -> 12 5C -> 25C* Вот и подумал сначала на светодиодах потренироваться, чтоб смысл понять...
     
    Последнее редактирование: 25 апр 2017
  6. Igor68

    Igor68 Гуру

    Код (C++):
    unsigned char  k=0x01; //тут биковая маска засветки (может 0xA5 = 10100101) на усмотрение
    unsigned char count=8;
    while(count < 8)
    {
      //если пины не по маске
      if(k & 0x01) //для певрого по счёту пина
         pin0 = true;   //тут зажигание для пина
      else
         pin0 = false; //тут погасание для пина
    ......
    ......
       if(k & 0x80) //для последнего по счёту пина
         pin7 = true;   //тут зажигание для пина
      else
         pin7 = false; //тут погасание для пина
       k = (k << 1);
       count++;
       //задержка (интервал по времени) сюда!
    }
    просто пример... для пинов будет свой вызов. для повторения применят циклически. Если нужна непрерывность, то через условие (и если в АССЕМБЛЕРЕ) бит переноса.
    Простите - это просто пример. Для зажигания и погасания вызов управления портом вывода.
     
  7. Larymar

    Larymar Нуб

    Код (C++):
    int a[8];
    void setup() {
      int b=0;
      for(int i=6;i<14;i++)
      {
        a[b]=i;
        pinMode(i,OUTPUT);
        b++;
      }

    }

    void loop() {
      // put your main code here, to run repeatedly:
      int k=0;
      while(true)
      {
        for(int i=0;i<9;i++){
          digitalWrite(a[i],LOW);
        }
        digitalWrite(a[k%8],HIGH);
        if(k==4095)
        {
          k=0;
        }
      }
    }
     
    Последнее редактирование модератором: 25 апр 2017
  8. Eragon

    Eragon Нерд

    Я извиняюсь, а каким образом это должно работать если k = 0 и она не инкрементируется далее в коде и толку вычислять этой функцией ( digitalWrite(a[k%8],HIGH) остаток если делимое всегда равно 0. И не понятно зачем эта куча строк в секции setup!? Если там просто нужно задать какие пины будут выходами, примерно так :

    byte arr[]{1,2,3};
    void setup()
    {
    for(byte pin =0; pin < 3; pin++) pinMode(pin, OUTPUT);
    }
     
    Последнее редактирование: 26 апр 2017
  9. mcureenab

    mcureenab Гуру

    Чтобы переключить сразу 8 пинов удобно использовать PORTB или PORTD.
     
  10. Eragon

    Eragon Нерд

    Битовыми масками я пользоваться умею... непонятно как сдвиг реализовать(((
     
  11. mcureenab

    mcureenab Гуру

    сдвиг влево
    Код (C++):
      uint8_t MyVar = 0b10110010;
      asm ("mov __tmp_reg__, %[v0]; rol __tmp_reg__; rol %[v0]" : [v0] "=r" (MyVar) :  "0" (MyVar));
    для сдвига вправо rol заменить на ror.

    ROL
    http://www.atmel.com/webdoc/avrassembler/avrassembler.wb_ROL.html

    mov __tmp_reg__, %[v0] // копия параметра
    rol __tmp_reg__ // выталкивает бит переноса в флаг C.
    rol %[v0]" // делает сдвиг и заталкивает флаг C в конец

    понятно, что это строго 8 битные вычисления. для произвольного числа бит несколько сложнее сделать.
     
    Последнее редактирование: 26 апр 2017
  12. mcureenab

    mcureenab Гуру

  13. ostrov

    ostrov Гуру

    arkadyf нравится это.
  14. ostrov

    ostrov Гуру

  15. mcureenab

    mcureenab Гуру

    Это не для начинающих.
     
  16. Eragon

    Eragon Нерд

    Это слишком сложно для меня, да и ассемблера я знать не знаю:(
     
  17. mcureenab

    mcureenab Гуру

    Поэтому я готовый код нарисовал. А ссылки для тех, кто найдет в себе силы разбираться с этой хернёй.

    Циклически двигает значение переменной MyVar на один бит влево.

    Код (C++):

        uint8_t MyVar = 0b10110010;
        asm ("mov __tmp_reg__, %[v0]; rol __tmp_reg__; rol %[v0]" : [v0] "=r" (MyVar) :  "0" (MyVar));
     
     
  18. Eragon

    Eragon Нерд

    Не совсем то, что мне надо, нужен именно цикл, чтоб крутился как транспортёрная лента и самые крайние биты не терялись, а перескакивали в начало. С другой стороны подобный сдвиг можно в моём проекте попробовать применить, там мне просто нужно сдвигать элементы массива, тем самым делая переход между временем и температурой.
     
  19. Eragon

    Eragon Нерд

    Только чтобы разобраться, нужно с командами разбираться которые там используются. Ну а ассемблер это не С++, там всё сложнее гораздо сложнее(((
     
  20. mcureenab

    mcureenab Гуру

    Как раз тот случай, когда в ассебмлере есть почти готовые команды, а в C++ нет.

    Вам не надо разбираться в ассемблере. Вы ведь не разбираетесь в каждой библиотеке, подключенной к проекту. Могу завернуть ассемблерную вставку C-шную функцию.