Dimmer com ATMega

Software e Hardware para ATMEL

Moderadores: 51, guest2003, brasilma

Dimmer com ATMega

Mensagempor Fandango » 23 Ago 2007 17:58

Olá senhores,

Sou novato em AVR e estou tentando implementar um dimmer com TRIAC via ATMega16. Quanto ao circuito está tudo tranquilo. Estou fazendo a detecção de passagem pelo Zero (Zero crossing) através de uma ponte, e obtendo um sinal de 120Hz que, ligado na INT0 do ATMega, gera as interrupções de sincronismo para disparo do TRIAC. O disparo do triac, via optoacoplador MOC3021, estou tentando fazer através do PD5 (OC1A) para poder usar PWM e assim controlar o ângulo de fase.
Pois bem, minha dificuldade está em sincronizar a passagem pelo zero com a geração do PWM através do software. Utilizo o AVR Studio com WinAVR e GCC.
Alguém já fez algo parecido ou tem alguma dica para ajudar?

Valeu!
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mudei a abordagem

Mensagempor Fandango » 24 Ago 2007 11:15

Olá gente,

Percebi que não estou sabendo fazer desse jeito e resolvi mudar um pouquinho a abordagem no que diz respeito ao PWM. Andei pesquisando aqui no forum mesmo e vi que é possivel fazer isto usando o Timer 1 no modo CTC. Vou tentar explicar:
Para disparar corretamente o Triac, digamos que numa rede de 60Hz, preciso aguardar 4,166ms depois da passagem pelo zero, para garantir que estou além dos 90° da onda, e então sim ativar o Triac entre os 90° e os 180°. Nos 180° haverá nova passagem pelo zero, então o Triac será desativado e a onda entrará na parte negativa. Novamente devo aguardar 4,166ms, para garantir que estou além dos 270°, e então ativar novamente o Triac entre os 270° e os 360°. Não sei se consegui me explicar direito. Em todo caso, algumas contas creio que ajudam:

1) 60Hz proporcionam períodos de 16,666..ms
2) Numa onda completa ocorrerão duas passagens pelo zero (uma a cada 8,333..ms)
3) O disparo deve ocorrer passada a metade desse tempo (4,166..ms)

Pois bem, até aí creio que entendi.

A questão agora é disparar o Timer1 toda vez que ocorrer passagem pelo zero. Como coloquei um circuito de zero crossing gerando INT0 isso é simples, porque toda vez que ocorrer uma interrupção externa via INT0, é só gerar um start no Timer1.

Bem, eu estou usando um ATMega16 com cristal de 11,0592MHz. Como quero ter um certo controle do disparo do Triac, para poder fazer o dimming, preciso de uma base de tempo de digamos 0,1ms para o Timer 1. Porque? Porque primeiro tenho que contar pelo menos 4,2ms (arredondando) para garantir o atraso de disparo, e depois poder variar o tempo alguns steps entre 0 e 4,2ms de maneira a ter um certo controle da intensidade da carga do Triac. Com isso conseguiria 42 steps de 0,1ms que permitiriam variar a intensidade entre quase 100% e quase 0, o que para a minha aplicação é mais do que suficiente.

Bom, até aí tudo bem também. A minha dificuldade agora está em configurar o Timer1 para isso.

Alguma dica?
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor Maurício » 24 Ago 2007 20:32

Olá.

Se vc utilizar o oscilador interno, ou cristal externo configurado para 8MHz, vc precisará fazer 800 contagens para atingir 0,1ms.

Programa TIMER1 para contar 800 vezes ( valor de carga dos Registradores: 65536 - 800 ), e a cada interrupção vc ajusta o PWM.
Dentro da interrupção de TIMER1, vc tb pode marcar o tempo em que o TRIAC deverá ficar acionado.

[]'s
"Não leve a vida tão à sério, afinal, nenhum de nós sairá vivo, dela!"
Avatar do usuário
Maurício
Word
 
