Você está na página 1de 34

UNIVERSIDADE DO RIO GRANDE DO SUL

DEPARTAMENTO DE ENGENHIARIA ELÉTRICA


ENG04475 – MICROPROCESSADORES I

CONTROLE DE TEMPERATURA
MICROCONTROLADO

Autor: Rafael Marquetto Vargas, aluno do sexto semestre de Engenharia


de controle e automação da Universidade Federal do Rio Grande do sul,
bolsista no Laboratório de Mecatrônica e Controle – Lamecc no
departamento de Engenharia mecânica.
Email: rafao_brs@hotmail.com

Porto Alegre, 11 de setembro de 2012.


Resumo

O trabalho a seguir apresenta uma solução para o controle de temperatura


de água através de uma manipulação da forma da onda de tensão elétrica
aplicada em um aquecedor resistivo. Uma vez dominado o controle sobre a
quantidade de energia entregue a uma carga resistiva, pode-se controlar
atuadores de diversos processos de naturezas diferentes.
O problema apresentado neste projeto é projetar um circuito
microcontrolado capaz de aquecer uma quantidade de água dentro de uma
térmica até uma temperatura escolhida pelo usuário do equipamento. Para
tal foi necessário construir um controlador capaz de ler a temperatura da
água dentro de uma térmica, controlar a potência dissipada em um
aquecedor de água e controlar uma interface homem-máquina que
possibilite o usuário escolher a temperatura desejada e se manter informado
sobre a temperatura momentânea do sistema.
Introdução

Dentre os diversos tipos de sistemas físicos existentes, os sistemas


térmicos estão entre os mais fáceis de controlar devido ao fato de serem
sistemas de natureza muito lenta e também pelo fato de que perturbações
externas dificilmente afetam a estabilidade do sistema. Essa facilidade de
controle dos sistemas térmicos foi um dos motivos para escolhermos um
sistema desse tipo para ser o alvo do nosso projeto.

O sistema escolhido foi uma garrafa térmica de um litro e meio


completamente ou parcialmente preenchida com água. Para projetar um
sistema de controle para a temperatura da água dentro dessa garrafa
necessita-se de três elementos de controle básicos: controlador, atuador e
sensor. O controlador será implementado através de programação na
linguagem C em um microcontrolador PIC16f877a, o atuador será um
aquecedor de água comum, encontrado em qualquer loja de ferragens, e o
sensor de temperatura será um circuito integrado chamado LM35.

O objetivo deste trabalho é projetar e montar um controlador capaz


de elevar e manter a temperatura da água na garrafa até uma temperatura
escolhida pelo usuário do equipamento. Para facilitar essa interação entre o
usuário e o controlador uma interface homem-máquina foi desenvolvida.
Esta interface conta com um display LCD para informar ao usuário a
temperatura instantânea da água e também mostrar a temperatura desejada
escolhida pelo usuário, conta também com dois botões que possibilitam o
usuário a aumentar ou diminuir a temperatura desejada.

Para o controlador ser capaz de tomar uma ação de controle, ele deve
possuir duas informações: a temperatura desejada que é dada pelo usuário,
e a temperatura atual do sistema que é dada pelo sensor de temperatura.
Uma vez calculada a ação de controle, o controlador deve ser capaz de
fazer o atuador do sistema executar esta ação. Neste caso, a ação de
controle é a potência entregue à água pelo aquecedor, para que o
controlador seja capaz de aplicar essa potência é necessário um driver de
potência.

Neste projeto, foi utilizado um componente eletrônico chamado triac


para ser o principal elemento do driver de potência. O triac foi escolhido
devia à sua capacidade de controlar a forma de onda da energia elétrica da
rede convencional, evitando a necessidade de circuitos retificadores ou
transformadores de tensão.
Ao final do projeto será implementado um controlador Proporcional-
Integral para fins de otimizar à resposta do sistema à referência dada pelo
usuário.
Desenvolvimento

Metodologia e etapas de desenvolvimento:

Para ser possível a implementação e teste do algoritmo de controle


do controlador, todas as outras partes do sistema, como sensoriamento e
driver de potência, devem estar montadas funcionando corretamente, pois
todas elas têm uma função importante na malha de controle do sistema.
Isso implica que as primeiras etapas de desenvolvimento do projeto
garantam o funcionamento do hardware do equipamento, e as últimas
etapas sejam a otimização do algoritmo de controle.

A primeira etapa do projeto foi a pesquisa bibliográfica sobre os


componentes eletrônicos e circuitos a serem utilizados no projeto. Após a
pesquisa foi necessária a busca e compra dos componentes necessários para
a montagem do projeto.
A metodologia utilizada para colocar em prática os conhecimentos
obtidos com a pesquisa foi começar o circuito com o mínimo de
componentes possível e gradualmente, ir agregando novas funções ou
novos componentes ao circuito, sempre testando o seu funcionamento antes
de dar o próximo passo.
A seguir uma breve descrição destas etapas desenvolvidas no projeto
em ordem cronológica, mais adiante será feita a explicação detalhada de
cada etapa e do funcionamento do projeto em si:

1. Conseguir um gravador de microcontrolador e uma fonte de


alimentação.
2. Comprar os componentes eletrônicos necessários para a
montagem do circuito.
3. Gravação do primeiro programa no microcontrolador, um led
piscando alternadamente;
4. Montagem e configuração de dois botões ligados ao
microcontrolador, com a função de aumentar ou diminuir a
temperatura alvo do sistema;
5. Configuração do canal analógico-digital do microcontrolador,
para a aquisição dos dados de temperatura do sistema;
6. Criar uma comunicação com um display LCD para que o
microcontrolador possa escrever dados relevantes na tela do
LCD;
7. Configurar o pino de interrupção externa do microcontrolador
fazendo-o mudar acender ou apagar um led;
8. Configurar o timer 0 do microcontrolador fazendo-o piscar um
led com a frequência de um segundo;
9. Montar o circuito detector de passagem por zero, ligando a sua
saída na interrupção externa do microcontrolador;
10.Projetar um algoritmo que compense a falta de sincronismo
entre o momento que a interrupção externa é gerada, com o
real momento de passagem por zero da tensão;
11.Projetar um algoritmo que possibilite gerar um pulso de tensão
em um pino do microcontrolador após um certo instante de
tempo, a partir do momento em que ocorreu a interrupção
externa;
12.Calcular e implementar os limites de tempo possíveis para a
geração desse pulso;
13.Montar o circuito de acionamento do triac e testar o seu
funcionamento usando hardware construído anteriormente;
14.Acoplar ao circuito do triac, o aquecedor de água e testá-lo
dentro da garrafa térmica;
15.Criar um isolamento contra água para o LM35 poder ser
mergulhado dentro da garrafa térmica;
16.Criar um algoritmo capaz de calcular um controle proporcional
para o disparo do triac em função da temperatura atual e a
temperatura alvo.
17.Criar um algoritmo para implementar um controlador
proporcional-integral para o sistema
18.Otimizar os parâmetros do controlador para obter uma resposta
do sistema satisfatória

