Controlador PLL – Phase Locked Loop

Controlador-PLL

Phase Locked Loop (PLL) é um sistema de controle que gera a partir de um sinal de entrada um sinal de saída com frequência e fase instantâneas em sincronismo com o sinal amostrado. Por manter em regime permanente a frequência de saída igual à frequência de entrada, tal sistema pode rastrear a frequência do sinal amostrado, assim como gerar frequências múltiplas do sinal de entrada.

As propriedades dessa malha de controle permite um uso amplo em aplicações tais como rádio, telecomunicações, análise de sinais entre outras. Em eletrônica de potência tal sistema é crucial para manter o sincronismo de fases em aplicações conectadas a rede elétrica (Grid-Tie). Como saída deste sistema temos os valores de seno e cosseno em fase com o sinal de entrada o que permite uma vasta utilização em  sistemas de controle, tais como transformada dq (Referência síncrona) ou mesmo na Teoria da Potência Instantânea (Transformação de Park).

Controlador PLL

O diagrama de um sistema PLL básico é composto por, um Phase Detect (PD), um Loop Filter (LPF) e um Voltage Controlled Oscilator (VCO). Tal como apresentado na Figura 1.

Controlador-PLL-01
Figura 1 – Diagrama de um sistema PLL básico

A função do bloco Phase Detect é comparar o seno do sinal de entrada com o cosseno do bloco VCO com o objetivo de gerar um sinal de erro proporcional ao erro do ângulo. Para isto o bloco em questão multiplica o sinal de saída do VCO com o sinal medido na entrada. No entanto, a informação do erro possui uma variável que é duas vezes a frequência de entrada o que necessariamente deve ser removida para o correto funcionamento do sistema.

O erro é a entrada para o Loop Filter que nada mais é que um controlador PI, este controlador é usado para zerar o erro permitindo que o sistema entre em regime permanente.

PLL com Filtro Rejeita-Faixa

Tal como dito anteriormente, a componente não linear da saída do Phase Detect deve ser removida. Um filtro Rejeita-faixa pode ser usado para eliminar a linearidade que é imposta pela multiplicação da frequência da rede por dois. A figura 2 mostra o diagrama PLL com o filtro Rejeita-Faixa como saída do PD.

Controlador-PLL-02
Figura 2 – diagrama PLL com o filtro Rejeita-Faixa

Software Phase Locked Loop

Para implementar um PLL via software é necessário que utilizemos das teorias de processamento digital de sinais tais como teoria da amostragem e transformada Z entre outras. Não entrarei no mérito de descrever as discretizações das malhas de controle propostas neste artigo, porém tentarei mostrar de forma simples a matemática necessária para o correto funcionamento do sistema.

Phase Detect

Ypd = Xpd * Cos[n-1]

Notch Filter

Função de Transferência:           

            

Controlador-PLL-eq-01

Aplicando a transformada Z:

Ynf = B0*Xnf[n] + B1*Xnf[n-1] + B2*Xnf[n-2] – A1*Ynf[n-1] – A2*Ynf[n-2]

Loop Filter

Função de Transferência:

Controlador-PLL-eq-02

Aplicando a transformada Z:

Ylf = B0*Xlf[n] + B1*Xlf[n-1] – A1*Ylf[n-1]

Gerador de Seno e Cosseno

Para evitar um alto consumo de CPU na geração do seno e cosseno, ambos serão gerados aplicando-se o princípio da integração:

Controlador-PLL-eq-03

Aplicando para seno e cosseno temos:

sen[n] = sen[n-1] + cos[n-1]*∆t

cos[n] = cos[n-1] + sen[n-1]*∆t

Aplicação

A placa de desenvolvimento Freedom KL25Z foi utilizada para a implementação do SPLL (Software Phased Locked Loop). Três periféricos são essênciais para o correto funcionamento da malha de controle: conversor Analógico-Digital (ADC) responsável pela amostragem do sinal de entrada, Timer0 define o tempo de amostragem do sistema, ou seja, define também a taxa com que processada a malha de controle, no caso em questão 10 kHz (0.1ms) e por último o conversor Digital-Analógico (DAC) usado como saída para o sistema.

