Ir ao conteúdo
  • Cadastre-se

Controle de fase a partir do timer do pic


advtec

Posts recomendados

Olá a todos. Desenvolvi um dimmer digital com o PIC e que está funcionando bem. Ele detecta a passagem pelo zero da senoide e dispara um timer, que provoca uma interrupção a cada 100us. Uma variável guarda quantas interrupções ocorreram e, passado o tempo ajustado pelo usuário, dispara uma saída que, por um MOC3021, dispara um triac. Acontece que preciso agregar outras funcionalidades ao projeto e, o que está ocorrendo, é que o timer do controle acima descrito está gerando tantas interrupções que os outros recursos não estão tendo tempo de máquina para rodar - mesmo quando altero as prioridades no atendimento das interrupções, isso ocorre. O que preciso é de outro tipo de funcionamento para o controle de fase, que não acarrete tantas interrupções do timer. Até pensei num algoritmo assim: a interrupção externa detecta a passagem pelo zero da senoide. O timer do controle de fase é setado para overflow no tempo correspondente ao tempo de disparo da fase, gerando interrupção que, atendida, emite o pulso ao triac, ou seja, quanto menor o tempo do overflow, mais rapidamente ocorrerá a interrupção, disparando mais "cedo", entregando mais potência à carga. Ocorre que não estou tendo resultado. Não sei se meu modo de pensar está errado ou se não estou conseguindo implementar o cógido. Eis os trechos essenciais do código:

#int_ext
void trata_int_ext(void) {
flagZero=1; //sinaliza passagem pelo zero da senoide
enable_interrupts(int_timer1);
set_timer1(58535+ajuste); //65535-7000=58535 - prevê quedas nas junções dos semicondutores, por isso o overflow em 7ms. "ajuste" vem da atuação em botões
}

#int_timer1
void trata_timer1(void) {
seta_potencia();
}

void seta_potencia(void) {
static unsigned char largPulso;
if(ajuste>0 && flagZero) { //"ajuste" vem da atuação em botões
output_high(controle);
largPulso++;
if(largPulso>=30) { //desliga o pulso após 30 incrementos (30us)
output_low(controle);
flagZero=0;
largPulso=0;
disable_interrupts(int_timer1); //a interrupção será habilitada na próxima passagem por zero
}
}
}

O compilador é o CCSC e o PIC é o 16f819.

Desde já, agradeço aos colaboradores.

Link para o comentário
Compartilhar em outros sites

Estou detectando a passagem por zero através de um circuito transistorizado. Enquanto há tensão na base, vinda diretamente da senoide retificada (onda completa), o transistor (npn) está conduzindo e, em seu coletor, portanto, não tem tensão (o coletor vai diretamente em RB0, que é a interrupção externa). Quando a senoide passa pelo zero, o transistor entra em corte e, então, há tensão no coletor (mantida por um capacitor eletrolítico). Esse pulso é enviado para a entrada da interrupção externa do PIC (RB0). O programa está configurado para haver a interrupção na borda de descida. Eu não conferi as formas de onda, entretanto, o circuito está funcionando. Mas, como a minha versão gera uma interrupção a cada 100us do timer, as outras funcionalidades do PIC estão "travadas".

Não tentei implementar PWM para controlar o MOC.

nota: meu circuito de detecção não é muito eficiente, pois há uma "perda" decorrente da tensão necessária para o início da condução do transistor (0,7V), tendo ainda a perda nos diodos da ponte retificadora. Depois que eu conseguir funcionar o programa do PIC, devo alterar a etapa de detecção, usando amplificador operacional.

Link para o comentário
Compartilhar em outros sites

figura_2_dimmer_resolucao.jpg

Esse detector é de alta resolução,perfeito para dimmer de precisão e totalmente isolado da rede.

entretanto, o circuito está funcionando. Mas, como a minha versão gera uma interrupção a cada 100us do timer, as outras funcionalidades do PIC estão "travadas".

Geralmente eu salvo todos os registradores principais na rotina de interrupção,executo a rotina e retorno os registradores como estavam antes da interrupção.

