- Sistemas Operacionais de Tempo Real – Introdução
- Sistemas Operacionais de Tempo Real – Timers
- Sistemas Operacionais de Tempo Real – Displays de 7 segmentos
- Sistemas Operacionais de Tempo Real – Teclados Matriciais
- Sistemas Operacionais de Tempo Real – Alguns Periféricos
Introdução
Este artigo é o último da série sobre rotinas padronizadas, desenvolvidas em C, para o sistema MCS-51 (8051) e para serem compilados no programa da Keil. Nesse contexto é necessário lembrar que o microcontrolador 8051 não possui em seu hardware muitas interfaces dedicadas e de certa forma autônomas para conexão com periféricos externos. Considerando isso, as rotinas dessa biblioteca realizam o acesso diretamente através dos pinos (bits) dos Ports do microcontrolador e a temporização desses procedimentos fica sendo definida implicitamente pelo clock da CPU ou pelos ciclos de máquina das instruções.
As arquiteturas mais modernas de microcontroladores já possuem hardwares dedicados para a implementação de barramentos de comunicação I2C, saídas PWM, entre outras, o que melhora o rendimento da execução do seu software embarcado, por conta do paralelismo das operações.
As rotinas apresentadas aqui, não são rotinas de tempo real propriamente ditas, mas funcionam bem como rotinas padronizadas, que podem ser utilizadas em ambientes de tempo real. São rotinas para o acesso a EEPROM serial, aos conversores A/D ADC0838 e AD7730 e ao sensor de temperatura DS1820.
Descrição das Rotinas
As rotinas que serão detalhadas a seguir foram desenvolvidas seguindo as sugestões apresentadas no artigo Boas práticas para o desenvolvimento de software – Parte I no que diz respeito a encapsular algumas funções de acesso aos periféricos.
Rotinas para memórias seriais do tipo EEPROM
A EEPROM serial modelo NM24CXX da Fairchild, que já está obsoleta, mas pode ser substituída pelo componente CAT24CXX da ON Semiconductor. Na Figura 1, pode-se observar o diagrama funcional da EEPROM serial.
Definições
O trecho de código a seguir pode ser inserido num arquivo de definições do tipo *.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
//******************************************************************************* //* * //* ROTINAS PADRONIZADAS PARA USO EM APLICAÇÕES * //* DE SISTEMAS MICROCONTROLADOS (FAMÍLIA 8051) * //* (Resp. Engenheiro Puhlmann) * //* * //* (06/07/2001 - Rev. 06/07/2001) * //* * //******************************************************************************* //=============================================================================== //= = //= ROTINAS REFERENTES AO ACESSO À EEPROM SERIAL DO TIPO NM24Cxx = //= = #define DESLIGA 0 #define LIGA 1 //**** INTERFACE COM A EEPROM SERIAL **** #define SCL P2_6 // Porta e bit onde está conectado este sinal #define SDA P2_5 // Porta e bit onde está conectado este sinal #define ENDERECO_EEPROM_ESCREVE 0xa0 // 1 0 1 0 - 0 0 0 - 0 Write no endereço 0 #define ENDERECO_EEPROM_LE 0xa1 // 1 0 1 0 - 0 0 0 - 1 READ no endereço 0 #define MARCA_0 0xa5 // As marcas são utilizadas mara informar se a EEPROM já foi gravada #define MARCA_1 0xc3 // #define ERRO_DE_COMUNICACAO_EEPROM 0x01 // Codigo de erro correspondente a alguma falha de comunicação com a EEPROM bit fFlagDeErro = DESLIGA; // Bit para sinalizar algum erro //**** PARAMETROS E VARIAVEIS DOS TIMERS DE SOFTWARE **** // Timer de 10 ms para a EEPROM #define TEMPO_DE_FIM_DE_ACESSO_EEPROM 10.0e-3 // (s) // Parametro abaixo calculado em função da freqüência de interrupção do timer principal #define CONST_TEMPORIZADOR_10ms (char)(TEMPO_DE_FIM_DE_ACESSO_EEPROM * FREQUENCIA_DE_INTERRUPCAO_TIMER_0) unsigned char ucContadorTimerEEPROM; bit fFlagFinalDeContagemTimerEEPROM; |
Observe que no trecho de código acima foram definidos um contador ucContadorTimerEEPROM, que deverá ser inserido na rotina de interrupção do timer principal, uma constante de inicialização e um bit de sinalização para indicar ao processo no loop principal que a temporização terminou. Esse recurso permite uma temporização virtual de 10 ms e é explicado em detalhes no artigo Sistemas Operacionais de Tempo Real – Timers.
Rotina do Timer de 10 ms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
//======================================================================= // // Rotina vTimer_10ms // // Descrição: Espera 10 ms // // //======================================================================= void vTimer_10ms(void) { ucContadorTimerEEPROM = CONST_TEMPORIZADOR_10ms; // Inicializa contador fFlagFinalDeContagemTimerEEPROM = DESLIGA; // Inicializa flag while(fFlagFinalDeContagemTimerEEPROM == DESLIGA); // Espera Passar o tempo } |
Rotina de escrita da EEPROM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
//============================================================================================ // // Rotina vEscreveE2PROM -Escreve Um dado para a EEPROM serial // // Entrada: - Ponteiro para o string terminado com 0 a ser transmitido a partir da RAM interna // Saida : - Nenhuma // //============================================================================================ void vEscreveE2PROM(unsigned char nEndereco, unsigned char nDado) { unsigned char nIndice, chAux; fFlagDeErro = DESLIGA; // Gera o "Start" SCL = LIGA; SDA = DESLIGA; SDA = DESLIGA; // Espera um tempinho SCL = DESLIGA; // Envia a Identificação do dispositivo (1 0 1 0) + Endereço da página (0 0 0) chAux = ENDERECO_EEPROM_ESCREVE; for (nIndice = 8; nIndice > 0; nIndice--) { SDA = chAux & 0x80; // Transmite 1 bit do endereço SDA = chAux & 0x80; // Espera um tempinho SCL = LIGA; chAux = chAux << 1; // Posiciona o bit de endereço SCL = DESLIGA; } // Verifica "Ack" SDA = LIGA; // Força a saida em 1 SDA = LIGA; // Espera um tempinho SCL = LIGA; if(SDA != DESLIGA) { // vErro(); // Se não deu "Ack", trata o erro } SDA = LIGA; // Gasta algum tempo SCL = DESLIGA; // Inicia a transmissão do endereço for (nIndice = 8; nIndice > 0; nIndice--) { SDA = nEndereco & 0x80; // Transmite 1 bit do endereço SDA = nEndereco & 0x80; // Espera um tempinho SCL = LIGA; nEndereco = nEndereco << 1; // Posiciona o bit de endereço SCL = DESLIGA; } // Verifica "Ack" SDA = LIGA; // Força a saida em 1 SDA = LIGA; // Espera um tempinho SCL = LIGA; if(SDA != DESLIGA) { // vErro(); // Se não deu "Ack", trata o erro } SDA = LIGA; // Gasta algum tempo SCL = DESLIGA; // Inicia a transmissão do dado for (nIndice = 8; nIndice > 0; nIndice--) { SDA = nDado & 0x80; // Transmite 1 bit do dado SDA = nDado & 0x80; // Espera um pouquinho SCL = LIGA; nDado = nDado << 1; // Posiciona o bit do dado SCL = DESLIGA; } // Verifica "Ack" SDA = LIGA; // Força a saida em 1 SDA = LIGA; // Força a saida em 1 SCL = LIGA; SDA = LIGA; // Gasta algum tempo SCL = DESLIGA; // Gera o "STOP" SDA = DESLIGA; SDA = DESLIGA; SCL = LIGA; SCL = LIGA; SDA = LIGA; vTimer_10ms(); } |
Rotina de leitura na EEPROM
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
//================================================================================== // // Rotina vLeE2PROM - Le um dado da EEPROM serial (Endereço ramdomico) // // Entradas: - Endereço do primeiro byte a ser lido // - Ponteiro para a area de RAM que receberá os dados // Saidas : - Dado lido // //================================================================================== void vLeE2PROM(char nEnderecoInicialE2PROM, char *pPonteiroRAM) { char nIndice, chAux; unsigned char nDado; fFlagDeErro = DESLIGA; // GEra o Start SCL = LIGA; SDA = DESLIGA; SDA = DESLIGA; // Espera um tempinho SCL = DESLIGA; // Envia a Identificação do dispositivo (1 0 1 0) + Endereço da página (0 0 0) chAux = ENDERECO_EEPROM_ESCREVE; for (nIndice = 8; nIndice > 0; nIndice--) { SDA = chAux & 0x80; // Transmite 1 bit do endereço SDA = chAux & 0x80; // Espera um tempinho SCL = LIGA; chAux = chAux << 1; // Posiciona o bit de endereço SCL = DESLIGA; } // Verifica "Ack" SDA = LIGA; // Força a saida em 1 SDA = LIGA; // Espera um tempinho SCL = LIGA; if(SDA != DESLIGA) { // vErro(); // Se não deu "Ack", trata o erro } SDA = LIGA; // Gasta algum tempo SCL = DESLIGA; // Inicia a transmissão do endereço for (nIndice = 8; nIndice > 0; nIndice--) { SDA = nEnderecoInicialE2PROM & 0x80; // Transmite 1 bit do endereço SDA = nEnderecoInicialE2PROM & 0x80; // Gasta algum tempo SCL = LIGA; nEnderecoInicialE2PROM = nEnderecoInicialE2PROM << 1; // Posiciona o bit de endereço SCL = DESLIGA; } // Verifica "Ack" SDA = LIGA; // Força a saida em 1 SDA = LIGA; // Espera um tempinho SCL = LIGA; if(SDA != DESLIGA) { // vErro(); // Se não deu "Ack", trata o erro } SDA = LIGA; // Gasta algum tempo SCL = LIGA; SCL = DESLIGA; SCL = DESLIGA; SCL = DESLIGA; SCL = DESLIGA; SCL = LIGA; // GEra o Start SCL = LIGA; SDA = DESLIGA; SDA = DESLIGA; // Espera um tempinho SCL = DESLIGA; // Envia a Identificação do dispositivo (1 0 1 0) + Endereço da página (0 0 0) chAux = ENDERECO_EEPROM_LE; for (nIndice = 8; nIndice > 0; nIndice--) { SDA = chAux & 0x80; // Transmite 1 bit do endereço SDA = chAux & 0x80; // Espera um tempinho SCL = LIGA; chAux = chAux << 1; // Posiciona o bit de endereço SCL = DESLIGA; } // Verifica "Ack" SDA = LIGA; // Força a saida em 1 SDA = LIGA; // Espera um tempinho SCL = LIGA; if(SDA != DESLIGA) { // vErro(); // Se não deu "Ack", trata o erro } SDA = LIGA; // Gasta algum tempo SCL = DESLIGA; SCL = DESLIGA; // Inicia a recepção do dado nDado = 0; for (nIndice = 8; nIndice > 0; nIndice--) { nDado = nDado << 1; // Posiciona o bit do dado SDA = LIGA; SCL = LIGA; SDA = LIGA; SCL = LIGA; nDado |= SDA; // Recebe um 1 bit do dado SCL = DESLIGA; } *pPonteiroRAM++ = nDado; // Atualiza o dado // Verifica "Ack" SDA = LIGA; // Força a saida em 1 SDA = LIGA; // Força a saida em 1 SCL = LIGA; SDA = LIGA; // Gasta algum tempo SCL = DESLIGA; // Gera o "STOP" SDA = DESLIGA; SDA = DESLIGA; SCL = LIGA; SCL = LIGA; SDA = LIGA; vTimer_10ms(); } //=============================================================================== //========================= Fim das rotinas da EEPROM =========================== |
Rotinas para acesso ao conversor A/D ADC0838
O conversor ADC0838 é um conversor A/D de 8 bits que possui um multiplexador de 8 canais na entrada do conversor. É um componente eletrônico muito robusto e versátil. Na Figura 2, pode-se observar uma aplicação típica para esse componente.
Definições
O trecho de código a seguir pode ser inserido num arquivo de definições do tipo *.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//******************************************************************************* //* * //* ROTINAS PADRONIZADAS PARA USO EM APLICAÇÕES * //* DE SISTEMAS MICROCONTROLADOS (FAMÍLIA 8051) * //* (Resp. Engenheiro Puhlmann) * //* * //* (06/07/2001 - Rev. 06/07/2001) * //* * //******************************************************************************* //=============================================================================== //= = //= ROTINAS REFERENTES AO ACESSO AO CONVERSOR A/D ADC0838 da National Semi. = //= = //**** Conversor A/D ***** #define CS_CONVERSOR_AD P2_4 // Chip Select #define SCLCK_CONVERSOR_AD P2_3 // Clock do A/D #define DIN_CONVERSOR_AD P2_1 // DOUT DO Conversor A/D #define DOUT_CONVERSOR_AD P2_2 // DIN do conversor A/D #define STATUS_AD P0_2 // Status bit fFlagStatusExisteAD; // Bit Para sinalizar a existência do A/D |
Rotina de leitura do ADC0838
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
//================================================================================== // // Rotina uchLe_AD - Seta o endereço do multiplexador do AD e lê a entrada // //================================================================================== unsigned char uchLe_AD(char chEnderecoMuxAD) { unsigned char uchAux, uchIndice; // Inicializa o MUX do conversor A/D uchAux = chEnderecoMuxAD; /// Transfere o endereço para uma variável // Gera o Start Bit DOUT_CONVERSOR_AD = LIGA; SCLCK_CONVERSOR_AD = DESLIGA; CS_CONVERSOR_AD = DESLIGA; SCLCK_CONVERSOR_AD = LIGA; SCLCK_CONVERSOR_AD = DESLIGA; // Sinaliza que tem entradas unipolares SCLCK_CONVERSOR_AD = LIGA; SCLCK_CONVERSOR_AD = DESLIGA; // Verifica se é endereço par ou ímpar if((uchAux & 0x01) == 0) { // Endereço par DOUT_CONVERSOR_AD = DESLIGA; } else { // Endereço ímpar DOUT_CONVERSOR_AD = LIGA; } // Gera o Clock SCLCK_CONVERSOR_AD = LIGA; SCLCK_CONVERSOR_AD = DESLIGA; // Transmite o bit mais significativo do endereço if((uchAux & 0x04) == 0) { DOUT_CONVERSOR_AD = DESLIGA; } else { DOUT_CONVERSOR_AD = LIGA; } // Gera o Clock SCLCK_CONVERSOR_AD = LIGA; SCLCK_CONVERSOR_AD = DESLIGA; // Transmite o segundo bit do endereço if((uchAux & 0x02) == 0) { DOUT_CONVERSOR_AD = DESLIGA; } else { DOUT_CONVERSOR_AD = LIGA; } // Gera o Clock SCLCK_CONVERSOR_AD = LIGA; SCLCK_CONVERSOR_AD = DESLIGA; // Espera estabilizar os Multiplexadores SCLCK_CONVERSOR_AD = LIGA; SCLCK_CONVERSOR_AD = DESLIGA; // inicia a leitura dos dados uchAux = 0; for(uchIndice = 8; uchIndice > 0; uchIndice--) { uchAux = uchAux << 1; // Gera o Clock SCLCK_CONVERSOR_AD = LIGA; if(DIN_CONVERSOR_AD != 0) { uchAux |= 1; } SCLCK_CONVERSOR_AD = DESLIGA; } // Descarta os oito bits restantes for(uchIndice = 7; uchIndice > 0; uchIndice--) { // Gera o Clock SCLCK_CONVERSOR_AD = LIGA; SCLCK_CONVERSOR_AD = DESLIGA; } // Desliga o conversor A/D e encerra a conversão CS_CONVERSOR_AD = LIGA; return(uchAux); } //=============================================================================== //========== Fim das rotinas do CONVERSOR A/D ADC0838 =========================== |
Rotinas para o conversor A/D AD7730
O conversor A/D AD7730 é um componente de grande precisão e ideal para a medição de sinais extraídos de pontes de strain gages, mais especificamente para a aplicação em balanças de precisão. Algumas características desse componente:
- Conversor sigma-delta de 24 bits;
- Resolução de até 1/230.000;
- Medidas razométricas com relação à fonte;
- Filtro digital programável;
- Etc.
Na Figura 3 pode ser observada uma aplicação típica desse componente e alguns módulos internos.
Para se aprofundar nos conceitos de medidas em ponte, recomendo a leitura do artigo técnico Ponte de Wheatstone.
Definições
O trecho de código a seguir pode ser inserido num arquivo de definições do tipo *.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
//=============================================================================== //= = //= ROTINAS REFERENTES AO ACESSO AO CONVERSOR A/D AD7730 da Analog Devices = //= = // INTERFACE COM O CONVERSOR A/D #define SYNC_AD_ACTVL P1_2 #define RESET_AD_ACTVL P1_3 #define STDBY_AD_ACTVL P1_4 #define DATA_OUT_AD P1_6 #define DATA_IN_AD P1_5 #define SER_CLK_AD P1_7 #define READY_ACTVL P3_2 #define ENDERECO_DO_REGISTRO_DE_OFFSET 0x05 #define COMANDO_LEITURA_DE_OFFSET 0x15 #define ENDERECO_DO_REGISTRO_DE_GANHO 0x06 #define COMANDO_LEITURA_DE_GANHO 0x16 #define ENDERECO_DO_REGISTRO_DE_FILTRO 0x03 #define COMANDO_DO_FILTRO 0x5d7030 #define NUMERO_DE_INTERRUPCOES 69 #define ENDERECO_DO_REGISTRO_DE_DAC 0x04 #define COMANDO_DO_DAC 0x00 // 0.0 mV #define ENDERECO_DO_REGISTRO_DE_MODO 0x02 // Referência em 5V e canal A1 #define COMANDO_DO_MODO_CALIBRACAO_FULL_SCALE 0xe1B0 // Calibração de fundo de escala // Escala Bipolar de -80 a +80 mV #define COMANDO_DO_MODO_CALIBRACAO_ZERO_SCALE 0x81B0 // Calibração de zero de escala // Escala Bipolar de -80 a +80 mV #define COMANDO_DO_MODO_CALIBRACAO_ZERO_SCALE_SISTEMA 0xc1B0 // Calibração de zero de escala // Escala Bipolar de -80 a +80 mV #define COMANDO_DO_MODO_LEITURA_CONTINUA 0x21B0 // Leitura Continua // Escala Bipolar de -80 a +80 mV #define COMANDO_DO_MODO_LEITURA_UNICA 0x41B0 // Leitura "single" // Escala Bipolar de -80 a +80 mV #define LEITURA_CONTINUA 0x21 #define LEITURA_UNICA 0x11 #define PARADA_DE_LEITURA 0x30 #define RESETA_COMUNICACAO 0xfffff #define OITO_BITS 8 #define DEZESSEIS_BITS 16 #define VINTE_E_QUATRO_BITS 24 #define TRINTA_E_TRES_BITS 33 #define VALOR_DE_ZERO_NOMINAL 0x800000 |
Rotina para escrever uma palavra no AD7730
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
//================================================================================= // // Rotina vEscreveAD7730 Escreve uma palavra para o conversor AD7730 // // Entradas: - Palavra a ser escrita no conversor A/D // - Tamanho da palavra // Saída: - Nenhuma // //================================================================================= void vEscreveAD7730(unsigned long int lnComandoAD, char nBits) { int nIndice; unsigned long int lnMascara; // Prepara a mascara lnMascara = (((unsigned long)0x01) << (nBits-1)); // Loop de geração de 8 bits for(nIndice = nBits; nIndice > 0; nIndice--) { SER_CLK_AD = DESLIGA; // Testa o bit do dado a ser enviado if((lnComandoAD & lnMascara) != DESLIGA) { DATA_OUT_AD = LIGA; } else { DATA_OUT_AD = DESLIGA; } // Gera a transicao do clock SER_CLK_AD = LIGA; // Posiciona o proximo bit lnComandoAD = lnComandoAD << 1; } SER_CLK_AD = DESLIGA; DATA_OUT_AD = DESLIGA; } |
Rotina para ler dados do AD7730
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
//======================================================================= // // Rotina uchLeAD7730 - Le os dados convertidos do conversor AD7730 // // Entradas: - Ponteiro para o buffer de recepcão // Saída: - Nenhuma // //======================================================================= void uchLeAD7730(unsigned char *uchBufferAD) { unsigned char uchDadoAD; unsigned char uchIndice0, uchIndice1; // Aguarda a confirmação do fim de conversão (5s) bTestaRdyDoAd(CTE_5s); // Permite a leitura da entrada DATA_IN_AD = LIGA; for (uchIndice0 = 3; uchIndice0 > 0; uchIndice0--) { // Inicializa o buffer de bits uchDadoAD = 0; for (uchIndice1 = 8; uchIndice1 > 0; uchIndice1--) { // Gera a transicao do clock SER_CLK_AD = LIGA; // Desloca o acumulador de bits uchDadoAD = uchDadoAD << 1; // Le o bit do dado uchDadoAD |= DATA_IN_AD ; // Gera a transicao do clock SER_CLK_AD = DESLIGA; } *uchBufferAD++ = uchDadoAD; } } |
Rotina para verificar se há dados disponíveis
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
//======================================================================= // // Rotina bTestaRdyDoAd // // Descrição: Temporiza a resposta do recepção do sinal de RDY do AD // // Entrada: Numero de 10 ms a serem esperados // //======================================================================= void bTestaRdyDoAd(int n10MiliSegundos) { bit bFlag; bFlag = DESLIGA; // Inicializa flag while((n10MiliSegundos-- > 0) && (bFlag == DESLIGA)) { TL0 = CTE_RELOGIO_10ms_LOW; // Inicializa o Timer para gerar 100 ms TH0 = CTE_RELOGIO_10ms_HIGH; TF0 = DESLIGA; // Reseta flag TR0 = LIGA; // Liga o timer while(TF0 == DESLIGA); // Espera completar os 100ms TR0 = DESLIGA; // Desliga o timer if (P3_2 == DESLIGA) { bFlag = LIGA; // Sinaliza que recebeu o RDY do AD } } if (bFlag == DESLIGA) { TR0 = DESLIGA; // Desliga o timer vErro(ERRO_DE_COMUNICACAO_AD); } } |
Exemplo de rotina de interrupção gerada pelo conversor AD7730
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
//********************************************************************************************* // // Rotina de interrupção do conversor A/D (exemplo) // //*********************************************************************************************/ void vInterrupcaoAD() interrupt 0 { unsigned char uchIndice0; register char chRegAux; long *plAux; // Permite a leitura da entrada DATA_IN_AD = LIGA; // Inicializa o acumulador chRegAux = 0; chNovoDadoAD[0] = 0; for (uchIndice0 = 8; uchIndice0 > 0; uchIndice0--) { // Gera a transicao do clock SER_CLK_AD = LIGA; // Desloca o acumulador de bits chRegAux = chRegAux << 1; // Gera a transicao do clock SER_CLK_AD = DESLIGA; // Le o bit do dado chRegAux |= DATA_IN_AD; } chNovoDadoAD[1] = chRegAux; for (uchIndice0 = 8; uchIndice0 > 0; uchIndice0--) { // Gera a transicao do clock SER_CLK_AD = LIGA; // Desloca o acumulador de bits chRegAux = chRegAux << 1; // Gera a transicao do clock SER_CLK_AD = DESLIGA; // Le o bit do dado chRegAux |= DATA_IN_AD; } chNovoDadoAD[2] = chRegAux; for (uchIndice0 = 8; uchIndice0 > 0; uchIndice0--) { // Gera a transicao do clock SER_CLK_AD = LIGA; // Desloca o acumulador de bits chRegAux = chRegAux << 1; // Gera a transicao do clock SER_CLK_AD = DESLIGA; // Le o bit do dado chRegAux |= DATA_IN_AD; } chNovoDadoAD[3] = chRegAux; fPermissaoParaCalculoAD = LIGA; // SInaliza que ocorreu uma interrupão do A/D } //=============================================================================== //========== Fim das rotinas do CONVERSOR A/D AD7730 =========================== |
Rotinas para o termômetro digital DS1820
O termômetro DS1820 é um componente com boa precisão, incrementos de 0,5ºC na faixa de medição de temperaturas entre -55º a +125ºC. Como pode ser observado na Figura 4, trata-se de um termômetro digital, compacto com a interface de comunicação 1-wire (um único fio). Esse componente já está obsoleto e não há reposição direta.
Definições
O trecho de código a seguir pode ser inserido num arquivo de definições do tipo *.h.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
//=============================================================================== //= = //= ROTINAS REFERENTES AO ACESSO AO Sensor de Temperatura DS1820 da Dallas = //= = // INTERFACE COM O SENSOR DE TEMPERATURA #define DQ P1_1 #define TEMPO_MINIMO_RESET 480.0e-6 // s #define TEMPO_DE_ESPERA_MINIMO 15.0e-6 // s #define TEMPO_DE_ESPERA_ESCRITA 45.0e-6 // s #define TEMPO_DE_ESPERA_ESCRITA_1B 55.0e-6 // s #define TEMPO_DE_ESPERA_LEITURA 8.0e-6 // s #define TEMPO_DE_ESPERA_ESCRITA_1 5.0e-6 // s #define TEMPO_DE_ESPERA_PRESENCA 240.0e-6 // s #define PERIODO_PADRAO_DE_TIME_OUT 255 #define PERIODO_RESET_TEMPERATURA_480u (int)((FREQUENCIA_OSCILADOR * TEMPO_MINIMO_RESET)/(12.0 * 2.0)) #define PERIODO_DE_ESPERA_5u (int)((FREQUENCIA_OSCILADOR * TEMPO_DE_ESPERA_ESCRITA_1)/(12.0 * 2.0)) - 2 #define PERIODO_DE_ESPERA_8u (int)((FREQUENCIA_OSCILADOR * TEMPO_DE_ESPERA_LEITURA)/(12.0 * 2.0)) #define PERIODO_DE_ESPERA_15u (int)((FREQUENCIA_OSCILADOR * TEMPO_DE_ESPERA_MINIMO)/(12.0 * 2.0)) #define PERIODO_DE_ESPERA_45u (int)((FREQUENCIA_OSCILADOR * TEMPO_DE_ESPERA_ESCRITA)/(12.0 * 2.0)) #define PERIODO_DE_ESPERA_55u (int)((FREQUENCIA_OSCILADOR * TEMPO_DE_ESPERA_ESCRITA_1B)/(12.0 * 2.0)) #define PERIODO_DE_ESPERA_PRESENCA (int)((FREQUENCIA_OSCILADOR * TEMPO_DE_ESPERA_PRESENCA)/(12.0 * 2.0)) #define MULTIPLICADOR_DE_TIME_OUT 3 // Vezes 255 // Sensor de temperatura #define NUMERO_DE_MEDIAS_DE_TEMPERATURA 4 // Potência de 2 #define MASCARA_DO_INDICE_DE_TEMPERATURA (NUMERO_DE_MEDIAS_DE_TEMPERATURA - 1) int idata nBufferDeLeiturasDeTemperatura[NUMERO_DE_MEDIAS_DE_TEMPERATURA]; unsigned char uchIndiceBufferDeTemperatura; int nValorMedioDeTemperatura; unsigned char uchContadorDeInterrupcoes; |
Rotina para aguardar n vezes 10 ms
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//======================================================================= // // Rotina vTimer_10ms // // Descrição: Espera múltiplos de 10 ms // // Entrada: Numero de 10 ms a serem esperados // //======================================================================= void vTimer_10ms(int n10MiliSegundos) { while(n10MiliSegundos-- > 0) { TL0 = CTE_RELOGIO_10ms_LOW; // Inicializa o Timer para gerar 100 ms TH0 = CTE_RELOGIO_10ms_HIGH; TF0 = DESLIGA; // Reseta flag TR0 = LIGA; // Liga o timer while(TF0 == 0); // Espera completar os 100ms TR0 = DESLIGA; // Desliga o timer } } |
Rotina para gerar Reset no Bus do DS1820
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
//======================================================================= // // Gera o Reset no Bus do DS1820, e espera receber o sinal de que o Sensor está presente // // Entradas: Endereço do primeiro byte a ser lido // Numero de bytes a serem transferidos // Ponteiro para a area de RAM que receberá os dados // Saidas : Dado lido // //======================================================================= void vResetaSensorDeTemperatura(void) { unsigned char chAux, chAux2; bit fFlag, fFlagAux; EA = DESLIGA; // Desabilita todas as interrupções habilitadas chAux = PERIODO_RESET_TEMPERATURA_480u; // Coloca a linha em zero DQ = DESLIGA; // Espera os 480 us while(chAux > 0) { chAux--; } // Libera a linha DQ = LIGA; chAux = PERIODO_DE_ESPERA_15u; chAux2 = MULTIPLICADOR_DE_TIME_OUT; fFlag = DESLIGA; fFlagAux = DESLIGA; while((chAux2-- > 0) && (fFlag == DESLIGA)) { while((fFlag == DESLIGA) && (chAux-- > 0)) { // Espera até 15us da primeira vez para a linha subir para 1, ou até time-out if(DQ == LIGA) { // Foi para 1 fFlag = LIGA; } } if((chAux2 == (MULTIPLICADOR_DE_TIME_OUT-1)) && (fFlag == LIGA)) { // Sinaliza que detetou 1 nos primeiros 15 us fFlagAux = LIGA; } chAux = PERIODO_PADRAO_DE_TIME_OUT; } // Testa se deu time-out if(fFlag == DESLIGA) { vErro(0x01); } // Testa se foi saida normal if(fFlagAux == LIGA) { // Se normal, espera o sensor retirar o sinal de presença chAux = PERIODO_DE_ESPERA_PRESENCA; fFlag = DESLIGA; while((fFlag == DESLIGA) && (chAux-- > 0)) { // Espera até 15us da primeira vez para a linha subir para 1, ou até time-out if(DQ == LIGA) { // Foi para 1 fFlag = LIGA; } } // Testa se deu time-out if(fFlag == DESLIGA) { vErro(0x01); } } EA = LIGA; // Habilita todas as interrupções } |
Rotina para escrever no DS1820
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
//============================================================================================ // // Rotina vEscreveByteNoSensorDeTEmperatura // // Entrada: - Byte a ser enviado // Saida : - Nenhuma // //============================================================================================ void vEscreveByteNoSensorDeTEmperatura(unsigned char nByte) { unsigned char chAux, chIndice; EA = DESLIGA; // Desabilita todas as interrupções habilitadas // PAra os 8 bits da palavra for (chIndice = 8; chIndice > 0; chIndice--) { // Sinaliza o início de uma operação de escrita DQ = DESLIGA; // Transmite o bit menos significativo o byte... DQ = nByte & 0x01; nByte = nByte >> 1; // Espera mais 55 us para completar o ciclo de escrita chAux = PERIODO_DE_ESPERA_55u; while(chAux > 0) { chAux--; } DQ = LIGA; } EA = LIGA; // Habilita todas as interrupções } |
Rotina para ler do DS1820
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
//============================================================================================ // // Rotina chLeByteDoSensorDeTEmperatura // // Entrada: - Nenhuma // Saida : - Byte lido do sensor // //============================================================================================ char chLeByteDoSensorDeTEmperatura(void) { unsigned char chAux, chIndice, chDadoLido; EA = DESLIGA; // Desabilita todas as interrupções habilitadas // Inicializa o dado a ser lido chDadoLido = 0; for (chIndice = 8; chIndice > 0; chIndice--) { // Sinaliza que pode iniciar a transferência do dado DQ = DESLIGA; DQ = DESLIGA; DQ = LIGA; // Espera 8 us antes de amostrar o sinal chAux = PERIODO_DE_ESPERA_8u; chDadoLido = chDadoLido >> 1; chAux = DQ; chDadoLido = chDadoLido | (chAux << 7); // Espera mais 45 us para completar o ciclo de escrita chAux = PERIODO_DE_ESPERA_45u; while(chAux > 0) { chAux--; } } EA = LIGA; // Habilita todas as interrupções return(chDadoLido); } //=============================================================================== //========== Fim das rotinas do Sensor DS1820 =================================== |
Resumo
Neste artigo técnico foram apresentadas algumas rotinas padronizadas, codificadas em C, para serem compiladas no programa da Keil. Essas rotinas permitem configurar e acessar alguns periféricos utilizando-se os microcontroladores da família MCS-51. Essas rotinas foram utilizadas em projetos reais e funcionam!
Este artigo é o quinto da série de artigos que abordam algumas funções comuns em projetos de sistemas embarcados de tempo real. Confira os demais artigos.
Sistemas Operacionais de Tempo Real
- Sistemas Operacionais de Tempo Real – Introdução – Apresentação introdutória do que é um sistema operacional e as características que o tornam um sistema de tempo real. Também são apresentadas algumas estruturas que facilitam o seu projeto.
Bibliotecas de funções e rotinas padronizadas em linguagem C para MCS-51
- Timers – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso dos Timers do MCS-51;
- Displays de 7 segmentos – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso em displays de 7 segmentos;
- Teclados Matriciais – É apresentada uma biblioteca desenvolvida em linguagem C para varredura, leitura, debounce, identificação da tecla acionada e desvio para a rotina de tratamento. A forma como as rotinas foram escritas, permitem sua fácil reutilização em outros projetos;
- Periféricos (este artigo) – É apresentada uma biblioteca desenvolvida em linguagem C para a inicialização e o uso de alguns periféricos, tais como conversores A/D, sensor de temperatura e memórias seriais.
Parabéns Henrique!
Um artigo no estilo “Embedded System Building Blocks” Brasileiro! muito legal! Abraços