Índice:

Ônibus I2C para ATtiny e ATmega: 8 etapas
Ônibus I2C para ATtiny e ATmega: 8 etapas

Vídeo: Ônibus I2C para ATtiny e ATmega: 8 etapas

Vídeo: Ônibus I2C para ATtiny e ATmega: 8 etapas
Vídeo: AVR 48# Аппаратный I2C 2024, Novembro
Anonim
Ônibus I2C para ATtiny e ATmega
Ônibus I2C para ATtiny e ATmega

Eu amo os microcontroladores Atmel AVR! Desde a construção do Ghetto Development System descrito neste Instructable, nunca me diverti muito experimentando o AVR ATtiny2313 e o ATmega168 em particular. Cheguei até a escrever um Instructable sobre o uso de interruptores como entradas e estendi o conceito de sistema de desenvolvimento de gueto para CPLDs. Durante um projeto recente, precisei de vários interruptores para definir valores de controle. Os AVRs não tinham pinos de E / S suficientes, então tive que pensar em algo. Eu poderia ter tentado um sistema de entrada complexo com teclado e tela, mas o ATtiny2313 teria ficado sem recursos. Felizmente, a Atmel ofereceu uma solução para esse problema incluindo uma interface que pode ser vinculada a chips adicionais (como memória ou portas de E / S) com uma interface simples de dois fios. Isso mesmo, usando apenas dois pinos de I / O em um AVR, podemos acessar muitos pinos de I / O adicionais e também outros recursos. Essa interface de dois fios é formalmente conhecida como barramento de circuito integrado, ou apenas barramento I2C, e foi inventada pela NXP quando ainda era a Philips Semiconductors. Se você está lendo este Instructable, provavelmente já ouviu falar do barramento I2C e pode até mesmo tê-lo usado em um PIC ou outro microcontrolador. Embora conceitualmente muito simples e com suporte de recursos de hardware nos AVRs, os drivers de software ainda são necessários para usar o barramento I2C. A Atmel fornece notas de aplicação (consulte os recursos mais adiante neste Instructable), mas estes estão incompletos e não mostram nenhum exemplo além da comunicação com outro dispositivo AVR. Não é o propósito deste Instructable ensinar alguém como criar drivers I2C para o AVRs. Em vez disso, fornecerei versões expandidas dos drivers Atmel para dispositivos ATtiny2313 e ATmega168, explicarei os requisitos e restrições que se aplicam ao usá-los e mostrarei exemplos funcionais de dispositivos I2C. Depois de trabalhar com este Instructable, você será capaz de usar o barramento I2C com sucesso em seus projetos AVR. Obviamente, você pode ignorar os drivers de tiny ou MEGA se estiver interessado apenas em um deles. Para aqueles interessados em aprender mais sobre o barramento I2C, fornecerei links para materiais apropriados.

Etapa 1: O que é todo esse material I2C, afinal?

O que são todas essas coisas I2C, afinal?
O que são todas essas coisas I2C, afinal?
O que são todas essas coisas I2C, afinal?
O que são todas essas coisas I2C, afinal?
O que são todas essas coisas I2C, afinal?
O que são todas essas coisas I2C, afinal?
O que é tudo isso I2C afinal?
O que é tudo isso I2C afinal?

