Escolar Documentos
Profissional Documentos
Cultura Documentos
CONTROLE DE TEMPERATURA
MICROCONTROLADO
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.
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.
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);
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
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.
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
( )
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:
( )
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.
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.
http://www.denteazul.com.br/
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
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;
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
#use delay(clock=20000000)
flex_lcd.c:
// flex_lcd.c
#define USE_LCD_RW 1
//========================================
//-------------------------------------
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();
//----------------------------------------
// 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);
//----------------------------
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);
lcd_send_nibble(0x02);
//----------------------------
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);
output_high(LCD_RS);
value = lcd_read_byte();
output_low(lcd_RS);
return(value);
}
#endif