Программный ШИМ на сдвиговом регистре.

uscr
Offline
Зарегистрирован: 17.08.2012

 Здравствуйте. Собственно, суть вопроса, который я собираюсь задать, отражена в заголовке темы. РЕализовать хочу это "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. Про внешние ШИМ-контроллеры знаю, занимаюсь этим для развлечения.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

На регистрах можно, конечно. Можно даже при желании замутить в прерывание какое-нибудь быстрое на таймере...

По теме.

Я бы пошел по другому пути. В цикле от 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++;
}

 

uscr
Offline
Зарегистрирован: 17.08.2012

AlexFisher пишет:

На регистрах можно, конечно. Можно даже при желании замутить в прерывание какое-нибудь быстрое на таймере...

По теме.

Я бы пошел по другому пути. В цикле от 0 до 255 сравнивал с установкой яркости светодиода (в диапазоне от 0 до 255) и устанавливал бы соответствующий бит. Где-то так:

 

 

А что делает оператор &?

Интернет говорит, что это побитовое сравнение (0&0=0; 0&1=0; 1&1=1;) но в данном случае, я так понимаю, этот оператор "насаживает" на buffer значения? Или...

 

uscr
Offline
Зарегистрирован: 17.08.2012

uscr]</p> <p>[quote=AlexFisher пишет:

На регистрах можно, конечно. Можно даже при желании замутить в прерывание какое-нибудь быстрое на таймере...

По теме.

Я бы пошел по другому пути. В цикле от 0 до 255 сравнивал с установкой яркости светодиода (в диапазоне от 0 до 255) и устанавливал бы соответствующий бит. Где-то так:

 

 

А что делает оператор &?

Интернет говорит, что это побитовое сравнение (0&0=0; 0&1=0; 1&1=1;) но в данном случае, я так понимаю, этот оператор "насаживает" на buffer значения? Или...

 UPD:

А! & складывает биты! Отлично. Спасибо!

 

P.S.

Промахнулся мимо кнопки редактирования.

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Не складывает, а побитно умножает. Это основы с - битовые операции. Так мы ставим 0 на нужное место. Правильно было написать buffer&=0b11110111 - сбросить бит в четвертой позиции в 0. Но боялся, что испугаетесь, а так - разобрались :)

Единицу поставить можно так: buffer|=0b00001000 (| - это как раз побитное сложение)

uscr
Offline
Зарегистрирован: 17.08.2012

 Слишком медленно... Цвет получается ровный, но... стробоскоп, одним словом :)
Код ещё не ковырял. Просто загрузил в ардуину в том виде, что вы дали :)

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Убавте delay в конце цикла - он должен быть не более 200. Мне было лень считать, когда текст писал. 

По большому счету, его можно вообще убрать. Но я бы оставил. Скажем, 50 или даже 100 :)

leshak
Offline
Зарегистрирован: 29.09.2011

 Если денег не жалко, то можно посмотреть в сторону I2C порт-расширителей. Некоторый умееют по I2C принять команду типа "включили вот такой PWM на такой своей ноге". И все. Дальше он сам его там генерить, не занимая основной контроллер.

Вот например такая "жирная штука" http://www.nxp.com/documents/data_sheet/PCA9685.pdf

Много чего еще умеет помимо "тупо включить PWM". 

uscr
Offline
Зарегистрирован: 17.08.2012

AlexFisher пишет:

Убавте delay в конце цикла - он должен быть не более 200. Мне было лень считать, когда текст писал. 

По большому счету, его можно вообще убрать. Но я бы оставил. Скажем, 50 или даже 100 :)

Его убрал первым делом - не помогло. Я думаю, что в моём способе - держать один цвет, потом другой, регистр переключается 1 раз и это незаметно. А в вашем - ему приходится переключаться огромное количество раз, что становится заметным глазу.

uscr
Offline
Зарегистрирован: 17.08.2012

leshak пишет:

 Если денег не жалко, то можно посмотреть в сторону I2C порт-расширителей. Некоторый умееют по I2C принять команду типа "включили вот такой PWM на такой своей ноге". И все. Дальше он сам его там генерить, не занимая основной контроллер.

Вот например такая "жирная штука" http://www.nxp.com/documents/data_sheet/PCA9685.pdf

Много чего еще умеет помимо "тупо включить PWM". 

ненене. Нужно именно на сдвиговом регистре :)

maksim
Offline
Зарегистрирован: 12.02.2012

А вы случайно сдвиговым регистром не ошиблись? Судя по даташиту 74hc165 имеет параллельный вход и последовательный выход. То есть данный регистр предназначен для размножения входов, а не выходов. Честно говоря странно, что у вас вообще что-то работает...
Может все таки вы используете 74hc595 ?

maksim
Offline
Зарегистрирован: 12.02.2012

 Если у вас все-таки 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МГц.

radiofannat
radiofannat аватар
Offline
Зарегистрирован: 11.09.2013

у меня почему то не хочет работать ни один код, поясните что делает в схеме/коде этот пин "byte rstPin = 7;" , мне сейчас нужно реализовать программный шим для 64 разрядов. сделано на 74HC595 регистрах, светики подключены через 220 ом на землю.

задача понизить яркость на всех светиках сразу, уж больно ярко в панели они светят) возможно ли это?

leshak
Offline
Зарегистрирован: 29.09.2011

radiofannat пишет:

программный шим для 64 разрядов. сделано на 74HC595 регистрах, светики подключены через 220 ом на землю.

В общем виде (регулировку отдельно каждого светика) - не получится. Выйдет "стробоскоп".

radiofannat пишет:

задача понизить яркость на всех светиках сразу, уж больно ярко в панели они светят) возможно ли это?

А вот это - уже реальней. Только регистры тут совсем не причем. Общая яркость и "кто включен, а кто нет" - это отдельные блоки.

У вас же все светики имеют "что-то общие" (общий катод или анод). Вот этот "общий" и нужно не на прямую подключать к земле (или питанию), а через какой-то полевик (или ULN2003 и т.п.). Через какой-то "краник", на который вы можете подать шим от ардуины.

Вообщем рассматривайте свою задачу как "нужно шимить нагрузку", а не "шимить регистры". 

radiofannat
radiofannat аватар
Offline
Зарегистрирован: 11.09.2013

посадить все сразу никак не получится физически, ну или получится только мне эти 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);

+ нет задержки для основного кода, - наблюдаются нестабильные моргания, немного напрягающие...

есть ещё варианты?

radiofannat
radiofannat аватар
Offline
Зарегистрирован: 11.09.2013

я заметил что моргание получается во всём коде а не в примере что я написал, выключил не нужную задержку и всё заработало как надо.

leshak
Offline
Зарегистрирован: 29.09.2011

> ну или получится только мне эти 64 провода нужно вырезать из 15 плат и соединять вмест

Какие 64-ре? Вы что? Я же расписывал что вам нужно смотреть на светики "с другого конца". У вас же эти светики имеют общую землю или общие питание. Вот это и нужно рулить.

Как у вас светик подключен? Грубо говоря "одной ногой к регистру, другой к земле". А вам нужно "к земле через полевик". Причем вас же никто не заставляет для каждого светика ставить отдельный полевик. Взять полевик помощьней и все к одному свести. Сейчас они у вас к земле сходятся, а будут к полевику.

По коду:

зачем вы оборачиваете в millis() какие-то составные части процесса "отсылки данных на сдвиговый"? Оберните все целиком.

Раз в 10 мсек - отсылать данные на сдвиговый.

Только еще раз - фигня это. Когда у вас увеличится количество сдвиговых вы задержку будете не добавлять, а бится как-бы ее поменьше сделать. Само время передачи данных на сдвиговый уже будет слишком большим для "комфортности глаз".

Или не мучайтесь и возмите i2c экспандер, который сам умеет шим делать, не запаривая мозг дуине (ссылку выше я давал).

std
Offline
Зарегистрирован: 05.01.2012
radiofannat
radiofannat аватар
Offline
Зарегистрирован: 11.09.2013

std пишет:
ShiftPWM

смотрел здесь, но долго не разбирался, спасибо но в следующиё раз)

radiofannat
radiofannat аватар
Offline
Зарегистрирован: 11.09.2013

leshak пишет:

Какие 64-ре? Вы что? Я же расписывал...

leshak

а теперь представте что у меня в конструкции 15 плат, на которых находятся кнопки, светодиоды, резисторы, транзисторы, разьёмы наружные и внутренние, платы разнесены по всему корпусу "прибора", подключены по разному, они все разные, Вы хотите что бы я резал дорожки от каждого светодиода на плате соединил так каждый катод светодиода а после ещё с каждой платы тянул эти провода в одну точку на колектор транзистора упровляемого шимом? ну и как Вы себе это представляете?)))) 

я про эту конструкцию как то Вам рассказывал, она готова и я как то не хочу её переделывать, осталось дело за кодом и только, когда закончу выложу здесь проект)

по коду, я эксперементировал, так работает и по другому работает, я оставил задержку именно на "гашение" а дальше пусть код работает своим ходом...