O barramento I2C é uma conexão simples de dois fios que pode conectar vários dispositivos e permitir que eles troquem dados. Em sua forma mais simples, há um dispositivo mestre que se comunica com vários dispositivos escravos. Todos os dispositivos são conectados em paralelo aos dois fios do barramento I2C. Os dois fios são conhecidos como SCL e SDA. SCL é a linha do relógio e é controlada pelo dispositivo mestre. SDA é a linha de dados bidirecional. Para transferir dados, o mestre envia um endereço de escravo combinado com um sinalizador de leitura / gravação de um bit. Se uma gravação for desejada, o mestre continuará a enviar dados para o escravo endereçado. Se uma leitura for solicitada, o escravo responderá com dados. Para coordenar as transações, as linhas SCL e SDA são manipuladas pelo mestre e pelo escravo para sinalizar várias condições. Isso inclui START, STOP, ACK (reconhecimento) e NAK (sem reconhecimento). Os detalhes dessas condições são tratados pelos motoristas. Os verdadeiros geeks entre vocês podem aprender todos os detalhes nos links fornecidos no final deste Instructable. Os requisitos elétricos são bastante simples. O mestre e os escravos devem usar o mesmo nível para Vcc, os aterramentos devem ser conectados e as linhas SCL e SDA devem ser puxadas para Vcc. O valor dos resistores pull-up é determinado com precisão por um cálculo baseado na capacitância total do barramento, mas praticamente pode ser qualquer valor entre 1,8K e 10K. Eu começo com 5.1K e uso valores menores até que funcione. Isso geralmente não é um problema, a menos que você tenha muitos dispositivos ou cabos longos entre os dispositivos. A taxa de dados nominal no barramento I2C é 100Kbits / segundo. Taxas de 400Kbits / segundo, 1Mbits / segundo e além também são possíveis, mas não são suportadas pelos drivers neste Instructable. Todos os dispositivos I2C funcionarão a 100Kbits / segundo. O ATtiny2313 e o ATmega168 implementam o barramento I2C de maneira diferente. ATtiny2313 usa o hardware de interface serial universal (USI) - que também pode ser usado para o barramento SPI. O ATmega168 tem hardware dedicado para o barramento I2C conhecido como Two Wire Interface (TWI). Depois que os drivers são gravados, essas diferenças são transparentes para o usuário. Uma diferença significativa está no software: o driver ATmega168 I2C é controlado por interrupção, enquanto o do ATtiny2313 não. Isso significa que um programa ATmega168 não precisa esperar que as transferências de dados I2C ocorram, mas apenas antes de iniciar outra transferência ou até que os dados cheguem de uma operação de leitura. Os exemplos e a discussão a seguir devem deixar isso claro. Os endereços I2C têm 7 bits de comprimento, portanto, até 127 dispositivos podem estar no barramento se cada um tiver um endereço exclusivo. Conforme mostrado na figura, este endereço de 7 bits é deslocado um bit para a esquerda e o bit menos significativo é usado para sinalizar uma leitura ou gravação do dispositivo no endereço. Assim, o endereço escravo completo é um byte de 8 bits. O endereço real é parcialmente determinado internamente para o dispositivo e não pode ser alterado (4 bits mais significativos), e parcialmente determinado por bits que podem ser conectados aos pinos do dispositivo (3 bits menos significativos) que podem ser ligados alto ou baixo para definir um endereço específico. Parece confuso, mas um exemplo deixará isso claro. A planilha de dados PCA8574A mostra que os quatro bits mais significativos do endereço I2C sempre serão 0111. Os próximos três bits são determinados pelas configurações nos pinos AD0, AD1 e AD2. Esses pinos podem ser ligados ao aterramento ou à fonte de tensão positiva (5 volts) para representar 0 ou 1, respectivamente. Portanto, a faixa de endereços possíveis é hexadecimal de 38 a 3F, conforme mostrado na outra figura da planilha de dados PCA8574. Portanto, alterando as configurações de bit de endereço, até 8 PCA8574As podem estar no barramento I2C ao mesmo tempo. Cada um responderá apenas ao seu endereço de escravo específico. Se ainda mais portas de E / S forem necessárias, o PCA8574 pode ser usado. A única diferença entre o PCA8574 e o PCA8574A é que o intervalo de endereço escravo I2C do PCA8574 é de 20 a 27 hexadecimal. Determinar o endereço de um determinado dispositivo pode ser confuso, pois algumas folhas de dados consideram o bit de leitura / gravação como parte do Morada. Leia a folha de dados com atenção e lembre-se de que o endereço do escravo terá 7 bits de comprimento. O bit de leitura / gravação deve ser tratado separadamente. Novamente, um exemplo ajudará. A folha de dados da EEPROM 24C16 que vamos experimentar diz que os primeiros quatro bits (mais significativos) do endereço do escravo são 1010. Os próximos três bits podem ser determinados por A0, A1 e A2; mas observe que a folha de dados também cobre 24C01 a 24C08, que são EEPROMs de menor tamanho. A figura da folha de dados mostra que as configurações desses bits de endereço são ignoradas à medida que o tamanho aumenta e são completamente ignoradas para o 24C16. Ou seja, os últimos três bits não importam e o 24C16 realmente usa todos os endereços de escravo I2C de 50 a 57 hexadecimais. A faixa de endereços de escravos irá, na verdade, endereçar diferentes seções dentro do 24C16. Os primeiros 256 bytes estão no endereço 50h, os próximos 256 às 51h e assim por diante até os últimos 256 às 57h - para um total de 2K bytes. Como o endereço da RAM PCF8570 que também experimentamos está nesta faixa, o 24C16 e o PCF8570 não podem ser usados juntos.

Etapa 2: solicitar alguns dispositivos I2C

