Программный ШИМ на сдвиговом регистре.
- Войдите на сайт для отправки комментариев
 
Здравствуйте. Собственно, суть вопроса, который я собираюсь задать, отражена в заголовке темы. РЕализовать хочу это "just for fun", хотя, если получится хорошо...
Есть rgb светодиод. Подключаю его через сдвиговый регистр 74hc165.
 
Наваял вот такой скетчк (подключены 2 светодиода, светодиоды с общим анодом):
 
byte latchPin = 8;
byte clockPin = 12;
byte dataPin = 11;
byte rstPin = 7;
void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
  pinMode(rstPin, OUTPUT);
  
  digitalWrite(rstPin, LOW);
  digitalWrite(rstPin, HIGH);
    
  // устанавливаем синхронизацию "защелки" на LOW
  digitalWrite(latchPin, LOW);
  // 255 - все светодиоды погашены
  shiftOut(dataPin, clockPin, MSBFIRST, 252); 
  //"защелкиваем" регистр, тем самым устанавливая значения на выходах
  digitalWrite(latchPin, HIGH);
}
 
unsigned int howred=25000;
unsigned int howgreen=0;
unsigned int howblue=100;
 
 
void loop() {
    if (howblue) {
    
    // устанавливаем синхронизацию "защелки" на LOW
    digitalWrite(latchPin, LOW);
    // передаем последовательно на dataPin для синего
    shiftOut(dataPin, clockPin, MSBFIRST, 252); 
    //"защелкиваем" регистр, тем самым устанавливая значения на выходах
    digitalWrite(latchPin, HIGH);
    // пауза перед следующей итерацией
    delayMicroseconds(howblue);
    }
    
    if (howred) {
   
    digitalWrite(latchPin, LOW);
    // передаем последовательно на dataPin для красного
    shiftOut(dataPin, clockPin, MSBFIRST, 207); 
    //"защелкиваем" регистр, тем самым устанавливая значения на выходах
    digitalWrite(latchPin, HIGH);
    // пауза перед следующей итерацией
    delayMicroseconds(howred);
    }
    
    if (howgreen){
    
    digitalWrite(latchPin, LOW);
    // передаем последовательно на dataPin для зелёного цвета
    shiftOut(dataPin, clockPin, MSBFIRST, 243); 
    //"защелкиваем" регистр, тем самым устанавливая значения на выходах
    digitalWrite(latchPin, HIGH);
    // пауза перед следующей итерацией
    delayMicroseconds(howgreen);
    }
    
}
Это работает(в данном случае получается красивый малиновый цвет), но не совсем адекватно. Например, если в данном скетче уменьшить Howred в 10 раз, то цвет не изменится, а при увеличении howblue в 10 раз, цвет меняется. И не регулируется общая яркость. Пробовал ставить задержку при полностью выключенных светодиодах, но тогда при установке значения задержки до 5000 яркость не меняется, а при 10000, например, яркость снижается, но появляется заметное глазу мигание.
Вопрос: вообще взлетит или на регистрах это нереально?
P.S. Про внешние ШИМ-контроллеры знаю, занимаюсь этим для развлечения.
          
На регистрах можно, конечно. Можно даже при желании замутить в прерывание какое-нибудь быстрое на таймере...
По теме.
Я бы пошел по другому пути. В цикле от 0 до 255 сравнивал с установкой яркости светодиода (в диапазоне от 0 до 255) и устанавливал бы соответствующий бит. Где-то так:
unsigned char howred=250; unsigned char howgreen=0; unsigned char howblue=10; unsigned char tact=0; unsigned char buffer; void loop() { buffer=255; if (howblue>tact) { buffer = buffer & 252; } if (howred>tact) { buffer = buffer & 207; } if (howgreen>tact){ buffer = buffer & 243; } digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, buffer); //"защелкиваем" регистр, тем самым устанавливая значения на выходах digitalWrite(latchPin, HIGH); // пауза перед следующей итерацией delayMicroseconds(1000); tact++; }На регистрах можно, конечно. Можно даже при желании замутить в прерывание какое-нибудь быстрое на таймере...
По теме.
Я бы пошел по другому пути. В цикле от 0 до 255 сравнивал с установкой яркости светодиода (в диапазоне от 0 до 255) и устанавливал бы соответствующий бит. Где-то так:
А что делает оператор &?
Интернет говорит, что это побитовое сравнение (0&0=0; 0&1=0; 1&1=1;) но в данном случае, я так понимаю, этот оператор "насаживает" на buffer значения? Или...
На регистрах можно, конечно. Можно даже при желании замутить в прерывание какое-нибудь быстрое на таймере...
По теме.
Я бы пошел по другому пути. В цикле от 0 до 255 сравнивал с установкой яркости светодиода (в диапазоне от 0 до 255) и устанавливал бы соответствующий бит. Где-то так:
А что делает оператор &?
Интернет говорит, что это побитовое сравнение (0&0=0; 0&1=0; 1&1=1;) но в данном случае, я так понимаю, этот оператор "насаживает" на buffer значения? Или...
UPD:
А! & складывает биты! Отлично. Спасибо!
P.S.
Промахнулся мимо кнопки редактирования.
Не складывает, а побитно умножает. Это основы с - битовые операции. Так мы ставим 0 на нужное место. Правильно было написать buffer&=0b11110111 - сбросить бит в четвертой позиции в 0. Но боялся, что испугаетесь, а так - разобрались :)
Единицу поставить можно так: buffer|=0b00001000 (| - это как раз побитное сложение)
Слишком медленно... Цвет получается ровный, но... стробоскоп, одним словом :)
Код ещё не ковырял. Просто загрузил в ардуину в том виде, что вы дали :)
Убавте delay в конце цикла - он должен быть не более 200. Мне было лень считать, когда текст писал.
По большому счету, его можно вообще убрать. Но я бы оставил. Скажем, 50 или даже 100 :)
Если денег не жалко, то можно посмотреть в сторону I2C порт-расширителей. Некоторый умееют по I2C принять команду типа "включили вот такой PWM на такой своей ноге". И все. Дальше он сам его там генерить, не занимая основной контроллер.
Вот например такая "жирная штука" http://www.nxp.com/documents/data_sheet/PCA9685.pdf
Много чего еще умеет помимо "тупо включить PWM".
Убавте delay в конце цикла - он должен быть не более 200. Мне было лень считать, когда текст писал.
По большому счету, его можно вообще убрать. Но я бы оставил. Скажем, 50 или даже 100 :)
Его убрал первым делом - не помогло. Я думаю, что в моём способе - держать один цвет, потом другой, регистр переключается 1 раз и это незаметно. А в вашем - ему приходится переключаться огромное количество раз, что становится заметным глазу.
Если денег не жалко, то можно посмотреть в сторону I2C порт-расширителей. Некоторый умееют по I2C принять команду типа "включили вот такой PWM на такой своей ноге". И все. Дальше он сам его там генерить, не занимая основной контроллер.
Вот например такая "жирная штука" http://www.nxp.com/documents/data_sheet/PCA9685.pdf
Много чего еще умеет помимо "тупо включить PWM".
ненене. Нужно именно на сдвиговом регистре :)
А вы случайно сдвиговым регистром не ошиблись? Судя по даташиту 74hc165 имеет параллельный вход и последовательный выход. То есть данный регистр предназначен для размножения входов, а не выходов. Честно говоря странно, что у вас вообще что-то работает...
Может все таки вы используете 74hc595 ?
Если у вас все-таки 74hc595 (или что то пожее), то попробуйте так:
byte latchPin = 8; byte clockPin = 12; byte dataPin = 11; byte rstPin = 7; byte RED[] = { 30, B11110011}; byte GREEN[] = {120, B11001111}; byte BLUE[] = { 80, B00111111}; int i = 0; byte OUT = B11111111; int BIT_PWM = 256; void setup() { pinMode(latchPin, OUTPUT); pinMode(clockPin, OUTPUT); pinMode(dataPin, OUTPUT); pinMode(rstPin, OUTPUT); digitalWrite(rstPin, LOW); digitalWrite(rstPin, HIGH); digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, OUT); digitalWrite(latchPin, HIGH); } void loop() { i = 0; while(i < BIT_PWM){ COLOR_OUT(RED); COLOR_OUT(GREEN); COLOR_OUT(BLUE); i++; digitalWrite(latchPin, LOW); shiftOut(dataPin, clockPin, MSBFIRST, OUT); digitalWrite(latchPin, HIGH); } } void COLOR_OUT(byte* COLOR){ if(i < COLOR[0]){ OUT &= COLOR[1]; } else{ OUT |= ~COLOR[1]; } }По моим подсчетам у вас должно получиться около 28 "кадров" в секунду, если для вас это медленно, то:
1. Сделайте разрядность ШИМ не 8 бит (BIT_PWM = 256), а например 7 бит (BIT_PWM = 128), из моей практики этого вполне достаточно, что бы плавно рулить цветами;
2. Используйте не shiftOut, а SPI он в разы быстрее. При частоте МК 16МГц частота линии CLK может достигать 8МГц.
у меня почему то не хочет работать ни один код, поясните что делает в схеме/коде этот пин "
byterstPin = 7;" , мне сейчас нужно реализовать программный шим для 64 разрядов. сделано на 74HC595 регистрах, светики подключены через 220 ом на землю.задача понизить яркость на всех светиках сразу, уж больно ярко в панели они светят) возможно ли это?
программный шим для 64 разрядов. сделано на 74HC595 регистрах, светики подключены через 220 ом на землю.
В общем виде (регулировку отдельно каждого светика) - не получится. Выйдет "стробоскоп".
задача понизить яркость на всех светиках сразу, уж больно ярко в панели они светят) возможно ли это?
А вот это - уже реальней. Только регистры тут совсем не причем. Общая яркость и "кто включен, а кто нет" - это отдельные блоки.
У вас же все светики имеют "что-то общие" (общий катод или анод). Вот этот "общий" и нужно не на прямую подключать к земле (или питанию), а через какой-то полевик (или ULN2003 и т.п.). Через какой-то "краник", на который вы можете подать шим от ардуины.
Вообщем рассматривайте свою задачу как "нужно шимить нагрузку", а не "шимить регистры".
посадить все сразу никак не получится физически, ну или получится только мне эти 64 провода нужно вырезать из 15 плат и соединять вместе((( а было бы идеально!!! я решил задачу так:
1 вариант
digitalWrite(ssLedPin, LOW); shiftOut(dataLedPin, sclkLedPin, MSBFIRST, "данные"); digitalWrite(ssLedPin, HIGH); digitalWrite(ssLedPin, LOW); // гасим все светики в "0" for(int i = 0; i < 8; i ++){ shiftOut(dataLedPin, sclkLedPin, MSBFIRST, 0); } digitalWrite(ssLedPin, HIGH); delay(10); // задержка на включение+ просто и без моргания, - 10 мс для меня уже проблема....
2 вариант
timer=millis(); digitalWrite(ssLedPin, LOW); shiftOut(dataLedPin, sclkLedPin, MSBFIRST, "данные"); digitalWrite(ssLedPin, HIGH); if (timer-ledTimerPrev>5){ digitalWrite(ssLedPin, LOW); ledTimerPrev=timer;} for (int i = 0; i < 8; i++){ shiftOut(dataLedPin, sclkLedPin, MSBFIRST, 0); } digitalWrite(ssLedPin, HIGH);+ нет задержки для основного кода, - наблюдаются нестабильные моргания, немного напрягающие...
есть ещё варианты?
я заметил что моргание получается во всём коде а не в примере что я написал, выключил не нужную задержку и всё заработало как надо.
> ну или получится только мне эти 64 провода нужно вырезать из 15 плат и соединять вмест
Какие 64-ре? Вы что? Я же расписывал что вам нужно смотреть на светики "с другого конца". У вас же эти светики имеют общую землю или общие питание. Вот это и нужно рулить.
Как у вас светик подключен? Грубо говоря "одной ногой к регистру, другой к земле". А вам нужно "к земле через полевик". Причем вас же никто не заставляет для каждого светика ставить отдельный полевик. Взять полевик помощьней и все к одному свести. Сейчас они у вас к земле сходятся, а будут к полевику.
По коду:
зачем вы оборачиваете в millis() какие-то составные части процесса "отсылки данных на сдвиговый"? Оберните все целиком.
Раз в 10 мсек - отсылать данные на сдвиговый.
Только еще раз - фигня это. Когда у вас увеличится количество сдвиговых вы задержку будете не добавлять, а бится как-бы ее поменьше сделать. Само время передачи данных на сдвиговый уже будет слишком большим для "комфортности глаз".
Или не мучайтесь и возмите i2c экспандер, который сам умеет шим делать, не запаривая мозг дуине (ссылку выше я давал).
ShiftPWM
смотрел здесь, но долго не разбирался, спасибо но в следующиё раз)
Какие 64-ре? Вы что? Я же расписывал...
leshak
а теперь представте что у меня в конструкции 15 плат, на которых находятся кнопки, светодиоды, резисторы, транзисторы, разьёмы наружные и внутренние, платы разнесены по всему корпусу "прибора", подключены по разному, они все разные, Вы хотите что бы я резал дорожки от каждого светодиода на плате соединил так каждый катод светодиода а после ещё с каждой платы тянул эти провода в одну точку на колектор транзистора упровляемого шимом? ну и как Вы себе это представляете?))))
я про эту конструкцию как то Вам рассказывал, она готова и я как то не хочу её переделывать, осталось дело за кодом и только, когда закончу выложу здесь проект)
по коду, я эксперементировал, так работает и по другому работает, я оставил задержку именно на "гашение" а дальше пусть код работает своим ходом...