Mensagens: 678
Registrado em: 14 Out 2006 17:23
Localização: São Paulo - SP

Mensagempor umoraes » 25 Ago 2007 08:52

Olá,

Tenho colecionado alguns projetos de dimmer para AVR. Não sei se te interessa, mas esse aqui é o melhor até agora.... Como sou novato, estou tentando desenvolver a board para este projeto.
abraços e boa sorte.

Semitone Diamond
http://semitone.sourceforge.net/wiki/in ... ne_Diamond

Current firmware features
Diamond RTOS: FSM based multitasking RTOS
20 independent channels with nearly 10bit resolution: 0.0% until 100.0% (1001 linearised levels)
detailed channel view, scene view or real-time channel monitor view
independent fade durations for each channel (from 0m00.0s until 99m59.9s)
independent output driver mode for each channel:
TRIAC (in both pulsed and static mode),
IGBT/MOSFET forward,
IGBT/MOSFET reverse,
electronic ballast (0-10V or 1-10V DC),
relay (on or off state)
autodetection of 50Hz and 60Hz mains frequency
10 scenes memory (can be loaded with single remote control button press)
10 timers (one or more weekdays, hour, minute) based on real time clock, load scene or switch on or off
optionally, lamp preheating when operational or online
optionally, symmetrical load of the mains
optionally, flashing of selected channel as feedback
individual blacking out of channels, optionally in a three-stage mode (0% > 100% > restore)
multi-lingual LCD user interface (English, German, Italian - more contributions wanted)
structured multi-level configuration menu
DMX512 interface support provides industry standard connectivity
three configurable backlight situations
global standby mode
umoraes
 
Mensagens: 1
Registrado em: 18 Jun 2007 20:51

Mensagempor Fandango » 26 Ago 2007 14:50

Obrigado pelas respostas.

Mauricio, é isso que estou tentando fazer, mas não estou sabendo como. Meu sistema usa Xtal de 11,0592MHz. Então calculei o seguinte:
Se usar um prescaler de 256 então 11059200/256 = 43200Hz, o que me dá um pulso a cada 23,148us (1/43200), certo?
Bem, para 0,1ms então precisaria de 0,1/23,148us = 4320 counts.
Como o overflow ocorrerá em 65536, então o número que tenho que ajustar no timer 1 será 65536 - 4320 = 61216. Desta forma, o Timer1 contará 4320 e irá gerar uma interrupção. Terão então se passado 0,1ms. Correto? Pois bem, a questão é que não estou sabendo configurar o Timer1 para que faça isso. Veja se está certo:

void T1_Init(void)
{
TCCR1A = 0;
TCCR1B |= _BV(CS12); // Prescaler de 256
TCNT1 = 61216; // Preload para 0,1ms
TIMSK |= _BV(TOIE1); // Interrupção por Overflow do Timer 1
}

Umoraes, agradeço tua dica, mas sinceramente não consegui entender esse dimmer, até porque está em assembler e tem muito mais do que eu preciso. Gostaria de entender direito o funcionamento dos timers.
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor Maurício » 27 Ago 2007 14:44

Com esse cristal, vc não irá conseguir os tempos que eu falei.
Porque vc está utilizando esse valor de cristal?
ehehehehe AVR não é 8051!! :lol: 8)

O AVR tem oscilador interno. Em alguns casos, não precisa de cristal.
Se a sua aplicação for só a de PWM, vc pode utilizar o oscilador interno.

[]'s
"Não leve a vida tão à sério, afinal, nenhum de nós sairá vivo, dela!"
Avatar do usuário
Maurício
Word
 
Mensagens: 678
Registrado em: 14 Out 2006 17:23
Localização: São Paulo - SP

Mensagempor Fandango » 27 Ago 2007 15:33

Maurício, estou usando 11,0592MHz para garantir comunicação serial a 9600bps e a 19200bps. Mas independente disso, não é esta a questão.