Agora que você sabe um pouco sobre o barramento I2C e quer usá-lo, por que não pedir alguns dispositivos I2C para experimentar agora, para que possam estar no seu caminho enquanto você prepara o software? Dispositivos apropriados incluem um I / O Interface Expander (meu favorito), um Static Ram e um EEPROM. Há muito mais, mas é um ótimo começo. Os processadores AVR que usaremos são o ATtiny2313 e o Atmega168 (usado no Arduino). Se você é novo nisso, dê uma olhada neste excelente Instructable para aprender sobre eles e construir seu Sistema de Desenvolvimento do Gueto. O esquema do ATmega168 no presente Instructable mostra como implementar o Ghetto Development System para este processador. O cabo da porta paralela é o mesmo do ATtiny2313. (Eu não experimentei a versão USB do Ghetto Development System, então não tenho certeza de como o barramento I2C é acessado nele. O mesmo para o Arduino.) Aqui estão os números de peça Digikey. Expansor de porta: IC I2C I / O EXPANDER 568-4236-5-NDRam: IC SRAM 256X8 W / I2C 568-1071-5-NDEEPROM: IC EEPROM SERIAL 16K CAT24C16LI-G-ND

Etapa 3: Drivers I2C

Aqui estão as descrições das funções do driver para o barramento I2C. Eles foram desenvolvidos usando o Atmel Apps Notes para iniciantes. Eu não poderia ter feito isso sem eles como base para construir. O desenvolvimento foi feito usando WinAVR e o compilador C gcc. As restrições de taxa de clock são descritas abaixo para cada processador. Como não posso testar todas as combinações de variação de velocidade / sabor do processador possíveis, vou apenas me limitar ao que realmente posso testar e tentar indicar as restrições e limitações. Aqui estão as funções do driver e como usá-las. Por favor, veja os exemplos para mais detalhes e para ver as funções em uso em programas completos. Para o ATtiny2313: Requisito de relógio: Os drivers são projetados para uma taxa de clock de 1 MHz (a taxa padrão) para ATtiny2313. Se você quiser rodar em outras taxas, terá que ajustar as constantes nos drivers. Envie-me um e-mail se precisar de ajuda para fazer isso. Você também pode obter algumas dicas das notas dos aplicativos Atmel nos links na Etapa de Recursos. USI_TWI_Master_Initialise () Esta função inicializa o hardware USI para operação no modo I2C. Ligue uma vez no início do seu programa. Ele retorna void e não há argumentos. USI_TWI_Get_State_Info () Esta função retorna informações de erro I2C e é usada se um erro ocorreu durante uma transação I2C. Como esta função retorna apenas um código de erro, eu uso a função TWI_Act_On_Failure_In_Last_Transmission (TWIerrorMsg) para piscar um LED de erro. Os códigos de erro são definidos em USI_TWI_Master.h. Veja como chamá-lo: TWI_Act_On_Failure_In_Last_Transmission (USI_TWI_Get_State_Info ()) USI_TWI_Start_Read_Write () Esta função é usada para ler e gravar bytes únicos em dispositivos I2C. Ele também é usado para gravar bytes múltiplos. Existem 6 etapas para usar esta função.1) Declare um buffer de mensagem em seu programa para conter o endereço do escravo e o byte de dados a ser enviado ou recebido. unsigned char messageBuf (MESSAGEBUF_SIZE); 2) Coloque o Slave Address como o primeiro byte no buffer. Mude um bit para a esquerda e OU no bit de Leitura / Gravação. Observe que o bit de leitura / gravação será 1 para leitura e 0 para gravação. Este exemplo é para uma leitura. messageBuf (0) = (TWI_targetSlaveAddress << TWI_ADR_BITS) | (VERDADEIRO << TWI_READ_BIT); 3) Ao fazer uma gravação, coloque o byte a ser escrito no próximo local no buffer.4) Chame a função USI_TWI_Start_Read_Write com o buffer de mensagem e o tamanho da mensagem como argumentos.temp = USI_TWI_Start_Read_Write (messageBuf, 2); 5) O o valor retornado (temp neste caso) pode ser testado para ver se ocorreu um erro. Nesse caso, ele é tratado conforme discutido acima. Veja exemplos nos programas.6) Se uma leitura foi solicitada, o byte lido estará no segundo local no buffer. Se vários bytes forem gravados (como em um dispositivo de memória), esta mesma rotina pode ser usada. Configurar o buffer e chamar a rotina são um pouco diferentes. O segundo byte no buffer será o endereço de memória inicial no qual gravar. Os dados a serem gravados estarão em bytes subsequentes. O tamanho da mensagem será o tamanho que inclui todos os dados válidos. Portanto, se 6 bytes forem gravados, o tamanho da mensagem será 8 (endereço escravo + endereço de memória + 6 bytes de dados). USI_TWI_Start_Random_Read () Esta função é usada para ler bytes múltiplos de um dispositivo I2C, normalmente só é significativo para uma lembrança de algum tipo. O uso dessa rotina é muito semelhante à rotina anterior, com duas exceções. A configuração do bit de leitura / gravação não importa. Chamar esta rotina sempre causará uma operação de leitura. O tamanho da mensagem deve ser 2 mais o número de bytes a serem lidos. Se nenhum erro ocorrer, os dados estarão no buffer começando no segundo local. Para o ATmega168: Requisito de relógio: O os drivers são projetados para uma freqüência de 4 MHz para ATmega168. O código de exemplo mostra como definir essa taxa de clock. Se você quiser rodar em outras taxas, terá que ajustar as constantes nos drivers. Envie-me um e-mail se precisar fazer isso. TWI_Master_Initialise () Esta função inicializa o hardware TWI para operação no modo I2C. Ligue uma vez no início do seu programa. Ele retorna vazio e não há argumentos. Certifique-se de habilitar as interrupções chamando swi () após a inicialização. TWI_Get_State_Info () Esta função retorna informações de erro I2C e é usada se um erro ocorreu durante uma transação I2C. Como essa função retorna apenas um código de erro, eu uso a função TWI_Act_On_Failure_In_Last_Transmission (TWIerrorMsg) para piscar um LED de erro. Os códigos de erro são definidos em TWI_Master.h, mas são modificados para sinalização em um LED de erro. Veja o código de exemplo para detalhes. Veja como chamá-lo: TWI_Act_On_Failure_In_Last_Transmission (TWI_Get_State_Info ()) Observe que a verificação de erros é feita certificando-se de que a transação I2C está completa (função descrita abaixo) e, em seguida, testando um bit na palavra de status global. TWI_Start_Read_Write () TWI_Start_Write () duas funções funcionam da mesma forma que as funções correspondentes descritas acima, mas com algumas exceções. Elas não retornam nenhum valor de erro. Os dados lidos não são transferidos para o buffer. Isso será feito com a função descrita a seguir. Ao chamar TWI_Start_Random_Read, o messageSize deve ser o número de bytes de dados solicitados mais um, não dois. O driver I2C para o ATmega168 é controlado por interrupção. Ou seja, as transações I2C são iniciadas e executadas independentemente enquanto a rotina principal continua a ser executada. Quando a rotina principal deseja dados de uma transação I2C iniciada, ela deve verificar se os dados estão disponíveis. A situação é a mesma para a verificação de erros. A rotina principal deve ter certeza de que a transação I2C foi concluída antes de verificar se há erros. As próximas duas funções são usadas para esses propósitos. TWI_Transceiver_Busy () Chame esta função para ver se uma transação I2C foi concluída antes de verificar se há erros. Os programas de exemplo mostram como usar this. TWI_Read_Data_From_Buffer () Chame esta função para transferir dados do buffer de recepção do driver I2C para o buffer de mensagem. Esta função garantirá que a transação I2C seja concluída antes de transferir os dados. Embora um valor seja retornado por esta função, acho que verificar o bit de erro diretamente é mais confiável. Veja como chamá-lo. O tamanho da mensagem deve ser um maior que o número de bits de dados desejados. Os dados estarão em messageBuf começando no segundo local.temp = TWI_Read_Data_From_Buffer (messageBuf, messageSize);

