Всем доброго времени суток! Интересен такой вопрос, как сделать простую (если это можно так назвать) бегущую строку на 8 светодиодах. Просто хочется понять смысл)) Т.е имеем 8 светодиодов, подключенных с 0 по 7 пины. При включении горят первые 3 (0,1,2 пины) затем начинают двигаться с заданным интервалов и когда 1 из 3 светодиодов дойдёт до конца (7 пин) должен загореться 0 пин ну и т.д. В общем получается принцип бегущей строки. Подскажите пожалуйста каким образом это реализовывается.
Во-первых, никогда не вяжите ничего на 0 и 1 пин, во вторых, в вашем случае будет простой вариант смотреть остатки от деления, если непонятно - накидаю код
Можно массив из 8 элементов использовать. Можно байт, что то же массив на 8 бит. Его и сдвигать проще.
Я понимаю, что нужен циклический сдвиг массива, смотрел несколько уроков по С++ на эту тему, но как это к ардуине и светодиодом применить не доходит. Так то мне это нужно для реализации моей, наверно бредовой задумки)) Написал вот такой скетч для отображения температуры и времени на семисегментном индикаторе. Спойлер: Скетч Код (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* Вот и подумал сначала на светодиодах потренироваться, чтоб смысл понять...
Код (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++; //задержка (интервал по времени) сюда! } просто пример... для пинов будет свой вызов. для повторения применят циклически. Если нужна непрерывность, то через условие (и если в АССЕМБЛЕРЕ) бит переноса. Простите - это просто пример. Для зажигания и погасания вызов управления портом вывода.
Код (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; } } }
Я извиняюсь, а каким образом это должно работать если 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); }
сдвиг влево Код (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 битные вычисления. для произвольного числа бит несколько сложнее сделать.
Поэтому я готовый код нарисовал. А ссылки для тех, кто найдет в себе силы разбираться с этой хернёй. Циклически двигает значение переменной MyVar на один бит влево. Код (C++): uint8_t MyVar = 0b10110010; asm ("mov __tmp_reg__, %[v0]; rol __tmp_reg__; rol %[v0]" : [v0] "=r" (MyVar) : "0" (MyVar));
Не совсем то, что мне надо, нужен именно цикл, чтоб крутился как транспортёрная лента и самые крайние биты не терялись, а перескакивали в начало. С другой стороны подобный сдвиг можно в моём проекте попробовать применить, там мне просто нужно сдвигать элементы массива, тем самым делая переход между временем и температурой.
Только чтобы разобраться, нужно с командами разбираться которые там используются. Ну а ассемблер это не С++, там всё сложнее гораздо сложнее(((
Как раз тот случай, когда в ассебмлере есть почти готовые команды, а в C++ нет. Вам не надо разбираться в ассемблере. Вы ведь не разбираетесь в каждой библиотеке, подключенной к проекту. Могу завернуть ассемблерную вставку C-шную функцию.