Pode ser que seu circuito detector esteja interrompendo mais que o nescessario.

Link para o comentário
Compartilhar em outros sites

Muito obrigado. Excelente circuito! Vou montá-lo.

Entendi sua explicação. Mas, veja o que acontece: coloquei o timer0 para gerar uma interrupção a cada 100us. Uma variável vai sendo incrementada até atingir o valor setado pelo usuário. Cada incremento, portanto, vale 100us. Atingido o valor setado, entra em um if e o pino que vai para o MOC é posto em nível alto. Dentro deste if, há outra variável que será incrementada e, ao ser atingido o valor equivalente a 1ms (10 incrementos), o pino que vai para o MOC é posto em nível baixo. Então, de todo o jeito esse timer0 fica gerando uma interrupção a cada 100us. É justamente isso que quero mudar, e percebi que a sua explicação pende para este lado. Não consigo pensar em um algoritmo diferente do que eu expus no início deste tópico. Como você fez? Se puder expor o algoritmo será ótimo. E quanto ao salvamento dos registradores principais, pode mostrar o trecho do código?

Novamente, muito obrigado.

Link para o comentário
Compartilhar em outros sites

void trata_timer1(void) {

seta_potencia();

}

Neste trecho se voce escrever a rotina de interrupção diretamente,voce vai evitar a uma outra chamada a uma outra rotina (e consequentemente mais um 'stack' usado).

Isso pode melhorar o desempenho de interrupções que são chamadas muitas vezes,mas pode depender do compilador(eu sempre tento programar em ASM).

voce tem que pesquisar no datasheet o no seu código,se ha a possibilidade de algum registrador ser alterado durante uma interrupção mais 'elaborada'.

Segue um simples exemplo que salva os valores do STATUS e WORK e depois recupera;

#BYTE STATUS = 0X03 

void seta_potencia(void) {
static unsigned char largPulso;
static byte W_TEMP,STATUS_TEMP;
#ASM
MOVWF W_TEMP
SWAPF STATUS,W
MOVWF STATUS_TEMP
#ENDASM
//..................................
disable_interrupts(int_timer1); //a interrupção será habilitada na próxima passagem por zero
if(ajuste>0 && flagZero) { //"ajuste" vem da atuação em botões
output_high(controle);
largPulso++;
if(largPulso>=30) { //desliga o pulso após 30 incrementos (30us)
output_low(controle);
flagZero=0;
largPulso=0;
}
}
#ASM
SWAPF STATUS_TEMP,W
MOVWF STATUS
SWAPF STATUS_TEMP,f
SWAPF STATUS_TEMP,W
#ENDASM
}

O CCS tem alguns procedimentos para se lidar com ASM e alguns registradores.

Sobre o algorítimo,eu utilizaria PWM,mas isso muda tudo no seu código.

Faça alguma experiencia,mas primeiro voce deve verificar se o detector de zero não é a causa de tudo.

Na verdade eu não alteraria muito coisa,voce usa a saida do circuito detector por zero e a cada passagem(nivel baixo,no circuito acima)setava um HIGH num pino de uma porta ligada ao MOC por um intervalo definido por uma variável,apenas isso.

Link para o comentário
Compartilhar em outros sites

vtrx e rafael.luc, obrigado pelas respostas.

vtrx, entendi as colocações e já vou alterar as questões de chamadas a funções, colocando-as diretamente nos timers (todas os timers são usados). No sentido que você mencionou em sua última mensagem: preciso "ligar o MOC" após algum tempo da passagem pelo zero. Quanto menos potência, mais tempo há de se passar entre a passagem pelo zero da senoide antes de acionar o MOC e o TRIAC. Quanto mais potência, menos tempo passará entre o disparo do TRIAC (o high do pino que vai ao MOC) e a passagem pelo zero da senoide. Se quero, por exemplo, 50% da potência, tenho que disparar o TRIAC aos 90º da senoide. Então, como vou contar esse tempo que compreende a passagem pelo zero da senoide e a potência setada (50%, ou 90º da senoide, por exemplo)?