Gostaria de entender o funcionamento do Timer1 do AVR e saber como fazer para dispará-lo toda vez que ocorrer uma INT0 (que uso como zero cross detection), de maneira a gerar os atrasos que descrevi. Meu código está errado?
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor Maurício » 27 Ago 2007 17:46

Fandango escreveu:Meu código está errado?


Não sei te dizer porque esses detalhes de configuração da máquina quem me faz é o Codevision.
Na realidade, eu não conheço a arquitetura interna do AVR.

O Codevision já configura e dispara o timer na inicialização da máquina.

Esse trecho foi extraído da configuração do Codevision aplicada no seu caso:

Código: Selecionar todos
TCCR1A=0x00;
TCCR1B=0x04;
TCNT1H=0xEF;
TCNT1L=0x20;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;


A idéia é que quando houver interrupção de INT0, vc dispare o TIMER1 pra contar os 0,1ms.
Programe TIMER1 como contador/interrupção.
Prepare TIMER1 pra contar 4320 eventos e gere uma interrupção quando isso ocorrer. Dentro da interrupção vc manipula os valores do seu PWM.

[]'s
"Não leve a vida tão à sério, afinal, nenhum de nós sairá vivo, dela!"
Avatar do usuário
Maurício
Word
 
Mensagens: 678
Registrado em: 14 Out 2006 17:23
Localização: São Paulo - SP

Mensagempor Fandango » 28 Ago 2007 08:07

Obrigado Maurício, vou fazer umas tentativas dentro dessa filosofia e depois vou postar os resultados.
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil

Mensagempor Fandango » 30 Ago 2007 10:37

Aí gente, implementei o código a seguir e estou obtendo o sinal abaixo.

Imagem

Não entendo porque a largura do PD5 está variando.

Código: Selecionar todos
// Initializations ------------------
//
void X0_Init(void)    // Init INT0 (is working)
{
 DDRD &= ~_BV(PD2);    // PD2 as input (120Hz)
 PORTD &= ~_BV(PD2);   // Disable pullups
 MCUCR |= (1<<ISC01);  // Interrupt on falling edge
 GIFR |= (1<<INTF0);   // Clear interrupt flag of INT0
 GICR |= _BV(INT0);    // Enable INT0
}
//
void T1_Init(void)    // Init Timer 1
{
  TIMSK |= _BV(OCIE1A); // Output compare int. enable
  TCCR1B |= _BV(CS12)   // Prescaler of 256
         | _BV(WGM12);  // CTC mode, TOP = OCR1A
  OCR1A = 150;          // Any value, just for test
}

// Interrupts vectors ----------------
//
// For INT0 interrupt
//
SIGNAL (SIG_INTERRUPT0)
{
 cli();                // Disable all interrupts
 OCR1A = 150;        // Any value, just for test
 PORTD |= _BV(PD5);  // Gate ON
 sei();              // Enable all interrupts
}
//
// For Timer 1 interrupt
//
SIGNAL (SIG_OUTPUT_COMPARE1A)
{
 PORTD &= ~_BV(PD5); // Gate OFF
}

// Main program:
//
int main(void)
{
 cli();                // Disable all interrupts

 DDRD |= (1<<PD5);     // PD5 as output
 PORTD |= (1<<PD5);    // Drive Hi
 PORTD &= ~_BV(PD5);   // Gate OFF (PD5)

 OCR1A = 0;         // I don't know why I wrote this
                    // but seems do none difference

 sei();             // Enable all interrupts

 for (; ;)       // Infinite loop (do nothing)
  {
  }
  cli();       // Never reached
  return(0);        // May be reset
}


Alguma sugestão ?
Fandango
Byte
 
Mensagens: 187
Registrado em: 11 Jun 2007 22:13
Localização: SC - Brasil


Voltar para AVR

Quem está online

Usuários navegando neste fórum: Nenhum usuário registrado e 1 visitante

x