Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Фазовое управление симисторами
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Sadmi
Добрый день. Это мой первый опыт программирования на Си вообще и под МК в частности. Прошу сразу не пинать.
Задача стоит такая: организовать управление восемнадцатью лампами накаливания с плавной регулировкой мощности.
После прохода напряжения через ноль счетчик отсчитывает определенное количество тактов (в зависимости от нужной мощности) и на 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);
}
}
}
kovigor
Цитата(Sadmi @ Aug 30 2011, 12:28) *
Задача стоит такая: организовать управление восемнадцатью лампами накаливания с плавной регулировкой мощности.


Это читали (я имею в виду "Два микроконтроллерных регулятора мощности"):

http://shema.org.ua/index.php?name=News&am...cle&sid=481

?
-SANYCH-
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 ?
На какой частоте работает микроконтроллер?
Sadmi
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.
-SANYCH-
Цитата
До, порт сбрасывается в ноль, но это происходит один раз в 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);
}
}


Когда выполняется этот цикл то прерывание от таймера может произойти даже в тот момент когда Вы обнуляете флаги установки каналов. На мой взгляд это не правильно.
xemul
А бездумные 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++;", имхо.
Sadmi
Цитата(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 обнулять только при переходе через ноль?
xemul
Цитата(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
Sadmi
2 xemul
Задумка такая: в tz[] - задержки включения симистора, чем больше задержка, тем меньше мощность. Всего 25 значений, они распределены в десяти миллисекундах полупериода напряжения, распределения предполагается не линейное.
ISR(TIMER0_COMP_vect) происходит каждый раз, при совпадении счетчика с регистром совпадения, в котором находится очередное значение из tz[]. Т.о. 25 раз за полупериод срабатывает прерывание и в обработчике устанавливаются каналы, с соответствующей определенной в pch[][] мощностью. pch[0][х] - номер канала, pch[1][х] - порядковый номер значения мощности из tz[], pch[2][х] - флаг установки канала в текущем полупериоде. В tz[] конечно значения не самих задержек, а приращения (1, 5, ...25, ... 5, 1). Да, сортировать массив, в данном случае, не нужно, это была предыдущая идея.
ARV
для 6 каналов делал тупо на программном счете, т.е. настраивал таймер на прерывания 250 раз в полупериод сети, в обработчике инкрементировал счетчик и сравнивал его с массивом текущих значений фаз по каналам - где нужно, там устанавливал выходные сигналы для оптронов. повторяю: для 6 каналов производительности AVR хватало, но вот для 18 каналов - это вопрос...

ноль в сети определяю самым простым способом: если нет требований по гальванической развязке конструкции, то тупо через резистор 1-1,5М подаю сигнал с сетевого провода на любой вывод, способный генерировать прерывания по фронту и спаду (пример вот тут: http://arv.radioliga.com/content/view/218/44/). это может быть встроенный компаратор, INT0 или INT1 или любой PCINT. если нужна гальваническая развязка - использую встроенный компаратор, подавая на его оба входа через резисторы сигналы прямо со вторичной обмотки питающего трансформатора. желательно, чтобы напряжение на этой обмотке было побольше, а резисторы должны ограничить ток на уровне не более 1 мА. оба способа неоднократно проверены и хорошо себя зарекомендовали.

яркость лампы меняется так же нелинейно от подводимой мощности, поэтому иметь массив с "линеаризующими" коэффициентами мне кажется бесполезным. либо уж учитывать в нем характеристику лампы - хотя зачем это может быть нужно - не представляю.
Sadmi
Цитата(ARV @ Sep 1 2011, 09:45) *
для 6 каналов делал тупо на программном счете, т.е. настраивал таймер на прерывания 250 раз в полупериод сети, в обработчике инкрементировал счетчик и сравнивал его с массивом текущих значений фаз по каналам - где нужно, там устанавливал выходные сигналы для оптронов


Т.е. у Вас было 250 значений мощности? Зачем так много? И если я уменьшу до 25, то нагрузка на процессор снизится?
А как сбрасывали канал (как устанавливали длительность управляющего импульса)? Или он был включен до следующего нуля?
С проходом через ноль я действительно перемудрил, можно проще, но сейчас это не так важно.
ARV
я делал диммер и мне нужно было обеспечить изменеие яркости незаметными шажками, отсюда столько уровней. разумеется, для 25 градаций нагрузка снизится. на всякий случай: я писал на Си, на асме будет меньше проблем. так же я делал от встроенного тактового генератора 8 Мгц, а при внешнем кварце 16 Мгц - тоже все упростится

включение импульса я делал по-всякому: и до конца полупериода, и заданный интервал, и до очередной грададации... все это делается достаточно просто в том же обработчике таймера.
domowoj
Цитата(Sadmi @ Aug 30 2011, 16:28) *
Задача стоит такая: организовать управление восемнадцатью лампами накаливания с плавной регулировкой мощности.

А чем собираетесь устанавливать мощность каждой.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.