rafael.luc, tem algum material que pode me indicar que trate de PWM para o PIC em C? No mesmo sentido, vtrx, mesmo que o meu código seja todo alterado, se você puder me dar algumas dicas, ainda que de material disponível em C, para funcionar o projeto com PWM, estou disposto a fazer os experimentos.

Novamente aos contribuintes, muito obrigado.

Link para o comentário
Compartilhar em outros sites

Seguinte,eu estou com um layout para corroer que vai utilizar isso que voce quer para dimerizar lampadas,mas ainda não coloquei na prático porque peguei uma gripe a 2 semanas e não consgio 'esquentar' a cabeça sem ficar 'tonto':D

Mas voce poderia testar o algorítimo para nós.

Se voce ,após a passagem por zero,ativar um port por um tempo,a cada passagem,voce estaria modulando como se fosse um PWM.

Quanto maior o tempo ativado (largura do pulso),mais intensidade terá na lampada.

Seguindo a fase,pela senoide,eu não experimentei,logo não posso opinar.

Link para o comentário
Compartilhar em outros sites

Quando trabalhamos com interrupções de períodos muito curtos é interessante usar a velocidade máxima de clock do PIC. Usando um cristal de 20MHz você consegue executar 500 instruções antes da próxima interrupção, que é razoável se você não usar delays no meio do código.

Link para o comentário
Compartilhar em outros sites

Eu não entendi ainda a aplicação, esta monitorando um senoide pra que exatamente? Qual é a frequência é variável?

Então Rafael.

quando se deseja controlar,por exemplo,a luminosidade de uma lampada ou tensão de algo conectado na tensão AC da rede usando um Triac,para se ter controle linear,de zero ao máximo,voce precisa monitorar quando a senoide da rede esta num ponto 'zero',para poder dar partida no triac a partir deste ponto,senão,voce poderia ativar o Triac num ponto onde a senoide esta no 'máximo' e com isso não iria controlar linearmente a tensão.

Então detecta quando a senoide esta em 'zero' e ativa o Triac por um tempo de zero até o topo da senoide(tensão máxima)fazendo assim o 'controle' preciso da tensão.

Imagine diparar o Triac num ponto indefinido de tensão(senoide) a cada ciclo?

Não conseguiria controlar com precisão a tensão.

PS:Achei o vídeo que ilustra esses sinais;

Link para o comentário
Compartilhar em outros sites

Pensei no seguinte algoritmo para implementar em meu projeto, porém, não estou conseguindo gerar o código. Sugestões? Segue o algoritmo:

- interrupção externa lê passagem pelo zero;

- o timer1 é configurado para overflow no tempo programado pelo usuário;

- com o overflow do timer1, há a sua interrupção e o atendimento de sua função, levando o pino que vai ao MOC a 1;

- o pulso dura alguns micro segundos e é desativado;

- o ciclo se repete na nova interrupção externa.

Tentei implementar um código, entretanto, não "dimeriza" - a carga (uma lâmpada incandescente) fica oscilante e não há variação de intensidade.

Não sei se não estou conseguindo setar corretamente o timer, ou zerar flags, enfim, aqui eu travei. Fiz os cálculos das "quedas" de meu hardware (ainda um transistor e diodos) e concluí que entre uma borda e outra das passagens pelo zero tenho 7ms (pouco eficiente mesmo, mas vou levando até conseguir fazer funcionar, depois faço o circuito sugerido neste tópico). Então, estou setando o timer1 para overflow em 7ms. Quando o usuário atua sobre os controles, a ideia é mudar o set_timer1(xxxx) a fim de que o overflow varie também e, com isso, se dê o disparo do triac em momentos distintos da senoide. Por cautela eu desabilitei as outras interrupções de meu projeto para verificar se o atendimento de interrupções estava influenciando no funcionamento, entretanto, o problema persiste. Segue abaixo o que fiz em relação ao assunto:

#int_ext

