Ir ao conteúdo
  • Cadastre-se

Cálculos e variáveis em C


Posts recomendados

Bom dia galera, 
Estou com problemas quanto aos cálculos do meu programa, fiz os testes simulados no proteus(inclusive checando os valores pela serial), porém na prática está um pouquinho mais trabalhoso.
 
Estou usando o PIC16F883, e estou trabalhando com 4kHz de frequência.
Quando eu jogo os cálculos usando variável tipo float o programa fica lento e eu não consigo mais ter o disparo dos SCR's no momento certo(eu sei disso, porque um led programado para mudar de estado a cada 1 segundo, começa a atrasar a mudança de estado).
 
O que o programa faz, ele calcula a tensão de entrada em cima de um banco de capacitores, e conforme um valor ajustado por potenciômetro o programa determinará se os SCR's deverão disparar ou não(para então carregar os capacitores).
 
Estou usando as entradas analógicas com 10 bits de resolução (0 - 1023).
Quando o potenciômetro estiver no mínimo 0(ou 0V), os capacitores deverão se carregar até 30V(libera disparo até chegar em 30V), e quando estiver no máximo, 1023(ou 5V) deverão chegar à no máximo 190V.
 
O circuito que checa a tensão em cima dos capacitores, é capaz de ler variações de 0 - 0V até 1023 - 240V.
 
Logo eu tenho o seguinte problema: 
 
valores.gif
 
Então eu crio uma relação entre eles, por exemplo:
Set_point = Potenciometro -0 /1024-0  -----------> Set_point = Potenciometro/1024
 
Valor_por_ajuste =( tensão lida na analogica -128)/(1024 - 128) ------------>Valor_por_ajuste = (tensão lida na analogica -128)/682
 
Tensao_real = (tensão real -30)/(190-30) -----------> Tensao_real = (tensão real - 30)/ 160
 
e eu faço a lógica de:
se a tensão lida no set point for maior que a tensão calculada em valor por ajuste libera disparo dos SCRs.
senão, não.
 
Porém ao final desses cálculos, eu obtenho valores minúsculos(variam de 0 à 1), onde acredito que eu teria que usar vária veis tipo float para interpretar estes dados. 
 
Vejam o código do meu programa.

/* Main.c file generated by New Project wizard * * Created:   qua jun 11 2014 * Processor: PIC16F883 * Compiler:  CCS C*///#include <htc.h>#include <16f883.h>           #device adc=10   //  2e10= 1024     #fuses XT,NOWDT,PUT,PROTECT       #use delay(clock=4000000)  //#use RS232(baud=9600, parity=N, bits=8, xmit=PIN_C6,rcv=PIN_C7)#define zero_crossing pin_c4#define sinal pin_c5#define scr1 pin_b4#define scr2 pin_b5boolean  carrega=0,nivel01=0,nivel02=1;long int ler_set_point=0, ler_cap=0;long int led01=0,led02=0;unsigned int16 setP=0,x1=0;long int y=0;int conta1=0, conta2=0;void inicia();void ler_analogicas();void main(){	inicia();	output_low(scr1);  //scr1	output_low(scr2);  //scr2	   // Write your code here   while (1)   {		 ler_analogicas();	////////////////////////////////////////////////			led02++;			if(led02>=1000) //1 em 1 segundo			{				Led02=0;				output_bit(pin_b1,nivel02);				nivel02=!nivel02;			}////////////////////////////////////////////////	} } #INT_TIMER2void timer2(){		////////////////////////////////////////////////	led01++;	if(led01>=4000) //1 em 1 segundo	{		Led01=0;		output_bit(pin_b0,nivel01);		nivel01=!nivel01;	}////////////////////////////////////////////////setP=ler_set_point/1023;	  x1=(ler_cap-128)/682;	  if(setP>=x1) 	  { 	 		if(input(zero_crossing)==0)	     	{				conta1++;				if(conta1>=28)				{					if(conta1<=30)output_bit(scr1,1);						else output_bit(scr1,0);				}				conta2=0;				}				else if(input(zero_crossing)==1)			{				conta2++;				if(conta2>=28)				{					if(conta2<=30)output_bit(scr2,1);						else output_bit(scr2,0);				}				conta1=0;			}	  }	if(setP<x1)	{		output_bit(scr1,0);		output_bit(scr2,0);		conta1=0;		conta2=0;	}	} void inicia() {	enable_interrupts (GLOBAL);	enable_interrupts (INT_TIMER2);	setup_timer_2 (T2_DIV_BY_1,250,1); //  4k	setup_ADC_ports (0x3);            //utilizar apenas os canais 0, 1 e 2.	setup_adc(ADC_CLOCK_INTERNAL);         //conversor usará clock interno }  void ler_analogicas() {		set_adc_channel(0);//altera para o 0////		delay_us(40);///////////////////////////		ler_cap=read_adc();//le canal 0/////////		set_adc_channel(1);//e altera para o 1//		delay_us(40);///////////////////////////		ler_set_point=read_adc();//le o canal 1/}

Se alguém puder me dar uma luz, seja referente ao cálculo, seja referente ao código, variável, ou outro, eu agradeço. 

Link para o comentário
Compartilhar em outros sites

Uso um circuito divisor de tensão.

Estou fazendo algumas alterações quanto a isso para facilitar os cálculos.

Estou mudando as entradas analógicas para 8 bits, e considerando os valores dos resistores para ter 5V sobre o pic caso tenha 256V(valor 8 bits) sobre os capacitores. Esse valor nunca vai chegar, o que me dá certa segurança a respeito de não passar de 5V nas entradas do uC.

 

Dai pelo menos não preciso fazer tanto calculo, e acho que consigo usar variáveis de 8bits. Agora só falta fazer a relação entre esse valor e o potenciômetro de ajuste.

 

=/

Ta um pouco complicado eu me expressar quanto a funcionalidade do projeto haha me desculpe. 

Link para o comentário
Compartilhar em outros sites

Bom, facilitei os cálculos que irá fazer a relação entre potenciômetro e tensão nos capacitores usando cálculo de Interpolação pelo método de Lagrange.

 

Ficou bem mais simples.

set_point = ((ler_set_point*0.62745)+30);	  if(ler_cap<=set_point) 	  { 	 		if(input(zero_crossing)==0)	     	{				conta1++;				if(conta1>=28)				{					if(conta1<=30)output_bit(scr1,1);						else output_bit(scr1,0);				}				conta2=0;				}				else if(input(zero_crossing)==1)			{				conta2++;				if(conta2>=28)				{					if(conta2<=30)output_bit(scr2,1);						else output_bit(scr2,0);				}				conta1=0;			}	  }	else	{		output_bit(scr1,0);		output_bit(scr2,0);		conta1=0;		conta2=0;	}	 

Na prática se eu coloco if(ler_cap<=30), o circuito carrega os capacitores com até 30V e então para.

Porém ele não está reconhecendo o valor de set_point.

 

Já coloquei ele como int, unsigned int16, float e nada, e sem contar que usando esses cálculos o LED(o mesmo que comentei antes que deveria mudar de estado a cada segundo contado), começa a mudar com atraso.

 

