Página 1 de 1

Ajuda 8051+I2C ???

MensagemEnviado: 11 Jun 2008 14:29
por Fernando
Pessoal já to há uma semana tentando implentar a comunicação do 89s52 pra 24c04... :?
Testes no keil, proteus, grava 89 e testa e horas na frente do pc e do scope e ainda não consigo fazer funcionar....
A principio pelo que vi as formas de onda de leitura estão ok, o problema está na leitura, a cada vez dá uma coisa diferente...
Escrita, vão 3 bytes (dev adr, adr e dado) na leitura, vão 4 bytes (dev adr, adr, dev adr e dado lido) peguei isso do manual da memória.
Na simulação no proteus, a gravação está funcionando, e apenas a 1ª leitura da memória funciona, já na 2ª leitura, vem tudo 0xFF.
Segue o código e as fotos q tirei mandando escrever e ler.

Imagem
escrita
Imagem
leitura

cpp code
#include <REG52.h>
#include <stdlib.h>
#include <stdio.h>
#include <intrins.h>
#include <rtx51tny.h>

sbit SCL = P2^0;
sbit SDA = P2^1;
sbit LED = P0^0;

/* 0x02 eeprom 24C04 , 0x0E eeprom 24C08 */
#define PG 0x02


void delay5(void)
{
_nop_(); /* modificado para Keil C51 */
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
/*
This function is hardware depended (CPU speed)
For fast processors you can define a loop
This concrete function written for I8051 on 12MHz (NOP=1uS)
*/


void i2c_start_tx(void)
{
SCL=1;
SDA=1;
delay5();
SDA=0;
delay5();
SCL=0;
}

void i2c_send_ack(void)
{
SDA=1;
SCL=1;
delay5();
SCL=0;
}

unsigned char i2c_read_char(void)
{
unsigned char res=0;
unsigned char cnt;
for ( cnt=0;cnt<8;cnt++ )
{
SDA=1;
SCL=1;
delay5();
res<<=1;
if (SDA) { res |= 0x01; }
SCL=0;
delay5();
LED = ~LED;
}
return res;
}


bit i2c_check_ack(void)
{
SDA=1;
SCL=1;
delay5();
if (SDA)
{
SCL=0;
return 1;
}
SCL=0;
return 0;
}


void i2c_write_char(unsigned char d)
{
unsigned char i;
for (i=0;i<8;i++)
{
SDA=(bit)(d & 0x80);
SCL=1;
delay5();
SCL=0;
d = d << 1;
}
i2c_send_ack();
}

void i2c_stop_tx(void)
{
SDA=0;
delay5();
SCL=1;
delay5();
SDA=1;
}

/* The following two functions are an entry points
* for the external calls */

void WR24C04(unsigned char index,unsigned char content)
{
unsigned char res;
res=(((unsigned char)(index>>7))& PG) | 0xA0;
i2c_start_tx();
//Send control word + page NUM
i2c_write_char(res);
i2c_write_char((unsigned char)(index&0xFF));
i2c_write_char(content);
i2c_stop_tx();
}

unsigned char RD24C04(unsigned char index)
{
unsigned char res;
//Send control word + page NUM
res=(((unsigned char)(index>>7))& PG) | 0xA1;
i2c_start_tx();
i2c_write_char(res);
i2c_write_char((unsigned char)(index&0xFF));
i2c_start_tx();
i2c_write_char(res);
res=i2c_read_char();
i2c_stop_tx();
return res;
}

MensagemEnviado: 11 Jun 2008 21:01
por Maurício
Salve, Fernando!

Experimenta este código aqui que eu mudei uma coisa ou outra.

http://www.4shared.com/file/50989552/e6 ... _code.html

Eu testei no Keil, e pelo Logic Analizer funcionou redondo.

O arquivo I2C_main.C é um programa completo que eu usei para testar, e os arquivos I2C_LIB.C e I2C_LIB.H são os arquivos que vc deve incluir no seu projeto e chamar as rotinas:

void e2prom_escrever ( unsigned char id, unsigned int address, unsigned char dado )

para escrever um dado, passando o ID (configuração dos pinos da memória), o endereço - address (em 16 bits) e o dado a ser armazenado - dado.

Para ler vc usa:

unsigned char e2prom_ler ( unsigned char id, unsigned int address )

passando o endereço do dispositivo ( id ) e o endereço do dado a ser lido (address; 16 bits).
A função devolve o byte lido.

Espero que ajude.

[]'s

MensagemEnviado: 19 Jun 2008 20:53
por Fandango
Olá Fernando,

Há mais de dois anos tenho usado as rotinas a seguir com 89C2051 e 89C4051 falando em I2C com as 24C04 sem problemas.
Vê se te ajuda. Eu uso o SDCC, não o KEIL, mas creio que as adequações (se necessárias) não devem ser nenhum mistério.

Código: Selecionar todos
// Funções do protocolo I2C -----------------------------------------
static void  I2C_Start(void);           // Inicia comunicação I2C
static void  I2C_Stop(void);            // Termina comunicação I2C
static uchar I2C_Clock(void);           // Gera pulso de clock
static uchar I2C_Write(uchar);          // Escrita em dispositivo I2C
static uchar I2C_Read(uchar);           // Leitura de dispositivo I2C
static void  _nop(void);                // Equivalente a um NOP

// Funções de manipulação da E2PROM ---------------------------------
static void  EE_WaitForReady(void);     // Aguarda prontidão da EEPROM
static uchar EE_GetStatus(void);        // Obtém status da EEPROM
static uchar EE_Write_Byte(uint, uchar);            // Escreve byte
static uchar EE_Write_Block(uchar *, uint, uchar);  // Escreve bloco
static uchar EE_Read_Byte(uchar *, uint);           // Lê byte

Esses são os protótipos das funções.

Agora as funções propriamente ditas:
Código: Selecionar todos
// ******************************************************************
// *                   FUNÇÕES DO PROTOCOLO I2C                     *
// ******************************************************************

// ------------------------------------------------------------------
//  I2C_Start - Promove Início de comunicação I2C
// ------------------------------------------------------------------

static void I2C_Start(void)
{
  SDA = SCL = 1;
  SDA = 0;
  _nop();
  _nop();
  _nop();
  _nop();
  _nop();
  SCL = 0;
}

// ------------------------------------------------------------------
//  I2C_Stop - Promove Fim de comunicação I2C
// ------------------------------------------------------------------

static void I2C_Stop(void)
{
  SDA = 0;
  SCL = 1;
  _nop();
  _nop();
  _nop();
  _nop();
  _nop();
  SDA = 1;
}

// ------------------------------------------------------------------
//  I2C_Clock - Gera pulso em SCL (Retorna SDA ou reconhecimento)
// ------------------------------------------------------------------

static uchar I2C_Clock(void)
{
  uchar level;               // Estado da linha SDA
  //
  SCL = 1;
  _nop();
  while(!SCL);               // Aguarda um pulso de SCL
  _nop();
  _nop();
  _nop();
  level = SDA;
  _nop();
  _nop();
  SCL = 0;
  return(level);
}

// ------------------------------------------------------------------
//  I2C_Write - Escreve um byte (bit mais significativo primeiro)
//              Retorna com bit de reconhecimento
// ------------------------------------------------------------------

static uchar I2C_Write(uchar byte)
{
  uchar mask = 0x80;
  //
  while(mask)
    {
    if (byte & mask)
      SDA = 1;
    else
      SDA = 0;
    I2C_Clock();             // Gera pulso em SCL
    mask >>= 1;              // Próximo bit a enviar
    }
  SDA = 1;                   // Libera a linha de dados
  return(I2C_Clock());       // Memória deve reconhecer
}

// ------------------------------------------------------------------
//  I2C_Read - Lê um byte (bit mais significativo primeiro)
//             O parâmetro indica reconhecimento (1) ou não (0)
// ------------------------------------------------------------------

static uchar I2C_Read(uchar acknowledgment)
{
  uchar mask = 0x80;
  uchar byte = 0x00;
  //
  while(mask)
    {
    if (I2C_Clock())
      byte |= mask;
    mask >>= 1;              // Próximo bit a receber
    }
  if (acknowledgment)
    {
    SDA = 0;
    I2C_Clock();
    SDA = 1;
    }
  else
    {
    SDA = 1;
    I2C_Clock();
    }
  return(byte);
}

// ------------------------------------------------------------------
//  _nop - Promove o equivalente a um NOP em assembler
// ------------------------------------------------------------------

static void _nop(void)
{
  _asm
  nop
  _endasm;
}

// ******************************************************************
// *                FUNÇÕES DE MANIPULAÇÃO DA E2PROM                *
// ******************************************************************

// ------------------------------------------------------------------
//  EE_GetStatus - Verifica o status da E2PROM
//                 Retorna 0 se Ready, 1 se Busy
// ------------------------------------------------------------------

static uchar EE_GetStatus(void)
{
  uchar status;
  //
  I2C_Start();                // Inicia a comunicação
  status = I2C_Write(EEPROM); // Obtém status
  I2C_Stop();                 // Termina a comunicação
  return(status);             // Se 1, ciclo de escrita em andamento
}

// ------------------------------------------------------------------
//  EE_WaitForReady - Aguarda completar ciclo de escrita
// ------------------------------------------------------------------

void EE_WaitForReady(void)
{
  while(EE_GetStatus())
    Delay_ms(10);             // Atraso de 10 ms
}

// ------------------------------------------------------------------
//  EE_Write_Byte - Escreve um byte no endereço especificado
//                  com 0 < address < 512
//                  Retorna com o status da operação (1 = sucesso)
// ------------------------------------------------------------------

uchar EE_Write_Byte(uint address,uchar dado)
{
  uchar status = 0;                // Falha por default
  uchar pgaddr = 0xA0;             // Página baixa por default
  uchar addrh = HI_BYTE(address);  // Obtém parte alta do endereço
  uchar addrl = LO_BYTE(address);  // Obtém parte baixa do endereço
  //
  EE_WaitForReady();         // Aguarda disponibilidade
  //
  if (addrh != 0)            // Para endereço > 0x00FF..
    pgaddr = 0xA2;           // Seleciona página alta
  //
  I2C_Start();               // Inicia comunicação
  if (!I2C_Write(pgaddr))    // Endereça a EEPROM em modo escrita
    if (!I2C_Write(addrl))   // Parte baixa do endereço
      if (!I2C_Write(dado))  // Armazena o dado
        status = 1;          // Operação bem sucedida
  I2C_Stop();                // Termina a comunicação
  Delay_ms(5);               // Promove atraso de 5 ms
  return(status);            // Retorna com o status da operação
}

// ------------------------------------------------------------------
//  EE_Write_Block - Escreve um bloco de N bytes no endereço especificado
//                   Retorna com o status da operação
// ------------------------------------------------------------------

static uchar EE_Write_Block(uchar *block, uint address, uchar size)
{
  uchar status,n;
  //
  for (n = 0; n < size; n++)  // Para o número de bytes solicitado...
    {
    status = EE_Write_Byte(address, *block); // Grava o dado
    if (status == 0)          // Se falha na operação
      break;                  // Aborta imediatamente
    else                      // Caso contrário
      {
      address++;              // Aponta para a próxima posição de memória
      block++;                // Aponta para o próximo byte do buffer
      }
    }
  return(status);             // Retorna com o status da operação
}

// ------------------------------------------------------------------
//  EE_Read_Byte - Lê um byte do endereço especificado
//                 com 0 < address < 512
//                 Armazena o byte lido na variável especificada
//                 Retorna com o status da operação (1 = sucesso)
// ------------------------------------------------------------------

uchar EE_Read_Byte(uchar *byte,uint address)
{
  uchar status = 0;                // Falha por default
  uchar pgaddr = 0xA0;             // Página baixa por default
  uchar addrh = HI_BYTE(address);  // Obtém parte alta do endereço
  uchar addrl = LO_BYTE(address);  // Obtém parte baixa do endereço
  //
  if (addrh != 0)                  // Para endereço > 0x00FF..
    pgaddr = 0xA2;                 // Seleciona página alta
  //
  I2C_Start();                     // Inicia a comunicação
  if (!I2C_Write(pgaddr))          // Endereça a EEPROM em modo escrita
    {
    if (!I2C_Write(addrl))         // Endereço baixo
      {
      I2C_Start();
      pgaddr++;                    // Prepara comando de leitura
      if (!I2C_Write(pgaddr))      // Operação de leitura
        {
        *byte = I2C_Read(0);       // Recupera byte (sem reconhecimento)
        status = 1;                // Indica sucesso da operação
        }
      }
    }
  I2C_Stop();                      // Termina a comunicação
  return(status);
}


As rotinas de atraso que uso são as seguintes:
Código: Selecionar todos
// ------------------------------------------------------------------
//  Delay_ms - Gera atraso de (n * 1) ms para Cristal de 11.0592 MHz
// ------------------------------------------------------------------

static void Delay_ms(int n)
{
  int i;
  //
  for (i = 0; i < n ; i++)
    Delay1ms();
}

// ------------------------------------------------------------------
//  Delay1ms - Gera atraso de 1 ms para Cristal de 11.0592 MHz
// ------------------------------------------------------------------

static void Delay1ms(void)
{
  int i;
  //
  for (i = 0; i < 8; i++);
}


A menos que busque algo diferente, é só copiar e colar.

A propósito, não esqueça de incluir as definições de tipo que usei:
Código: Selecionar todos
// Tipos ------------------------------------------------------------
typedef unsigned char uchar;
typedef unsigned int  uint;


Um abraço e boa sorte.

MensagemEnviado: 25 Jun 2008 17:25
por ze
a procurar rotinas 93c66* pro nosso amigo me deparei com esta que usei no século passado. Pra converter pra C é só colocar #asm #endasm em pontos estratégicos (pode ser no início e fim). Já fiz isto e funcionou. Definir algumas variáveis como global ou extern. Pode funcionar até pras 24C512.

*tinha pras 93c66 mas perdi, um dia eu acho!!!

Código: Selecionar todos
;*****************************************************************************
; This code was designed to demonstrate how the x24c01a/02/04/08/16 family   *
; of parts could be interfaced to 8051 microcontrollers. the interface       *
; uses 2 lines from port 1 (p1.0 and p1.1) to communicate. Other i2c         *
; compatible parts can be added to the bus as long as they do not have $a    *
; as their device identifier. The routines rdbyt and wrbyt are tailored      *
; specifically to this family. The routines start, stop, ack, nack, outbyt,  *
; and inbyt can be considered generic i2c routines.                          *
;                                                                            *
; The code shown demonstrates a 'random read' and 'byte write'. The other    *
; modes of operation can be created by expanding upon these routines.        *
; Acknowledge polling is used to determine when the write cycle is complete. *
;                                                                            *
; This code will work with all xicor i2c compatible eeproms reguardless of   *
; their size. As long as the address pins are configured correctly this      *
; code will not know the difference between a bus with a single x24c16 and a *
; bus with eight x24c02s.                                                    *
;                                                                            *
; The mainline of this program reads the data located at address 002dh and   *
; then writes that data back to address 0041h.                               *
; Revised: january 1997                                                      *
;*****************************************************************************

sda     equ p1.0        ; the sda bit is port 1 bit 0
scl     equ p1.1        ; the scl bit is port 1 bit 1
addr    equ 40h         ; location for x24c04 address to access
count   equ 41h         ; counter variable
data1   equ 42h         ; location for x24c04 data transfered
temp    equ 43h         ; scratch pad byte
count2  equ 44h         ; counter for ack polling

;***************************
; Reset vector entry point *
;***************************

        defseg  zero,start=0
        seg     zero

        ljmp begin      ; jump to beginning of program

;**********************
; Program entry point *
;**********************

       org      50           ; program code starts at 0100h
begin  mov      sp,#60h      ; initialize stack pointer
       mov      dptr,#002dh  ; prepare to read address #002dh
       mov      addr,dph
       mov      addr+1,dpl
       call     rdbyt        ; read data from address 002dh
       mov      dptr,#0041h  ; prepare to write value stored in
       mov      addr,dph     ; data1 to dut location #0041h
       mov      addr+1,dpl
       call     wrbyt        ; write data to address #0041h
       call     ackpol       ; use acknowledge polling
done   ljmp     done         ; loop until reset

;***************************************************************************
; Read a byte "random read sequence". the address to read is stored        *
; in addr. The data from the dut (device under test) is stored in data1.   *
;***************************************************************************

rdbyt  call     start      ; read a byte from the address indicated
       mov      a,addr     ; in 'addr'
       clr      c
       rlc      a
       orl      a,#0a0h    ; build slave address for write
       mov      temp,a
       mov      data1,a
       call     outbyt     ; send slave address
       call     nack       ; send sda hi to receive an acknowledge
       mov      a,addr+1
       mov      data1,a
       call     outbyt     ; send word address
       call     nack       ; send sda hi to receive an acknowledge
       call     start      ; send start command
       mov      a,temp     ; build slave address for read
       orl      a,#01h     ; r/w bit = 1
       mov      data1,a
       call     outbyt     ; send slave address
       call     nack       ; send sda hi to receive an acknowledge
       call     inbyt      ; read data from x24c04
       call     nack       ; clock without acknowledge
       call     stop       ; send stop command
       ret

;**********************************************************************
; Write a byte "byte write sequence". The address to write is stored  *
; in addr. The data to write is stored in data1.                      *
;**********************************************************************

wrbyt  mov      a,data1    ; write to byte pointed to by addr the
       mov      temp,a     ; value in location 'data1'
       call     start      ; send start command
       mov      a,addr     ; build slave address for write
       clr      c
       rlc      a
       orl      a,#0a0h
       mov      data1,a
       call     outbyt     ; send slave address
       call     nack       ; send sda hi to receive an acknowledge
       mov      a,addr+1
       mov      data1,a
       call     outbyt     ; send word address
       call     nack       ; send sda hi to receive an acknowledge
       mov      a,temp
       mov      data1,a
       call     outbyt     ; send write data
       call     nack       ; send sda hi to receive an acknowledge
       call     stop       ; send stop
       ret

;***************************************************************
; Read 8 bits from the dut. The results are returned in data1. *
;***************************************************************

inbyt  setb     sda         ; read 8 bits, make sda an input
       mov      count,#08h
loopi  call     clock       ; clock data
       mov      a,data1     ; build byte using data1
       rlc      a           ; roll in next bit from dut
       mov      data1,a     ; save data
       djnz     count,loopi ; loop until 8 bits are read
       ret

;*********************************************************
; Write 8 bits to the dut. The data to send is in data1. *
;*********************************************************

outbyt mov      count,#08h   ; prepare to shift out 8 bits
loopo  mov      a,data1      ; load data to be sent to dut
       rlc      a            ; rotate bit to send into carry flag
       mov      sda,c        ; send carry to sda
       mov      data1,a      ; save rotated data
       call     clock        ; send clock signal to dut
       djnz     count,loopo  ; loop until all 8 bits have been sent
       ret

;*********************************************************************
; Perform acknowledge polling to determine when the write cycle      *
; completes. Upon return from this routine the carry bit indicates   *
; whether the dut ever acknowledged the write. carry=0 part          *
; acknowledged, carry=1 no acknowledge received.                     *
;*********************************************************************

ackpol mov      count2,#080h ; max number of times to check the part
akloop djnz     count2,look  ; return if the part
       sjmp     outack       ; never issues an acknowledge.
look   call     start        ; set up for a read
       mov      a,#0a0h      ; make slave address for a read
       mov      data1,a
       call     outbyt       ; send slave address
       call     nack         ; get acknowledge
       jc       akloop       ; loop if no acknowledge received
outack call     stop         ; issue a stop before returning
       ret

;***********************
; Issue a stop command *
;***********************

stop   clr      sda          ; send stop condition to dut, sda low
       setb     scl          ; scl high
       nop                   ; make sure the set up time is valid
       nop
       nop
       nop
       setb     sda          ; sda high
       ret

;************************
; Issue a start command *
;************************

start  setb     sda          ; send start condition to dut, sda high
       setb     scl          ; scl high
       nop                   ; make sure scl set up time is valid
       nop
       nop
       nop
       clr      sda          ; sda low
       nop                   ; make sure the set up time is valid
       nop
       nop
       nop
       clr      scl          ; scl low
       ret

;************************
; Issue an acknowledge. *
;************************

ack    clr      sda          ; perform an acknowledge, sda low
       call     clock        ; generate a clock pulse
       ret

;****************************************************
; Send sda high. The nack routine does not check    *
; to see if the dut actually issues an acknowledge. *
;****************************************************

nack   setb     sda          ; sda high
       call     clock        ; generate a clock pulse
       ret

;*****************************************************************
; Issue a clock pulse. While the clock is high the value on the  *
; sda line is placed in the carry flag. when a read is taking    *
; place the carry flag will indicate the value from the dut.     *
;*****************************************************************

clock  nop                  ; make sure the data set up time is valid
       setb     scl         ; generate a clock pulse, scl high
       nop                  ; make sure clock high time is valid
       nop
       nop
       mov      c,sda       ; read state on sda, save in carry flag
       clr      scl         ; scl low
       ret

       end

MensagemEnviado: 25 Jun 2008 23:16
por Fernando
Bahhh valeu, problema com o código resolvido.. depois de muita briga.. mas valeu a força.

[]s