A biblioteca SPLL.h define algumas das constantes do sistema tais como, frequência da rede, π e frequência de amostragem. Além é claro de declarar a estrutura das variáveis e os protótipos  das funções.

#ifndef SPLL_H_
#define SPLL_H_
	
//Tipos de variaveis*************************

typedef int             		int16;
typedef long            		int32;
typedef long long			int64;
typedef unsigned int		Uint16;
typedef unsigned long		Uint32;
typedef unsigned long long	Uint64;
typedef float           		float32;
typedef long double     		float64;
	
//******************************************

	#define PI 3.141592653 
	#define GRID_FREQ 60
	#define ISR_FREQUENCY 10000

	#define B0_LPF 0.0166877556
	#define B1_LPF -0.0166322444
	#define A1_LPF -1.0

	//Variáveis do Filtro Rejeita Faixa
	typedef struct{
		float32 B2_notch;
		float32 B1_notch;
		float32 B0_notch;
		float32 A2_notch;
		float32 A1_notch;
	}SPLL_NOTCH_COEFF;
	
	//Variáveis do Loop Filter
	typedef struct{
		float32 B1_lf;
		float32 B0_lf;
		float32 A1_lf;
	}SPLL_LPF_COEFF;
	
	typedef struct{
		int16 AC_input;
		float32 theta[2];
		float32 cos[2];
		float32 sin[2];
		float32 wo;
		float32 wn;
		SPLL_NOTCH_COEFF notch_coeff;
		SPLL_LPF_COEFF lpf_coeff;
		float32 Upd[3];
		float32 ynotch[3];
		float32 ylf[2];
		float32 delta_t;
	}SPLL_1ph;
	
	
	void SPLL_1ph_init(int Grid_freq, float32 DELTA_T, SPLL_1ph *spll, SPLL_LPF_COEFF lpf_coeff);
	
	void SPLL_1ph_notch_coeff_update(float32 delta_T, float wn,float c2, float c1, SPLL_1ph *spll_obj);
	
	inline void SPLL_1ph_run_FUNC(SPLL_1ph *spll1);
	
	void initPLLVar(SPLL_1ph *spll1);

#endif /* SPLL_H_ */

A função initPLLVar(SPLL_1ph  *spll1) tem por objetivo inicializar todo o sistema de controle, em suma o que esta função faz é definir as variáveis através do ponteiro que lhe é passado. É importante ressaltar que outras duas funções de inicialização são chamadas dentro desta função. Uma inicializa os valores do Filtro Rejeita-Faixa e a outra inicializa a demais variáveis do sistema.