Etapa 4: vamos construir

Vamos construir!
Vamos construir!
Vamos construir!
Vamos construir!
Vamos construir!
Vamos construir!
Vamos construir!
Vamos construir!

Comece baixando o arquivo I2C Schematics.zip. Você pode criar uma pasta I2C em sua área de trabalho para armazenar os esquemas e os arquivos de programa de exemplo. Descompacte os esquemas neste diretório. Você encontrará uma pasta chamada I2C Schematics. Abra o arquivo chamado tiny I2C.pdf. Este esquema mostra o ATtiny2313 Ghetto Development System e o PCA8574A I / O Port Expander (tem a grande caixa tracejada ao seu redor). O circuito do expansor de portas é construído em uma placa de ensaio. Dê uma olhada nas fotos para ver como são esses circuitos. Eles são realmente muito simples. A parte ATtiny2313 do esquema é apenas o Sistema de Desenvolvimento do Gueto com três luzes piscantes (LED1, 2 e 3, mais R4, 5 e 6) e um botão (S1) conectado a ele, mais um detalhes adicionais. Esse detalhe é a adição de jumpers (JP4, 5 e 6) que podem ser removidos para permitir a conexão das linhas SCL e SDA do barramento I2C. Os jumpers devem estar no lugar para programação e, em seguida, removidos para que SCL e SDA possam ser conectados. As fotos mostram os jumpers no lugar e removidos. A colocação desses jumpers é com você, você só precisa colocá-los em seu Ghetto Development System se quiser usar o barramento I2C. O barramento I2C deve ser desconectado e os jumpers instalados para programação. Observe que você realmente só precisa se preocupar com JP4 e JP6 para o barramento I2C. Coloque JP5 se você acha que algum dia vai querer usar o barramento SPI. A leitura do expansor de porta de E / S PCA8574A é muito simples. Fornece conexões Vcc (+5 volts) e Gnd (aterramento) e conecte AD0, 1 e 2 ao aterramento (torna o endereço escravo I2C 38 hex). Em seguida, conecte 4 luzes piscantes e 4 interruptores DIP. (Se você não tiver chaves DIP, você pode apenas usar fios. Amarre ao aterramento ou deixe flutuando para sinalizar ligado ou desligado respectivamente.) Finalmente, conecte os resistores pull-up (R11 e 12) de SDA e SCL para Vcc. Eles são mostrados como 3,3 K, mas qualquer valor de 1,8 K a 5,1 K deve funcionar (talvez até 10 K, mas não tentei isso). Depois de programar o ATtiny2313, você pode remover os jumpers e conectar o SDA e o SCL para teste. Agora, para o ATmega168. O único problema aqui é que você pode não ter construído um sistema de desenvolvimento de gueto para este processador. Se for esse o caso, o esquema que forneço (MEGA I2C.pdf) mostrará como. Esta é apenas uma permutação da versão ATtiny2313. Se você planejar com antecedência, certifique-se de que seu cabo de programação se encaixará em ambos os sistemas. A principal diferença é a adição de C2 e C3. Veja as fotos para colocação destes, devem ficar bem próximos ao chip; um deles está realmente sob o chip. Isso ajuda a manter o ruído longe do conversor analógico para digital em particular. Você não precisa colocar jumpers, a menos que planeje usar o barramento SPI, uma vez que eles não são necessários para o barramento I2C neste chip. Observe que a placa de ensaio PCA8754A não será alterada. Você apenas conectará SDA e SCL e pronto! Fácil, hein?

