более скоростной shiftOut?
- Войдите на сайт для отправки комментариев
Сб, 21/01/2012 - 06:38
К примеру, цитата:
Для сравнения быстродействия:
Ниже приведены команды, которые при понимании логики работы программы в 99% случаев делают одно и то же.
digitalWrite(5,HIGH); // 72 такта
DDRD |=(1<<PIN5); // 2 такта
А писал ли кто нибудь аналог shiftOut, который реально быстрее работает? Особенно когда надо последовательно вызывать его 8 раз?
Нашел так
void ShiftOutByte (unsigned char Data) { int Bit; for(Bit=0; Bit < 8; ++Bit) { while(ClkPin == HIGH); while(ClkPin == LOW); DataPin = Data & 1; Data >>= 1; } }вот еще
#include <avr/io.h> // Access bits like variables struct bits { uint8_t b0:1; uint8_t b1:1; uint8_t b2:1; uint8_t b3:1; uint8_t b4:1; uint8_t b5:1; uint8_t b6:1; uint8_t b7:1; } __attribute__((__packed__)); #define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin) // Hardware definitions #define SER SBIT( PORTB, 0 ) #define SER_DDR SBIT( DDRB, 0 ) #define SCK SBIT( PORTB, 1 ) #define SCK_DDR SBIT( DDRB, 1 ) #define RCK SBIT( PORTB, 2 ) #define RCK_DDR SBIT( DDRB, 2 ) void spi_wr8( uint8_t val ) // write a byte to software SPI { for( uint8_t i = 8; i; i-- ){ SCK = 0; SER = 0; if( val & 0x80 ) // MSB first SER = 1; val <<= 1; SCK = 1; } } void spi_wr16( uint16_t val ) // write to two cascaded 74HC595 { RCK = 0; spi_wr8( val >> 8 ); spi_wr8( val ); RCK = 1; }и еще
void setup(){ DDRB = B11111000; //set digit pins_8-12 as outputs PORTB=B10000000; //set LATCH pin_8 HIGH for (int i=0; i<8; i++) { //here is a simple condition to see if this code does work if (i MODULO 2==0) PORTB |= (1<<1); //set DATA pin_9 HIGH if condition TRUE else PORTB &= ~(1<<1); //set DATA pin_9 LOW if condition FALSE PORTB |= (1<<4); //set CLOCK pin_12 HIGH PORTB &= ~(1<<4); //set CLOCK pin_12 LOW } PORTB=B00000000; //set LATCH pin_8 LOW } void loop(){}Заработало это:
/74hc595 wiring //Pin connected to ST_CP of 74HC595 #define _latch 8 #define _latchPORTB _latch - 8 //Pin connected to SH_CP of 74HC595 #define _clock 12 #define _clockPORTB _clock - 8 //Pin connected to DS of 74HC595 #define _data 11 #define _dataPORTB _data - 8 ... //shiftOut bitClear(PORTB,_latchPORTB); shiftOut2(_data,_clock,registers[7]); shiftOut2(_data,_clock,registers[6]); shiftOut2(_data,_clock,registers[5]); shiftOut2(_data,_clock,registers[4]); shiftOut2(_data,_clock,registers[3]); shiftOut2(_data,_clock,registers[2]); shiftOut2(_data,_clock,registers[0]); shiftOut2(_data,_clock,registers[1]); bitSet(PORTB,_latchPORTB); ... void shiftOut2(int my_data, int my_clock, byte myDataOut) { bitClear(PORTB,_dataPORTB); for (int i=7; i>=0; i--) { bitClear(PORTB,_clockPORTB); if ( bitRead(myDataOut,i) == 1) bitSet(PORTB,_dataPORTB); else bitClear(PORTB,_dataPORTB); bitSet(PORTB,_clockPORTB); bitClear(PORTB,_clockPORTB); } digitalWrite(my_clock, 0); }Экспериментирую, с твоей функцией, а можешь вставить весь скетч? а то у меня чето не работает....
чуть позже выложу
когда то делал так
функцию шифтаут можно еще лучше
//формирование потока на регистр из одного байта void shiftout(char _byte){ //цикл по битам for (char i=0; i<8; i++) { if(_byte&(1<<i)) PORTB |= (1<<DATA); else PORTB &= ~(1<<DATA); //обозначим clock PORTB |= (1<<SH_CP); PORTB &= ~(1<<SH_CP); } }Возможно "не в тему". Но можно еще посмотреть в сторону SPI. Вообщем-то логика сдвиговых регистров совпадает с ней. Так что можно попробовать воспользоватся "аппаратными" возможностями. И выводить простым SPI.transfer сразу байт . Не знаю на какой скорости передает аппаратный, но шансы что "выйдет быстрее", за счет того что сам процессор будет меньше загружен - имеются. Да и скетч "красивше" получится.
Вот тут, можно поглядеть как это примерно делается.
leshak, той функции что я выкинул последней, хватает по быстродействию ЗА глаза в огромным запасом..
Возможно "не в тему". Но можно еще посмотреть в сторону SPI.
Сорри. Не доглядел. Выше вы уже рассматривали этот вариант. Но в любом случае, думаю, ссылочка будет полезной. Там более "ардуинистая" работа с SPI.
leshak, той функции что я выкинул последней, хватает по быстродействию ЗА глаза в огромным запасом..
Ну хватает, так хватает. Хорошо :) Я же просто "упомянул" еще один вариант (и то слегка "мимо кассы"). Может кому-нибудь он "проще покажется", или, когда важней память - пригодится.
а вот по поводу память (не влеш а оперативки) - это очень интересно. Я уже сталкивался с нехваткой (не в шифтауте), начал читать.. макросы и тд..
Ну, по моим наблюдениям, самым быстрым "пожирателем памяти" являются всякие Serial.println("Bla-bla"); и проч. строки. Так что самую ощутимую экономию дает переход на PROGMEM, PSTR . Да и "поменять" довольно быстро, особо не "корежа логику".
Посидел, побаловался, есть интересное наблюдение:
делал для своего куба,
основной код скетча:
#define _data 2 #define _clock 4 #define _lat 3 byte matrix[8][8]; byte i,j,k, x, y, z; byte pause; void setup(){ pinMode(_data, OUTPUT); pinMode(_clock, OUTPUT); pinMode(_lat, OUTPUT); blankMatrix(); matrix[1][1] = 4; } void loop(){ effectRandomeMagik(); writeMatrix(); } void blankMatrix(){ for(i = 0; i < 8; i++){ for(j = 0; j < 8; j++){ matrix[i][j] = 0; } } } /* эффект хаоса */ void effectRandomeMagik(){ pause = 10; x = random(0,8); y = random(0,8); z = random(0,8); if(bitRead(matrix[z][x], y) == 1) bitClear(matrix[z][x], y); else bitSet(matrix[z][x], y); }И собственно функция которая выводит информацию на куб
так она работает:
void writeMatrix(){ for(i = 0; i < 8; i++){ bitClear(PORTD, _lat); for(j = 0; j < 8; j++){ for(k = 0; k < 8; k++){ bitClear(PORTD, _data); if(bitRead(matrix[i][j], k) == 1) bitSet(PORTD, _data); bitSet(PORTD, _clock); bitClear(PORTD, _clock); } } for(j = 0; j < 8; j++){ bitClear(PORTD, _data); if(i == j){ bitSet(PORTD, _data); } bitSet(PORTD, _clock); bitClear(PORTD, _clock); } digitalWrite(_lat, HIGH); digitalWrite(_lat, LOW); //bitSet(PORTD, _lat); //bitClear(PORTD, _lat); } }А вот так сигнал на защелку идет не всегда:
void writeMatrix(){ for(i = 0; i < 8; i++){ bitClear(PORTD, _lat); for(j = 0; j < 8; j++){ for(k = 0; k < 8; k++){ bitClear(PORTD, _data); if(bitRead(matrix[i][j], k) == 1) bitSet(PORTD, _data); bitSet(PORTD, _clock); bitClear(PORTD, _clock); } } for(j = 0; j < 8; j++){ bitClear(PORTD, _data); if(i == j){ bitSet(PORTD, _data); } bitSet(PORTD, _clock); bitClear(PORTD, _clock); } // digitalWrite(_lat, HIGH); // digitalWrite(_lat, LOW); bitSet(PORTD, _lat); bitClear(PORTD, _lat); } }Я бы даже сказал что крайне редко, причем тактовый сигнал и сигнал данных идут нормально, в рабочем состоянии скорость порадовала по сравнению с прошлой версией на digitalWrite.
для справки: функция 8 раз записывает 64 бита данных о этаже и потом 8 бит о том какой этаж зажигать
не используй bitset/write, сразу меняй битовыми операциями на PORTD
установить PORTD |= (1<<NUMPIN)
снять PORTD &= ~(1<<NUMPIN)
NUMPIN = PD0..PD7 или 0..7
где кто в даташите
01 //формирование потока на регистр из одного байта 02 void shiftout(char _byte){ 03 //цикл по битам 04 for (char i=0; i<8; i++) { 05 if(_byte&(1<<i)) PORTB |= (1<<DATA); 06 else PORTB &= ~(1<<DATA); 07 //обозначим clock 08 PORTB |= (1<<SH_CP); 09 PORTB &= ~(1<<SH_CP); 10 } 11 12 }выдрано из рабочего проекта, быстрее некуда) только аппаратно наверно, хз
тут нет защелки, защелка СНИМАЕТСЯ перед посылками шифтаут и ставится после того как все посылки ушли. Задержек никаких нет у меня, все пашет. Кондюка на защелке нет. тестилось на 1 регистра и на 9 штуках.
да, у тебя ошибка. Снимай защелку вначале работы.
//формирование потока на регистр из одного байта void shiftout(char _byte){ //цикл по битам for (char i=0; i<8; i++) { if(_byte&(1<<i)) PORTB |= (1<<DATA); else PORTB &= ~(1<<DATA); //обозначим clock PORTB |= (1<<SH_CP); PORTB &= ~(1<<SH_CP); } }выдрано из рабочего проекта, быстрее некуда) только аппаратно наверно, хз
тут нет защелки, защелка СНИМАЕТСЯ перед посылками шифтаут и ставится после того как все посылки ушли. Задержек никаких нет у меня, все пашет. Кондюка на защелке нет. тестилось на 1 регистра и на 9 штуках.
Сегодня попробую обязательно.
да, у тебя ошибка. Снимай защелку вначале работы.
Малость не понял.... если вы про то, что перед запись новых данных надо защелку перевести в логический 0 то это я знаю, но после первого прохода функции там уже точно будет 0 .
Поэкспериментировав заметил одну фишку.... если я к ноге подключаю диод и контроллер до диод мигает через раз, собсно когда мигает тогда данные и выводятся, т.е. такое ощущение что на ноге просто нет сигнала.... но если отключить контроллер то диод начинает тускло гореть(видимо начинает выдавать нормальный сигнал..... я стал подозревать что есть трабл в контроллере...
Малость не понял.... если вы про то, что перед запись новых данных надо защелку перевести в логический 0 то это я знаю, но после первого прохода функции там уже точно будет 0 .
Поэкспериментировав заметил одну фишку.... если я к ноге подключаю диод и контроллер до диод мигает через раз, собсно когда мигает тогда данные и выводятся, т.е. такое ощущение что на ноге просто нет сигнала.... но если отключить контроллер то диод начинает тускло гореть(видимо начинает выдавать нормальный сигнал..... я стал подозревать что есть трабл в контроллере...
digitalWrite(_lat, HIGH);
digitalWrite(_lat, LOW);
вторую строчку убери, проверь правильность соединений
в динамической индикации принято делать так:
зажигаем секцию
задержка (оч малая), если не по таймеру
тушим секцию (катоды/строки/этаж)
идем в начало цикла
можно тушение вынести перд зажиганием, между ними нет задержки или очень малая
эм... как я понимаю логику работы регистров 74HC595N(скорее всего далее всем извесная вода) то у них есть пин данных, пин такта, и пин защелки, сам в себе этот регистр состоит из 8 элементов в которых по 2 D-триггера, собсно в первый триггер(в каждом элементе) мы загоняем при помощи пина данных и тактового. Затем когда информация записана в первые триггеры, сигналом на дащелку я синхронизирую значения на выходе для первого и второго триггера в каждом элементе, и могу продолжать дальше записывать в первый. т.е. состояние второго триггера меняется только тогда, когда подана логическая единица на пин защелки, как только убираем логическую единицу состояния на выходе регистра замирают.
исходя из этого я, используя тактовый пин и пин данных записываю в регистр 64+8 бита данных о том какие диоды и на каком этаже зажеч и в конце даю короткий сигнал на защелку чтобы перевести данные из внутренних регистров на внешние, и пока горит этаж я могу записать следующие 72 бита о следующем этаже, поэтому я делаю короткий сигнал на защелку в конце первого цикла. получается что диоды горят до тех пор пока не подготовится следующая порция информации о следующем этажеи быстро меняются сигналом на защелку...
и всетаки мне не понятно почему
Работает а
нет хотя логика ни как не меняется... если ссылатся на косяк в плате то почему первый вариант работает? может дело в малом времени реагирования на сигнал защелки? сигнал данных и тактовый реагируют нормально, и опятьже по даташиту регистры готовы работать на куда большей частоте чем может выдать дуина.
нашел в чем был косяк.... в конденсаторе на 0.1мкФ на защелке.... он все и портил, выпоял и все как часики работает)))) 2whoim еще вы мне говорили что не нужно....но я всеже один распаял, видимо при тех скоростях что сейчас работает он не успевал разряжатся....
скорость просто радует))) спасибо)))
Отчитываюсь:
логика кода одна и таже, до оптимизации:
скетч:
#define clock 3 //синхро контакт #define reset 4 // контакт сброса #define data 2 // контакт данных boolean matrix[9][9][9]; // массив который является внутренним byte i,j,k,x,y,z; // переменные для циклов void setup() // функция установки начальных праметров { pinMode(clock, OUTPUT); pinMode(reset, OUTPUT); pinMode(data, OUTPUT); blankMatrix(); } void loop() { effectRandomeMagik(); writeMatrix(); //выводим массив на куб } /* эффект хаоса */ void effectRandomeMagik(){ //pause = 10; x = random(0,8); y = random(0,8); z = random(0,8); matrix[z][x][y] = !matrix[z][x][y]; } void blankMatrix(){ for(i = 0; i < 8; i++){ for(j = 0; j < 8; j++){ for(k = 0; k < 8; k++){ matrix[i][k][j] = false; } } } } void writeMatrix(){ // функция которая отображает внутренний массив на кубе digitalWrite(clock, LOW); for(i = 0; i < 8; i++){ // цикл по этажам координата Z digitalWrite(reset, LOW); for(j = 0; j < 8; j++){ //цикл по рядам(координата Y) // сбрасываю синхросигнал // цикл по строкам(координата X) for(k = 0; k < 8; k++){ // сбрасываю сигнал на выходе данных digitalWrite(data, LOW); //проверка на истину if(matrix[i][k][j] == true){ // если истина то даю логическую 1 на выход digitalWrite(data, HIGH); } if(matrix[i][k][j] == true){ } else{ } digitalWrite(clock, HIGH); digitalWrite(clock, LOW); } //сбрасываю сигнал по координате Z } for(j = 0; j < 8; j++ ){ digitalWrite(data, LOW); if(i == j){ digitalWrite(data, HIGH); } digitalWrite(clock, HIGH); digitalWrite(clock, LOW); } digitalWrite(reset, HIGH); //зажигаю ряд(этаж) digitalWrite(reset, LOW); //гашу ряд(этаж) } }ссылка на видео: youtu.be/wBigrc10NPc
после оптимизации:
скетч:
#define _data 2 #define _clock 4 #define _lat 3 byte matrix[8][8]; byte i,j,k, x, y, z; byte pause; void setup(){ pinMode(_data, OUTPUT); pinMode(_clock, OUTPUT); pinMode(_lat, OUTPUT); blankMatrix(); matrix[1][1] = 4; } void loop(){ effectRandomeMagik(); writeMatrix(); } void blankMatrix(){ for(i = 0; i < 8; i++){ for(j = 0; j < 8; j++){ matrix[i][j] = 0; } } } /* эффект хаоса */ void effectRandomeMagik(){ pause = 10; x = random(0,8); y = random(0,8); z = random(0,8); if(bitRead(matrix[z][x], y) == 1) bitClear(matrix[z][x], y); else bitSet(matrix[z][x], y); } void writeMatrix(){ for(i = 0; i < 8; i++){ for(j = 0; j < 8; j++){ for(k = 0; k < 8; k++){ PORTD &= ~(1 << _data); if(bitRead(matrix[i][j], k) == 1) PORTD |= (1 << _data); PORTD |= (1 << _clock); PORTD &= ~(1 << _clock); } } for(j = 0; j < 8; j++){ PORTD &= ~(1 << _data); if(i == j){ PORTD |= (1 << _data); } PORTD |= (1 << _clock); PORTD &= ~(1 << _clock); } PORTD |= (1 << _lat); PORTD &= ~(1 << _lat); } }ссылка на видео: youtu.be/Le9mrlMyOyQ
Я думаю результат на лицо, даже если посмотреть что у меня не самый лучший фотик для въемки видео но разница очевидна.
Видео посмотрю из дома, на работе дорогой интернет.
Я работаю с регистром так.
1) Ноль на защелку - регистр доступен для записи.
2) Посылки по количеству регистров (всех микросхем). Ставлю дата и дергаю клок. Посылка - байт на микросхему.
3) Когда все посылки ушли, защелку в единичку. Выходы микросхем в этот момент меняются в соответствии с посылками.
первая посылка попадает в "дальнюю" микросхему. Последняя - в "ближнюю".
PORTD |= (1 << _lat);
66
PORTD &= ~(1 << _lat);
вы по прежнему дергаете защелку туда-сюда.
В начале записи сняли, в конце поставили. Все!
PORTD |= (1 << _lat);
66
PORTD &= ~(1 << _lat);
вы по прежнему дергаете защелку туда-сюда.
В начале записи сняли, в конце поставили. Все!
Эм.. не вижу разницы.... в начале цикла её в 0 поставить или в конце передернуть.... просто в вашем случае получается что после того как функция отработала то на защелке останется 1 а в моем случае останется 0...
так правильнее логически, как задумано производителем. Мало ли что вылезет - пока защелка снята, регистры ждут, может мусор прийти например. Или мало времени будет чтобы сработала защелка.
понял)))) поправлю)))) пасибо за инфу)))
Да за что :) когда я уже доберусь до своего куба..
Хотя у меня будет не куб, будет 3D сфера )
мммм сфера тоже прикольно, как то видел на ютубе))) я вот подумываю сделать еще один тока из белых диодов в дите полусферы на потолок как люстру)))))