sábado, 2 de abril de 2011

I2C en ATmega8

En éste post mostrare un par de ejemplos de como usar el bus i2c para escribir y leer datos en una memoria EEPROM serial (24C16).
La memoria 24C16 tiene una capacidad de 2048 bytes agrupados en 8 páginas de 256 bytes cada una.
Todos los esclavos del bus i2c tienen una dirección la cual les permite ser seleccionados; en el caso de estas memorias seriales la dirección es en parte configurable o fija, esto dependerá del fabricante. En el siguiente gráfico se muestra dos memorias de fabricantes distintos.

Programas

eeproms

La primera es una memoria de ATMEL, la cual permite configurar parte de la dirección mediante los pines A2, A1 y A0; los bits B2B1B0 son usados para seleccionar la pagina de la dentro de la memoria.
La otra es una memoria de FAIRCHILD, ésta ya trae parte de dirección fija(1010), los bit A2A1A0 permiten seleccionar la pagina de la memoria a la cual se accederá.
La velocidad del reloj para esto dispositivos se indica en sus hojas de datos, en los ejemplos usaremos 100KHz. Para configurar dicha velocidad se usa la siguiente formula, la cual se puede encontrar en el manual de del uC que se usara, yo la obtuve del manual del ATmega8.
 formula

La forma de escribir  datos es la siguiente:

write

Se inicia la transmisión con un “START”, luego se selecciona el esclavo “DEVICE ADDRESS” en modo de escritura “0”, después del “ACK” enviado por el esclavo se enviara la dirección donde se escribirá “WORD ADDRESS”, por último se envía el dato a escribir. Hay que cada ciclo de escritura tiene una duración, éste tiempo dependerá del dispositivo.

La forma de leer datos es la siguiente:

read

Se inicia la transmisión con un “START”, luego se selecciona el esclavo “DEVICE ADDRESS” en modo de escritura “0”, después del “ACK” enviado por el esclavo se enviara la dirección donde se leerá “WORD ADDRESS”, nuevamente se envía un “START” o también conocido como “RESTART”, por último se lee el dato.

Bueno, como ya es costumbre mostrare las macros que usare en estos ejemplos.

macros

Ahora mostrare las rutinas para iniciar y usar el bus i2c.

rutinasi2c1

rutinasi2c2

rutinasi2c3

Ahora el un sencillo ejemplo se escritura y lectura de algunos datos en una EEPROM.

i2c1

El ejemplo no hace otra cosa que escribir algunos datos a partir de la dirección 0x05 de la pagina 0, luego los leerá.

El circuito es el siguiente:

simu_0

Con el uso de debuger i2c se puede ver la transferencia de datos:

simu_2

El final la memoria debe quedar así:

simu_1

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

El segundo ejemplo consiste en enviarle al ATmega8, por el uart, un comando para que éste lea o escriba en la EEPROM.

‘W’+PAGINA+DIRECCIÓN+DATO se usara para escribir en la EEPROM.
‘R’+PAGINA+DIRECCIÓN se usara para leer en la EEPROM.

Por ejemplo si se envía por el uart ‘W’ 0x06 0x66 0x99 el uC escribirá en la dirección 0x66 de la pagina 6 el dato 0x99. Y si enviamos ‘R’ 0x03 0x79 estaremos leyendo el datos que esta guardado en la posición 0x79 de la pagina 3.

Agregaremos algunas rutinas al archivo RUTINAS_I2C.asm:

rutinasi2c4

Las rutinas usadas en el uart son las siguientes:

uart1

uart2

El programa principal es mostrado a continuación.

i2c2

Ahora les mostrare un vídeo con el funcionamiento del programa anterior.

En el vídeo se escribe 0x99 en la dirección 0x66 de la página 6 (dirección absoluta en la memoria es 6*0x100 + 0x66 = 0x666), luego éste dato es leído. También se escribe 0x58 en la posición 0x67 de la misma página.