Etapa 5: vamos codificar e testar

Vamos codificar e testar!
Vamos codificar e testar!
Vamos codificar e testar!
Vamos codificar e testar!
Vamos codificar e testar!
Vamos codificar e testar!

É hora de construir os drivers e os programas de exemplo. Começaremos com o ATtiny2313 e a placa de ensaio PCA8574A que acabamos de construir. Baixe o arquivo I2C.zip em seu diretório de trabalho I2C e descompacte-o. Você terá uma nova pasta chamada I2C. Nele você encontrará USI I2C (para ATtiny2313) e TWI I2C (para ATmega168). No USI I2C, você encontrará a pasta I_O Port. Essa pasta contém o código para nosso primeiro programa de exemplo e os drivers USI I2C. Usando o WinAVR, compile e carregue o código no ATtiny2313. Respire fundo e ligue a energia. Aqui está o que esperar: Ao ligar, o LED 1 na porta PD6 do ATtiny2313 pisca duas vezes. Nada mais acontecerá até que você pressione o botão (S1). Cada vez que o botão é pressionado, os interruptores são lidos e suas configurações são exibidas nos LEDs conectados ao PCA8574A. Altere o valor das chaves, pressione o botão e os LEDs devem mudar. Continue fazendo isso até superar a emoção de vê-lo funcionar. Se (Deus me livre!) As coisas não funcionarem como o esperado, verifique cuidadosamente a sua fiação. Os erros I2C serão sinalizados por piscadas no LED3 (PD4) e provavelmente significam que você precisa verificar se o SDA e o SCL estão conectados aos pinos corretos e puxados corretamente. Se as coisas ainda não funcionarem, leia o restante desta seção para aprender sobre depuração. Agora volte e vamos dar uma olhada no código. Abra o arquivo USI_I2C_Port.c. Este é o código do programa de exemplo. (USI_TWI_Master.c e USI_TWI_Master.h contêm os drivers - você pode ignorá-los, a menos que esteja curioso.) Use o exemplo para guiar seus próprios aplicativos I2C. Geralmente, o programa mostra como inicializar e usar os drivers I2C, incluindo a configuração o endereço do escravo e o resto do buffer de mensagem, e obtendo os dados dele. Você também verá como eu elimino o botão e configuro o loop while. Existem alguns detalhes do programa que valem a pena mencionar. Observe que os dados das chaves são invertidos antes de serem gravados nos LEDs no expansor de porta. Observe também que as portas de entrada no expansor de porta devem ser escritas como Alta para que funcionem corretamente. Esses detalhes são descritos na planilha de dados PCA8574A. Sempre leia as folhas de dados com atenção! Mais interessante é o uso de depuração condicional. Perto do início do arquivo de programa está a instrução // # define DEBUG e espalhadas por todo o código estão as instruções #ifdef DEBUG. Enquanto DEBUG não for definido (as duas barras tornam a linha um comentário e evitam que seja definida), o código dentro das instruções #ifdef a #endif não será compilado. Mas se as coisas não funcionarem como você espera, recompile e recarregue o código com #define DEBUG não comentado. Você terá muito mais piscadas nos LEDs que você pode decodificar para seguir a execução do seu programa e ajudá-lo a descobrir exatamente onde as coisas deram errado. Na verdade, eu recomendo que você tente isso apenas para ver o que acontece. O que você verá é que o LED 2 (no PD5) piscará conforme o andamento da execução do programa. O valor lido dos interruptores piscará no LED 1 (PD6) antes de ser exibido nos LEDs do expansor de porta. Você deve conseguir acompanhar o programa enquanto ele é executado usando esses LEDs. Trabalharemos com o ATmega168 a seguir; pule esta seção se você estiver interessado apenas no ATtiny2313. Ainda comigo? Boa. Mova para a pasta TWI_I2C, altere seu diretório de trabalho para IO_Port e compile e carregue TWI_I2C_Port.c no ATmega168. Desconecte as linhas SDA e SCL do ATtiny2313 e conecte-as ao ATmega168. Conecte a energia e o aterramento e ligue. A operação deve ser a mesma! Jogue até que a emoção desapareça, então vamos dar uma olhada no código. Abra TWI_I2C_Port.c. O código é quase idêntico, exceto para tratamento de erros e acomodação de drivers direcionados a interrupções. Aqui estão as diferenças: Observe que o clock deve ser ajustado para 4 MHz para que o barramento I2C funcione corretamente. O sei (); declaração ativa interrupções após a inicialização dos drivers I2C. Para verificar se há erros, um bit de status específico é testado. Durante uma leitura, a função TWI_Read_Data_From_Buffer deve ser chamada para transferir os dados lidos para o buffer de mensagem. Durante uma gravação, while (TWI_Transceiver_Busy ()) deve ser usado para garantir que a transferência seja concluída antes de verificar se há erros. Essas duas últimas funções são descritas acima na descrição dos drivers. Fora isso, o código é praticamente o mesmo do ATtiny2313. DEBUG funciona da mesma forma, se você quiser fazer experiências com isso.

