Добрый день. Столкнулся с такой проблемой: используя программный ШИМ при выставлении скважности на выходе 0 остается слабое свечение с мерцанием светодиодной ленты. Вкладка программы Код (C++): #define PIN_4 // подсветка FANfront №1 #define PIN_7 // подсветка FANtop int brightnessDDR = 0; // яркость модулей ОЗУ int fadeAmountDDR = 1; // прирощение яркости ОЗУ int modDDR = 0; // режим подсветки ОЗУ byte incomingByte; // переменная сериал void setup() { Init_PWM(); analog_Frequence(1); // предделитель от 1 до 7 частоты ШИМ pinMode(4, OUTPUT); pinMode(7, OUTPUT); Serial.begin(9600); // скорость порта 9600 } void loop() { if (Serial.available() > 0) { incomingByte = Serial.read() ; if(incomingByte == '0'){ modDDR = 0 ; Serial.println("Подсветка ОЗУ выключены") ; } else if (incomingByte == '1'){ modDDR = 1 ; Serial.println("Подсветка ОЗУ постоянный режим") ; } else if (incomingByte == '2'){ modDDR = 2 ; brightnessDDR = 0 ; Serial.println("Подсветка ОЗУ синхронная пульсация") ; } else if (incomingByte == '3'){ modDDR = 3 ; brightnessDDR = 0 ; Serial.println("Подсветка ОЗУ асинхронная пульсация") ; } } if (modDDR == 0) { brightnessDDR = 0 ; analog_Write(4, brightnessDDR) ; analog_Write(7, brightnessDDR) ; } if (modDDR == 1) { brightnessDDR = 50 ; analog_Write(4, brightnessDDR) ; analog_Write(7, brightnessDDR) ; } if (modDDR == 2) { analog_Write(4, brightnessDDR) ; analog_Write(7, brightnessDDR) ; brightnessDDR = brightnessDDR + fadeAmountDDR ; if (brightnessDDR == 0 || brightnessDDR == 50) { fadeAmountDDR = -fadeAmountDDR ; } } if (modDDR == 3) { analog_Write(4, 50-brightnessDDR) ; analog_Write(7, brightnessDDR) ; brightnessDDR = brightnessDDR + fadeAmountDDR ; if (brightnessDDR == 0 || brightnessDDR == 50) { fadeAmountDDR = -fadeAmountDDR ; } } delay(40); } Вкладка PWM Код (C++): #ifdef ALL #define PIN_0 #define PIN_1 #define PIN_2 #define PIN_3 #define PIN_4 #define PIN_5 #define PIN_6 #define PIN_7 #define PIN_8 #define PIN_9 #define PIN_10 #define PIN_11 #define PIN_12 #define PIN_13 #define PIN_14 #define PIN_15 #define PIN_16 #define PIN_17 #define PIN_18 #define PIN_19 #endif #define SBI(port, pin) asm volatile ("sbi %0, %1" :: "I" (_SFR_IO_ADDR(port)), "I" (pin)) #define CHECK(pwms, port, pin) \ asm volatile ( \ "cpc %0, %1 \n\t" \ "brlo 0f \n\t" \ "cbi %2, %3 \n\t" \ "rjmp 1f \n\t" \ "0: sbi %2, %3 \n\t" \ "1: nop \n\t" \ : "+r" (pwm), "+r" (pwms) \ : "I" (_SFR_IO_ADDR(port)), "I" (pin)\ )\ void Init_PWM() { cli(); TCCR2A = 0; TCCR2B = 3; //CLK OCR2A = 1; TIMSK2 = 2; //разрешаем прерывание по совпадению sei(); #ifdef PIN_0 SBI(DDRD, 0); #endif #ifdef PIN_1 SBI(DDRD, 1); #endif #ifdef PIN_2 SBI(DDRD, 2); #endif #ifdef PIN_3 SBI(DDRD, 3); #endif #ifdef PIN_4 SBI(DDRD, 4); #endif #ifdef PIN_5 SBI(DDRD, 5); #endif #ifdef PIN_6 SBI(DDRD, 6); #endif #ifdef PIN_7 SBI(DDRD, 7); #endif #ifdef PIN_8 SBI(DDRB, 0); #endif #ifdef PIN_9 SBI(DDRB, 1); #endif #ifdef PIN_10 SBI(DDRB, 2); #endif #ifdef PIN_11 SBI(DDRB, 3); #endif #ifdef PIN_12 SBI(DDRB, 4); #endif #ifdef PIN_13 SBI(DDRB, 5); #endif #ifdef PIN_14 SBI(DDRC, 0); #endif #ifdef PIN_15 SBI(DDRC, 1); #endif #ifdef PIN_16 SBI(DDRC, 2); #endif #ifdef PIN_17 SBI(DDRC, 3); #endif #ifdef PIN_18 SBI(DDRC, 4); #endif #ifdef PIN_19 SBI(DDRC, 5); #endif } volatile uint8_t pwm; volatile uint8_t pwms[20]; void analog_Frequence(byte prescaler) { TCCR2B = prescaler; //CLK } void analog_Write(byte pin, byte value) { pwms[pin] = value; } byte analog_State(byte pin) { return pwms[pin]; } ISR(TIMER2_COMPA_vect) { asm volatile ("clr %0" : "+r" (TCNT2)); // TCNT2 = 0; #ifdef PIN_0 CHECK(pwms[0], PORTD, 0); #endif #ifdef PIN_1 CHECK(pwms[1], PORTD, 1); #endif #ifdef PIN_2 CHECK(pwms[2], PORTD, 2); #endif #ifdef PIN_3 CHECK(pwms[3], PORTD, 3); #endif #ifdef PIN_4 CHECK(pwms[4], PORTD, 4); #endif #ifdef PIN_5 CHECK(pwms[5], PORTD, 5); #endif #ifdef PIN_6 CHECK(pwms[6], PORTD, 6); #endif #ifdef PIN_7 CHECK(pwms[7], PORTD, 7); #endif #ifdef PIN_8 CHECK(pwms[8], PORTB, 0); #endif #ifdef PIN_9 CHECK(pwms[9], PORTB, 1); #endif #ifdef PIN_10 CHECK(pwms[10], PORTB, 2); #endif #ifdef PIN_11 CHECK(pwms[11], PORTB, 3); #endif #ifdef PIN_12 CHECK(pwms[12], PORTB, 4); #endif #ifdef PIN_13 CHECK(pwms[13], PORTB, 5); #endif #ifdef PIN_14 CHECK(pwms[14], PORTC, 0); #endif #ifdef PIN_15 CHECK(pwms[15], PORTC, 1); #endif #ifdef PIN_16 CHECK(pwms[16], PORTC, 2); #endif #ifdef PIN_17 CHECK(pwms[17], PORTC, 3); #endif #ifdef PIN_18 CHECK(pwms[18], PORTC, 4); #endif #ifdef PIN_19 CHECK(pwms[19], PORTC, 5); #endif asm volatile ("inc %0" : "+r" (pwm)); //pwm++; } Схема управления нагрузкой собрана на мосфетах IRF520, токоограничительные резисторы затвора 100 Ом, подтягивающие резисторы 10 кОм, лента 12 В пробовал на разных светодиодах, эффект один и тот же. Без ардуино лента не горит вообще, утечки через мосфет нет, напряжение на выходе ардуино при скважности 0 тестером не померить, несколько милливольт, осцилографа к сожалению нет. Свечение появляется только после запуска программы контроллера и перехода в режим "отключено". Информации по этому поводу не нашел в интернете. Само мерцание меня не напрягает и в режиме, когда лента горит или "дышит" - незаметно для глаза. Но почему при скважности 0 на затворе мосфета остается потенциал, который заставляет ленту подсвечиваться мне не понятно. Если возможно, подскажите вариант аппаратной или программной корректировки. Было желание увеличить токоограничительный резистор, или уменьшить подтягивающий, но под рукой нет других номиналов, поможет ли это? Заранее благодарю за ответ! Не судите строго, если просмотрел эту тему на форуме, но инфу нашел только о сглаживании пульсаций ШИМ. Еще говорили о том, что у ШИМ таймера нет нулевого дьюти... Это не понял и мне, немного далекому от глубин программирования, ни о чем не говорит.
Есть мнение, что перемудрили с ассемблерными вставками -- лучше бы это дело (генерацию ассемблерного кода) доверили компилятору языка С. В макросе 'CHECK' в ассемблерной вставке первая команда сравнение регистров с учётом флага переноса: Вы уверены, что флаг переноса во время выполнения команды всегда будет иметь определённое значение? И на чём основана Ваша уверенность? Регистр TCNT2 доступен только через адрес оперативной памяти (команды sts и lds). У меня есть сомнения, что команда очистки регистра сработает правильно. Есть и нулевое (когда постоянный низкий уровень) и стопроцентное (когда постоянный высокий уровень).
Код PWM не мой, взят с форума ардуино.ру. Иного способа генерации ШИМ на более чем 6 пинах нано не нашел. Описал что стоят подтягивающие резисторы 10 кОм.
Подтягивающие, это к Vcc надо понимать? А зачем он вообще нужен? Out порт всегда подключен к Gnd или к Vcc. И нечего там подтягивать или приземлять.
cpc это сравнение операндов минус бит переноса. Бит переноса у вас нигде явно не устанавливается, поэтому может оказаться любым и при pwms == 0 это значение будет определяющим. Замените cpc на cp.
Код кривой, требует исправлений. Как минимум: и строку: Код (C++): asm volatile ("clr %0" : "+r" (TCNT2)); на Код (C++): TCNT2 = 0; Прежде, чем порт станет OUT пройдёт определённое время, в течении которого состояние выхода МК будет не пойми каким, и чтобы в течении этого времени затвор полевика был в определённом состоянии применяют подтягивающие резисторы. Потом уже такие резисторы скорее мешают, чем помогают.
AlexU и mcureenab спасибо огромное. Все получилось. При нулевой скважности наглухо тухнет. Отмечу, что на форуме arduino.ru так и не смогли ответить или не захотели.
На arduino.ru вообще какая то недружелюбгая атмосфера. Есть кучка завсегдатаев, которые не очень жалуют новичков.
Я это понял уже. Но общение в режиме "Холоп, иди учи матчасть - здесь тебе объяснять ничего не будут" меня немного напрягло, при условии что я вежливо объяснил, что знания в программировании на уровне школьного Паскаля. Может я конечно не прав был в чем-то, но когда люди перешли на личности - стало понятно, что туда я еще раз напишу в крайнем случае.
Не, ты не прав. Если вопрос задан корректно - обязательно ответят, и по теме. Со ссылками на матчасть. А вот если холопу этого недостаточно и он начинает вставать в позу - тогда да, осадят по самые не балуй. Думающему и желающему учиться человеку - вполне достаточно ссылки на матчасть, в 99% случаев. Все остальные - ленивые жопы
В общем с нулевой скважностью разобрался, но вот код программного ШИМ почему-то блокирует обмен по serial. Скину еще раз код, с которым работаю. Спойлер: Основной код Код (C++): #define PIN_4 #define PIN_7 int brightnessDDR = 0; // яркость модулей ОЗУ int fadeAmountDDR = 1; // прирощение яркости ОЗУ int modDDR = 3; // режим подсветки ОЗУ int incomingByte; // переменная сериал void setup() { Init_PWM(); analog_Frequence(3); // предделитель от 1 до 7 частоты ШИМ pinMode(4, OUTPUT); pinMode(7, OUTPUT); Serial.begin(9600); // скорость порта 9600 } void loop() { if (Serial.available() > 0) { incomingByte = Serial.read() ; if(incomingByte == '0'){ modDDR = 0 ; Serial.println("подсветка ОЗУ выключена") ; } else if (incomingByte == '1'){ modDDR = 1 ; Serial.println("подсветка ОЗУ постоянный режим") ; } else if (incomingByte == '2'){ modDDR = 2 ; Serial.println("подсветка ОЗУ синхронная пульсация") ; } else if (incomingByte == '3'){ modDDR = 3 ; Serial.println("подсветка ОЗУ асинхронная пульсация") ; } else { Serial.println("недопустимая команда") ; } } if (modDDR == 0) { brightnessDDR = 0 ; analog_Write(4, brightnessDDR) ; analog_Write(7, brightnessDDR) ; } if (modDDR == 1) { brightnessDDR = 50 ; analog_Write(4, brightnessDDR) ; analog_Write(7, brightnessDDR) ; } if (modDDR == 2) { analog_Write(4, brightnessDDR) ; analog_Write(7, brightnessDDR) ; brightnessDDR = brightnessDDR + fadeAmountDDR ; if (brightnessDDR == 0 || brightnessDDR == 50) { fadeAmountDDR = -fadeAmountDDR ; } } if (modDDR == 3) { analog_Write(4, 50-brightnessDDR) ; analog_Write(7, brightnessDDR) ; brightnessDDR = brightnessDDR + fadeAmountDDR ; if (brightnessDDR == 0 || brightnessDDR == 50) { fadeAmountDDR = -fadeAmountDDR ; } } delay(40); } Спойлер: Код PWM Код (C++): #ifdef ALL #define PIN_0 #define PIN_1 #define PIN_2 #define PIN_3 #define PIN_4 #define PIN_5 #define PIN_6 #define PIN_7 #define PIN_8 #define PIN_9 #define PIN_10 #define PIN_11 #define PIN_12 #define PIN_13 #define PIN_14 #define PIN_15 #define PIN_16 #define PIN_17 #define PIN_18 #define PIN_19 #endif #define SBI(port, pin) asm volatile ("sbi %0, %1" :: "I" (_SFR_IO_ADDR(port)), "I" (pin)) #define CHECK(pwms, port, pin) \ asm volatile ( \ "cp %0, %1 \n\t" \ "brlo 0f \n\t" \ "cbi %2, %3 \n\t" \ "rjmp 1f \n\t" \ "0: sbi %2, %3 \n\t" \ "1: nop \n\t" \ : "+r" (pwm), "+r" (pwms) \ : "I" (_SFR_IO_ADDR(port)), "I" (pin)\ )\ void Init_PWM() { cli(); TCCR2A = 0; TCCR2B = 3; //CLK OCR2A = 1; TIMSK2 = 2; //разрешаем прерывание по совпадению sei(); #ifdef PIN_0 SBI(DDRD, 0); #endif #ifdef PIN_1 SBI(DDRD, 1); #endif #ifdef PIN_2 SBI(DDRD, 2); #endif #ifdef PIN_3 SBI(DDRD, 3); #endif #ifdef PIN_4 SBI(DDRD, 4); #endif #ifdef PIN_5 SBI(DDRD, 5); #endif #ifdef PIN_6 SBI(DDRD, 6); #endif #ifdef PIN_7 SBI(DDRD, 7); #endif #ifdef PIN_8 SBI(DDRB, 0); #endif #ifdef PIN_9 SBI(DDRB, 1); #endif #ifdef PIN_10 SBI(DDRB, 2); #endif #ifdef PIN_11 SBI(DDRB, 3); #endif #ifdef PIN_12 SBI(DDRB, 4); #endif #ifdef PIN_13 SBI(DDRB, 5); #endif #ifdef PIN_14 SBI(DDRC, 0); #endif #ifdef PIN_15 SBI(DDRC, 1); #endif #ifdef PIN_16 SBI(DDRC, 2); #endif #ifdef PIN_17 SBI(DDRC, 3); #endif #ifdef PIN_18 SBI(DDRC, 4); #endif #ifdef PIN_19 SBI(DDRC, 5); #endif } volatile uint8_t pwm; volatile uint8_t pwms[20]; void analog_Frequence(byte prescaler) { TCCR2B = prescaler; //CLK } void analog_Write(byte pin, byte value) { pwms[pin] = value; } byte analog_State(byte pin) { return pwms[pin]; } ISR(TIMER2_COMPA_vect) { TCNT2 = 0; // asm volatile ("clr %0" : "+r" (TCNT2)); #ifdef PIN_0 CHECK(pwms[0], PORTD, 0); #endif #ifdef PIN_1 CHECK(pwms[1], PORTD, 1); #endif #ifdef PIN_2 CHECK(pwms[2], PORTD, 2); #endif #ifdef PIN_3 CHECK(pwms[3], PORTD, 3); #endif #ifdef PIN_4 CHECK(pwms[4], PORTD, 4); #endif #ifdef PIN_5 CHECK(pwms[5], PORTD, 5); #endif #ifdef PIN_6 CHECK(pwms[6], PORTD, 6); #endif #ifdef PIN_7 CHECK(pwms[7], PORTD, 7); #endif #ifdef PIN_8 CHECK(pwms[8], PORTB, 0); #endif #ifdef PIN_9 CHECK(pwms[9], PORTB, 1); #endif #ifdef PIN_10 CHECK(pwms[10], PORTB, 2); #endif #ifdef PIN_11 CHECK(pwms[11], PORTB, 3); #endif #ifdef PIN_12 CHECK(pwms[12], PORTB, 4); #endif #ifdef PIN_13 CHECK(pwms[13], PORTB, 5); #endif #ifdef PIN_14 CHECK(pwms[14], PORTC, 0); #endif #ifdef PIN_15 CHECK(pwms[15], PORTC, 1); #endif #ifdef PIN_16 CHECK(pwms[16], PORTC, 2); #endif #ifdef PIN_17 CHECK(pwms[17], PORTC, 3); #endif #ifdef PIN_18 CHECK(pwms[18], PORTC, 4); #endif #ifdef PIN_19 CHECK(pwms[19], PORTC, 5); #endif asm volatile ("inc %0" : "+r" (pwm)); //pwm++; } Если в скетче закоментировать строку Init_PWM(); - общение по serial команда/ответ проходят без проблем, как только подключаю код PWM - все, глухо. При попытке вывести значение любой переменной по serial - программа зависает вовсе. Пытался понять, что мешает: предположил, что код PWM задействует ШИМ и на RX TX, так как рассчитан на все пины ардуино. Пробовал оставить только строки с пинами, которые использую - эффект тот же. Видимо там что-то "зарыто" в коде ассемблера...
Код (C++): TCCR2B = 3; устанавливает деление частоты i/o для таймера на 32. Вызов analog_Frequence(1); заменяет 1/32 на 1/1, т.е. без деления. Код (C++): OCR2A = 1; выставляет порог для прерывания на TCNT2 == 1. те получается прерывание срабатывает на каждый тик таймера TCNT2. В самом прерывании есть очистка счётчика Код (C++): asm volatile ("clr %0" : "+r" (TCNT2)); // TCNT2 = 0; но пока процедура возится с пинами, счётчик может прокрутиться дальше 1 и даже пройти несколько циклов. Процессор занимается только обслуживанием прерываний по таймеру. Тебе какую частоту ШИМ надо выставить? Например: Код (C++): TCCR2B = 3; // Прескейлер 1/32 OCR2A = 8; // Считаем до 8 тиков Чатота прерываний 20MHz/32/8=78 kHz чатота ШИМ 20MHz/32/8/256 = 305 Hz. тут 256 - полный цикл счёта переменной pwm. и вызов analog_Frequence(1); убери.
Nano на Atmega328P. Частота нужна, чтобы на глаз не видно было мерцание. Сейчас попробую, что посоветовал. Спасибо за ответы: Спасибо, вроде вылечил. Теперь осталось подобрать значения для адекватной частоты при работе с 11 пинами одновременно. Я так понимаю для увеличения частоты нужно TCCR2B и OCR2A уменьшать, но в разумных пределах, чтобы не заставлять контроллер постоянно заниматься только PWM
Прерывание будет срабатывать только на втором такте таймера и только один раз за "цикл". Соответственно: Частота прерываний -- 16MHz / 32 / 256 = ~2kHz. Это с учётом настроек таймера. При таком режиме работы таймера (WGM22..WGM20 = 0) на частоту будет влиять только TCCR2B. Значение OCR2A в данном коде ни на что влиять не будет. И -- "Да" -- с увеличением частоты надо быть аккуратнее.