14 comentarios:

  1. Excelente, y tienes algun ejemplo para leer o escribir en memorias RAM?. Gracias!!!

    ResponderEliminar
  2. ¿Te refieres a RAM seriales?. Las únicas RAM que con las que he trabajado son las RAM spi de Microchip (23K256).
    También las FRAM(RAM Ferromacticas) que usan el protocolo I2C, su funcionamiento es idéntico a las EEPROM I2C pero con un número mucho mayor de ciclos de lectura/escritura y sin tener retardos en la escritura.
    Ahora no tengo ejemplos sobre eso, pero su funcionamiento es similar entre si. Envío de dirección del esclavo(I2C) ó selección del mismo(SPI), más el comando de escritura/lectura, luego envío de la dirección donde se escribirá/leerá y por ultimo enviar el dato(cuando se escribe) y esperar el dato(cuando se lee).

    ResponderEliminar
  3. Para que sirve el retraso de 10ms en TX_BUS_I2C antes del retorno?

    atte: Jahir

    ResponderEliminar
  4. El tiempo de escritura en una EEPROM es muy lento, según las hojas de datos es de 10ms como máximo, es por ello que le pongo ese retardo.

    ResponderEliminar
  5. Termine el programa pero no me funciono =( otra cosa que note esque no configuras ningun puerto porque? yo he usado el UART anteriormente pero siempre le configure el TX como salida.
    De necesitar config las i/o tengo que cambiar manualmente SDA si ocupo transmitir o reconocer?

    Gracias de cualquier forma =)
    atte:Jahir

    ResponderEliminar
  6. Cuando habilitas algún periférico éste toma posesión de los pines que usa, es por ello que al habilitar la transmisión y la recepción ya no es necesario poner Tx como salida ó Rx como entrada.
    Dime cual de los dos ejemplos estas probando ?.. Dame un poco mas de información para poder ayudarte.

    ResponderEliminar
  7. Bueno pues yo estoy usando nada mas el primero de I2C y los estoy mostrando con leds lo que tenga R16 al terminar cada RX_I2C_ pero ademas no estoy usando la memoria que tienes en el ejemplo sino un ADC el MCP3221(es de 12bits). El que tengo supuestamente me da el codigo 1001 101 asi que mando Tx la direccion y 0 de lectura ($9A) y luego un Rx ack seguido de un Rx nack y stop

    LEER:
    START_I2C
    TX_I2C $9A ;Direccion del ADC
    RX_I2C_ACK
    OUT PORTB,R16
    RX_I2C_NACK
    OUT PORTD,R16
    STOP_I2C
    delay_ms(10)
    RJMP LEER

    mañana voy a buscar el AT24C16 para probarlo como en tu ejemplo y ver si lo que tengo mal es la direccion o falta de configuracion del adc o si copie mal el programa xD
    Aqui dejo el programa

    https://skydrive.live.com/redir.aspx?cid=0b35f0905045c622&resid=B35F0905045C622!333

    atte:Jahir

    ResponderEliminar
  8. Hola Jahir, el 99.9% de tu ejemplo esta bien, pero tienes que enviar 0x9B en lugar de 0x9A. No lo digo yo, el manual dice eso.
    Esto es parte del manual.
    When set to a ‘1’, a read operation is selected. When set to a ‘0’, a write operation is selected. There are no writable registers on the MCP3221. Therefore, this bit must be set to a ’1’ in order to initiate a conversion.

    ResponderEliminar
  9. (facepalm) jaja estaba casi seguro de haber leido lo contrario, pero ya modificando el dato si funciono :D gracias por la ayuda todo esto me es muy util, casi diria que deberias hacer un libro ;) (yo lo compraria)

    uhm y si quiero ponerlo como esclavo solo tengo que modificar los registros de config? o tambien tengo que hacer otras macros?

    atte:Jahir

    ResponderEliminar
  10. mmm.. nunca he usado un uC como esclavo, pero en este caso tienes que:
    - Darle una dirección al uC.
    - Tienes que testear(o habilitar su interrupción) el bit TWINT del registro TWCR.
    - Cada vez que "TWINT=1" tienes que leer los bits de estado(TWS) del registro TWSR para determinar que es lo que ocurrió, en base a eso serán tus respuestas.

    ResponderEliminar
  11. Hola,

    Disculpa que salga un poco del tema. Soy de trujillo y quiero saber si en lima hay memorias ram seriales spi del tipo 23k256.

    Saludos.

    ResponderEliminar
  12. Muito bom artigo, continue.
    AlvaroLuiz2@yahoo.com.br
    São Paulo - Brasil
    http:/alvieletronica.blogspot.com

    ResponderEliminar
  13. Hola amigo, muy útil tu ejemplo. Un favor, crees que podria compartirnos tu codigo de la macro DELAY_MS . Gracias de antemano =)

    ResponderEliminar
    Respuestas
    1. Estimado. Hay un post con las rutinas del DELAY.
      http://avrperu.blogspot.com/2011/03/delaymsms-y-delayusus-para-cualquier.html

      Eliminar