Etapa 6: Usando a memória I2C

Usando memória I2C
Usando memória I2C
Usando memória I2C
Usando memória I2C
Usando memória I2C
Usando memória I2C
Usando memória I2C
Usando memória I2C

Agora que aprendemos a usar o barramento I2C para ler e escrever um expansor de porta de E / S, vamos passar a usar memórias I2C, tanto RAM quanto EEPROM. A principal diferença é que vários bytes podem ser lidos ou gravados nas memórias com um único comando I2C. Para nos prepararmos para esses experimentos, precisamos modificar um pouco o hardware e construir alguns novos circuitos na placa de ensaio. Mantenha o circuito do expansor de porta, pois o usaremos para exibir alguns valores de memória. Remova as chaves DIP do PCA8574A e coloque luzes piscantes nesses pinos. Se você não tiver luzes piscantes suficientes, mova as de P4 a P7 para P0 a P3. (Os valores a serem exibidos são pequenos o suficiente.) Agora, olhe para o esquema I2C Ram.pdf e conecte o PCF8570 na placa de ensaio. Dê uma olhada na foto também. Certifique-se de amarrar o pino 7 ao Vcc. Passe os fios para SDA e SCL do PCA8574A. Não são necessários resistores pull-up adicionais. Se você também estiver interessado no EEPROM, construa esse circuito também usando I2C EEPROM.pdf para o 24C16, mas esteja avisado que o exemplo usa o ATmega168. Este circuito é muito simples. Conforme discutido acima, os bits de endereço devem ser ignorados. Basta conectar a energia e o aterramento. Não conecte SDA e SCL ainda, já que não terminamos os experimentos com o Ram. Vamos começar nossos experimentos de memória com o ATtiny2313 conectado ao Expansor de Porta PCA8574A e ao Ram PCF8570. O programa gravará alguns números no Ram, depois os lerá de volta e os exibirá no Port Expander. Mude seu diretório de trabalho para RAM sob USI I2C. Use o arquivo make para compilar e baixar USI_I2C_RAM.c. Observe que os arquivos do driver I2C são idênticos aos que usamos anteriormente. Conecte a energia e você verá uma única piscada no LED 1 (PD6). Os dados serão gravados nos primeiros 4 bytes de memória. Pressione o botão e dois bytes serão lidos e exibidos. Você deve ver uma luz de LED no Expansor de porta (P0), uma pausa de dois segundos e, em seguida, duas luzes de LED (P0 e P1). Outra pausa de dois segundos e os LEDs devem desligar. Pressione o botão novamente para reiniciar a sequência. A depuração é semelhante ao método descrito acima. Vamos dar uma olhada no código. Abra USI_I2C_RAM.c. Deve ser muito semelhante ao código anterior. As principais diferenças são os detalhes de leitura e escrita da memória. Observe a maneira como o buffer de mensagem é carregado antes da chamada que realmente faz a gravação. O primeiro byte é o endereço do escravo com o bit de leitura / gravação definido apropriadamente. Mas o próximo byte é o endereço de memória no qual iniciar a gravação de dados. Em seguida, vêm os bytes de dados reais que serão carregados sequencialmente na memória, começando no endereço que especificamos. Especificamos o tamanho da mensagem como 6. Portanto, começamos a escrever no endereço 00 e escrevemos os valores 01, 03, 02 e 06 nas localizações de memória 00 a 03. Para ler os dados de volta da memória, devemos usar a função USI_TWI_Start_Random_Read. O buffer de mensagem obtém o endereço do escravo no primeiro byte e o endereço inicial no segundo byte. Em seguida, chame a função com o tamanho da mensagem definido como o número de bytes a serem lidos mais 2. Observe que o bit de leitura / gravação não importa, pois uma leitura será feita independentemente. Os dados retornados começarão no segundo local do buffer de mensagem. Depois que os dados são lidos, eles são invertidos para exibição no expansor de porta e gravados um byte por vez nele com uma pausa entre os valores. Finalmente, os LEDs do expansor de porta são desligados. As gravações no expansor de porta são idênticas ao que foi feito nos exemplos anteriores. Para se divertir, você pode descomentar a instrução #define DEBUG como acima e ver muitos LEDs piscando. Pulsos de entusiasmo após outro experimento bem-sucedido, vamos passar para o ATmega168 e um EEPROM. Mude seu diretório de trabalho para EEPROM sob TWI I2C. Use o arquivo make para compilar e baixar TWI_I2C_EEPROM.c. Observe que os arquivos do driver I2C são idênticos aos que usamos anteriormente para o PCA8574A. Para testar o programa, desconecte o ATtiny2313 e conecte o ATmega168. Deixe o barramento I2C conectado ao Ram e ligue-o. Os resultados são diferentes, pois agora estamos escrevendo e lendo mais dados. O LED 1 no PD7 deve piscar na inicialização. Pressione o botão e os dados serão lidos de volta da memória e exibidos. Os LEDs no PCA8574 devem piscar na seguinte sequência: P1, P0 e P2, (todos apagados), P0 e P1, P1 e P2. Finalmente, todos os LEDs da porta devem desligar. Pressione o botão novamente para repetir isso. Oh, mas espere, você diz. Não é este programa para a EEPROM? Como estamos acessando um dispositivo de memória no mesmo endereço I2C, o mesmo programa funciona para Ram e EEPROM. Desligue e mova SDA e SCL da Ram para a EEPROM e execute o programa novamente. Deve funcionar exatamente da mesma forma. Observe que a EEPROM e a Ram não podem ser conectadas ao barramento I2C ao mesmo tempo, pois compartilham o mesmo endereço. (Os mais espertos podem considerar alterar os bits de endereço programáveis no Ram, mas isso ainda não funcionará. O 24C16 usa todo o bloco de endereços que podem ser programados para o Ram.) OK, vamos dar uma olhada neste último programa. Abra TWI_I2C_EEPROM.c. A primeira coisa a notar é que indiquei como lidar com a EEPROM 24C16 completa. Ele pode ser acessado em blocos de 256 bytes em 8 endereços diferentes de escravos I2C. Veja como MEMORY_ADDR é definido como o endereço inicial em 50 hexadecimal; é por isso que o Ram funcionou. Se você quiser acessar outros blocos do 24C16, use os outros endereços que indiquei. Dê uma olhada em como configurei para gravar na memória. Primeiro, o endereço do escravo com o conjunto de bits de leitura / gravação é colocado no buffer, depois o endereço inicial de 00 e, em seguida, 16 bytes de dados. A função TWI_Start_Read_Write é chamada para escrever os dados (como antes) com o tamanho da mensagem definido como 18. Quando o botão é pressionado, usamos TWI_Start_Random_Read e TWI_Read_Data_From_Buffer para ler os dados de volta. Cada terceiro byte é exibido nos LEDs do expansor de porta. Finalmente, os LEDs são desligados para aguardar o próximo botão pressionado. Você pode se perguntar por que escolhi escrever 16 bytes. Se você ler a planilha de dados com atenção, verá que o 24C16 faz um ciclo de gravação sempre que recebe 16 bytes, mesmo se mais bytes estiverem sendo enviados. Então parecia um bom número para usar. Se você optar por aumentar isso, terá que alterar o tamanho de MESSAGEBUF_SIZE. Você também terá que alterar o valor TWI_BUFFER_SIZE em TWI_Master.h. Isso ocorre porque o driver copia os dados do buffer de mensagem para uso pela rotina de serviço de interrupção. Parabéns! Agora você está pronto para usar o barramento I2C em seus próprios projetos!

