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.