• Nenhum resultado encontrado

Comunicação pela Porta Serial

3 DESENVOLVIMENTO DO SOFTWARE

4.6 Estruturação e Desenvolvimento do Programa do Micro-Controlador

4.6.2 Comunicação pela Porta Serial

Como citado anteriormente, quando algum pacote chega pela porta serial, qualquer outro processamento deve ser interrompido para o atendimento deste (o protocolo ModBus, assim como a definição de “pacote” estão mostrados no ANEXO A).

Isso ocorre pela função de interrupção da UART que o micro-controlador possui. Ou seja, quando uma mensagem “ModBus” chega no pino da porta serial, o dispositivo gera automaticamente uma interrupção no processamento para atender a esse evento.

Na verdade, a interrupção é acionada sempre que chega um byte, e por isso, dentro da função da interrupção deve haver uma lógica para interpretar esse dado, pois ele pode ser de um pacote que está chegando, ou de um novo pacote. Isso é identificado pelo período de espaçamento entre um byte e outro (endereçamento por linha inativa, ou em inglês, idle line). Se um novo byte tiver chegado em menos de 10 tempos de bit (período para 10 bits serem enviados, a uma certa taxa de transmissão), este byte será do atual pacote que está sendo recebido. Entretanto, se o período de tempo for maior (representado pela letra “b” na Figura 4.11), o byte será caracterizado como um novo pacote, de forma a poder haver uma identificação dos campos de dados da mensagem.

98

O trecho de programa a frente mostra a função da interrupção da serial. Pode-se observar que logo que um byte é recebido e a interrupção é iniciada, ocorre uma comparação entre “U0RCTL” e “RXWAKE”. O primeiro citado é um registrador que contém informações da UART0, e o segundo é um campo deste registrador, que indica se o período entre os bytes foi maior ou menor que os 10 tempos de bit. Então a comparação “bit a bit” (símbolo “&”) será verdadeira se o período for maior, e falsa se for menor. Isso significa que se for verdadeira, o byte recebido é um valor de endereço, e por isso ele é comparado à constante “endereco”.

Ainda na primeira comparação, pode-se observar que o “U0RCTL” é zerado, e isso é feito para atribuir o valor “zero” ao bit 4 (“BRK”), que indica uma inatividade na linha maior que os 10 tempos de bit, ao bit 1(“RXWAKE “), que é um sinalizador do tipo de caractere recebido, e aos outros bits, que são sinalizadores de erros e outras funções.

Endereço Nome BIT 7 BIT 6 BIT 5 BIT 4 BIT 3 BIT 2 BIT 1 BIT 0

Leitura Escrita Reset 0 0 0 0 0 0 0 0 U0RCTL U1RCTL 0x0072 0x007A

FE PE OE BRK URXEIE URXWIE RXWAKE RXERR

Figura 4.12 – Registrador de controle de recepção da UART.

Caso o valor do endereço recebido seja igual ao da constante “endereco”, então isso significa que o pacote enviado pela rede RS485 (todos dispositivos conectados recebem) é deste medidor, e ele deve receber o restante dos dados até o final do pacote. Por isso existe a linha “U0RCTL |= ~URXWIE”, para a habilitação da recepção dos bytes pela porta serial. Caso contrário (endereço for de outro medidor), a recepção de bytes é desabilitada, descartando qualquer dado que chegue pela UART, até que um novo pacote seja percebido.

A variável “L”, que é zerada quando um novo pacote é recebido (e o endereço é o correto), e que é incrementada a cada recepção de cada byte, é utilizada para o monitoramento do tamanho do pacote recebido, para ser utilizado na verificação da checagem de erro (“CRC”), mostrado mais adiante.

Depois de corretamente recebido, o pacote deve ser analisado e interpretado, para o conhecimento da função que ele tem.

A primeira etapa é a certificação de que a mensagem não chegou com erros, devido à alguma falha na transmissão de dados. Assim, antes da interpretação do pacote, o micro- controlador deve recalcular o “CRC”, e comparar com o enviado no pacote. Se estiverem iguais, o pacote estará validado, e pronto para ser analisado.

Após essa primeira etapa concluída, o pacote deve ser interpretado, de forma a poder entender o que o mestre, que enviou o pacote, está pedindo. Essa rotina é mostrada a seguir, na função “interpreta_pacote”.

unsigned char checa_CRC(void) {

unsigned char temp_return, temp2; for (temp2 = 0; temp2 < L; temp2++) CRC_pak[temp2] = serial_in[temp2]; CRC = CRC16(L-2);

CRC_HI = parte_alta(CRC); CRC_LO = parte_baixa(CRC);

if ((CRC_HI == serial_in[L-1]) & (CRC_LO == serial_in[L-2])) temp_return = 1; else temp_return = 0; return(temp_return); } --- unsigned int CRC16(unsigned char quant_pacote)

{

int Index;

unsigned int temp1, temp_return = 0xFFFF; for (temp1 = 0; temp1 < quant_pacote; temp1++) { … } return (temp_return); } #pragma vector=UART0RX_VECTOR __interrupt void usart0_rx (void) {

if (U0RCTL & RXWAKE) //verifica se o byte é o endereço ...

L = L + 1; }

100

A frente está mostrada a função “formula_pacote”, onde se observa que um valor, chamado “funcao”, deve ser passado como parâmetro. Esse valor pode ser “16” ou “03”, ou seja, função de escrita ou leitura de registradores, respectivamente.

É interessante observar que mesmo sendo uma função de escrita de registradores (“0x10”), o medidor (que é um dispositivo escravo) deve responder ao mestre da rede, ou seja, ao software gerenciador. Essa resposta é só uma confirmação de que a mensagem foi recebida e executada.

void formula_pacote(unsigned char funcao) {

unsigned char temp4;

unsigned int tamanho_pak_out; serial_out[0] = endereco; serial_out[1] = serial_in[1]; if (funcao == 0x10) { … } if (funcao == 0x03) { … }

for (temp4 = 0; temp4 < tamanho_pak_out; temp4++) CRC_pak[temp4] = serial_out[temp4]; CRC = CRC16(tamanho_pak_out); ... } void interpreta_pacote(void) {

unsigned char temp8; if (serial_in[1] == 0x10) { ppc = unir_alto_baixo(serial_in[7],serial_in[8]); n_ciclos = unir_alto_baixo(serial_in[9],serial_in[10]); ... n_fases=0;

for(temp8=0; temp8 <= 5; temp8++) if(boo[temp8] == 1) n_fases = n_fases + 1; ... config_timer(); } if (serial_in[1] == 0x03) { start_reg = unir_alto_baixo(serial_in[2],serial_in[3]); count_reg = unir_alto_baixo(serial_in[4],serial_in[5]); } formula_pacote(serial_in[1]); }

A função abaixo mostrada é a que envia os dados pela serial. Isto é feito colocando-se os dados do vetor “serial_out” no “buffer” de saída da serial, ou seja, em “TXBUF0”, um a um. A linha de comando “while (!(IFG1 & UTXIFG0));” serve para controlar o fluxo dos dados, de forma a manter a taxa de transferência determinada.

Como um teste para a comunicação trifásica, foi construído um vetor representativo dos pontos de três fases de tensão, como mostrado a seguir. Deve-se reparar que na função “leituraSRAM”, a leitura é feita deste vetor fictício, e não da memória externa.

A Figura 4.13 mostra um teste prático realizado, para a validação da comunicação. Como eram três vetores de 64 pontos por ciclo (1 ciclo cada), foram feitos 3 pedidos, da seguinte forma: 1° pedido, do registrador 0 ao 63; 2° pedido, do registrador 64 ao 127; 3° pedido, do registrador 128 ao 191.

unsigned int seno3f [] = {1900,1998,2095,2190,2283,2371,2456,2534,2607,2673,

2731, 2782,2824,2857,2881,2895,2900,2895,2881,2857,2824,2782,2731,2673,2607,2534, 2456,2371,2283,2190,2095,1998,1900,1802,1705,1610,1517,1429,1344,1266,1193, 1127,1069,1018,976,943,919,905,900,905,919,943,976,1018,1069,1127,1193,1266, 1344,1429,1517,1610,1705,1802,2776,2725,2665,2599,2525,2446,2361,2272,2179, 2084,1986,1888,1790,1693,1598,1506,1418,1335,1257,1185,1120,1062,1013,972, 940,917,904,900,906,922,947,981,1024,1075,1135,1201,1275,1354,1439,1528,1621, 1716,1814,1912,2010,2107,2202,2294,2382,2465,2543,2615,2680,2738,2787,2828, 2860,2883,2896,2900,2894,2878,2853,2819,1056,1007,967,936,915,903,900,907,924, 950,985,1029,1082,1142,1210,1284,1364,1450,1539,1632,1728,1825,1924,2021,2118, 2213,2304,2392,2475,2552,2624,2688,2744,2793,2833,2864,2885,2897,2900,2893, 2876,2850,2815,2771,2718,2658,2590,2516,2436,2350,2261,2168,2072,1975,1876, 1779,1682,1587,1496,1408,1325,1248,1176,1112}; --- void leituraSRAM (unsigned int start_reg,unsigned int count_reg)

{ for(x=0;x <= (2*count_reg-1);x++) { serial_out[x+3]=parte_alta(seno3f[start_reg+(x/2)]); serial_out[x+4]=parte_baixa(seno3f[start_reg+(x/2)]); x=x+1; } }

void envia_pacote(unsigned char quant_pacote_out) {

unsigned char temp5;

for (temp5 = 0; temp5 < quant_pacote_out; temp5++) {

TXBUF0 = serial_out[temp5]; while (!(IFG1 & UTXIFG0)); }

... }

102

Figura 4.13 – Teste prático da comunicação serial.

Documentos relacionados