void initPLLVar(SPLL_1ph *spll1){
	SPLL_LPF_COEFF spll_lpf_coef1;
	
	spll_lpf_coef1.B0_lf = B0_LPF;
	spll_lpf_coef1.B1_lf = B1_LPF;
	spll_lpf_coef1.A1_lf = A1_LPF;
	
	SPLL_1ph_init(GRID_FREQ,((float)(1.0/ISR_FREQUENCY)), spll1,spll_lpf_coef1);
	float c1=0.1;
	float c2=0.00001;
	SPLL_1ph_notch_coeff_update(((float32)(1.0/ISR_FREQUENCY)),(float)(2*PI*GRID_FREQ),(float)c2,(float)c1, spll1);
}
void SPLL_1ph_init(int Grid_freq, float32 DELTA_T, SPLL_1ph *spll_obj, SPLL_LPF_COEFF lpf_coeff){
	spll_obj->Upd[0]=0.0;
	spll_obj->Upd[1]=0.0;
	spll_obj->Upd[2]=0.0;
	spll_obj->ynotch[0]=0.0;
	spll_obj->ynotch[1]=0.0;
	spll_obj->ynotch[2]=0.0;
	spll_obj->ylf[0]=0.0;
	spll_obj->ylf[1]=0.0;
	spll_obj->sin[0]=0.0;
	spll_obj->sin[1]=0.0;
	spll_obj->cos[0]=0.999;
	spll_obj->cos[1]=0.999;
	spll_obj->theta[0]=0.0;
	spll_obj->theta[1]=0.0;
	spll_obj->wn=(2*3.14*Grid_freq);
		//coefficients for the loop filter
		spll_obj->lpf_coeff.B1_lf = lpf_coeff.B1_lf;
		spll_obj->lpf_coeff.B0_lf = lpf_coeff.B0_lf;
		spll_obj->lpf_coeff.A1_lf = lpf_coeff.A1_lf;
		spll_obj->delta_t = DELTA_T;
}
void SPLL_1ph_notch_coeff_update(float delta_T, float wn,float c2, float c1, SPLL_1ph *spll_obj){
	// Note c2<<c1 for the notch to work
	float x,y,z;
	x=(float)(2.0*c2*wn*delta_T);
	y=(float)(2.0*c1*wn*delta_T);
	z=(float)(wn*delta_T*wn*delta_T);
	spll_obj->notch_coeff.A1_notch= (y-2);
	spll_obj->notch_coeff.A2_notch=(z-y+1);
	spll_obj->notch_coeff.B0_notch=(1.0);
	spll_obj->notch_coeff.B1_notch=(x-2);
	spll_obj->notch_coeff.B2_notch=(z-x+1);
}

A malha de controle está implementada dentro da função SPLL_1ph_run_FUNC(SPLL_1ph *spll1), é nela que se encontra a código de todos os blocos da malha de controle do PLL já discretizados. Ou seja, toda vez que esta função é chamada o sinal de entrada passa pelo Phase Detect, Notch Filter, Loop Filter e VCO.

//-------------------//
// Phase Detect //
//-------------------//
spll_obj->Upd[0]=(spll_obj->AC_input * spll_obj->cos[1]);

//-------------------//
//Notch filter structure//
//-------------------//
spll_obj->ynotch[0]= -(spll_obj->notch_coeff.A1_notch * spll_obj->ynotch[1]) - (spll_obj->notch_coeff.A2_notch * spll_obj->ynotch[2]) + (spll_obj->notch_coeff.B0_notch * spll_obj->Upd[0]) + (spll_obj->notch_coeff.B1_notch * spll_obj->Upd[1]) + (spll_obj->notch_coeff.B2_notch * spll_obj->Upd[2]);
// update the Upd array for future
spll_obj->Upd[2]=spll_obj->Upd[1];
spll_obj->Upd[1]=spll_obj->Upd[0];
//---------------------------//
// PI loop filter //
//---------------------------//
spll_obj->ylf[0]= -(spll_obj->lpf_coeff.A1_lf * spll_obj->ylf[1]) + (spll_obj->lpf_coeff.B0_lf * spll_obj->ynotch[0]) + (spll_obj->lpf_coeff.B1_lf * spll_obj->ynotch[1]);
//update array for future use
spll_obj->ynotch[2]=spll_obj->ynotch[1];
spll_obj->ynotch[1]=spll_obj->ynotch[0];
spll_obj->ylf[1]=spll_obj->ylf[0];
//------------------//
// VCO //
//------------------//
spll_obj->wo = spll_obj->wn + spll_obj->ylf[0];
//integration process to compute sine and cosine
spll_obj->sin[0] = spll_obj->sin[1] + (((spll_obj->delta_t * spll_obj->wo)) * spll_obj->cos[1]);
spll_obj->cos[0] = spll_obj->cos[1] - (((spll_obj->delta_t * spll_obj->wo)) * spll_obj->sin[1]);
//compute theta value
spll_obj->theta[0] = spll_obj->theta[1] + ((spll_obj->wo * (0.159154943)) * spll_obj->delta_t);
	
spll_obj->theta[1] = spll_obj->theta[0];
spll_obj->sin[1] = spll_obj->sin[0];
spll_obj->cos[1] = spll_obj->cos[0];

Dentro da main.c  temos apenas a inicialização dos periféricos e do SPLL. Já na função pllFunc(), toda vez que essa é chamada grava o valor convertido pelo ADC na variável de entrada do sistema AC_input. Além disso, chama a função SPLL_1ph_run_FUNC(SPLL_1ph *spll1) e escreve o valor calculado no DAC.

void pllFunc(){
	//Função PLL
	spll_var.AC_input = adc_read(ADC_5);
	SPLL_1ph_run_FUNC(&spll_var);
	dac_write((uint16_t)(2044*spll_var.sin[0] + 2046));
}

int main(void)
{
	
	initPin();
	pll_init(8000000, LOW_POWER, CRYSTAL, 4, 24, MCGOUT);
	adc_init(_12BIT);
	dac_init();
	tpm_init();
	TPM0Start(10000);
	
	initPLLVar(&spll_var);
	
	
	for(;;) {

	}	
	return 0;
}

Tal como mostra o código abaixo, toda vez que o que a interrupção do Timer 0 acontece, a cada 0.1ms, a função pllFunc() é chamada e a malha de controle é executada.

void FTM0_IRQHandler(){
	
	if(TPM0_SC & TPM_SC_TOF_MASK){
		TPM0_SC |= TPM_SC_TOF_MASK;
	}
	pllFunc();
	
}

Conclusão e Teste

O sistema descrito neste artigo foi todo escrito em C, permitindo que esse código seja reutilizado em outras plataformas. O microcontrolador usado para os testes não possui operações com float implementadas em hardware o que deixou o sistema praticamente no limite de operação, ou seja a função pllFunc()  é chamada a cada 0.1ms e ela demora 0.08s para ser executada. Porém como prova de conceito o sistema funcionou perfeitamente, apenas os coeficientes do LoopFilter, B0_LPF e B1_LPF, ainda podem ser otimizados.

A figura 3 mostra o resultado da implementação. O sinal de entrada em amarelo está sendo gerado por gerador de sinais e opera em 60Hz com um Vpp  de aproximadamente 3.3V. O sinal de saída em verde estabiliza a frequência e em seguida a faze com o sinal de entrada mantendo o ângulo Teta entre eles próximo de zero. A figura 3 mostra em três etapas o sinal de saída (verde) se aproximando do sinal de entrada (amarelo).

Controlador-PLL-03
Figura 3 – Resultado da implementação
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Comentários:
Notificações
Notificar
3 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Vicente Henriquez
Vicente Henriquez
06/10/2020 11:54

Prezado Paulo, interessante projeto Eu queria um suporte técnico para micro da família PIC , com compilador ccs se for possível.

JOSE ALBERTO DROVANDI
JOSE ALBERTO DROVANDI
10/12/2018 14:44

SDS Mr. Pedro Andrade de Oliveira, boa tarde! Sou técnico e máquinas e motores da Escola Federal de São Paulo. Atualmente trabalho com energias renováveis, instalamos equipamentos de energia solar fotovoltaica, estamos com uma instalação fotovoltaica em andamento, porém temos um questão a ser resolvida e pedimos help. Acontece que estamos alimentando um trifásico de balanceamento de rede, projetamos usar três inversores de 12 v de entrada e saída de 127 v cada um, mas ao fazer os testes de campo observamos oscilações quando nos precisamos de duas fases para 220 V percebemos que existe uma discrepância na entrega da… Leia mais »

Vinicius
Vinicius
04/04/2017 23:17

Bem legal. Dá para passar “algumas horas” com essa brincadeira. O resultado esperado é simples, porém a elaboração do algoritmo é muito complexa. Parabéns pelo seu conhecimento.

Home » Controlador PLL – Phase Locked Loop

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste: