Para esta semana preparei um exemplo que vale por 2, pois vou demonstrar como comunicar
via SPI com um microcontrolador que não possui hardware pra isso(ou seja vou emular)
outro detalhe é usar o chip max31855K.
O maior limitante deste projeto foi a memoria de programa pequena do microcontrolador para
fazer cálculos(tive que limitar o range de -25° a 300°c), por não conhecer o sensor termopar julguei
erradamente que o sinal seria totalmente linear foi não era linear,quando percebi que era necessário
montar um polinômio para que o resultado se aproxime do valor o mais próximo possível, então foi
montado varias equações para cobrir todo o range.
Logo depois percebi que conforme a temperatura ambiente muda isso interfere também na leitura
da temperatura,mas dai já havia montado as equações para uma temperatura ambiente de 25°C,então
foi montado a função "compensa_vari_ambiente() " para que a equação se comportasse de forma
dinâmica ou seja independente da temperatura e mesmo que o valor lido no sensor fosse variado a
temperatura sempre ficava próximo do esperado.A função "debugue(int val)" serve para visualizar
o pacote recebido do chip(para isso este bloco do programa deve ser descomentado e chamado).
O circuito montado para simulação foi este:
Depois de tudo funcionando no proteus coloquei o circuito pra funcionar na pratica e então
pra minha surpresa o valor lido era completamente diferente da simulação, embora a temperatura
ambiente tenha ficado correta.Enfim não sei se o termopar tipo K que estou usando é diferente
ou se o chip max31855K não está coerente, vou investigar para descobrir o que aconteceu.Mas
resolvi postar o código aqui para haver uma troca de experiencia e espero que de alguma forma
essas informações seja uteis pra alguém.
O código fonte:
/* * sensor de temperatura termopar com max31855K * * Compilador : MPlabXC8 * Microcontrolador: 16F648A * Autor: aguivone * Versão: 1 * Data : 29/04/2016 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header#define _XTAL_FREQ 4000000 // oscilador interno de 20 Mhz
#include <xc.h>
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////#define SPI_DADOS PORTBbits.RB0
#define SPI_CLCK PORTBbits.RB3
#define SPI_CS PORTBbits.RB5
#define LED_STATUS PORTBbits.RB7
#define TEMPO_LED 1000
// CONFIG#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD disabled)#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
unsigned long gulTempo_us=0;
unsigned long gulTempo_ms=0;
unsigned long gulLed_status_tempo=0;
unsigned long gulTempo_aux_ms=0;
unsigned long gulTemp_termopar=0;
unsigned long gulTemp_chip=0;
unsigned char gucSinal_termopar=' ';//se + não precisa de colocar nada
unsigned char gucSinal_termp_chip=' ';//se + não precisa de colocar nada
bit gbErro_ausente=0;
bit gbErro_curto_vcc=0;
bit gbErro_curto_gnd=0;
bit gbIndica_erro =0;
void interrupt interrupcao(void)//vetor de interrupção
{
if(T0IF)
{//interrupção do timer0 - estoura quando atinge 255(0XFF)
gulTempo_us++;
if(gulTempo_us == 13)
{ //acontece a cada 1ms
gulTempo_us =0;
gulLed_status_tempo++;
gulTempo_ms++;
if(gulTempo_aux_ms>0)
{ gulTempo_aux_ms--;}
if(gulTempo_ms == 1000)
{//acontece a cada 1s
gulTempo_ms = 0;
}
}
TMR0 = 254;//reinicia timer com o valor calculado
T0IF = 0;
//62.5us + os delay da rotinas = 75us
}
}
void inicializa_timer0()
{
OPTION_REG = 0X03;//timer0 com prescaler dividido por 16
T0IE = 1;//habilita interrupção do timer0
TMR0 = 254;//zera registrador de contagem
}
/////////////////////////////////funçoes usadas pela uart //////////////////////////////////////////////////////void inicializa_RS232(long velocidade,int modo)
{////por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.//verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.int valor;
if(modo == 1)
{//modo = 1 ,modo alta velocidade
TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.
valor =(int)(((_XTAL_FREQ/velocidade)-16)/16);//calculo do valor do gerador de baud rate
}
else
{//modo = 0 ,modo baixa velocidade
TXSTA = 0X20;//modo assincrono,trasmissao 8 bits.
valor =(int)(((_XTAL_FREQ/velocidade)-64)/64);//calculo do valor do gerador de baud rate
}
SPBRG = valor;
RCIE = 0;//não habilita interrupção de recepção - se habilitar e não tratar a recpção trava o chip(se deixar o pino em aberto quando habilitado é pior))
TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
void escreve_char(char valor)
{
TXIF = 0;//limpa flag que sinaliza envio completo.
TXREG = valor;
NOP();
NOP();//espera 2 ciclos
while(TXIF == 0);//espera enviar caracter
}
void imprime(constchar frase[])
{
char indice = 0;
char tamanho = strlen(frase);
while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
{
escreve_char(frase[indice]);
indice++;
}
}
void long_to_char(long ulQuant,int virgula)
{
char cTexto[4]={'0','0','0','0'};
/* char cTexto[5]={'0','0','0','0','0'}; while(ulQuant>=10000) { ulQuant=ulQuant-10000; cTexto[4]++; } escreve_char(cTexto[4]);*/if(virgula == 4){escreve_char(',');}
while(ulQuant>=1000)
{
ulQuant=ulQuant-1000;
cTexto[3]++;
}
escreve_char(cTexto[3]);
if(virgula == 3){escreve_char(',');}
while(ulQuant>=100)
{
ulQuant=ulQuant-100;
cTexto[2]++;
}
escreve_char(cTexto[2]);
if(virgula == 2){escreve_char(',');}
while(ulQuant>=10)
{
ulQuant=ulQuant-10;
cTexto[1]++;
}
escreve_char(cTexto[1]);
if(virgula == 1){escreve_char(',');}
while(ulQuant>=1)
{
ulQuant=ulQuant-1;
cTexto[0]++;
}
escreve_char(cTexto[0]);
}
void delay_ms(long tempo)
{
gulTempo_aux_ms = 2;//tempo pra garantir que chip está pronto
while(gulTempo_aux_ms>0);
}
/* * usado para ver o pacote inteiro do pacote * void debugue(int val){ switch(val) { case 14: { escreve_char('_'); }break; case 16: { escreve_char('_'); }break; case 28: { escreve_char('_'); }break; } if(SPI_DADOS == 1) { escreve_char('1'); } else { escreve_char('0'); }}*/void Ler_sensor()
{//lê e já trata informação
SPI_CS = 0;
int contador=0;
long multiplicador =1;
delay_ms(2);
gbErro_ausente=0;///zera variaveis
gbErro_curto_vcc=0;
gbErro_curto_gnd=0;
gbIndica_erro =0;
gucSinal_termp_chip =' ';//se + não precisa de colocar nada
gucSinal_termopar =' ';//se + não precisa de colocar nada
gulTemp_termopar = 0;
gulTemp_chip=0;
while(contador < 32)
{
SPI_CLCK = 1;
delay_ms(20);
if(SPI_DADOS == 1)//só há processamento quando o bit é 1.
{//processa dadosif(contador == 0)
{//pega sinal da temperatura do termopar
gucSinal_termopar ='-';
}
else
{
if(contador <15)
{//pega temperatura do termopar//2^12 = 4096, pois a contagem comeca em zero e 1 bit é o sinal
gulTemp_termopar = gulTemp_termopar + (4096/multiplicador);
multiplicador = multiplicador*2;
}
else
{
if((contador > 16)&&(contador < 29))
{//pega sinal da temperatura ambiente//2^10 = 1024, pois a contagem comeca em zero e 1 bit é o sinal
gulTemp_chip = gulTemp_chip + (1024/multiplicador);
multiplicador = multiplicador*2;
}
if(contador == 16)
{//pega sina da temperatura do chip(temperatura ambiente)
gucSinal_termp_chip ='-';
multiplicador =1;//para pegar a segunda temperatura
}
if((gbIndica_erro == 1)&&(contador>28))
{//se tem erro checa estas possibilidadesswitch(contador)
{
case 29:
{
gbErro_curto_gnd=1;
}break;
case 30:
{
gbErro_curto_vcc=1;
}break;
case 31:
{
gbErro_ausente=1;
}break;
}
}
else
{if(contador == 15)
{gbIndica_erro =1;}
}
}
}
}
else
{
if(contador == 16)
{
multiplicador =1;//para pegar a segunda temperatura
}
if(((contador > 0)&&(contador < 15))||((contador > 16)&&(contador < 29)))
{//pega temperatura do termopar
multiplicador = multiplicador*2;
}
}
///so pra testar o codigo, se quiser não precisa chamar essa função// debugue(contador);/////////////////////
SPI_CLCK = 0;
contador++;
}
SPI_CS = 1;
}
/* * usado para simular * void compensa_vari_ambiente(){//essa função compensa a variação de temperatura da junta fria do sensor long_to_char(gulTemp_termopar,0); escreve_char('('); if(gulTemp_chip>404) {//maior que 25°C gulTemp_termopar = gulTemp_termopar-((long)((gulTemp_chip - 404)*0.247)); long_to_char(gulTemp_termopar,0); } else {//menor que 25°c if(gulTemp_chip<404) { gulTemp_termopar = gulTemp_termopar+((long)((404 - gulTemp_chip)*0.247)); long_to_char(gulTemp_termopar,0); } } escreve_char(')');}*/void compensa_vari_ambiente()
{//essa função compensa a variação de temperatura da junta fria do sensorif(gulTemp_chip>404)
{//maior que 25°C
gulTemp_termopar = gulTemp_termopar-((long)((gulTemp_chip - 404)*0.247));
}
else
{//menor que 25°cif(gulTemp_chip<404)
{gulTemp_termopar = gulTemp_termopar+((long)((404 - gulTemp_chip)*0.247));}
}
}
void trata_termopar()
{///funcionou de -25°C até 300°C - na simulação
compensa_vari_ambiente();;
if(gulTemp_termopar>100)
{
gulTemp_termopar = (long)(((gulTemp_termopar/0.395)-250)+ 3.5);//equação base if((gulTemp_termopar>0)&&(gulTemp_termopar<69))//0 até 7
{
gulTemp_termopar = gulTemp_termopar - 2 ;
return;//isso garante que depois de processado vá executar os proximos passos
}
if((gulTemp_termopar>500)&&(gulTemp_termopar<1008))//50 até 100
{
gulTemp_termopar = (long)gulTemp_termopar*0.995;
return;//isso garante que depois de processado vá executar os proximos passos
}
/* * para reduzir o numero de memoria usada juntei 2 intevalos num só * if((gulTemp_termopar>500)&&(gulTemp_termopar<900))//50 até 93 { gulTemp_termopar = (long)gulTemp_termopar*0.998; return;//isso garante que depois de processado vá executar os proximos passos } if((gulTemp_termopar>899)&&(gulTemp_termopar<1008))//93 até 100 { gulTemp_termopar = (long)gulTemp_termopar*0.992; return;//isso garante que depois de processado vá executar os proximos passos } */if((gulTemp_termopar>1007)&&(gulTemp_termopar<1500))//100 até 150passa
{
gulTemp_termopar = gulTemp_termopar-9;
return;//isso garante que depois de processado vá executar os proximos passos
}
if((gulTemp_termopar>1499)&&(gulTemp_termopar<1981))//150 até 200passa a ignorar o vem depois da virgula
{
gulTemp_termopar = (long)(gulTemp_termopar/10)*10;
return;//isso garante que depois de processado vá executar os proximos passos
}
if((gulTemp_termopar>2100)&&(gulTemp_termopar<2490))//200 até 250passa a ignorar o vem depois da virgula
{
gulTemp_termopar = gulTemp_termopar+4;
return;//isso garante que depois de processado vá executar os proximos passos
}
/* * para reduzir o numero de memoria usada * if((gulTemp_termopar>2490)&&(gulTemp_termopar<3500))//250 até 300passa a ignorar o vem depois da virgula { gulTemp_termopar = gulTemp_termopar+8; return;//isso garante que depois de processado vá executar os proximos passos }*/
}
else
{
if(gulTemp_termopar==99)
{
gulTemp_termopar = 0;
}
else
{
escreve_char('-');
gulTemp_termopar = (long)(251 - (gulTemp_termopar/0.395));//equação base if((gulTemp_termopar>40)&&(gulTemp_termopar<96))//-1 até -8
{
gulTemp_termopar = gulTemp_termopar+2;
return;//isso garante que depois de processado vá executar os proximos passos
}
if((gulTemp_termopar>95)&&(gulTemp_termopar<126))//-8 até -13passa a ignorar o vem depois da virgula
{
gulTemp_termopar = gulTemp_termopar+4;
return;//isso garante que depois de processado vá executar os proximos passos
}
if((gulTemp_termopar>125)&&(gulTemp_termopar<300))//-13 até 150passa a ignorar o vem depois da virgula
{
gulTemp_termopar = gulTemp_termopar+8;
return;//isso garante que depois de processado vá executar os proximos passos
}
}
}
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////void main(void)
{
TRISB = 0X03;//configura portB
TRISA = 0XFF;//configura portA
PORTB = 0; // limpar as portas que estão configuradas como saidas
PORTA = 0;
CMCON = 7;//desabilita comparadores
inicializa_RS232(9600,1);//modo de alta velocidade
inicializa_timer0();
PEIE = 1;//habilita interrupção de perifericos do pic
GIE = 1; //GIE: Global Interrupt Enable bit
SPI_CLCK = 0;
SPI_CS = 1;
for(;;)
{
if(gulLed_status_tempo > TEMPO_LED)
{
LED_STATUS = ~LED_STATUS;
gulLed_status_tempo = 0;
// imprime("\r\n Pacote recebido : \r\n "); ///so pra testar o codigo na aplicação pode ser removido essa linha
Ler_sensor();
imprime("\r\nT= ");
//////converte temperatura do termopar/////////////////////////////////////////* if(gucSinal_termopar == '-') {//não será usado aqui // escreve_char('-'); } else { trata_temp_positiva(); escreve_char('/'); } */
trata_termopar();
long_to_char(gulTemp_termopar,1);
imprime(" °C\r\nChip= ");
///converte temperatura do chip //////////////////////////////////if(gucSinal_termp_chip == '-')
{//converte a parte negativa da temperatura
gulTemp_chip = (long)gulTemp_chip*6.2;
gulTemp_chip =(long) 12698 - gulTemp_chip;
escreve_char('-');
}
else
{
gulTemp_chip = (long)gulTemp_chip*6.2;
}
long_to_char(gulTemp_chip,2);
///////////////////////////////////////////////////////////////////
imprime(".");
if(gbIndica_erro)
{//tem erro
imprime("\r\n Falha:");
if(gbErro_ausente)
{imprime("\r\n Sensor aberto ou inexistente!");}
if(gbErro_curto_vcc)
{imprime("\r\n Sensor em curto com o gnd!");}
if(gbErro_curto_gnd)
{imprime("\r\n Sensor em curto com o vcc!");}
}
}
}//loop infinito
}
Olá, provavelmente muitos que visitam o blog já sabem que o microcontrolador PIC 16F877 e 16F877A será descontinuado (http://www.microchip.com/wwwproducts/en/PIC16F877A ) para suprir a falta deste microcontrolador em projetos antigos e em novos projeto temos a opção de usar o 16F887 (ou suas variantes).Apesar de ser compatível pino a pino conforme mostrado na imagem a seguir,tem algumas pequenas alterações em seu hardware.
Dentre as alterações as mais relevantes estão; não tem a porta paralela,o pino de "mclr" pode ser usado como um pino de IO,o PORTB tem entradas analógicas totalizando 14 entradas,o oscilador interno é de 8MHZ e o PORTB tem entradas com interrupções de pino, além de uma memoria de programa maior.
Ou seja é uma troca boa usar o 16F887 em vez do nosso velho amigo 16F877.
O algoritmo usado verifica as tensões e joga em um vetor acumulador e ao encontrar uma decida do sinal por meio de comparações de valores,a probabilidade de ser um pico de tensão é muito grande,veja na figura a seguir como isso ocorre(supondo haver somente 3 pontos pra identificar o pico de tensão AC):
Todas as especificações estão no manual que montei deem uma olhada neste link http://microcontroladores-c.blogspot.com.br/2015/12/medindo-tensao-ac-com-o-sensor-amf-16.html,
O microcontrolador usado é o PIC 12F675 como ele não tem serial, foi usado o artificio de emular uma serial,ele apenas envia os dados num intervalo pré definido, sendo então o sinal elétrico convertido em luz através do optoacoplador para que seja ligado diretamente a um microcontrolador secundário sem risco de queimar.
O código fonte:
/* * voltimetro via serial * * Compilador : MPlabXC8 * Microcontrolador: 12F675 * Autor: aguivone * Versão: 1 * Data : 15/09/2015 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>//para trabalhar com string
#include <xc.h>
#define _XTAL_FREQ 20000000
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////// CONFIG#pragma config FOSC = HS // Oscillator Selection bits (HS oscillator: High speed crystal/resonator on GP4/OSC2/CLKOUT and GP5/OSC1/CLKIN)#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)#pragma config PWRTE = OFF // Power-Up Timer Enable bit (PWRT disabled)#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 = OFF // Data Code Protection bit (Data memory code protection is disabled)///parametros da porta serial emulada////////////////////////////////////////////////////////////////#define TIME_BAUD 97//9600 bps - 20MHZ#define BAUD_RX 95//9600 bps - 20MHZ /////para simulação do proteus//#define TIME_BAUD 202//4800 bps - 20MHZ//#define BAUD_RX 198//4800 bps - 20MHZ #define TX GP2//configura o pino que será TX#define RX GP1//configura o pino que será RX#define TAM_MAX 10//tamanho maximo do vetor de amostragem(numero de pontos)#define NUM_MED 6//numero de intens para formar a media dos picos//#define TEMPO_ENVIO 110//(aprox. 15ms)tempo para enviar os dados(em tempo de rotina))//#define TEMPO_ENVIO 1100//(aprox. 150ms)tempo para enviar os dados(em tempo de rotina))#define TEMPO_ENVIO 7000//(aprox. 1s)tempo para enviar os dados(em tempo de rotina))int vetor[TAM_MAX+1];
int tensao=0;
char flag_libera=0;
int ler_adc_un()
{
int ultensao;
PIR1bits.ADIF = 0;//limpa flag
ADCON0bits.GO = 1; //inicia conversão
while (PIR1bits.ADIF == 0); //espera finalizar leitura
ultensao = (ADRESH << 8) | ADRESL;
return(ultensao);
}
/////////////////////Funçoes utilizadas pela serial/////////////////////////////////////////////////////////////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 simpleschar 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++;
}
}
void long_to_char(int ulQuant)
{
char cTexto[4]={'0','0','0','0'};
while(ulQuant>=1000)
{
ulQuant=ulQuant-1000;
cTexto[3]++;
}
escreve_char(cTexto[3]);
while(ulQuant>=100)
{
ulQuant=ulQuant-100;
cTexto[2]++;
}
escreve_char(cTexto[2]);
while(ulQuant>=10)
{
ulQuant=ulQuant-10;
cTexto[1]++;
}
escreve_char(cTexto[1]);
while(ulQuant>=1)
{
ulQuant=ulQuant-1;
cTexto[0]++;
}
escreve_char(cTexto[0]);
escreve_char('-');
}
void gira_vetor()
{
int aux=0;
while(aux<TAM_MAX)
{
vetor[aux] = vetor[aux+1];
aux++;
}
}
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////void main(void) {
CMCON = 7;//desabilita comparadores
ANSEL = 0X31;//usa AN0 como entrada analogica e usa o clock interno do chip(2 a 6 us)
WPU = 0X00;//desabilita pull ups
TMR0 = 0;
ADCON0 = 0X80;//justificado a esquerda , ref ligado ao vdd,usando AN0
OSCCAL = 0XFF;//configura o oscilador interno para frequencia maxima(4mhz)
OPTION_REG = 0X81;//pull up desabilitado/preescaler ligado ao timer0(dividido por 4)
PIR1 = 0X00; //desabilita interrupções não necessaria
TRISIO = 0X03;//configura gp0 e gp1 como entrada
INTCON = 0XC0;//desabilita interrupção do timer 0 e habilitainterrupção global e de perifericos
ADCON0bits.ADON = 1;//liga modulo de conversão
__delay_ms(300);//debounce e tempo para preparar hardware//logo a frequencia de interrupção é 250khzchar tempo_media = 0;
int tempo_de_envio = 0;
int flag=0;
for(;;)
{ //
__delay_us(1); //para garantir uma leitura estavel
vetor[TAM_MAX] = ler_adc_un();
if((flag_libera == 1))
{
if(vetor[TAM_MAX]<vetor[0])//localizou o pico de tensão
{
int valor =0;
int aux=0;
while(valor<TAM_MAX)
{//procura o maior valorif(aux < vetor[valor])
{
aux = vetor[valor];
}
valor++;
}
tensao = aux + tensao;//sempre pega a média da leitura de pico atual pela anteriorif(tempo_media >= NUM_MED)
{
tensao =tensao/(NUM_MED + 2);//sempre pega a média da leitura de pico atual pela anteriorif(tempo_de_envio >= TEMPO_ENVIO)
{
long_to_char(tensao);
tempo_de_envio = 0;
}
tempo_media =0;
}
else
{
tempo_media++;
}
flag_libera=0;
}
}
else
{//flag de liberação desligadoif(vetor[TAM_MAX] > vetor[0])//localizou o inicio de subida
{
flag_libera=1;
}
}
gira_vetor();
tempo_de_envio++;
}//loop infinito
}
//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
Olá, a melhor forma de se comunicar hoje com qualquer PC ou smartphone é via wifi ou bluetooth,como já foi abordado aqui no blog o uso do bluetooth vamos hoje "brincar" com a wifi.Antes porém é preciso saber de alguns detalhes, que vou comentar logo a seguir.
Para comunicar pelo padrão TCP/IP é preciso abrir um "socket'(canal de comunicação) , uma vez aberto o socket é possível comunicar via pagina http ou enviando os dados diretamente a um dispositivo, por este motivo neste exemplo demonstro como fazer as 2 interfaces.Para a comunicação via socket tudo que for enviado ao microcontrolador é devolvido(funcionando exatamente igual a um terminal serial rs-232).Para a comunicação http criei uma pequena página(pois este microcontrolador tem pouca memoria) que permite ligar ou desligar um led, essa pagina é acessada via navegador do google chrome(não funcionou muito bem no firefox e internet(pode ter sido alguma configuração do navegador)),para isso basta digitar o IP+Porta se for usada a porta 80(padrão) não será necessário digitar no buscador a porta.
Uma configuração de rede que deve ser respeitado é quanto ao DHCP(que pegar um ip automaticamente), no modulo deixei ele desligado para conseguir trabalhar com IP fixo, mas no PC pode até ficar habilitado embora eu recomendo que para a conexão entre pc e placa já deixe o DHCP desligado e coloque um IP diferente do modulo wifi, por exemplo aqui estou usando como gateway do modulo e IP o valor = 192.168.5.1 na porta 5000, então meu pc deve estar no ip 192.168.5.10 por exemplo e configure como gateway o IP 192.168.5.1 deixando a mascara com o valor 255.255.255.0.Se deixar o DHCP de ambos ligado toda vez que você se conectar será gerado um novo ip que talvez vc nem saiba qual será.
O modulo ESP8266 tem a possibilidade de trabalhar como "Acess point"(AP) ou "station"(estação de trabalho) e ainda como ambos, para este exemplo deixei ele como AP.Um ponto um pouco chato de se configurar este modulo é que as respostas dos comandos devem ser sempre checadas e não se tem a possibilidade de configurar ele todo de uma vez só, ou seja deve ser configurado parâmetro por parâmetro, para isso criei uma maquina de estados (Inicializa_ESP8266), que controla a inicialização do modulo.Dependendo de como for a configuração desejada alguns comandos retornam erros o que complica mais um pouco a situação nossa como programadores de firmware(como eu sofri com esse modulo pois não encontrei material sobre ele para pic somente para arduino).
Este modulo apesar de ser simples para configurar exige um certo conhecimento de comunicação TCP e arquitetura de redes sem fio, pois isso é fundamental para que se alcance o resultado desejado,pois parâmetros específicos como DHCP,canal de frequência da wifi, protocolo TCP/IP,etc... , com o exemplo aqui é possível ter um caminho de exemplo para que vocês possam prosseguir com seu projetos.Link pra baixar o programa para testar o modulo na ethernet(terminal): https://drive.google.com/file/d/0Bx9JeN0dmzXxaXh5MHI4aF9fT1U/view?usp=sharing ,com este programa você pode acessar dispositivos por meio de protocolo TCP/IP, roda no windows.
Vamos conhecer a aparência e pinagem do modulo ESP8266 na figura abaixo, juntamente com a pinagem do microcontrolador.
Fisicamente um limitante é o hardware que trabalha com tensões diferentes(3,3V e 5V) dai a solução que adotei foi a do esquemático a seguir,o ponto critico é que em altas velocidades da porta serial o microcontrolador deve trabalhar em 5V para não prejudicar a comunicação.A solução consiste de divisor resistivo para casar os valores de tensão.
Para alimentar o circuito usei os tradicionais LM7805, sendo um ligado ao microcontrolador e outro ligado a um segundo regulador LD1086V33(3,3V) e só então ligado ao modulo wifi.Coloquei 3 leds para indicações de status, conexão(abertura de socket) e falha(usado também para ser acionado pela web).Quando o circuito é alimentado o microcontrolador irá tentar configurar o modulo caso não dê certo pisca os leds verde e amarelo,caso contrário apenas o led verde pisca.
No firmware o único arquivo adicional é o da comunicação serial "serial_rs232.c", não uso o famoso "print" do código C por consumir mais memoria do que esta funções(pois as que implementei são mais enxuta, o que as vezes gera um pouquinho de trabalho mas o código se torna mais leve).
O código fonte:
/** * Comunicando via WIFI com modulo ESP8566 * * Compilador : MPlabXC8 * Microcontrolador: 16F648A * Autor: aguivone * Versão: 1 * Data : 19 de janeiro de 2016 * Atualizações : 19/02/2016 - melhorando a inicialização * Atualizações : 23/02/2016 - melhorando a inicialização * Atualizações : 29/02/2016 - finalizado. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header#define _XTAL_FREQ 20000000 // oscilador interno de 4 Mhz
#include <xc.h>
#include "serial_rs232.c"/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////#define LED_CONEX RB3
#define LED_WEB RB5
#define LED_STATUS RB0
#define TAM_MAX 40 //tamanho maximo do buffer da serial#define NUM_MAX_TENTATIVA 3 //numero maximo de tentativa em cada comando#define TEMPO_RX 100 //tempo max entre caracteres recebidos(timeout)#define FIM_CONF 'x'#define TIMEOUT_CONF 40//40S//tempo maximo sem resposta de envio#pragma config FOSC = HS
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT ligado) para que o microcontrolador inicialize corretamente#pragma config MCLRE = OFF // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)#pragma config BOREN = OFF // Brown-out Detect Enable bit (BOD enabled)#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)#pragma config CPD = OFF // Data EE Memory Code Protection bit (Data memory code protection off)#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)//variaveis de tempoint giTempo_us=0;
long glTempo_ms=0;
long glTempo_led=0;
long glTempo_rx=0;
//variaveis da serialchar gcCaracter=0;
unsigned char gucStatus_serial='N';//N = sem dados,R= recebendo dados , F=fim de recepção
unsigned char gucEstado_de_envio='0';//0 = sem dados,V=dados validados , N=dados não validado
unsigned long gulTam_recebido=0;
unsigned char gucBuffer_serial[TAM_MAX];//só pra testar//variaveis da maquina de estado
unsigned char gucStado_do_modulo='A';//inicia em A
unsigned long gulTempo_de_espera=0;
unsigned long gulTimeout_de_envio=0;
unsigned int guiTentativa_envio=0;
//*******************************************************************************///===================================================================================//Função: _limpa_buffer//Parâmetro: não tem.//Retorno: não tem.//Descrição: usada para limpar o buffer de recpção.//===================================================================================void limpa_buffer()
{
long tamanho = 0;
while(tamanho<TAM_MAX)
{
gucBuffer_serial[tamanho]=0X00;
tamanho++;
}
}
//*******************************************************************************///===================================================================================//Função: _trata_int_conf//Parâmetro: não tem.//Retorno: não tem.//Descrição: usada para pegar as respostas das configurações.//===================================================================================void trata_int_conf()
{
if(gucStatus_serial == 'R')
{//primeiro dado
gucBuffer_serial[gulTam_recebido] = gcCaracter;
gulTam_recebido++;
if(gulTam_recebido>(TAM_MAX-1))
{
gucStatus_serial = 'N';//sobrescreve o vetor
}
}
if(gucStatus_serial == 'N')
{//primeiro dado
gucBuffer_serial[0] = gcCaracter;
gulTam_recebido =1;
gucStatus_serial = 'R';
}
}
//*******************************************************************************///===================================================================================//Função: trata_int_dados//Parâmetro: não tem.//Retorno: não tem.//Descrição: usada para pegar os dados(após a configuração).//===================================================================================void trata_int_dados()
{
if(gcCaracter == '\n')//caracter de retorno de carro "LR" (depois vejo, talvez verificar se tem o \n))
{//final da fraseif((gucBuffer_serial[gulTam_recebido-2]=='D')||(gucBuffer_serial[gulTam_recebido-2]=='T'))
{//verifica se está abrindo ou fechando o socket(comunicação) if((gucBuffer_serial[gulTam_recebido-5]=='N')&&(gucBuffer_serial[gulTam_recebido-4]=='E')&&(gucBuffer_serial[gulTam_recebido-3]=='C'))
{//abrindo conexão recebe "CONNECT"
LED_CONEX = 1;
gulTam_recebido=0;//zera variavel de tamanho
gucStatus_serial = 'N';//prepara para pegar novos dados
}
if((gucBuffer_serial[gulTam_recebido-5]=='O')&&(gucBuffer_serial[gulTam_recebido-4]=='S')&&(gucBuffer_serial[gulTam_recebido-3]=='E'))
{//fechando conexão recebe "CLOSED"
LED_CONEX = 0;
gulTam_recebido=0;//zera variavel de tamanho
gucStatus_serial = 'N';//prepara para pegar novos dados
}
}
}
if(gucStatus_serial == 'R')
{//está recebendo dados
gucBuffer_serial[gulTam_recebido] = gcCaracter;
gulTam_recebido++;
if(gulTam_recebido>TAM_MAX-2)
{//gera aviso de estouro pois o buffer de recpção não consegue armazenar o pacote todo.
gucStatus_serial = 'E';
}
}
if(gucStatus_serial == 'N')
{//primeiro caracter capturado
gucBuffer_serial[0] = gcCaracter;
gulTam_recebido =1;
gucStatus_serial = 'R';
}
}
//*******************************************************************************///===================================================================================//Função: _interrupcoes//Parâmetro: não tem.//Retorno: não tem.//Descrição: usada para tratar as interrupções geradas pelo microcontrolador.//===================================================================================void interrupt interrupcoes(void)//vetor de interrupção
{
if(T0IF)
{//rotinas de tempo(15us))
giTempo_us++;
TMR0 = 254;//reinicia timer com o valor calculado
T0IF = 0;
if(giTempo_us>66)//1ms
{//executa a cada 1ms
giTempo_us = 0;
glTempo_ms++;
if(glTempo_rx > 0)
{
glTempo_rx--;
}
if(gulTempo_de_espera > 0)
{
gulTempo_de_espera--;
}
if(glTempo_led>0)
{
glTempo_led--;
}
if(glTempo_ms>1000)
{//executa a cada 1s
glTempo_ms=0;
gulTimeout_de_envio++;
}
}
//62.5us + os delay da rotinas = 75us
}
if(RCIF)//verifica interrupção da serial
{//interrupção da serial
gcCaracter = RCREG;
if(gucStado_do_modulo == FIM_CONF+1)
{//Modulo já foi configurado
trata_int_dados();
}
else
{
trata_int_conf();
}
glTempo_rx = TEMPO_RX;
RCIF =0;//limpa flag de interrupçao
}
}
//===================================================================================//Função: _inicializa_CPU//Parâmetro: não tem.//Retorno: não tem.//Descrição: configura o microcontrolador.//===================================================================================void inicializa_CPU()
{
TRISA = 0XFF;//todas são entradas
TRISB = 0X02;//configura portB como saida exceto rb2(pino RX))
PORTB = 0; // limpar as portas que estão configuradas como saidas
CMCON = 0X07;
///////////inicializa o timer0
OPTION_REG = 0X03;//timer0 com prescaler dividido por 16
T0IE = 1;//habilita interrupção do timer0
TMR0 = 254;//zera registrador de contagem/////////////////////////////////////////////
inicializa_RS232_HS(115200,20);//modo de alta velocidade de clock// inicializa_RS232(9600,1,20);//na simulação(pra não sobrecarregar o simulador se necessario))
PEIE = 1;//habilita interrupção de perifericos do pic
GIE = 1; //GIE: Global Interrupt Enable bit
LED_CONEX=0;
LED_WEB=0;
}
//===================================================================================//Função: _Verifica_resposta//Parâmetro: char prox,char anterior,unsigned char procurar[].//Retorno: não tem.//Descrição: verifica se o comando foi aceito.//===================================================================================void Verifica_resposta(char prox,char anterior,unsigned char procurar[])
{
if(glTempo_rx == 0)//espera receber o comando
{
if(BuscaPalavra(gucBuffer_serial,procurar) != 0XFFFF)
{
gucStado_do_modulo = prox;//vai para proximo passo
guiTentativa_envio = 0;
}
else
{
if(guiTentativa_envio >= NUM_MAX_TENTATIVA)
{///registra falha na inicialização
gulTimeout_de_envio = 250;//Deu erro na configuração
}
else
{
gucStado_do_modulo = anterior;//passo anterior
}
}
}
}
//*******************************************************************************///===================================================================================//Função: Envia_comando//Parâmetro: unsigned char dados[],char mod,int multi_tempo.//Retorno: não tem.//Descrição: usada para enviar comandos.//===================================================================================void Envia_comando(unsigned char dados[],char mod)
{
PEIE = 0;//desabilita interrupção de perifericos do pic
gulTimeout_de_envio=0;
gucStado_do_modulo = mod;
limpa_buffer();
gucStatus_serial = 'N';//põe pra ir pro inicio do vetor
gulTam_recebido =0;//zera o tamanho
imprime_RS232(dados);
glTempo_rx = TEMPO_RX*3;//seta tempo de recepção de dados um pouco maior para primeira amostra
guiTentativa_envio++;
PEIE = 1;//habilita interrupção de perifericos do pic
}
//===================================================================================//Função: _Inicializa_ESP8266//Parâmetros: não tem.//Retorno: não tem.//Descrição: é uma maquina de estado que configura o modulo ESP8266.//===================================================================================//maquina de estado de inicialização do modulovoid Inicializa_ESP8266(void)
{
switch(gucStado_do_modulo)
{
case'A'://teste inicial pra saber se o modulo está ativo
{
Envia_comando("AT\r\n",'B');//comando , proximo passo
}break;
case'B'://verifica resposta
{
if(glTempo_rx == 0)//espera receber o comando
{
if(BuscaPalavra(gucBuffer_serial,"OK") != 0XFFFF)
{
gucStado_do_modulo = 'C';//vai para proximo passo
guiTentativa_envio = 0;
}
else
{
gucStado_do_modulo = 'A';//passo anterior
}
}
}break;
case'C':
{ //configura o modo que o modulo vai funcionar, aqui deixei para funcionar como AP// Envia_comando("AT+CWMODE=3\r\n",'D');//comando , proximo passo - AP ou servidor
Envia_comando("AT+CWMODE=2\r\n",'D');//comando , proximo passo - AP// Envia_comando("AT+CWMODE=1\r\n",'D');//comando , proximo passo - servidor
}break;
case'D'://verifica resposta
{
Verifica_resposta('E','C',"OK");//colopra onde vai e qual é a palavra procurado
}break;
case'E'://manda resetar o modulo
{
Envia_comando("AT+RST\r\n",'F');//comando , proximo passo
}break;
case'F'://verifica resposta
{
gulTimeout_de_envio=0;//zera variavel de contagem
while(gulTimeout_de_envio < 15);//espera pelo menos 10segundos para resetar com segurança
gulTimeout_de_envio=0;//zera variavel novamente
Verifica_resposta('G','E',"ready");
}break;
case'G':
{ //configura o modo que o modulo vai funcionar, aqui deixei para funcionar como AP
Envia_comando("ATE0\r\n",'H');//pede pra não repetir o comando
}break;
case'H'://verifica resposta
{
Verifica_resposta('I','G',"OK");//colopra onde vai e qual é a palavra procurado
}break;
case'I':
{
Envia_comando("AT+CIPMUX=1\r\n",'J');//comando , proximo passo//cofigura para conexões multiplas// Envia_comando("AT+CIPMUX=0\r\n",'J');//comando , proximo passo//cofigura para conexao simples
}break;
case'J'://verifica resposta
{
Verifica_resposta('L','I',"OK");
}break;
case'L'://configura DHCP e modo de operação
{ //quando DHCP é ligado ele busca ip automaticamente// Envia_comando("AT+CWDHCP=2,1\r\n",'M');//só aceitou o modo AP+STATION/dhcp ligado.(pega ip automatico))// Envia_comando("AT+CWDHCP=2,0\r\n",'M');//AP+STATION/dhcp desligado.(para usar ip fixo))
Envia_comando("AT+CWDHCP=0,0\r\n",'M');//modo AP(o sinal fica mais estavel)/DHCP desligado(para usar ip fixo)
}break;
case'M'://verifica resposta
{
Verifica_resposta('N','L',"OK");
}break;
case'N'://Nome do ponto de wifi,senha,canal(deixei o canal de freq.7)),tipo de segurança(0 = aberta))
{ //
Envia_comando("AT+CWSAP=\"MICROCONTROLARES-C\",\"senha\",7,0\r\n",'O');
}break;
case'O'://verifica resposta
{
Verifica_resposta('P','N',"OK");
}break;
case'P':
{ //configura para criar servidor na porta 5000
Envia_comando("AT+CIPSERVER=1,5000\r\n ",'Q');
}break;
case'Q'://verifica resposta
{
Verifica_resposta('R','P',"OK");
}break;
case'R':
{ //configura para criar servidor na porta 5000
Envia_comando("AT+CIPAP=\"192.168.5.1\" \r\n",'S');//usa este IP para gateway e para o servidor
}break;
case'S'://verifica resposta
{
Verifica_resposta('T','R',"OK");
}break;
case'T':
{ //seta time out
Envia_comando("AT+CIPSTO=100\r\n",'U');//comando , proximo passo
}break;
case'U'://verifica resposta
{
gucStado_do_modulo = FIM_CONF;//não precisa de confimação
}break;
}
}
//===================================================================================//Função: _envia_pacote//Parâmetros: long *tamanho,char *pacote[].//Retorno: não tem.//Descrição: usado para o modo socket(sem usar uma pagina web, envia um pacote TCP).//===================================================================================void envia_pacote(long *tamanho,char *pacote[])
{
char valor[]="0000";
int pos = LongToChar(tamanho,valor);
imprime_RS232("AT+CIPSEND=0,");//para conexão multipla informa o canal // imprime_RS232("AT+CIPSEND=");//na conexoes simples não precisa informar o canal
while(pos<4)
{
escreve_RS232(valor[pos]);
pos++;
}
escreve_RS232(0X0D);
escreve_RS232(0X0A);
glTempo_rx = TEMPO_RX/2;
while(glTempo_rx > 0);//espera receber a resposta do comando
pos=0;//
while(pacote[pos] != 0X00 )//envia os dados recebidos novamente
{ //imprime na porta TCP os dados recebidos(o tamanho max aqui será uns 30 caracteres))
escreve_RS232(pacote[pos]);
pos++;
}
escreve_RS232(0X0D);
escreve_RS232(0X0A);
}
//===================================================================================//Função: _envia_pagina//Parâmetros: não tem.//Retorno: não tem.//Descrição: Monta e envia a pagina HTML.//===================================================================================void envia_pagina()
{//lembre que \" = " logo conta como 1 caracter e não 2
limpa_buffer();//solicita pra limpar o buffer serial ///como vou conectar apenas 1 então o canal é 0
imprime_RS232("AT+CIPSEND=0,285");//para conexão multipla informa o canal (prepara envio e tamanho))// imprime_RS232("AT+CIPSEND=");//na conexoes simples não precisa informar o canal
escreve_RS232(0X0D);
escreve_RS232(0X0A);
glTempo_rx = TEMPO_RX;
while(glTempo_rx > 0);//espera receber a resposta do comando
imprime_RS232("<html><head><title>Teste</title></head><h1>PROGRAMA DE TESTE</h1>");
imprime_RS232("<body><p><form method=\"GET\"></p>");
imprime_RS232("<p><input type=\"radio\" name=\"Bot\" value=\"1\" CHECKED> Ligar led.</p>");
imprime_RS232("<p><input type=\"radio\" name=\"Bot\" value=\"0\" > Desligar led.</p>");
imprime_RS232("<p><input type=\"submit\" value=\"Enviar\"></p></body></html>");
escreve_RS232(0X0D);
escreve_RS232(0X0A);
}
//////////////////////////////////////////////////////Rotina principal/////////////////////////////////////////////////////////////////===================================================================================//Função: _main//Parâmetros: não tem.//Retorno: não tem.//Descrição: função principal.//===================================================================================///////////////http://microcontroladores-c.blogspot.com.br//////////////////////////////////////////void main(void)
{
inicializa_CPU();
for(;;)
{
if(glTempo_led == 0)
{//led de status
LED_STATUS = ~LED_STATUS;
if(gucStado_do_modulo == FIM_CONF+1)
{//Modulo pronto pra funcionar
glTempo_led = 100;//tempo mais rapido
}
else
{
glTempo_led = 500;//tempo normal if(gulTimeout_de_envio>TIMEOUT_CONF)//se for maior que 30S o tempo de espera então indica falha
{
LED_WEB = ~LED_WEB; //modulo não responde - falha
}
}
}
if(gucStado_do_modulo == FIM_CONF+1)
{//Modulo pronto pra funcionarif((glTempo_rx == 0)&&(gulTam_recebido<8))//tamanho minimo em cada recpção de dados
{//descarta
gulTam_recebido=0;//zera variavel de tamanho
gucStatus_serial = 'N';//prepara para pegar novos dados
}
if(gucStatus_serial == 'E')//espera terminar tempo de time out
{//houve estouro do buffer logo a comunicação é via pagina HTTP if(BuscaPalavra(gucBuffer_serial,"GET") != 0XFFFF)//conexão por modo get
{//pagina web solicitada ou resposta recebidaif(BuscaPalavra(gucBuffer_serial,"GET / HTTP") != 0XFFFF)//solicitou pagina web
envia_pagina();
if(BuscaPalavra(gucBuffer_serial,"GET /?Bot") != 0XFFFF)//resposta da pagina web
{
if(BuscaPalavra(gucBuffer_serial,"Bot=0") != 0XFFFF)//vê se é pra desligar led
LED_WEB=0;
if(BuscaPalavra(gucBuffer_serial,"Bot=1") != 0XFFFF)//vê se é pra desligar led
LED_WEB=1;
}
}
gulTam_recebido=0;//zera variavel de tamanho
gucStatus_serial = 'N';//prepara para pegar novos dados
}
else
{//é comunicação via socketif((glTempo_rx == 0)&&(gulTam_recebido>8))//tamanho minimo
{//verifica se tem dados pra ler pois o tempo limite pra finalizar recpção já passouif(BuscaPalavra(gucBuffer_serial,"+IPD") != 0XFFFF)
{//chegou dados validadolong auxiliar = BuscaPalavra(gucBuffer_serial,":");
long tamanho ;//tamanho dos dadoslong pos_buffer=0;//tamanho dos dados
unsigned char gucBuffer_tratamento[TAM_MAX];
if(auxiliar != 0XFFFF)
{//pega o tamanho dos dadosif(CharToInt(gucBuffer_serial[auxiliar-2])<10)
{// o tamanho dos dados é maior que 10
tamanho = (10*CharToInt(gucBuffer_serial[auxiliar-2]))+CharToInt(gucBuffer_serial[auxiliar-1]);
if(CharToInt(gucBuffer_serial[auxiliar-3])<10)
{
tamanho = (100*CharToInt(gucBuffer_serial[auxiliar-3]))+tamanho;
}
}
else
{//o tamanho é menor que 10
tamanho = CharToInt(gucBuffer_serial[auxiliar-1]);
}
auxiliar++;//pra não pegar o ":"
while( pos_buffer < tamanho )
{ //copia os dados para o vetor de tratamento
gucBuffer_tratamento[pos_buffer] = gucBuffer_serial[auxiliar+pos_buffer];
pos_buffer++;
}
gucBuffer_tratamento[pos_buffer]=0X00;//caracter null
}
//limpa_buffer();//solicita pra limpar o buffer serial
envia_pacote(tamanho,gucBuffer_tratamento);
}
gulTam_recebido=0;//zera variavel de tamanho
gucStatus_serial = 'N';//prepara para pegar novos dados
}
}
}
else
{//durante inicializaçãoif(gucStado_do_modulo < FIM_CONF)
{//inicializa o moduloif(gulTimeout_de_envio<TIMEOUT_CONF)//se for menor que 30S o tempo de espera terminar configuração
{
Inicializa_ESP8266();
}
}
if(gucStado_do_modulo == FIM_CONF)
{
///neste momento a WIFI já esta configurada e pronta pra receber dados
gucStado_do_modulo = FIM_CONF+1;
}
}
}//loop infinito
}
O código do arquivo serial:
/* * FUNÇOES USADAS NA SERIAL * * Compilador : MPlabXC8 * Microcontrolador: 16F877A * Autor: Aguivone * Versão: 1.0.1 * Descrição: ->Assim como o printf tem a função de comunicar via rs232, mas com o diferencial de ter um menor processamento e gasto de memoria * ->Mas exige um pouco mais de trabalho para imprimir caracteres numericos * Data de criação: 08/09/2014. * Data de atualização : 29/01/2016 - melhorando o baud rate para altas velocidades */
#include <xc.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header//===================================================================================//Função: _inicializa_RS232//Parâmetros: unsigned long ulVelocidade// : unsigned int uiModo//Retorno: não tem retorno.//Descrição: usada para iniciar a porta serial com velocidades até 38400.//===================================================================================void inicializa_RS232(float fVelocidade,unsigned int uiModo,float fClock)
{//// por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.// verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.float valor;
if(uiModo == 1)
{//modo = 1 ,modo alta velocidade
TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.// valor =(((_XTAL_FREQ/ulVelocidade)-16)/16);//calculo do valor do gerador de baud rate
valor =(((fClock/fVelocidade)-16)/16);//calculo do valor do gerador de baud rate
}
else
{//modo = 0 ,modo baixa velocidade
TXSTA = 0X20;//modo assincrono,trasmissao 8 bits.//valor =(((_XTAL_FREQ/ulVelocidade)-64)/64);//calculo do valor do gerador de baud rate
valor =(((fClock/fVelocidade)-64)/64);//calculo do valor do gerador de baud rate
}
SPBRG =(int)valor;
// PIE1 = 0X20;
RCIE = 1;//habilita interrupção de recepção
TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
//===================================================================================//Função: _inicializa_RS232_HS//Parâmetros: unsigned long ulVelocidade// : unsigned int uiModo//Retorno: não tem retorno.//Descrição: usada para iniciar a porta serial com velocidades acima 38400.//===================================================================================void inicializa_RS232_HS(float fVelocidade,int iClock)//o clock em mhz
{//// por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.// verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.modo = 1 ,modo alta velocidadeswitch(iClock)//cadastrei somente os cristais mais usados
{
case 4:
{
if(fVelocidade > 57600)
{//é 115200
SPBRG =1;
}
else
{//é 57600
SPBRG =3;
}
}break;
case 10:
{
if(fVelocidade > 57600)
{//é 115200
SPBRG =4;
}
else
{//é 57600
SPBRG =10;
}
}break;
case 16:
{
if(fVelocidade > 57600)
{//é 115200 ou 250000if(fVelocidade == 115200)
{//é 115200
SPBRG =8;
}
else
{//é 250000
SPBRG =3;
}
}
else
{//é 57600
SPBRG =16;
}
}break;
case 20:
{
if(fVelocidade > 57600)
{//é 115200,250000,625000 ou 1250000(1,25Mbs))if(fVelocidade == 115200)
{//é 115200
SPBRG =10;
}
if(fVelocidade == 250000)
{
SPBRG =4;
}
if(fVelocidade == 625000)
{
SPBRG =1;
}
if(fVelocidade == 1250000)
{
SPBRG =0;
}
}
else
{//é 57600
SPBRG =21;
}
}break;
}
// PIE1 = 0X20;
RCIE = 1;//habilita interrupção de recepção
TXIE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
//===================================================================================//Função: _escreve_RS232//Parâmetros: char cValor//Retorno: não tem retorno.//Descrição: usada para escrever 1 caracter.//===================================================================================void escreve_RS232(char cValor)
{
TXIF = 0;//limpa flag que sinaliza envio completo.
TXREG = cValor;
while(TXIF ==0);//espera enviar caracter
}
//===================================================================================//Função: _imprime_RS232//Parâmetros: const char ccFrase[]//Retorno: não tem retorno.//Descrição: usada para escrever uma string.(maximo 255 caracteres)//===================================================================================void imprime_RS232(constchar *ccFrase[])
{
unsigned char indice = 0;
unsigned char tamanho = strlen(ccFrase);
while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
{
escreve_RS232(ccFrase[indice]);
indice++;
}
}
//===================================================================================//Função: _imprime_vetor_RS232//Parâmetros: const char ccFrase[]//Retorno: não tem retorno.//Descrição: usada para escrever uma vetor de caracteres.(maximo 255 caracteres))//===================================================================================void imprime_vetor_RS232(unsigned char *ccFrase[])
{
unsigned char indice = 0;
unsigned char tamanho = strlen(ccFrase);
while(indice < tamanho )
{
escreve_RS232(ccFrase[indice]);
indice++;
}
}
//===================================================================================//Função: _BuscaPalavra//Parâmetros: long ulQuant//Retorno: se = 0 não tem palavra igual, senão retorna a posição+1.//Descrição: buscar uma palavra em um vetor de caracteres(máximo 65535 caracteres) e retorna primeira posição)//===================================================================================// As funçoes "strstr()" e "strspn()" não funcionaram bem por isto resolvi montar esta função//fica mais rapidaint BuscaPalavra(unsigned char *ccFrase[],unsigned char *palavra[])
{
unsigned int indice = 0;
unsigned int tamanho_palavra = strlen(palavra);
unsigned int tamanho_vetor = strlen(ccFrase) - tamanho_palavra;//tamanho do vetor subtraido do tamanho da palavra
while(indice <= tamanho_vetor) //tamanho do vetor subtraido do tamanho da palavra
{
if(palavra[0]==ccFrase[indice])
{//se for parecido com a primeira letra da palavra então testa se é a palavra certa
unsigned int tamanho_comparado=1;//inicia comparando próxima letra
while(tamanho_comparado < tamanho_palavra)
{
if(palavra[tamanho_comparado]!=ccFrase[indice+tamanho_comparado])
{
tamanho_comparado=tamanho_palavra+1;
indice = indice + tamanho_comparado;//já pula pra frente pois foi testado)
}
tamanho_comparado++;
}
if(tamanho_comparado<tamanho_palavra+1)
{
return(indice); //retorna a posição da primeira ocorrencia da palavra
}
}
indice++;
}
return(0XFFFF);//indica que não achou palavra igual
}
//===================================================================================//Função: _LongToChar//Parâmetros: long ulQuant//Retorno: não tem retorno.//Descrição: usada para escrever um numero(int ou long) pela serial//===================================================================================int LongToChar(long ulQuant,char *valor[])
{
int ret=3;//indica apartir de quando pegar os dados para não imprimir 0 antes do numeroif(ulQuant>=1000)//mas se quiser imprimir o zero basta usar o vetor inteiro
{ret=0;}
else
{if(ulQuant>=100)
{ret=1;}
else
{if(ulQuant>=10)
{ret=2;}
}
}
while(ulQuant>=1000)
{
ulQuant=ulQuant-1000;
valor[0]++;
}
while(ulQuant>=100)
{
ulQuant=ulQuant-100;
valor[1]++;
}
while(ulQuant>=10)
{
ulQuant=ulQuant-10;
valor[2]++;
}
while(ulQuant>=1)
{
ulQuant=ulQuant-1;
valor[3]++;
}
return(ret);
}
//===================================================================================//Função: _CharToInt//Parâmetros: unsigned char convert_char//Retorno: retorna um numero.//Descrição: usada para conveter um caracter em um numero//===================================================================================int CharToInt(unsigned char convert_char)
{
switch(convert_char)
{
case'0':
return(0);
case'1':
return(1);
case'2':
return(2);
case'3':
return(3);
case'4':
return(4);
case'5':
return(5);
case'6':
return(6);
case'7':
return(7);
case'8':
return(8);
case'9':
return(9);
default:
return(10);
}
}
Depois de tanto trabalho o resultado pode ser visto no vídeo abaixo, a página http pode ser acessado por um smartphone também(testei com um samsung com android).