:(

 

Já fiz outros programas usando cálculos do tipo e não tive problemas, comparo ambos e não encontro nada. :(

Link para o comentário
Compartilhar em outros sites

@Rafaela-Sama.

Pelo que pude perceber do seu código, O led nunca vai piscar com o intervalo de 1 segundo, sempre sera com mais do que isso, pois o o led mudara de estado a cada 1000 incrementos da variavel led02 quando na verdade deveria piscar a cada 1000 ciclos de maquina, imagine que quando o PIC sai para atender a interrupção do timer os incrementos param até o PIC retornar da interrupção e esse tempo que o PIC esta fora não esta sendo computado.

  A variável set_point deve ser float ou int (só que vai ter uma pequena diferença por conta das casas decimais), depois do calculo imprima ela pela serial e veja se o valor esta condizente ou use o Debuger da sua IDE e acompanhe o valor dela ao final do calculo.

  Pelo que eu entendi, quando o valor lido do Set_point for 255 a variavel Set_point deve assumir o valor de 190 é isso mesmo? Pode ser que a variável ler_set_point esteja entrando com o valor errado. como você esta "simulando" a resolução de 8 bits num pic que tem o ADC de 10 bits de resolução?

 

 

Espero ter ajudado.

   Grato Luiz Gustavo.

Link para o comentário
Compartilhar em outros sites

@Rafaela-Sama.

Pelo que entendi voce esta com problemas em escalar o valor medido para o banco de capacitores certo?

Usar ponto flutuante é ruim demais no PIC, mas talvez o uso de aritmetica de ponto fixo de 32bits lhe ajude com os cálculos.

Seu sistema me parece uma planta de malha fechada, você lê a tensão no potenciometro, atua sobre o banco pelos SCRs e em seguida realimenta o sistema com base no valor corrente lido pela entrada analogica é isso? Se for porque não pensar em codificar o PIC para trabalhar como um controlador do tipo proporcional? Além disso você não precisa trabalhar no controle com os valores escalados para volts,visto que perante a saida do A/D seja um sinal de 190V escalado para 5V ou seja um sinal vindo do potenciometro de 0 a 5V ambos irão retornar um valor compreendido entre 0 e 0x3FF (1023). Assim basta adicionar os limites superior e inferior do sistema que no seu caso são 190V e 30V respectivamente certo?
 

Vou te mostrar como ficaria simples isso em codigo usando um controlador tipo P, mas antes vamos mapear seus valores:

a tensão no banco de capacitores deve ficar compreendida entre 30 e 190V certo? Agora usamos uma regra de tres simples para mapear isso num range nao de 0 a 5V mas um range dentro deles, é bom evitar o uso de toda a escala do A/D pois isso pode gerar nao linearidades que vão causar mau comportamento na hora da conversão, optarei pela seguinte forma:
 

190V = 4.0V, Ok? assim:


190 ----- 4.0
  30 ------ x 

Logo 30V esta para : 0.631V

Otimo, agora vamos mapear isso para o valor digital apos a conversao:

Sabemos que a tensão em volts dado um valor digital do A/D é calculado por:

V = VRef * (A/D/Resolucao) queremos o valor binario para os dois ranges, logo queremos o valor do codigo A/D dado a tensão de entrada: A/D = V * Resolucao / VRef,

Fazendo Vref = 5,0V tensão de alimentacao do PIC e Resolucao usando os 10Bits temos:

A/D = V * 1023 / 5,0

Otimo, agora podemos mapear a tensão do barramento dos capacitores como simples valores digitais, sem nos preocuparmos se estão no bus ou no potenciometro, vamos definir os limites, 

Superior:

A/D = 4 * 1023 / 5,0 = 818

 

Inferior:
A/D = 0.631 * 1023 / 5,0 = 129

Portanto, se tivermos 190V na entrada o A/D ira retornar 818 na hora de usar a funcao ReadADC(), e quando estiver em 30V o mesmo ira retornar 129.

O mesmo vale para o potenciometro, que ja esta escalado para 0 - 5V.

Agora pensando em código, um controle malha fechada tem que ser executado de forma periodica e a base de tempo deve ser razoavelmente estavel.

O que proponho, configure um timer para interromper a cada 1ms, e dentro da interrupção execute o controle em malha fechada, no seu caso é um ON-OFF, acredite ele nao é um controle muito bom, mas ira conseguir colocar o sistema de forma estavel por um tempo:

Assim:

 

#include <stdint.h> //para usar os tipos padrãovoid ControlaCapacitores(uint16_t SetPoint, uint16_t Sensor){	//Variaveis auxiliares:	int16_t Erro; //Pode ser inteira mas deve ser sinalizada	//Calcula o erro entre valor desejado e lido:	Erro = (int16_t)( SetPoint - Sensor);		//Checa se saturou:	if(Erro > 818) Erro = 818; // passou de 190V, mantem aqui	if(Erro < 129) Erro = 129; // Menor que 30V, mantem aqui		//Erro, igual a 0?	if(Erro > 0)	{			//Se for diferente de zero, aciona o SCR:		AcionaSCR(); //sua funcao de acionamento	}	else	{		DesacionaSCR(); //desliga o SCR	}	}//Rotina de interrupcao do timer0:#int_timer0void T0_ISR(void) //Essa rotina ocorra 1 vez a cada 1ms.{	//Variaveis auxiliares:	uint16_t Potenciometro;	uint16_t Capacitores;		//Primerio le o setpoint desejado:	//Nessa linha aqui coloque sua funcao para mudar o canal do A/D	Potenciometro = readADC();		//Nessa linha aqui coloque sua funcao para mudar novamente o canal do A/D	Capacitores = readADC();		//Computa o loop de controle:	ControlaCapacitores(Potenciometro, Capacitores);		//Fim da subrotina.}

Tem coisas que voce precisa implementar como a forma correta de ler o A/D, trocar de canal para potenciometro e capacitores.

Espero ter sido claro mas se tiver mais, duvidas nao hesite em perguntar.

Abs.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Coloquei o o cálculo fora do timer, no caso no main(), funcionou.

 

=p

 

Alguém sabe me explicar o porque? 


@Rafaela-Sama.

Pelo que pude perceber do seu código, O led nunca vai piscar com o intervalo de 1 segundo, sempre sera com mais do que isso, pois o o led mudara de estado a cada 1000 incrementos da variavel led02 quando na verdade deveria piscar a cada 1000 ciclos de maquina, imagine que quando o PIC sai para atender a interrupção do timer os incrementos param até o PIC retornar da interrupção e esse tempo que o PIC esta fora não esta sendo computado.

  A variável set_point deve ser float ou int (só que vai ter uma pequena diferença por conta das casas decimais), depois do calculo imprima ela pela serial e veja se o valor esta condizente ou use o Debuger da sua IDE e acompanhe o valor dela ao final do calculo.

  Pelo que eu entendi, quando o valor lido do Set_point for 255 a variavel Set_point deve assumir o valor de 190 é isso mesmo? Pode ser que a variável ler_set_point esteja entrando com o valor errado. como você esta "simulando" a resolução de 8 bits num pic que tem o ADC de 10 bits de resolução?

 

 

Espero ter ajudado.

   Grato Luiz Gustavo.

 

Luiz, eu coloquei 2 leds para mudarem de estado, repare que como eu coloquei o timer com 4kHz, logo é o led01 quem deverá mudar de estado a cada segundo.

E o pic também pode trabalhar com 8bits, o que pra mim simplificou e muito os meus cálculos. 

basta eu dizer lá no começo:

#device adc=8   //  2e8=256   

 

Bom, pelo menos aqui deu certo. 

 

E realmente, a que eu usei e deu certo foi int, esses negócios de váriaveis são um pouco confusos haha

 

mas muito obrigada pela ajuda! 


Usar ponto flutuante é ruim demais no PIC, mas talvez o uso de aritmetica de ponto fixo de 32bits lhe ajude com os cálculos.

 

Felipe entendi o que você quis dizer, realmente até que eu estava indo pelo caminho certo.

Consegui simplificar mais ainda o circuito!! 

Obrigada.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Visitante
Este tópico está impedido de receber 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...