void trata_int_ext(void) {

set_timer1(65535-ajuste); //variável ajuste vem da atuação do usuário

}

#int_timer1

void trata_timer1(void) {

unsigned char i;

if(ajuste>0) {

output_high(controle);

for(i=0;i<150;i++); //duração do pulso

output_low(controle);

}

}

Note que posicionei o timer para ocorrer overflow imediatamente. Quando o usuário atua, o valor de set_timer diminui e, com isso, aumenta o tempo de overflow e, conseguintemente, demora mais para disparar o triac, reduzindo a potência da carga. Entretanto, não está funcionando.

Estou usando clock interno de 4Mhz, pic16f819 e compilador CCS-C.

Desde já, agradeço aos colaboradores.

Bons trabalhos.

Link para o comentário
Compartilhar em outros sites

Veja se não é a rotina de interrupção que não esta no tempo correto;

Teste o código abaixo;

//crie uma variável global


//carrega com 1 e depois teste com 255
int8 tempo = 1;


#int_ext
void trata_int_ext(void) {
//set_timer1(65535-ajuste); //variável ajuste vem da atuação do usuário
}

#int_timer1
void trata_timer1(void) {
do
{
output_high(controle);
tempo --;
}while(tempo > 0);
output_low(controle);
tempo = 1;//1 a lampada deve ficar apagada,255 totalmente acesa
}
}

Link para o comentário
Compartilhar em outros sites

Então Rafael.

quando se deseja controlar,por exemplo,a luminosidade de uma lampada ou tensão de algo conectado na tensão AC da rede usando um Triac,para se ter controle linear,de zero ao máximo,voce precisa monitorar quando a senoide da rede esta num ponto 'zero',para poder dar partida no triac a partir deste ponto,senão,voce poderia ativar o Triac num ponto onde a senoide esta no 'máximo' e com isso não iria controlar linearmente a tensão.

Então detecta quando a senoide esta em 'zero' e ativa o Triac por um tempo de zero até o topo da senoide(tensão máxima)fazendo assim o 'controle' preciso da tensão.

Imagine diparar o Triac num ponto indefinido de tensão(senoide) a cada ciclo?

Não conseguiria controlar com precisão a tensão.

PS:Achei o vídeo que ilustra esses sinais;

Poderia retificar o PWM aplicando uma tensão DC na base do TRIAC?

Assim não precisaria monitorar o zero da senoide.

Link para o comentário
Compartilhar em outros sites

vtrx, devo setar o timer para overflow em quantos ms/us? Visto que o timer1 é de 16 bits, devo testar com 1 e 65535?

Muito obrigado pela colaboração.

Rafale.luc, se houver um pulso no gate do triac, ele ficará em estado de condução plena durante todo o semi-ciclo. Ao passar pelo zero ele entra em corte (ou um pouco antes do zero, pois depende da corrente de manutenção e, conseguintemente, de quanta corrente a carga consome. Então, para que ele sirva ao propósito, ele tem que disparar algum tempo depois de passar pelo zero, pois ficará conduzindo, mesmo se retirar o sinal do gate, até o próximo cruzamento pelo zero. Quanto mais perto do cruzamento pelo zero ele dispara (o cruzamento "da esquerda"), maior a potência. Quanto mais distante do cruzamento, menor a potência. É como se "cortasse" a senoide. Por isso a sua ideia não funciona neste caso, pois ele entrará em condução plena assim que o pulso no gate for suficiente para o disparo.

Muito obrigado pelo colaboração

advtec.

Link para o comentário
Compartilhar em outros sites

vtrx, devo setar o timer para overflow em quantos ms/us? Visto que o timer1 é de 16 bits, devo testar com 1 e 65535?

Eu confundí o código!

Não usei a Interrupção do timer,mas sim a da interrupção externa;

void trata_int_ext(void) {
do
{
output_high(controle);
tempo --;
}while(tempo > 0);
output_low(controle);
tempo = 1;//1 a lampada deve ficar apagada,255 totalmente acesa
}
}

Se fazer os testes,não vejo vantagem em usar o timer.

Link para o comentário
Compartilhar em outros sites

vtrx, independentemente do valor que coloco na variável "tempo", o que está havendo é um pulso sempre no início da senoide. Coloquei 1, 255 e 128 e o resultado é o mesmo. Parece que está acontecendo assim: a cada ciclo de máquina, após a interrupção externa, a variável "tempo" decrementa, até que a condição fique falsa e leve o pino controle a zero. Mas, isso acontece muito rápido, de modo que, o que se observa, é apenas o pulso no início de cada semi-ciclo. Acho que precisamos pensar em uma maneira de, ocorrendo a passagem pelo zero (e por consequência a interrupção externa), haja a contagem de um tempo, o disparo do triac pelo pino controle e, antes da próxima passagem (ou na próxima passagem pelo zero), o pino controle volte a zero. O que acha?

Obrigado e bons trabalhos.

Link para o comentário
Compartilhar em outros sites

Então,dei a ideia de usar a rotina na Interrupção externa,pois assim fica independente da velocidade do micro mas fica DEPENDENTE do seu circuito detector de zero funcionar bem.

voce está usando o circuito que postei?

Verifique com a rotina abaixo que tem um range maior (1 a 65535).

int16 tempo = 1;
//////////////////
#int_ext
void trata_int_ext(void) {
do
{
output_high(pin_a1);
tempo --;
}while(tempo > 0);
output_low(pin_a1);
tempo = 1;//1 a lampada deve ficar apagada.
}

Posta o código todo,quem sabe voce não esta inicializando algo errado.

Como esta comportando a lampada?

Qual pino esta usando para a interrupção externa?

Link para o comentário
Compartilhar em outros sites

  • mês depois...

Bom dia a todos.

O problema que postei neste tópico foi resolvido agora.

Aproveitei as ideias aqui lançadas, notadamente a de vtrx.

Para o tópico não ficar "flutuando" no forum e também para que possa servir de consulta, descrevo aqui a solução.

Seguindo a linha de raciocínio do vtrx, o que fiz foi usar a interrupção externa para comandar o timer0. Como o que eu precisava era gerar o mínimo de interrupções (ao contrário do modelo que descrevi no início do tópico), a interrupção externa o "seta" consoante a potência desejada. Abaixo, o que há na interrupção externa:


#int_ext
void trata_int_ext(void) {
ENABLE_INTERRUPTS(INT_TIMER0);
set_timer0(45+ajuste); a variável "ajuste" recebe valores pelo usuário
flagZero=1; //sinaliza para a rotina de leitura de temperatura
}
exibe(); //rotina de exibição nos diplays 7 seg.
}

E, no timer0, está assim:


#int_timer0
void trata_timer0(void) {
output_high(controle); //pino que vai para o MOC
delay_us(200); //tempo do pulso
output_low(controle);
DISABLE_INTERRUPTS(INT_TIMER0);
}

Desta maneira, o sistema trabalha em sincronismo.

Em razão de meu hardware, o tempo total da passagem pelo zero da senoide é de 1,3ms (junções de diodos da ponte retificadora e do transistor), tempo mais do que o suficiente para a realização das tarefas acima.

Na prática, funcionou perfeitamente, sem interferência de uma interrupção sobre a outra, já que a do timer0 ocorre apenas depois que a interrupção externa "permite".

A parte do termômetro (meu projeto é um controlador de temperatura) só é habilitada após o disparo do timer0. Ressalto que não coloquei a leitura de temperatura em um timer. Preferi deixar em main, dentro de um while(true) - uma variável incrementa a cada ciclo e, após alguns (ajustei "na unha" e observando na prática até obter uma estabilização) incrementos, a rotina de leitura é acionada.

Aos que cooperaram comigo, muito obrigado.

O tópico pode ser fechado como resolvido.

Bons trabalhos.

Link para o comentário
Compartilhar em outros sites

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

Ebook grátis: Aprenda a ler resistores e capacitores!

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!