sexta-feira, 28 de agosto de 2015

Emulando uma porta serial no PIC12F675

 Olá,
            As vezes precisamos usar um microcontrolador como o nosso "pequeno notável" PIC12F675, pois tem somente 8 pinos ficando ideal para aplicações em que a simplicidade e redução de tamanho são significativo,mas apesar de contar com um conversor analógico digital não tem uma porta serial para entregar uma informação, qual a solução? Isso mesmo criar uma porta serial virtual ou seja emular uma porta serial.Esse algoritmo pode ser usado em outros microcontroladores para ser ter mais portas seriais(sem a necessidades de comprar um microcontrolador dedicado).
            A primeira coisa que deve ser feita é escolher o cristal a ser utilizado pois a velocidade de comunicação depende disto,neste exemplo vou colocar pra 3 tipos de configurações:usando o oscilador interno,usando um cristal externo de 4MHZ e usando um cristal de 20MHZ.
           Para ajustar os tempos de bits tive que definir 2 variáveis de controle ; "TIME_BAUD" ,usado pela transmissão e "BAUD_RX" usado pela recepção(o motivo desta variável é compensar atrasos de leitura do pino),as velocidades testadas foram 4800bps,9600bps e 19200bps, os tempos de cada bit são 208us,104us e 52us respectivamente.
           Para o cristal de 4MHZ ou o oscilador interno(que é de 4MHZ mas é do tipo RC) o melhor desempenho foi na faixa de 4800bps, enquanto o de 20MHZ funcionou bem nas três velocidades, pois os tempos de execução de rotina se tornam pouco expressivo nos tempos de envio e recepção.Os resultados foram colocados no final do código para ajudar a orientar no processo de uso do algoritmo.
           Neste exemplo o ao ser ligado o circuito ele envia uma mensagem para que o usuário digite algo no terminal serial e então repete o carácter, conta ainda com um botão que ao ser pressionado enviar o carácter "A", veja abaixo como ficou o código fonte e a simulação:

O código-fonte:

/*
 *                                               Emulando(cria uma porta virtual) uma porta serial
 *
 * Compilador : MPlabXC8 V.3.0
 * Data: 28/08/2015.
 * Microcontrolador: 12F675.
 * Autor: aguivone.
 * Versão: 1.
 * 
 * Emulado para 8 bits de dados/sem paridade/1bit de stop bit
 * Emulado para 1 bit de start e stop 
 * sem paridade
 * 
 * Antes de usar leia os parametros no final do codigo.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>//para trabalhar com string
#include <xc.h>
#define  _XTAL_FREQ  20000000//usado pela rotina de tempo do pic
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#pragma config FOSC = HS  // Oscillator Selection bits (INTOSC oscillator: I/O function on GP4/OSC2/CLKOUT pin, I/O function on GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON       // Power-Up Timer Enable bit (PWRT enabled)
#pragma config MCLRE = OFF      // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = ON         // Data Code Protection bit (Data memory code protection is enabled)

#define  BOT  GP2//configura o pino que será RX
///parametros da porta serial emulada////////////////////////////////////////////////////////////////
#define  TIME_BAUD  45//19200 bps - 20MHZ
#define  BAUD_RX    42//19200 bps - 20MHZ
#define  TX  GP0//configura o pino que será TX
#define  RX  GP1//configura o pino que será RX
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////Funçoes utilizadas/////////////////////////////////////////////////////////////
void escreve_char(char dados) {
    int contagem = 0;
    TX = 0;
    while (contagem < 8) //envia dados + stop bit
    {
        __delay_us(TIME_BAUD);
        if ((dados & 0X01) == 0X01) //testa bit menos significativo (LSB)
        {
            TX = 1;
        } else {
            TX = 0;
        }
        dados = dados >> 1; //rotaciona bit
        contagem++;
    }
    __delay_us(TIME_BAUD); //tempo do ultimo bit
    TX = 1; //volta pra nivel alto (fim de transmissão de caracter)
    __delay_us(TIME_BAUD); //stop bit
}
///é interessante observar que está rotina irá trabalhar melhor se for 
///usado na rotina de interrupção de estado no pino, aqui vou deixar na rotina principal por 
// ser um exemplo simples
char ler() {
    int contagem = 0;
    char RX_dados = 0X00;
    if (RX == 0)//tem dados pra ler
    {
        RX_dados = 0X00;
        while (contagem < 8) //recebe dados + stop bit
        {
            __delay_us(BAUD_RX); //compensa algum delay na recepção 
            if (RX == 1) {
                RX_dados = (RX_dados | 0X80); //seta bit mais significativo (MSB)
            }
            RX_dados = RX_dados >> 1;
            contagem++;
        }
        contagem = 0;
        while ((RX == 0) && (contagem < 4)) {
            contagem++; //time out para não travar,tempo de 4 bits
            __delay_us(TIME_BAUD);
        }
    }
    return (RX_dados);
}

void escreve_frase(char *frase) {
    int tam_frase = strlen(frase);
    int contagem = 0;
    while (contagem < tam_frase)           
    {
        escreve_char(frase[contagem]);
        contagem++;
    }

}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void) {
    TRISIO = 0X06; //configura gp1 e gp2 como entrada
    CMCON = 7; //desabilita comparadores
    ANSEL = 0; //habilita as portas de I/O
    char teste;
    TX = 1; //prepara pino de TX
    escreve_frase("testando comunicação serial emulada!");
    escreve_frase("\n\r digite algo?");
    for (;;) {

        if (BOT == 1) {//quando o botão é pressionado escreve 1 caracter
            escreve_char('A');
            __delay_ms(100);
        }
        teste = ler(); //verifica se tem dados pra ser lido 
        if (teste != 0X00)//tem dados se for diferente de 0X00(este caracter pode ser alterado)
        {
            escreve_frase("\n\r");
            escreve_frase("Caracter digitado = ");
            escreve_char(teste); //escreve o caracter digitado

        }

    }

}

//NOTAS DE PARAMETRIZAÇÃO:

//***********************************************************************************************************
//oscilador interno do pic 12F675(testado fisicamente )//////////////////////////////////////////////////////
// configure assim o fuse do oscilador interno => #pragma config FOSC = INTRCIO
// configure assim o fuse do cristal de 4MHZ => #pragma config FOSC = XT
//
//#define  TIME_BAUD  14//19200 bps - 4MHZ - usando cristal 
//#define  TIME_BAUD  16//19200 bps - 4MHZ - usando oscilador interno
//#define  BAUD_RX    12//19200 bps - 4MHZ - osc. interno - recepção não fica confiavel 

//#define  TIME_BAUD  68//9600 bps - 4MHZ - usando oscilador interno
//#define  BAUD_RX    58//9600 bps - 4MHZ - cristal 4MHZ - recepção funcionou bem(houve pouquissimo erros) - no proteus esse valor não funciona
//#define  BAUD_RX    60//9600 bps - 4MHZ - recepção funcionou bem(houve pouquissimo erros)

//#define  TIME_BAUD  175//4800 bps - 4MHZ 
//#define  BAUD_RX    165//4800 bps - 4MHZ - recepção funcionou muito bem(100% confiavel fisicamente, mas no proteus apresenta falhas)


//*************************************************************************************************
// oscilador com cristal de 20MHZ (testado fisicamente funcionou ok)
// configure assim o fuse do cristal => #pragma config FOSC = HS
//#define  TIME_BAUD  45//19200 bps - 20MHZ
//#define  BAUD_RX    42//19200 bps - 20MHZ 

//#define  TIME_BAUD  97//9600 bps - 20MHZ
//#define  BAUD_RX    95//9600 bps - 20MHZ 

//#define  TIME_BAUD  202//4800 bps - 20MHZ
//#define  BAUD_RX    198//4800 bps - 20MHZ 

A simulação:


     

Nenhum comentário :

Postar um comentário

olá,digite aqui seu comentário!