Para que o leitor entenda todas as etapas do desenvolvimento do


projeto, iremos explicar o caminho para a solução adotado para o nosso
problema, pois este abrange todos os problemas e soluções que tivemos
durante o projeto. A seguir serão explicados da forma mais completa
possível, os desafios encontrados no projeto na ordem cronológica em que
eles ocorreram.
Caminho para a solução:

Escolha do Hardware:
Este projeto é um trabalho final da disciplina de Microprocessadores
I do departamento de engenharia elétrica da UFRGS. Portanto a condição
inicial do projeto é que seja usado um microprocessador para implementar
a solução do problema explicitado.

Assim sendo, a primeira decisão a ser tomada foi a de qual


microprocessador utilizar. O microprocessador escolhido foi o
microcontrolador da Microchip chamado PIC16f877a. Este
microcontrolador foi escolhido pelo fato de ter embutido alguns periféricos
interessantes para o nosso projeto como timer, portas I/O, mas
principalmente por ele ter um conversor analógico/digital, um clock de alta
frequência e um pino interrupção externa. O conversor analógico/digital
desse PIC possui uma resolução máxima de 10 bits para uma entrada de 0 a
5 volts em seus pinos de A/D. O clock máximo
suportado por este PIC é de 20 MHz, sabendo
que o seu ciclo de máquina consome 4 ciclos de
clock, concluímos que esse microcontrolador é
capaz de realizar cinco milhões de ciclos de
máquina por segundo. A interrupção externa
deste PIC é capaz de gerar a qualquer momento,
uma interrupção no programa principal quando
ocorrer uma mudança de tensão no pino
correspondente do microcontrolador.
Já sabíamos que iríamos projetar um controlador de temperatura,
então precisamos escolher um sensor de temperatura para ser utilizado. O
sensor escolhido foi o LM35 da National Semiconductors. Este sensor foi
escolhido devido à sua facilidade de decodificação
e ao seu baixo custo, ele possui dois pinos de
alimentação e um de sinal, este último gera uma
tensão elétrica proporcional à temperatura do
sensor, partindo de 0 volts para 0°C e
incrementando 10 milivolts a cada grau Celsius
incrementado. Apesar do encapsulamento dele não
ser adequado para ser colocado embaixo d’água, a
sua facilidade de uso compensa o trabalho de
impermeabiliza-lo, para tal, foi utilizado silicone,
facilmente encontrado em lojas de ferragens.
Para criar uma interface visual
com o usuário do circuito foi
escolhido utilizar um display LCD de
2 linhas por 16 colunas. Apesar do
seu preço elevado, ele foi nossa
escolha pela capacidade de mostrar
muita informação ao mesmo tempo,
32 caracteres.

Após escolhidos os itens anteriores foi necessário escolher uma


estratégia de controle para a etapa de potência do circuito. Previamente já
estava decidido que se usaria um aquecedor de água comum, que nada mais
é que uma resistência com alta
capacidade dissipativa de calor. Só não
sabíamos ainda qual seria o tipo de
acionamento dessa resistência. O resistor
é um componente passivo, portanto, pode
ser alimentada com qualquer tipo de
tensão, contínua, alternada senoidal ou
mesmo qualquer forma de onda. A
potência dissipada na resistência sempre
será a tensão sobre a carga elevada ao
quadrado dividida pelo valor da
resistência.
Durante a pesquisa bibliográfica descobrimos um método muito
eficiente de controle de tensão sobre uma carga, o chaveamento de tensão
alternada com um triac. Este método consiste em usar o triac como um
comutador para chavear uma tensão alternada a ser aplicada sobre a carga.
Dependendo dos instantes em que os chaveamentos ocorrem, pode-se
controlar a quantidade de energia entregue à carga.
Para entendermos o que vem adiante, precisamos entender o
funcionamento do Triac. O triac possui três pinos, um deles recebe a
alimentação, ou seja a tensão alternada de 127 volts por exemplo. Em outro
se liga a carga, pois ali que vai aparecer a onda de tensão chaveada. E o
outro pino se chama “gate”, este é usado para controlar o momento em que
o triac é disparado. A principal característica do triac é que quando ele
recebe um pulso de corrente de largura de alguns micro segundos no
terminal “gate”, ele passa a conduzir a corrente gerada pela tensão senoidal
até que essa corrente chegue a zero (fim do semi-ciclo), a partir desse
momento, o triac só passa a conduzir corrente novamente se receber outro
pulso no gate. Isso significa que o gate deve ser disparado a cada semi-
ciclo da senóide de tensão.
O resultado de um chaveamento de tensão alternada com triac pode
ser observado na figura abaixo:

O momento da descontinuidade do gráfico é o momento que o triac


recebe um pulso de corrente no gate e passa a conduzir a tensão alternada.
Como podemos observar, assim que a onda chega a zero no gráfico o triac
para de conduzir até que outro pulso de corrente no gate ocorra. A energia
entregue à carga alimentada por essa tensão é proporcional à área desta
curva de tensão. Mudando o momento em que
esse pulso no gate ocorre, podemos mudar o
formato dessa onda, e consequentemente mudar a
quantidade de energia entregue à carga. O triac
escolhido para esta aplicação se chama BTA-12-
600BW, escolhemos este triac pelo fato de
aguentar mais corrente que os triacs comuns que
são capazes de fornecer uma corrente de 6
amperes, este triac fornece uma corrente de até
12 amperes e aguenta tensões de até 600 volts.

Tendo o triac, para o microcontrolador ser capaz de escolher a


quantidade de energia entregue à carga, ele precisa saber quanto tempo
esperar para disparar o triac após a passagem por zero da onda, e também
precisa da informação do exato momento em que a onda passa por zero,
para que a partir desde momento comece a contar o tempo que falta para
gerar o pulso de corrente que irá disparar o triac. Para gerar esta
informação para o microcontrolador usamos um circuito chamado “circuito
detector de passagem por zero”.
Foi utilizado um circuito encontrado no site Projetos e TI, em um
post criado por Fabio Betoni, o link está na bibliografia do projeto. O
circuito consistem em dois optoacopladores PC817, modelos facilmente
encontrados no mercado. O esquemático do circuito está abaixo:

Figura 1 - Esquemático do circuito detector de passagem por zero

O funcionamento do circuito é muito simples. Os leds dos


optoacopladores são alimentados pela tensão alternada da rede. Esta tensão
fica alternando entre positiva e negativa, quando ela é positiva, o led do
optoacoplador U5 acende e polariza o foto transistor ao seu lado, este por
sua vez deixa a saída do circuito em curto circuito com o terra, ou seja, a
tensão de saída neste momento é zero. Quando a tensão de alimentação é
negativa, a mesma coisa acontece, só que desta vez com o optoacoplador
U6, neste instante a saída do circuito também é levada a zero. No momento
de passagem por zero da tensão de alimentação, não existe diferença de
potencial sobre nenhum dos leds e nenhum dos foto transistores é
polarizado, deixando a corrente que passava pelo resistor R9 sem caminho
para seguir, sem corrente, não há diferença de potencial sobre o resistor e a
saída do circuito vai a 5 volts.
Este circuito foi montado e observado no osciloscópio. A saída do
circuito pode ser observada na figura a seguir:

Os picos de tensão observados ocorrem exatamente no instante de


passgem por zero da tensão de alimentação. Pode-se notar que a frequência
destes picos é 120Hz já que a frequência da rede elétrica é 60Hz.
Para enviarmos esta informação gerada pelo circuito detector de
passagem por zero para o microcontrolador, utilizamos o seu pino de
interrupção externa. Fizemos esta escolha porque não poderíamos arriscar
que o microcontrolador estivesse ocupado fazendo outro processamento
enquanto a passagem por zero ocorresse e ele acabasse perdendo essa
informação. Usando a interrupção externa, assim que ocorrer a passagem
por zero, o microcontrolador irá detectar este pico de tensão no pino da
interrupção e interromper o programa principal onde quer que ele esteja e
executar o código que estiver dentro da função de interrupção. Esta
estratégia nos garante que nenhuma passagem por zero será passada
despercebida pelo microcontrolador.

Primeiramente ligamos a saída deste circuito diretamente no pino de


interrupção externa do PIC, e configuramos para a interrupção externa ser
gerada pela borda de subida da tensão. Dentro do código da interrupção
colocamos uma instrução para colocar um outro pino I/O do PIC em nível
lógico alto por alguns micro segundos. A resposta obtida está abaixo:
Podemos perceber que o momento que o microcontrolador considera
a borda de subida ocorre um pouco antes do real momento de passagem por
zero que é o pico da parábola. Para corrigir este erro, foi utilizado o timer 0
do microcontrolador.
O timer 0 é um temporizador de 8 bits que é incrementado a cada
ciclo de máquina. Uma variável de 8 bits pode ser considerado uma
variável que pode assumir valores decimais de 0 a 255, como esta variável
está sendo incrementada a cada ciclo de máquina, vai chegar um momento
em que esta variável vai chegar ao seu limite (255), este momento se
chama “overflow do timer”. A principal característica do timer é gerar uma
interrupção quando o overflow acontece, esta interrupção é similar à
interrupção externa, ela para o programa principal onde estava, e executa o
código dentro da chamada da interrupção. Depois de gerada a interrupção
do timer, o temporizador é zerado e começa a ser incrementado novamente,
assim que ele estourar novamente, gerará uma nova interrupção.
O cristal oscilador utilizado neste projeto é de 20Mhz, um ciclo de
máquina do PIC leva 4 ciclos de clock, ou seja, os ciclos de máquina tem
uma frequência de 5MHz. Sendo assim, um ciclo de máquina dura 200
nano segundos. Sabendo que o timer 0 gera uma interrupção a cada 256
ciclos de máquina, podemos calcular que as interrupções do timer 0
ocorrem a cada 51.2 micro segundos.
Analisando a figura acima, podemos perceber que o pulso gerado
pelo microcontrolador é gerado aproximadamente 500 microsegundos antes
do que deveria. Então, para atrasar esse pulso, criamos um código que só
gera esse pulso de tensão depois que ocorram 10 overflows de timer, ou
seja, 512 micro segundos.

delay_trigger=0;
#int_EXT
void EXT_isr(void)
{
enable_timer=1;
}

#int_RTCC
void RTCC_isr(void)
{
if(enable_timer)
{
contador_int++;

if(contador_int>=10+delay_trigger)
{
output_high(PIN_D2);
contador_int=0;
enable_timer=0;
disable_trigger=6;
}
}

if(disable_trigger)
disable_trigger--;
else
output_low(PIN_D2);

A interrupção externa seta uma variável auxiliar que serve de


condição para começar a contar o atraso de 10 overflows na rotina da
interrupção do timer. O pino D2 foi escolhido para gerar o pulso,
analizando o código pode-se perceber depois que ocorre a interrupção
externa gerada pelo circuito detector de passagem por zero, existe um
atraso correspondente a 10 overflows do timer 0 até que o pino D2 seja
setado. Após isso ele demora 6 overflows para retornar ao valor lógico “0”.
Abaixo, uma imagem com o resultado obtido com este código:

Sobrepondo este sinal gerado pelo pino D2 do microcontrolador na


onda de tensão senoidal, temos a figura abaixo:

Podemos perceber que o pulso está sendo gerado exatamente no


instante de passagem por zero da onda.
Com este código conseguimos resolver mais um problema além da
sincronização do circuito de passagem por zero. Variando aquela variável
delay_trigger que é somada ao 10 dentro do if responsável pela geração do
pulso, podemos atrasar ainda mais o pulso de controle de triac, isso
possibilita gerarmos este pulso em qualquer momento do semi ciclo.

A seguir algumas imagens do pulso sendo gerado em diferentes


instantes do semi ciclo:

Figura 2 – delay_trigger=5; Figura 3- delay_trigger=50;

Figura 4 - delay_trigger=100; Figura 5 - delay_trigger=150;

Como sabemos que a frequência da rede elétrica é 60Hz, sabemos


que o período de um semi ciclo é 8.333 mili segundos. Dividindo 8.333
mili segundos por 51.2 micro segundos, temos que durante um semi ciclo
da rede o timer gera 162.76 overflows. Com isso podemos concluir que a
faixa de operação da variável delay_trigger será de 0 a 162.
Tendo em mãos o sinal de controle do triac, só nos falta agora um
circuito capaz de usar esse sinal para de fato disparar o triac. Como a nossa
carga é ligada na rede elétrica de 127 volts, é perigoso manter esta tensão
com algum caminho possível para o circuito de acionamento que trabalha
com baixas tensões. O artifício utilizado para fazer um isolamento elétrico
entre o circuito de alta potência e o de baixa potência é usar um acoplador
óptico. Da mesma forma que ocorreu com o circuito detector de passagem
por zero, o acoplamento óptico garante que mesmo que todos os
componentes de potência alta queimem ou apresentem algum defeito, será
impossível o circuito de baixa potência sofrer algum dano devido a alta
tensão do circuito de alta potência.
O circuito utilizado para o acionamento do triac neste projeto foi
retirado da folha de dados do opto acoplador MOC3011. O circuito pode
ser visualizado na figura abaixo:

Figura 4 - Esquemático do circuito de acionamento do Triac

Além de este circuito garantir um acoplamento óptico, ele utiliza a


própria tensão da rede para acionar o triac, isso garante que o triac dispare
nos quadrantes que exigem menos corrente no gate. O funcionamento dele
é simples, quando o led foto emissor acende, o foto diac conduz
eletricidade para o gate do triac disparando-o. Já que este circuito estava na
folha de dados do MOC3011, consideramos que tenha uma boa
confiabilidade.

Quando este circuito opera com potência máxima, uma corrente de


até 6 amperes pode passar pelo triac, então para não estragarmos a
protoboard, construímos uma placa de fenolite para aguentar a alta
corrente. Colocamos um dissipador de calor de alumínio no triac devido à
grande quantidade de calor que ele gera sob altas correntes. Da placa saem
dois pares de fios, um par para ligar na rede e outro para ligar o aquecedor
de água.

Abaixo algumas imagens da tensão sobre o aquecedor variando a


variável delay_trigger:
Abaixo estão as fotos de todos os circuitos descritos acima:

Figura 7 - Protoboard com o microcontrolador e o display LCD

Figura 8 - Sensor LM35 impermeabilizado com silicone


Figura 9 - Circuito detector de passagem por zero

Figura 10 - Placa de acionamento do Triac

Ao fim deste relatório existe um anexo onde se encontra o


esquemático completo do circuito.
Desenvolvimento do Software:
O compilador utilizado foi o PCWHD Compiler versão 4.120 da
CCS, um compilador versátil e organizado. O índice de comandos e
sintaxes foi muito útil do desenvolvimento do projeto, organizado e de fácil
compreensão. Para apresentar o software desenvolvido, podemos explicar
ele por partes, começando pelo laço principal do programa:

while (true)
{
if(calcular_controle==1) //Código que calcula os parâmetros de controle
{ //de 1 em 1 segundo
output_low(LED_RED); //Led usado para saber quanto tempo demora o cálculo
sensor=read_adc(); //Faz a leitura do canal AD e salva em sensor os 10 bits
temperatura_atual=sensor*500/1024; //Converte o valor binário em graus celcius
erro=temperatura_alvo-temperatura_atual; //Calcula o erro do controlador

P=Kp*erro; //Controle proporcional


/*
if(potencia>=0&&potencia<=1) //Controle integral
{
I=I_anterior + Kp*(erro+erro_anterior)/(2*Ti); //Integrador
}
erro_anterior=erro;
I_anterior=I;
potencia=P+I;
*/
potencia=P; //Potência a ser entregue em porcentagem
if(potencia>=1) //Garante que a potencia esteja no domínio
potencia=1; //da função arco cosseno
if(potencia<=0)
potencia=0;

delay_trigger=(162.76/3.1416)*acos(2*potencia-1); //Linearização do atuador


//Cálculo do delay_trigger equivalente à potência
if(delay_trigger>150) //Garante que o delay_trigger estará
delay_trigger=150; //na sua faixa de operação estável
if(delay_trigger<5)
delay_trigger=5;

printf(lcd_putc,"\f"); //limpa o lcd e apresenta os dados


printf(lcd_putc,"TEMP %f%cC",temperatura_atual,letra);
printf(lcd_putc,"\nTEMP ALVO %u%cC", temperatura_alvo,letra);

output_high(LED_RED); //Apaga o Led vermelho


calcular_controle=0; //Habilita o programa a calcular
} //os próximos parâmetros

if(!input(PIN_D0)) //Lógica para que o botão não incremente


{ //muitas vezes em um curto intervalo de tempo
contador_MAIN++;
if(contador_MAIN==100)
{
temperatura_alvo++;
contador_MAIN=0;
}
}
if(!input(PIN_D1)&&temperatura_alvo>=1) //Mesma coisa que o anterior
{ //porém para decremento
//Também garante que a temperatura
contador_MAIN--; //alvo não seja negativa
if(contador_MAIN==-100)
{
temperatura_alvo--;
contador_MAIN=0;
}
}
delay_ms(1); //Delay utilizado para que as lógicas dos botões funcionem
}
A primeira coisa que o programa faz é verificar se é hora de calcular
os parâmetros de controle. Se for, ele faz a leitura do sensor, para o LM35
0 volt equivale a 0ºC e 5 volts equivale a 500ºC, e para o conversor AD 0
volt equivale a 0 decimal e 5 volts equivale a 1024 decimal pelo fato de ser
um conversor de 10 bits. Então foi usada uma regra de três para determinar
a temperatura. Após isso o programa calcula um erro entre a temperatura
atual e a temperatura alvo, multiplica por uma constante para gerar a ação
de controle. A rotina que está como comentário foi utilizada para fazer um
controle proporcional integral, mas acabou não sendo vantagiosa e não foi
utilizada no programa final.

Para descobrir quanto tempo deve demorar para o triac ser disparado,
em função da potência desejada, tivemos que fazer uma linearização do
atuador, pois a tensão que determina a potência tem forma senoidal.
Sabemos que a potência entregue ao aquecedor é proporcional à área
da onda de tensão cortada pelo triac, então podemos calcular a integral
dessa área para descobrirmos a potência entregue.

Chegamos à expressão abaixo:


( )

Sendo x em radianos. O gráfico da função está abaixo:

0.9

0.8

0.7

0.6
Potência

0.5

0.4

0.3

0.2

0.1

0
0 0.5 1 1.5 2 2.5 3
Ângulo de disparo em radianos

Isolando o ângulo de disparo em função da potência desejada:


( )

( )
Para o nosso programa, já foi provado anteriormente que a variável
delay_trigger pode variar de 0 a 162.76, logo podemos concluir que
delay_trigger=0 equivale a 0 radianos e delay_trigger=162.76 equivale a π
radianos de ângulo de disparo. Então podemos aplicar uma regra de três
para calcular o delay_trigger equivalente à potência desejada:

Substituindo uma equação na outra:

( )

Assim encontramos a equação usada no programa.


Após calcular o valor da variável delay_trigger, o programa principal
garante que esta variável esteja em sua faixa de operação limitando seus
valores entre 5 e 150. Valores fora desta faixa deixam o triac instável pelo
fato de estarem muito próximos dos limites do semi ciclo.

Em seguida o programa envia para o lcd informações de temperatura


alvo e atual, como essa rotina acontece de 1 em 1 segundo, o display é
atualizado nessa frequência.

O “LED_RED” que é acendido e apagado nessa rotina, é usado para


termos noção de quanto tempo demora para o microcontrolador calcular
todos os parâmetros do controle, pois além de ter a função acos() da
biblioteca math.c, fizemos testes com o código do controlador integral que
também tem um certo tempo de processamento. Se o led ficasse aceso por
muito tempo, saberíamos que deveríamos dar mais de 1 segundo para o
processador executar essa função.

O último comando deste “if” zera a variável calcula_controle para


que seja possível calcular novamente na próxima vez que for requisitado.
Após isso tem um código simples que verifica os dois botões a cada
100 mili segundos aproximadamente e modifica a variável temperatura
alvo conforme necessário.

Os demais comandos da rotina void_main são apenas de


configuração dos periféricos do microprocessador e podem ser facilmente
compreendidos com os comentários que estão no código em anexo no fim
deste relatório.

Como visto, o programa principal serve basicamente para calcular o


valor da variável delay_trigger em função dos parâmetros do sistema.
Agora, a parte que resta explicar do programa, é responsável por setar esta
variável calcula parâmetros de 1 em 1 segundo e também é responsável por
usar esta variável delay_trigger calculada no main para acionar o triac no
tempo certo.
Estas duas temporizações são baseadas no fato de que o timer gera
uma interrupção de overflow a cada 51.2 micro segundos, como explicado
anteriormente. Assim, temos uma base de tempo para fazer as
manipulações necessárias.
Como explicado anteriormente, o disparo do triac é dado baseado no
instante em que a passagem por zero da onda da rede ocorre. Como o
circuito detector de passagem por zero está conectado na interrupção
externa, esta é gerada sempre que o circuito detecta uma passagem por
zero. O código da função da interrupção externa vem abaixo:
#int_EXT //definição para sinalizar que a seguir vem o
void EXT_isr(void) //código da função de interrupção externa
{ //a única coisa que essa interrupção faz é
enable_timer=1; //sinalizar que ocorreu a passagem por zero
} //para que na próxima interrupção gerada pelo
//timer 0 o programa tome as devidas providências

A única coisa que esta função faz é setar uma variável para que na
hora que ocorrer a próxima interrupção do timer 0, o programa saiba que
ocorreu uma passagem por zero, e tome as devidas providências.

A última parte do programa a ser explicada é a função da interrupção


gerada pelo timer 0 que já foi parcialmente explicada no início do relatório.
O código vem a seguir:
#int_RTCC //definição que a seguir vem o código da interrupção do timer0
void RTCC_isr(void)
{

if(enable_timer) //se esta variável for diferente de zero, quer


{ //dizer que acabou de acontecer uma passagem por 0
contador_int++; //incrementa um a cada 51.2us na contagem
//para o disparo
if(contador_int>=10+delay_trigger) //entra aqui se já é a hora
{ //de disparar o triac
output_high(TRIGGER); //pino D2 é ligado ao circuito de acionamento do triac
contador_int=0; //zera o contador para se preparar para a próxima contagem
enable_timer=0; //zera esta variável para ficar apta a avisar quando houver
disable_trigger=6; //a próxima passagem por zero
} //essa última linha define a largura de 6 overflows para o pulso

if(disable_trigger) //essas 4 linhas servem para dar uma largura


disable_trigger--; //ao pulso de controle do triac
else
output_low(TRIGGER);

contador++; //contador responsável pela contagem de 1 segundo


if(contador==19531) //19531*51.2us dá 1 segundo
{
contador=0; //zera o contador para a próxima contagem
output_toggle(LED_GREEN); //muda o nível lógico do LED verde de 1 em 1s
calcular_controle=1; //avisa o programa principal que pode
} //calcular os parâmetros de controle
}

Como já foi dito antes, a principal característica desta função é que


ela é executada de 51.2 em 51.2 micro segundos. O primeiro “if” dela já foi
explicado, ele gera o pulso de controle do triac um certo tempo depois que
ocorre a passagem por zero, este tempo é definido pela variável
delay_trigger que é calculada no loop principal do programa. Após colocar
o pino responsável pela geração do pulso em nível lógico alto, o segundo
“if” é encarregado de colocar este pino em nível lógico baixo novamente
depois de um tempo de 6 overflows de timer.
O terceiro e último “if” desta função não foi explicado
anteriormente, ele é responsável por a cada 1 segundo (19531*51.2 micro
segundos) trocar o estado de um led e mandar setar a variável
calcula_controle. Este é o trecho de código responsável por fazer o PIC
calcular os parâmetros de controle de 1 em 1 segundo. Com isso
terminamos de explicar todas as partes do software desenvolvido, que pode
ser encontrado completo e com comentários no anexo ao final deste
relatório.

Observações finais:
Duas bibliotecas foram utilizadas para a execução deste código,
math.c que contém a função acos() utilizada, e a bibliotela flex_lcd.c
utilizada para a comunicação do PIC com o display lcd, ambas podem ser
encontradas no anexo ao fim deste relatório.
Conclusões
Ao longo do projeto foram encontradas muitas dificuldades, algumas
por falta de atenção e outras realmente complicadas, mas a metodologia
utilizada para desenvolver o projeto se mostrou eficiente na hora de superar
estas dificuldades.

Uma delas que tomou uma grande parte do tempo foi conseguir
escrever dados no display lcd. Por diversas vezes, o periférico não
funcionou, depois de muitas tentativas descobrimos dois erros graves no
nosso desenvolvimento. Um deles é que o pino de contraste do lcd tem que
estar obrigatoriamente com em um potenciômetro calibrado porque de
maneira diferente, pode-se escrever no lcd mas não será possível visualizar
o que está escrito. Outro problema grave, é que aparentemente a biblioteca
lcd.c de interface com lcd disponível no compilador utilizado,
aparentemente não funciona corretamente, o código só passou a funcionar
direito depois que passamos a utilizar a biblioteca flex_lcd.c encontrada em
um fórum da CCS, desenvolvedora do compilador utilizado. Além de esta
biblioteca funcionar corretamente, ela permite escolher da forma que quiser
quais será os pinos utilizados para a comunicação com o lcd,
funcionalidade indispensável para o nosso projeto, pois as outras
bibliotecas que não eram flexíveis utilizavam o único pino de interrupção
externa do nosso PIC para comunicação com o lcd.
Outra problema encontrado que atrapalhou um pouco no
desempenho final do projeto foi o fato de o sensor LM35 ser muito
instável, variando em uma faixa de 2ºC para mais ou para menos mesmo
quando a água não sofre influencia do aquecedor. Este problema faz com
que o nosso controle “contínuo” não seja tão contínuo assim. Outro
problema que não deu trabalho mas provavelmente deixou o sistema
impreciso foi a inércia térmica gerada pelo silicone colocado sobre o
sensor, que provavelmente gera um atraso considerável na leitura do
sensor.

Tentamos também implementar um controlador integral que acabou


não se mostrando muito útil. Como dito na introdução, o processo térmico
é um processo muito lento, e a ação integradora acabava gerando um
overshoot muito grande na resposta do sistema, porque até a temperatura da
água chegar à de referência o integrador chegava no seu limite, e demorava
muito para ficar negativo, gerando um tremendo overshoot. Tentamos
utilizar técnicas de anti-wind-up mas mesmo assim o controlador
proporcional integral não mostrou vantagem sobre o proporcional. Baseado
nos experimentos percebemos que o efeito de erro em regime permanente
do controlador proporcional não se manifesta neste caso em função da
inércia térmica do processo e também da instabilidade do sensor que
esporadicamente cai 1 ou 2 graus da temperatura real e acaba ligando o
aquecedor. Mesmo assim deixamos o código do controle integral
comentado, para que em alguma aplicação futura possa ser utilizado.

Fazendo alguns ensaios, observamos que o sistema é capaz de


aquecer 1 litro de água de 20ºC a 70ºC em aproximadamente 4 minutos
sem que a temperatura passe mais que 2ºC da temperatura de referência.
Resultado muito satisfatório para um controle proporcional simples.
Próximos passos
Este projeto ainda pode ser melhorado em muitos aspectos, um deles
é que foi utilizado um microcontrolador de 40 pinos sendo que nem metade
deles foi utilizado. Encontramos um microcontrolador PIC da família 16f
também que tem todas as funcionalidades e periféricos utilizados neste
projeto mas que é bem mais barato e possui somente 18 pinos, o
PIC16f716. No futuro, o programa pode ser adaptado para este
microcontrolador.

Outro ponto importante que deve ser melhorado é o sensor. O LM35


não é o sensor ideal para este tipo de aplicação, ele se provou
extremamente instável, o que perturba muito o desempenho do sistema.
Outro fato que prejudicou o desempenho foi que o canal AD do PIC é
capaz de medir tensões de 0 a 5 volts e nós usamos ele para medir apenas
tensões de 0 a 1 volt, se colocarmos um amplificador operacional com
ganho de 5 vezes podemos aumentar 5 vezes a resolução do dado de
temperatura. A opção ideal seria utilizar um termopar do tipo K, que é bem
estável e possui inércia térmica praticamente nula, não utilizamos este no
projeto porque ele exige um circuito de compensação de junta fria que
acabaria tomando muito tempo de projeto, já que o foco deste projeto é a
programação resolvemos optar pelo LM35 mesmo.
O circuito detector de passagem por zero também pode ser
melhorado, durante a execução do projeto encontramos outro circuito deste
tipo no mesmo site em que havíamos encontrado o circuito anterios. Este
novo circuito utiliza um outro optoacoplador chamado PC814 que possui
dois leds dentro do encapsulamento, substituindo os dois optoacopladores
utilizados no circuito utilizado por nós. O link deste novo circuito pode ser
encontrado no anexo.

Por fim, obviamente o circuito ficaria muito mais robusto e livre me


mau contato se fosse construído em uma placa de circuito impresso pois a
protoboard é uma boa solução para montagem rápida de circuitos mas
apresenta muitas desvantagens no quesito confiabilidade.
Bibliografia
Este projeto foi inteiramente desenvolvido com conhecimentos
adquiridos na disciplina de Microprocessadores I e de pesquisas na internet.
Os dois sites que mais ajudaram no desenvolvimento do software foram:
http://mecatronicadegaragem.blogspot.com.br/

http://www.denteazul.com.br/

Os links abaixo contém praticamente todo o conteúdo utilizado para


o desenvolvimento deste projeto:
http://www.denteazul.com.br/2011/07/tutorial-temporizador-timer-0.html

http://www.ccsinfo.com/forum/viewtopic.php?t=24661

http://mecatronicadegaragem.blogspot.com.br/2011/06/tutorial-interrupcao-
externa-ccs.html

http://www.datasheetcatalog.org/datasheet/stmicroelectronics/7473.pdf

http://www.datasheetcatalog.org/datasheet/fairchild/MOC3011-M.pdf

http://projetoseti.com.br/hardware/eletronica/detector-de-passagem-por-zero/

http://projetoseti.com.br/hardware/eletronica/detector-de-passagem-por-zero-
versao-2/
Anexo
Esquemático completo do circuito:
Código completo comentado:
/*
CONTROLADOR DE TEMPERATURA COM TRIAC BTA12 E LM35
NA INTERRUPÇÃO EXTERNA DO PIC ESTÁ LIGADO UM CIRCUITO DETECTOR DE PASSAGEM POR ZERO
*/
#include <PROGRAMA.h>
#include <flex_lcd.c>
#include <math.h>
#define Kp 0.11 //Parâmetros do controlador
#define Ti 2
#define LED_RED PIN_D3 //Define o pino D3 como led vermenlho
#define LED_GREEN PIN_C2 //Define o pino C2 como led verde
#define TRIGGER PIN_D2 //Define o pino D2 como gatilho do triac

int delay_trigger=140; //Variável responsável deslocar o pulso de comando do triac


int enable_timer=0; //Variável que habilita o início da contagem do disparo do triac
int disable_trigger=0; //Variável que determina a largura do pulso de comando do triac
int16 contador=0; //Contador utilizado para gerar um clock de 1 segundo no timer 0
int16 contador_int=0; //Contador utilizado na contagem do atraso do disparo do triac
float P=0; //Variável de controle proporcional
float I=0; //Variável de controle integral
float I_anterior=0; //Utilizado para possibilitar a ação integradora
float erro_anterior=0; //Utilizado para possibilitar a ação integradora
float erro=0; //Diferença entre temperatura alvo e atual
float temperatura_atual; //Temperatura da água
int temperatura_alvo=30; //Temperatura definida pelo usuário,Inicia em 30ºC
float potencia=0; //Potência a ser aplicada no atuador
int calcular_controle=0; //Habilita o cálculo dos parâmetros de controle

#int_RTCC //definição que a seguir vem o código da interrupção do timer0


void RTCC_isr(void)
{

if(enable_timer) //se esta variável for diferente de zero, quer


{ //dizer que acabou de acontecer uma passagem por 0
contador_int++; //incrementa um a cada 51.2us na contagem
//para o disparo
if(contador_int>=10+delay_trigger) //entra aqui se já é a hora
{ //de disparar o triac
output_high(TRIGGER); //pino D2 é ligado ao circuito de acionamento do triac
contador_int=0; //zera o contador para se preparar para a próxima contagem
enable_timer=0; //zera esta variável para ficar apta a avisar quando houver
disable_trigger=6; //a próxima passagem por zero
} //essa última linha define a largura de 6 overflows para o pulso

if(disable_trigger) //essas 4 linhas servem para dar uma largura


disable_trigger--; //ao pulso de controle do triac
else
output_low(TRIGGER);

contador++; //contador responsável pela contagem de 1 segundo


if(contador==19531) //19531*51.2us dá 1 segundo
{
contador=0; //zera o contador para a próxima contagem
output_toggle(LED_GREEN); //muda o nível lógico do LED verde de 1 em 1s
calcular_controle=1; //avisa o programa principal que pode
} //calcular os parâmetros de controle
}

#int_EXT //definição para sinalizar que a seguir vem o


void EXT_isr(void) //código da função de interrupção externa
{ //a única coisa que essa interrupção faz é
enable_timer=1; //sinalizar que ocorreu a passagem por zero
} //para que na próxima interrupção gerada pelo
//timer 0 o programa tome as devidas providências
void main() //função principal do programa
{
float sensor; //variável onde vai ser jogado o valor da conversão AD de 10 bits
char letra=223; //usado apenas para colocar o º no ºCelsius (código ASCII)
signed int contador_MAIN=0; //contador usado para que os botões não modifiquem
//a temperatura muito rápido
setup_adc_ports(AN0); //Configura a porta AD número zero para ser usada
setup_adc(ADC_CLOCK_INTERNAL); //Clock de amostragem como sendo o clock interno
setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1); //Configura o clock de amostragem como
//o clock interno dividido por 1
enable_interrupts(INT_RTCC); //Habilita a interrupção do timer 0
enable_interrupts(INT_EXT); //Habilita a interrupção externa
enable_interrupts(GLOBAL); //Liga todas as interrupções
ext_int_edge( L_TO_H ); //Define a interrupção externa como borda de subida

lcd_init(); //Função da biblioteca flex_lcd.c que inicia o lcd


printf(lcd_putc,"CONTROLE DE TEMP\nRAFAEL VARGAS"); //Saudação
set_adc_channel(0); //Escolhe o canal AD zero para ser lido
delay_ms(2000);

while (true)
{
if(calcular_controle==1) //Código que calcula os parâmetros de controle
{ //de 1 em 1 segundo
output_low(LED_RED); //Led usado para saber quanto tempo demora o cálculo
sensor=read_adc(); //Faz a leitura do canal AD e salva em sensor os 10 bits
temperatura_atual=sensor*500/1024; //Converte o valor binário em graus celcius
erro=temperatura_alvo-temperatura_atual; //Calcula o erro do controlador
P=Kp*erro; //Controle proporcional
/*
if(potencia>=0&&potencia<=1) //Controle integral
{
I=I_anterior + Kp*(erro+erro_anterior)/(2*Ti); //Integrador
}
erro_anterior=erro;
I_anterior=I;
potencia=P+I;
*/
potencia=P; //Potência a ser entregue em porcentagem
if(potencia>=1) //Garante que a potencia esteja no domínio
potencia=1; //da função arco cosseno
if(potencia<=0)
potencia=0;
delay_trigger=(162.76/3.1416)*acos(2*potencia-1); //Linearização do atuador
//Cálculo do delay_trigger equivalente à potência
if(delay_trigger>150) //Garante que o delay_trigger estará
delay_trigger=150; //na sua faixa de operação estável
if(delay_trigger<5)
delay_trigger=5;

printf(lcd_putc,"\f"); //limpa o lcd e apresenta os dados


printf(lcd_putc,"TEMP %f%cC",temperatura_atual,letra);
printf(lcd_putc,"\nTEMP ALVO %u%cC", temperatura_alvo,letra);
output_high(LED_RED); //Apaga o Led vermelho
calcular_controle=0; //Habilita o programa a calcular
} //os próximos parâmetros
if(!input(PIN_D0)) //Lógica para que o botão não incremente
{ //muitas vezes em um curto intervalo de tempo
contador_MAIN++;
if(contador_MAIN==100)
{
temperatura_alvo++;
contador_MAIN=0;
}
}
if(!input(PIN_D1)&&temperatura_alvo>=1) //Mesma coisa que o anterior
{ //porém para decremento
contador_MAIN--; //Também garante que a temperatura
if(contador_MAIN==-100) //alvo não seja negativa
{
temperatura_alvo--;
contador_MAIN=0;
}
}
delay_ms(1); //Delay utilizado para que as lógicas dos botões funcionem
}
Códigos auxiliares:

Math.h e 16F877A.h são muito grandes e podem ser encontradas no compilador, então não tem
porque colocálas aqui, só colocarei a PROGRAMA.h e flex_lcd.c.

PROGRAMA.h:

#include <16F877A.h>
#device adc=10

#FUSES NOWDT //No Watch Dog Timer


#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT //No Power Up Timer
#FUSES NOPROTECT //Code not protected from reading
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOBROWNOUT //No brownout reset
#FUSES NOLVP //No low voltage programing
#FUSES NOCPD //No EE protection
#FUSES NOWRT //Program memory not write protected

#use delay(clock=20000000)

flex_lcd.c:
// flex_lcd.c

// These pins are for the Microchip PicDem2-Plus board,


// which is what I used to test the driver. Change these
// pins to fit your own board.

#define LCD_DB4 PIN_B4


#define LCD_DB5 PIN_B5
#define LCD_DB6 PIN_B6
#define LCD_DB7 PIN_B7

#define LCD_E PIN_B3


#define LCD_RS PIN_B1
#define LCD_RW PIN_B2

// If you only want a 6-pin interface to your LCD, then


// connect the R/W pin on the LCD to ground, and comment
// out the following line.

#define USE_LCD_RW 1

//========================================

#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines


#define lcd_line_two 0x40 // LCD RAM address for the 2nd line

int8 const LCD_INIT_STRING[4] =


{
0x20 | (lcd_type << 2), // Func set: 4-bit, 2 lines, 5x8 dots
0xc, // Display on
1, // Clear display
6 // Increment cursor
};

//-------------------------------------
void lcd_send_nibble(int8 nibble)
{
// Note: !! converts an integer expression
// to a boolean (1 or 0).
output_bit(LCD_DB4, !!(nibble & 1));
output_bit(LCD_DB5, !!(nibble & 2));
output_bit(LCD_DB6, !!(nibble & 4));
output_bit(LCD_DB7, !!(nibble & 8));

delay_cycles(1);
output_high(LCD_E);
delay_us(2);
output_low(LCD_E);
}

//-----------------------------------
// This sub-routine is only called by lcd_read_byte().
// It's not a stand-alone routine. For example, the
// R/W signal is set high by lcd_read_byte() before
// this routine is called.
#ifdef USE_LCD_RW
int8 lcd_read_nibble(void)
{
int8 retval;
// Create bit variables so that we can easily set
// individual bits in the retval variable.
#bit retval_0 = retval.0
#bit retval_1 = retval.1
#bit retval_2 = retval.2
#bit retval_3 = retval.3

retval = 0;

output_high(LCD_E);
delay_cycles(1);

retval_0 = input(LCD_DB4);
retval_1 = input(LCD_DB5);
retval_2 = input(LCD_DB6);
retval_3 = input(LCD_DB7);

output_low(LCD_E);

return(retval);
}
#endif

//---------------------------------------
// Read a byte from the LCD and return it.

#ifdef USE_LCD_RW
int8 lcd_read_byte(void)
{
int8 low;
int8 high;

output_high(LCD_RW);
delay_cycles(1);

high = lcd_read_nibble();

low = lcd_read_nibble();

return( (high<<4) | low);


}
#endif

//----------------------------------------
// Send a byte to the LCD.
void lcd_send_byte(int8 address, int8 n)
{
output_low(LCD_RS);

#ifdef USE_LCD_RW
while(bit_test(lcd_read_byte(),7)) ;
#else
delay_us(60);
#endif

if(address)
output_high(LCD_RS);
else
output_low(LCD_RS);

delay_cycles(1);

#ifdef USE_LCD_RW
output_low(LCD_RW);
delay_cycles(1);
#endif

output_low(LCD_E);

lcd_send_nibble(n >> 4);


lcd_send_nibble(n & 0xf);
}

//----------------------------
void lcd_init(void)
{
int8 i;

output_low(LCD_RS);

#ifdef USE_LCD_RW
output_low(LCD_RW);
#endif

output_low(LCD_E);

delay_ms(15);

for(i=0 ;i < 3; i++)


{
lcd_send_nibble(0x03);
delay_ms(5);
}

lcd_send_nibble(0x02);

for(i=0; i < sizeof(LCD_INIT_STRING); i++)


{
lcd_send_byte(0, LCD_INIT_STRING[i]);

// If the R/W signal is not used, then


// the busy bit can't be polled. One of
// the init commands takes longer than
// the hard-coded delay of 60 us, so in
// that case, lets just do a 5 ms delay
// after all four of them.
#ifndef USE_LCD_RW
delay_ms(5);
#endif
}

//----------------------------

void lcd_gotoxy(int8 x, int8 y)


{
int8 address;

if(y != 1)
address = lcd_line_two;
else
address=0;

address += x-1;
lcd_send_byte(0, 0x80 | address);
}

//-----------------------------
void lcd_putc(char c)
{
switch(c)
{
case '\f':
lcd_send_byte(0,1);
delay_ms(2);
break;

case '\n':
lcd_gotoxy(1,2);
break;

case '\b':
lcd_send_byte(0,0x10);
break;

default:
lcd_send_byte(1,c);
break;
}
}

//------------------------------
#ifdef USE_LCD_RW
char lcd_getc(int8 x, int8 y)
{
char value;

lcd_gotoxy(x,y);

// Wait until busy flag is low.


while(bit_test(lcd_read_byte(),7));

output_high(LCD_RS);
value = lcd_read_byte();
output_low(lcd_RS);

return(value);
}
#endif

Você também pode gostar