Etapa 7: Recursos da Web

Aqui estão os links para as planilhas de dados das peças usadas para os experimentos. Você definitivamente deveria obtê-los, se não conseguir mais nada. Port ExpanderRamEEPROMSendo o criador do I2C, o NXP (Philips) tem um monte de coisas boas. (Eles gostam de usar colchetes em seus URLs, então não posso incluí-los corretamente aqui. Desculpe.] Para chegar à área I2C, selecione Interface na lista de Produtos. Você será capaz de chegar ao site I2C e acesso a todas as folhas de dados e notas de aplicativos que eles oferecem. A descrição do barramento I2C e detalhes técnicos em particular estão aqui. Obtenha as folhas de dados ATtiny2313 e ATmega168 (livros de dados?) do Atmel. As notas de aplicativos do Atmel estão aqui. Veja AVR310 e AVR315. Pegue o código também. Dê uma olhada aqui para muito mais coisas I2C.

Etapa 8: Notas para Geeks

Para o verdadeiro geek que deseja saber os detalhes, aqui estão algumas coisas para se manter em mente se você olhar as notas do Atmel Apps e o código do driver: - O método de endereçamento e comando de um dispositivo I2C não faz parte das especificações! Além do endereço do escravo e do bit de leitura / gravação, comandos, modos, etc. não são especificados e são específicos para um determinado dispositivo. Para deixar isso bem claro, observe que o esquema usado no exemplo da Atmel só se aplica a esse exemplo e é praticamente fora do padrão.- A implementação do USI difere da implementação do TWI em alguns aspectos importantes. + Com USI, o clocking é fornecido por software; com o TWI, é fornecido por um gerador de taxa de bits. + O método USI não usa interrupções; o TWI faz. Isso faz muito sentido, já que a família Mega (usando o TWI) poderia estar fazendo muitas outras coisas e não deveria ser monopolizada por transferências I2C. Uma versão controlada por interrupção para USI certamente é possível, mas não está implementada neste Instructable. + O hardware USI não é otimizado para I2C e só pode lidar com transferências de 8 bits. Isso significa que duas transferências são necessárias para enviar o nono bit (NACK ou ACK). O hardware TWI lida com isso automaticamente. Isso torna a implementação do driver USI um pouco mais complicada. + A detecção de erros para o TWI é tratada no hardware. A USI requer manipulação em software, o que complica um pouco as coisas. + O hardware TWI controla a configuração da porta diretamente. O hardware USI requer que os bits da porta sejam configurados antes que a porta possa ser usada. Você verá isso na rotina Master_Initialize para o USI.- Atmel afirma que é possível usar pull-ups da porta AVR para os pull-ups do barramento I2C. Não descobri uma maneira de fazer essa abordagem funcionar. Usar dois resistores externos parece um esquema bem simples, então não gastei muito tempo nisso.

Recomendado: