Continuaremos hoje com o tópico anterior, tendo o todo conceito em vista, devemos avançar para a implementação, serão ao todo seis rotinas simples (i2c_Ini(), i2c_Start(), i2c_RepeatedStart(), i2c_Stop(), i2c_Write(BYTE), i2c_Read(ACKTYPE)) que farão parte de nossa biblioteca i2c, com toda a inicialização, leitura e escrita no dispositivo, segue abaixo a implementação destas rotinas com as respectivas descrições.
Função: i2c_Ini()
Propósito: Inicializa a configuração dos registradores do modulo I²C
Parâmetros: Nenhum
Descrição: O modulo é habilitado setando o bit I2CEN(I2CCON<15>), então o modulo ira liberar os pinos SDA e SCL, colocando o barramento no modo ocioso. A lógica de funcionamento para mestre e escravo é ativada simultaneamente e irão responder ao software e eventos no barramento. As funções mestre irão permanecer em estado ocioso até que o software setar um bit de controle que gere um evento mestre. Habilitamos Entrada/Saída do I²C, configuramos a interrupção e ajustamos o baud rate para operar como mestre no barramento.
void i2c_Ini(void) { // Configura os pinos SCL1/SDA1 como open-drain //ODCGbits.ODCG2 = 1; //ODCGbits.ODCG3 = 1; // IFS1 - Interrupt Flag Status register 1 // _______________________________________________________________ // Upper Byte |U2TXIF |U2RXIF |INT2IF | T5IF | T4IF | OC4IF | OC3IF |DMA21IF| // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | IC8IF | IC7IF | AD2IF |INT1IF | CNIF | ----- |MI2C1IF|SI2C1IF| // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // MI2C1IF - I2C1 Master events Interrupt Flag status bit // // limpa o flag de interrupcao do I²C1 IFS1bits.MI2C1IF = 0; // IPC4 - Interrupt Priority Control register 4 // _______________________________________________________________ // Upper Byte | ----- | CNIP<2:0> | ----- | ----- | ----- | ----- | // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | ----- | MI2C1<2:0> | ----- | SI2C1<2:0> | // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // MI2C1<2:0> - I²C Master Events Interrupt Priority bits // 011 = prioridade de interrupcao eh 3 // // IPC4bits.MI2C1IP = 3; // IEC1 - Interrupt Enable Control register 1 // _______________________________________________________________ // Upper Byte |U2TXIE |U2RXIE |INT2IE | T5IE | T4IE | OC4IE | OC3IE |DMA2IE | // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | IC8IE | IC7IE | AD2IE |INT1IE | CNIE | ----- |MI2C1IE|SI2C1IE| // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // MI2C1IE - I2C1 Master events Interrupt Enable bit // // habilita a interrupcao mestre do I²C1 // IEC1bits.MI2C1IE = 1; // Setando o baud rate // // a seguinte equacao eh usada para calcular o valor de recarga // do gerador da taxa de comunicacao: // // / Fcy Fcy \. // I2CBRG = | ------ - ----------- | - 1 // \ Fscl 1.111.111 / // // desejamo comunicar a 400kHz, entaum os valores seraoh: // // Fcy = 40 MHz // Fscl = 400 kHz // // / 40.000.000 40.000.000 \. // I2CBRG = | ------------ - ------------ | - 1 // \ 400.000 1.111.111 / // // I2CBRG = (100 - 36 ) - 1 // // I2CBRG = 63 // I2C1BRG = 63; // // I2C1CON - I²C 1 CONtrol register // _______________________________________________________________ // Upper Byte | I2CEN | ----- |I2CSIDL| SCLREL| IPMIEN| A10M | DISSLW| SMEN | // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | GCEN | STREN | ACKDT | ACKEN | RCEN | PEN | RSEN | SEN | // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // assegura que estah com o valores zerados I2C1CON = 0; // A10M: 10-bit Slave Address bit // 0 = I2CxADD eh o endereco escravo de 7-bit // I2C1CONbits.A10M = 0; // SCLREL: SCLx Release Control bit // 1 = Libera o pulso SCLx // 0 = Mantem pulso baixo do SCLx (esticar pulso) // Se STREN = 0: // Bit eh R/S (ou seja, soh pode escrever '1' para liberar o pulso. // Hardware limpa no inicio da transmissao do escravo. // // STREN: SCLx Clock Stretch Enable bit // Usado em conjuncao com o bit SCLREL. // 1 = Habilita o software ou esticamento do pulso de recepcao // 0 = Desabilita o software ou esticamento do pulso de recepcao // // libera a linha de pulso I2C1CONbits.SCLREL = 1; // desabilita o o I²C para funcionar como escravo I2C1ADD = 0; I2C1MSK = 0; // I2CEN: I2Cx Enable bit // 1 = Habilita o modulo I²Cx e configura os pinos SDAx e SCLx como porta seriais // I2C1CONbits.I2CEN = 1; IFS1bits.MI2C1IF = 0; } |
Função: i2c_Start()
Propósito: Condição de inicio para usar o protocolo do barramento I²C
Parâmetros: Nenhum
Descrição: Após o barramento estar em estado ocioso, uma transição de alto para baixo da linha SDA, enquanto o relógio SCL estiver alto, determina uma condição de inicio. Todos os dados transferidos devem começar com a condição de inicio.
void i2c_Start(void) { // // I2C1CON - I²C 1 CONtrol register // _______________________________________________________________ // Upper Byte | I2CEN | ----- |I2CSIDL| SCLREL| IPMIEN| A10M | DISSLW| SMEN | // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | GCEN | STREN | ACKDT | ACKEN | RCEN | PEN | RSEN | SEN | // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // SEN: Start Condition Enable bit (quando o I²C opera como mestre) // 1 = Inicia condicao de inicio nos pinos SDAx e SCLx. // Hardware limpa no final da sequencia de inicio do mestre // 0 = Condicao de inicio nao estah em progresso // // Start Condition I2C1CONbits.SEN = 1; // IFS1 - Interrupt Flag Status register 1 // _______________________________________________________________ // Upper Byte |U2TXIF |U2RXIF |INT2IF | T5IF | T4IF | OC4IF | OC3IF |DMA21IF| // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | IC8IF | IC7IF | AD2IF |INT1IF | CNIF | ----- |MI2C1IF|SI2C1IF| // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // MI2C1IF - I2C1 Master events Interrupt Flag status bit // // espera pelo fim do START while (!IFS1bits.MI2C1IF); // limpa o flag IFS1bits.MI2C1IF = 0; } |
Função: i2c_RepeatedStart()
Propósito: Condição de inicio repetido para usar o protocolo do barramento I²C
Parâmetros: Nenhum
Descrição: O Modulo joga o pino SCL para baixo. Quando o modulo amostra o pino SCL baixo, irá liberar o pino SDA pela contagem de um baud rate(Tbrg). Quando o gerador de baud rate esgota o tempo, se a amostra do pino SDA for alta, o modulo libera o pino SCL. Quando o modulo amostrar o pino SCL alto, o gerador de baud rate recarrega e inicia a contagem. Os pinos SDA e SCL devem ser amostrados alto por um Tbgr. Esta ação é seguida pela declaração do pino SDA para baixo por um Tbgr enquanto o pino SCL estiver alto.
void i2c_RepeatedStart(void) { // // I2C1CON - I²C 1 CONtrol register // _______________________________________________________________ // Upper Byte | I2CEN | ----- |I2CSIDL| SCLREL| IPMIEN| A10M | DISSLW| SMEN | // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | GCEN | STREN | ACKDT | ACKEN | RCEN | PEN | RSEN | SEN | // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // RSEN: Repeated Start Condition Enable bit (quando o I²C opera como mestre) // 1 = Inicia condicao de inicio repetido nos pinos SDAx e SCLx. // Hardware limpa no final da sequencia de inicio repetido do mestre // 0 = Condicao de inicio repetido nao estah em progresso // // Start Condition I2C1CONbits.RSEN = 1; // IFS1 - Interrupt Flag Status register 1 // _______________________________________________________________ // Upper Byte |U2TXIF |U2RXIF |INT2IF | T5IF | T4IF | OC4IF | OC3IF |DMA21IF| // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | IC8IF | IC7IF | AD2IF |INT1IF | CNIF | ----- |MI2C1IF|SI2C1IF| // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // MI2C1IF - I2C1 Master events Interrupt Flag status bit // // espera pelo fim do REPEATED START while (!IFS1bits.MI2C1IF); // limpa o flag IFS1bits.MI2C1IF = 0; } |
Função: i2c_Stop()
Propósito: Condição de parada ao termino dos dados enviados no barramento
Parâmetros: Nenhum
Descrição: Uma transição de baixo para alto da linha SDA enquanto a linha de relógio SCL estiver alta determina uma condição de parada. Toda a transferência de dados deve terminar com a condição de parada.
void i2c_Stop(void) { // // I2C1CON - I²C 1 CONtrol register // _______________________________________________________________ // Upper Byte | I2CEN | ----- |I2CSIDL| SCLREL| IPMIEN| A10M | DISSLW| SMEN | // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | GCEN | STREN | ACKDT | ACKEN | RCEN | PEN | RSEN | SEN | // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // // PEN: Stop Condition Enable bit (quando o I²C opera como mestre) // 1 = Inicia a condicao de parada nos pinos SDAx e SCLx. // Hardware limpa no final da sequencia de parada do mestre // 0 = Condicao de parada nao estah em progresso // // // Stop Condition I2C1CONbits.PEN = 1; // espera pelo fim do STOP while (!IFS1bits.MI2C1IF); // limpa o flag IFS1bits.MI2C1IF = 0; } |
Função: i2c_Write(BYTE)
Propósito: Enviar um byte pelo barramento I²C
Parâmetros: btData - Informação a ser transmitida pela I²C
Retorno: 1 - Recebeu confirmação do dispositivo (ACK), 0 - O dispositivo escravo não respondeu.
Descrição: Para cada byte de dado transmitido pelo mestre, o escravo deve confirmar cada um com o ACK.
BOOL i2c_Write(BYTE btData) { // IWCOL: Write Collision Detect bit //1 = Tentativa de escrever no registrador I2CxTRN falhou pq o modulo I²C estava ocupado. //0 = Sem colisao //Hardware setado por uma escrita em I2CxTRN enquanto ocupado(limpo por software). // //while (I2C1STATbits.IWCOL); // envia o dado I2C1TRN = btData; // espera pelo fim do Operacao de escrita while (!IFS1bits.MI2C1IF); // limpa o flag IFS1bits.MI2C1IF = 0; // I2C1STAT - I²C1 STATus register // _______________________________________________________________ // Upper Byte |ACKSTAT| TRSTAT| ----- | ----- | ----- | BCL | GCSTAT| ADD10 | // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | IWCOL | I2COV | D_A | P | S | R_W | RBF | TBF | // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // // ACKSTAT: Acknowledge Status bit // (quando o I²C opera como mestre, aplicavel durante a transmissao do mestre) // 1 = recebido do escravo um NACK // 0 = recebido do escravo um ACK // Hardware setado ou limpo ao fim da confirmacao do escravo. // // se o ACK nao retornou, indica erro if (I2C1STATbits.ACKSTAT) return FALSE; else return TRUE; } |
Função: i2c_Read(BYTE)
Propósito: Ler o dado no barramento I²C passado pelo dispositivo escravo.
Parâmetros: ackType - Tipo de confirmação a ser enviado(ACK ou NACK).
Retorno: O byte lido do dispositivo escravo
Descrição: Para cada byte que o escravo transmitir para o mestre, deve ser confirmado com ACK, o ultimo byte transmitido pelo escravo, o mestre deve enviar um NACK, para encerrar a transmissão.
BYTE i2c_Read(ACKTYPE ackType) { // // I2C1CON - I²C 1 CONtrol register // _______________________________________________________________ // Upper Byte | I2CEN | ----- |I2CSIDL| SCLREL| IPMIEN| A10M | DISSLW| SMEN | // |__15___|__14___|__13___|__12___|__11___|__10___|___9___|___8___| // _______________________________________________________________ // Lower Byte | GCEN | STREN | ACKDT | ACKEN | RCEN | PEN | RSEN | SEN | // |__ 7___|___6___|___5___|___4___|___3___|___2___|___1___|___0___| // BYTE btInfo; //RCEN: Receive Enable bit (quando o I²C opera como mestre) //1 = Habilita a recepcao do I²C. Hardware limpa ao final do oitavo bit do byte recebido I2C1CONbits.RCEN = 1; // espera pela indicacao de dado recebido while (!IFS1bits.MI2C1IF); // limpa o flag IFS1bits.MI2C1IF = 0; // recebe o dado btInfo = I2C1RCV; // ACKDT: Acknowledge Data bit (quando o I²C opera como mestre, aplicavel durante a recepcao) // Valor que sera transmitido quando o software inciar uma sequencia de confirmacao. // 1 = Envia NACK durante a confirmacao // 0 = Envia ACK durante a confirmacao // // seleciona a confirmacao passada para a funcao (ACK ou NACK) I2C1CONbits.ACKDT = ackType; //ACKEN: Acknowledge Sequence Enable bit //1 = Incia a sequencia de confirmacao nos pinos SDAx e SCLx e transmite o bit de dados ACKDT. // Hardware limpa ao final da sequencia de confirmacao do mestre. // // envia a confirmacao escolhida I2C1CONbits.ACKEN = 1; // espera pela transmissao da confirmacao while (!IFS1bits.MI2C1IF); // limpa o flag IFS1bits.MI2C1IF = 0; return btInfo; } |
Com isso finalizamos a rotinas necessárias para comunicar com um dispositivo I²C, lembre-se de incluir no arquivo de cabeçalho do projeto a seguinte declaração.
typedef enum tagACKTYPE { ACK, NACK } ACKTYPE; void i2c_Ini(); void i2c_Start(); void i2c_RepeatedStart(); void i2c_Stop(); BOOL i2c_Write(BYTE); BYTE i2c_Read(ACKTYPE); |
E na próxima parte iremos comunicar o microcontrolador com outro dispositivo pela I²C.
0 comentários:
Postar um comentário