|
|
  |
Фазовое управление симисторами |
|
|
|
Aug 30 2011, 09:28
|
Группа: Новичок
Сообщений: 9
Регистрация: 30-08-11
Пользователь №: 66 930

|
Добрый день. Это мой первый опыт программирования на Си вообще и под МК в частности. Прошу сразу не пинать. Задача стоит такая: организовать управление восемнадцатью лампами накаливания с плавной регулировкой мощности. После прохода напряжения через ноль счетчик отсчитывает определенное количество тактов (в зависимости от нужной мощности) и на 100 микросекунд (считает второй таймер) включается соответствующий порт. Использую Atmega32, AVR Studio 5. Написал код, анализирую в AVS Studio, все работает правильно, загоняю в Протеус ничего не работает. Предполагаю, что что-то не так с прерываниями делаю, но что? Прошу помощи! Спасибо! CODE #include <avr/io.h> #include <avr/interrupt.h>
volatile unsigned long u; // переменная - результат АЦП volatile unsigned long f; // первый нуль - 1, не первый - 0 /* Таблица задержек */ volatile unsigned char tz[25]={20,30,70,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; // Массив мощностей volatile unsigned char pch[3][17]={ {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16}, // Номер канала {0,1,2,4,4,4,4,4,4,4, 4, 4, 4, 4, 4, 4, 4}, // Текущая мощность {0,0,0,0,0,0,0,0,0,0, 0, 0, 0, 0, 0, 0, 0}}; // Флаг установки канала volatile unsigned char i=0; // volatile unsigned char t=0; volatile unsigned char q, fl=0;
/***Обработчик прерываний по окончанию преобразования АЦП***/ ISR (ADC_vect) { u = (ADC*11/4); }
/*** Обработчик прерываний по совпадению таймера ***/ ISR(TIMER0_COMP_vect) { //-------------------------------------------------------------------------------------------- for (i=0; i<18; i++) // Перебираем каналы и включаем нужный { if (pch[1][i]==t) { if (pch[2][i]==0) // Если еще не установлен в текущем периоде { pch[2][i]=1; switch (pch[0][i]) // Определяем линию и порт для установки - 1 { case 0: PORTD |=1<<0; break; case 1: PORTD |=1<<1; break; case 2: PORTD |=1<<2; break; } } } } t++; if (t==25) {t=0;} OCR0=tz[t]; // Новое значение совпадения
//------------------------------------------------------------------------------------------- TCNT0=0; //Обнуляем таймер TCNT2=0; //Обнуляем таймер }
/***Обработчик прерывания по совпадению таймера (длительность откывающего импульса)***/ ISR(TIMER2_COMP_vect) { //Выключаем канал PORTD=0; /* switch (t) { case 0: PORTD &=~(1<<0); break; case 1: PORTD &=~(1<<0); break; case 2: PORTD &=~(1<<2); break; } */ TCNT2=0; //Обнуляем таймер }
/***Главная функция***/ int main (void) { DDRD = 0xFF; // Порт на выход PORTD = 0x00; DDRA = 0x00; /* Настройка АЦП */ ADCSRA = (1 << ADEN) // разрешение работы АЦП |(1 << ADSC) // запуск преобразования |(1 << ADATE) // непрерывный режим работы АЦП |(1 << ADPS2)|(1 << ADPS1)|(0 << ADPS0) // предделитель на 64 |(1 << ADIE); // разрешение прерывания ADMUX = (1 << REFS1)|(1 << REFS0) // внутренний ИОН 2,56V |(0 << MUX3)|(0 << MUX2)|(0 << MUX1)|(0 << MUX0); // вход (ADC0) PA1
/* Настройка таймера задержки (мощность канала)*/ TCCR0|=(1<<CS00); // TCCR0|=(0<<CS01); // Установка предделителя (1024)--------------------------------------!!! TCCR0|=(1<<CS02); // OCR0=tz[pch[1][t]]; // Установка значения в регистре совпадения. TCCR0|=(0<<FOC0)|(1<<COM00)|(1<<WGM01); TIMSK|=(1<<OCIE0); // Разрешить прерывание по совпадению.
/* Настройка таймера (время открывающего импульса 100мкс)*/ TCCR2|=(1<<CS21); // TCCR2|=(1<<CS20); // Установка предделителя (32)--------------------------------!!! TCCR2|=(0<<CS20); // OCR2=50; // Установка значения в регистре совпадения. TCCR2|=(0<<FOC2)|(1<<COM20)|(1<<WGM21); TIMSK|=(1<<OCIE2); // Разрешить прерывание по совпадению.
sei(); //глобальное разрешение прерываний TCNT0=0; TCNT2=0; while(1) { if (u<20) { f++; } else { f=0; }
if (f==1) // Обнаружен проход через нуль { t=0; for (q=0; q<18; q++) //обнуляем флаги установки каналов { pch[2][q]=0; } TCNT0=0; TCNT2=0; PORTD |= 1<<3; } else { PORTD &= ~(1<<3); } } }
Сообщение отредактировал IgorKossak - Aug 30 2011, 17:55
|
|
|
|
|
Aug 30 2011, 21:10
|
Местный
  
Группа: Свой
Сообщений: 289
Регистрация: 6-12-05
Пользователь №: 11 864

|
CODE while(1){
if (u<20){ f++; } else{ f=0; }
if (f==1) // Обнаружен проход через нуль { t=0; for (q=0; q<18; q++){ //обнуляем флаги установки каналов pch[2][q]=0; } TCNT0=0; TCNT2=0; PORTD |= 1<<3; } else{ PORTD &= ~(1<<3); } } Исходя из данного куска кода PORTD будет постоянно сбрасываться в ноль. Что включает вот этот кусок кода PORTD |= 1<<3 ? На какой частоте работает микроконтроллер?
Сообщение отредактировал IgorKossak - Aug 31 2011, 07:31
Причина редактирования: [codebox]
|
|
|
|
|
Aug 31 2011, 03:59
|
Группа: Новичок
Сообщений: 9
Регистрация: 30-08-11
Пользователь №: 66 930

|
kovigor Цитата(kovigor @ Aug 30 2011, 22:31)  Это читали (я имею в виду "Два микроконтроллерных регулятора мощности"): http://shema.org.ua/index.php?name=News&am...cle&sid=481? Спасибо, но там применяется другой принцип регулирования, "выбрасывают" полупериоды сетевого напряжения. А при таком способе заметны мерцания, особенно при низкой мощности. -SANYCH- Цитата(-SANYCH- @ Aug 31 2011, 01:10)  Исходя из данного куска кода PORTD будет постоянно сбрасываться в ноль. Что включает вот этот кусок кода PORTD |= 1<<3 ? На какой частоте работает микроконтроллер? До, порт сбрасывается в ноль, но это происходит один раз в 10 миллисекунд (в начале каждого полупериода). PORTD |= 1<<3 - включаю третью линию порта D чтобы посмотреть на осциллографе (в протеусе), как срабатывает проход через ноль. Собственно поэтому и сбрасываю PORTD. Но даже, если убрать этот кусок, ничего не изменяется. Микроконтроллер работает на 16 МГц. Если отключаю обработку прерываний от нулевого таймера, вижу на осциллографе, на третьей линии PORTD, проход через ноль, иначе вообще никаких сигналов. Может слишком долго в обработчике прерываний ISR(TIMER0_COMP_vect) нахожусь? И это только три канала, а мну нужно будет 17.
Сообщение отредактировал Sadmi - Aug 31 2011, 04:03
|
|
|
|
|
Aug 31 2011, 06:35
|
Местный
  
Группа: Свой
Сообщений: 289
Регистрация: 6-12-05
Пользователь №: 11 864

|
Цитата До, порт сбрасывается в ноль, но это происходит один раз в 10 миллисекунд (в начале каждого полупериода). PORTD |= 1<<3 - включаю третью линию порта D чтобы посмотреть на осциллографе (в протеусе), как срабатывает проход через ноль. Собственно поэтому и сбрасываю PORTD. Но даже, если убрать этот кусок, ничего не изменяется. Микроконтроллер работает на 16 МГц. Если для строба на осцилограф то это нормально. CODE while(1) { if (u<20) { f++; } else { f=0; }
if (f==1) // Обнаружен проход через нуль { t=0; for (q=0; q<18; q++) //обнуляем флаги установки каналов { pch[2][q]=0; } TCNT0=0; TCNT2=0; PORTD |= 1<<3; } else { PORTD &= ~(1<<3); } } Когда выполняется этот ц икл то прерывание от таймера может произойти даже в тот момент когда Вы обнуляете флаги установки каналов. На мой взгляд это не прав ильно.
Сообщение отредактировал IgorKossak - Aug 31 2011, 07:37
Причина редактирования: [codebox], орфография, лишняя фигурная скобка в коде
|
|
|
|
|
Aug 31 2011, 07:58
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
А бездумные volatile, unsigned long и *4/11 там, где достаточно 1 бита и сравнения - типа правильно? Прерывание от АЦП здесь вообще бессмысленно - с тем же успехом можно читать АЦП непосредственно там, где используется (якобы) u.
2Sadmi: у Вас в прерываниях изменяется только t, вот и оставьте его volatile. Зачем Вам нужен pch[0][] в виде {0,1,2,3,...}? Вместо "t++; if (t==25) {t=0;}" логичней будет, н-р, "if (t < 25) t++;", имхо.
|
|
|
|
|
Aug 31 2011, 10:48
|
Группа: Новичок
Сообщений: 9
Регистрация: 30-08-11
Пользователь №: 66 930

|
Цитата(xemul @ Aug 31 2011, 11:58)  у Вас в прерываниях изменяется только t, вот и оставьте его volatile. Ок, но ведь от volatile "хуже не будет"? Цитата(xemul @ Aug 31 2011, 11:58)  Зачем Вам нужен pch[0][] в виде {0,1,2,3,...}? Это в тестовом режиме - последовательно, а потом массив предполагается сортировать по возрастанию значения задержки и номера каналов в массиве будут вразброс. Цитата(xemul @ Aug 31 2011, 11:58)  Вместо "t++; if (t==25) {t=0;}" логичней будет, н-р, "if (t < 25) t++;", имхо. Т.е. t обнулять только при переходе через ноль?
|
|
|
|
|
Aug 31 2011, 15:55
|
    
Группа: Свой
Сообщений: 1 928
Регистрация: 11-07-06
Пользователь №: 18 731

|
Цитата(Sadmi @ Aug 31 2011, 14:48)  Ок, но ведь от volatile "хуже не будет"? Сравните время выполнения, н-р, ISR(TIMER0_COMP_vect). Заодно ответите на свой вопрос "Может слишком долго в обработчике прерываний ISR(TIMER0_COMP_vect) нахожусь?" Цитата Это в тестовом режиме - последовательно, а потом массив предполагается сортировать по возрастанию значения задержки и номера каналов в массиве будут вразброс. Ну если совсем заняться нечем... Цитата Т.е. t обнулять только при переходе через ноль? Я не понимаю Вашу задумку и назначение t и tz[]. Если в массиве tz[] живут задержки до следующего шага по мощности в пределах полупериода сети, то непонятно его содержимое. имхо, для бо-ме пропорциональных шагов по моще задержки должны убывать ~обратно-квадратично до половины полупериода, а после примерно также увеличиваться. Если это задержки только для половины полупериода, то t должно бегать 0, 1, ... 23, 24, 23, ... 1, 0. Для выделения 0 напряжения сети ей-ей не нужен АЦП и unsigned long f - в пределе простоты достаточно одного компаратора. От помех можно добавить к нему фильтр типа (н-р, на три отсчёта): Код uint8_t zc; zc <<= 1; if(ACO) zc |= 1; // с точностью до фазы компаратора if((zc & 0x07) == 0x03) ...;// перепад 0/1 //else if((zc & 0x07) == 0x04) // перепад 1/0
|
|
|
|
|
Sep 1 2011, 04:27
|
Группа: Новичок
Сообщений: 9
Регистрация: 30-08-11
Пользователь №: 66 930

|
2 xemul Задумка такая: в tz[] - задержки включения симистора, чем больше задержка, тем меньше мощность. Всего 25 значений, они распределены в десяти миллисекундах полупериода напряжения, распределения предполагается не линейное. ISR(TIMER0_COMP_vect) происходит каждый раз, при совпадении счетчика с регистром совпадения, в котором находится очередное значение из tz[]. Т.о. 25 раз за полупериод срабатывает прерывание и в обработчике устанавливаются каналы, с соответствующей определенной в pch[][] мощностью. pch[0][х] - номер канала, pch[1][х] - порядковый номер значения мощности из tz[], pch[2][х] - флаг установки канала в текущем полупериоде. В tz[] конечно значения не самих задержек, а приращения (1, 5, ...25, ... 5, 1). Да, сортировать массив, в данном случае, не нужно, это была предыдущая идея.
|
|
|
|
|
Sep 1 2011, 05:45
|

Профессионал
    
Группа: Свой
Сообщений: 1 143
Регистрация: 30-09-08
Из: Новочеркасск
Пользователь №: 40 581

|
для 6 каналов делал тупо на программном счете, т.е. настраивал таймер на прерывания 250 раз в полупериод сети, в обработчике инкрементировал счетчик и сравнивал его с массивом текущих значений фаз по каналам - где нужно, там устанавливал выходные сигналы для оптронов. повторяю: для 6 каналов производительности AVR хватало, но вот для 18 каналов - это вопрос... ноль в сети определяю самым простым способом: если нет требований по гальванической развязке конструкции, то тупо через резистор 1-1,5М подаю сигнал с сетевого провода на любой вывод, способный генерировать прерывания по фронту и спаду (пример вот тут: http://arv.radioliga.com/content/view/218/44/). это может быть встроенный компаратор, INT0 или INT1 или любой PCINT. если нужна гальваническая развязка - использую встроенный компаратор, подавая на его оба входа через резисторы сигналы прямо со вторичной обмотки питающего трансформатора. желательно, чтобы напряжение на этой обмотке было побольше, а резисторы должны ограничить ток на уровне не более 1 мА. оба способа неоднократно проверены и хорошо себя зарекомендовали. яркость лампы меняется так же нелинейно от подводимой мощности, поэтому иметь массив с "линеаризующими" коэффициентами мне кажется бесполезным. либо уж учитывать в нем характеристику лампы - хотя зачем это может быть нужно - не представляю.
--------------------
Я бы взял частями... но мне надо сразу.
|
|
|
|
|
Sep 1 2011, 07:02
|
Группа: Новичок
Сообщений: 9
Регистрация: 30-08-11
Пользователь №: 66 930

|
Цитата(ARV @ Sep 1 2011, 09:45)  для 6 каналов делал тупо на программном счете, т.е. настраивал таймер на прерывания 250 раз в полупериод сети, в обработчике инкрементировал счетчик и сравнивал его с массивом текущих значений фаз по каналам - где нужно, там устанавливал выходные сигналы для оптронов Т.е. у Вас было 250 значений мощности? Зачем так много? И если я уменьшу до 25, то нагрузка на процессор снизится? А как сбрасывали канал (как устанавливали длительность управляющего импульса)? Или он был включен до следующего нуля? С проходом через ноль я действительно перемудрил, можно проще, но сейчас это не так важно.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|