Standard AVR microcontroller emulates the host program of I2C bus

Publisher:ShiningSmileLatest update time:2017-11-26 Source: eefocus Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

.H file preprocessing

 

typedef unsigned char INT8U; //0~255

typedef signed char INT8S; //-128~127

typedef unsigned int INT16U; //0~65535

typedef signed int INT16S; //-32768~32767

typedef unsigned long INT32U; //0~0xFFFFFFFF

typedef signed long INT32S; //0x8000 0000~7FFFFFFF

typedef float FP32; //Single precision floating point

typedef double FP64; //Double precision floating point

 

 

#define XTAL 1 //Crystal frequency, unit MHz

#define m_delayus(x) __delay_cycles((unsigned long)(x*XTAL))

#define m_delayms(x) __delay_cycles((unsigned long)(x*XTAL*1000UL))

#define m_delays(x) __delay_cycles((unsigned long)(x*XTAL*1000000UL))

 


#define BIT0 0x01

#define BIT1 0x02

#define BIT2 0x04

#define BIT3 0x08

#define BIT4 0x10

#define BIT5 0x20

#define BIT6 0x40

#define BIT7 0x80

//Assume SCL is port PD6, SDA is port PD7, WP is port PD5

#define m_EnE2pWrite PORTD&=~BIT5 //Allow EEPROM to read and write

#define m_DiE2pWrite PORTD|=BIT5 //EEPROM read only

#define m_I2CWritePort DDRD|=BIT7 //When I2C writes, the SDA port direction is output

#define m_I2CReadPort DDRD&=~BIT7 //When I2C reads, the SDA port direction is input

#define m_SetSCL PORTD|=BIT6 //PD6=1, SCL set

#define m_ClrSCL PORTD&=~BIT6 //PD6=0, SCL cleared to 0

#define m_SetSDA PORTD|=BIT7 //PD7=1,SDA set

#define m_ClrSDA PORTD&=~BIT7 //PD7=0, SDA is cleared to 0

#define m_SDAIn (PIND&BIT7) //SDA read in 

#define m_I2C_Delay m_delayus(5) //I2C delay, different devices may have different

#define m_I2C_StopDelay m_delayms(10) //Bus idle time before next Start is generated

 

 

 

void I2C_Start(); //Generate the start state of the I2C bus

void I2C_Write(INT8U dat); //Write 1 byte of data to the I2C bus

INT8U I2C_Read(); //Read 1 byte of data from the slave

INT8U I2C_GetAck(); //Read the slave response bit

void I2C_PutAck(INT8U ack); //The host generates an acknowledge bit or a non-acknowledge bit

void I2C_Stop(); //Generate the stop state of the I2C bus

INT8U I2C_Puts(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size); //I2C bus integrated sending function, sends multiple bytes of data to the slave

INT8U I2C_Gets(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size); //I2C bus integrated receiving function, receiving multiple bytes of data from the slave

INT8U I2C_DigitalPot(INT8U SlaveAddr,INT8U dat); //I2C bus digital potentiometer sending program


.C Files

 

void I2C_Start()

{

  m_I2CWritePort; //Port direction is output

  m_SetSDA;

  m_I2C_Delay;

  m_SetSCL;

  m_I2C_Delay;

  m_ClrSDA;

  m_I2C_Delay;

  m_ClrSCL;

  m_I2C_Delay;

}

 

void I2C_Write(INT8U dat)

{

  m_I2CWritePort; //Port direction is output

  for(INT8U t=0;t<8;t++)

  {

    if ((dat&0x80)!=0)

    {

      m_SetSDA; //SDA=1

    }

    else

    {

      m_ClrSDA; //SDA=0

    }

    m_I2C_Delay;

    dat<<=1;

    m_SetSCL; //SCK=1

    m_I2C_Delay;

    m_ClrSCL; //SCK=0

    m_I2C_Delay;

  }

}

 

INT8U I2C_Read()

{

  INT8U dat;

  m_I2CReadPort; //Port direction is input

  for(INT8U t=0;t<8;t++)

  {

    m_SetSCL; //SCK=1

dat<<=1;

    if(m_SDAIn)

{

  dat|=0x01;

}

m_ClrSCL; //SCK=0

m_I2C_Delay;

  }

  return dat;

}


INT8U I2C_GetAck()

{

  INT8U ack=0;

  m_I2CReadPort; //Port direction is input

  //Bus ready, accept response

  m_SetSDA;  

  m_I2C_Delay;

  m_SetSCL;  

  m_I2C_Delay;

  if(m_SDAIn!=0)

    ack = 1;

  m_ClrSCL;

  m_I2C_Delay;

  return ack;

}


/****************************************************** *******************************

Function: I2C_PutAck()

Function: The host generates an acknowledge bit or a non-acknowledge bit

parameter:

ack=0: the host generates an acknowledge bit; ack=1: the host generates a non-acknowledge bit

illustrate:

The host should generate an acknowledge bit after receiving each byte of data.

After receiving the last byte of data, the host should generate a non-acknowledge bit.

*************************************************** ****************************/

void I2C_PutAck(INT8U ack)

{

  m_I2CWritePort; //Port direction is output

  if(ack==0)

  {

    m_ClrSDA;

  }

  else

  {

    m_SetSDA;

  }

  m_I2C_Delay;

  m_SetSCL;    

  m_I2C_Delay;

  m_ClrSCL;    

  m_I2C_Delay;

  m_SetSDA;

}

/****************************************************** *******************************

Function: I2C_Stop()

Function: Generate the stop state of the I2C bus

illustrate:

When a rising edge appears on SDA while SCL is at a high level, the I2C bus is stopped.

Regardless of the level of SDA and SCL, this function can always correctly generate the stop state

After this function is executed, the I2C bus is in idle state

*************************************************** ****************************/

void I2C_Stop()

{

  m_I2CWritePort; //Port direction is output

  m_ClrSDA;  

  m_I2C_Delay;

  m_SetSCL;  

  m_I2C_Delay;

  m_SetSDA;

  m_I2C_StopDelay;

}


/****************************************************** *******************************

Function: I2C_Puts()

Function: I2C bus integrated sending function, sending multiple bytes of data to the slave

parameter:

SlaveAddr: slave address (7-bit pure address, excluding read/write bit, 0xxx xxxx)

SubAddr: slave subaddress

SubMod: Subaddress mode, 0 - no subaddress, 1 - single-byte subaddress, 2 - double-byte subaddress

*dat: data to be sent

Size: The number of bytes of data

return:

0: Send successfully

1: An exception occurred during the sending process

illustrate:

This function works well with all common I2C devices, whether or not they have subaddresses.

When the slave has no subaddress, the parameter SubAddr is arbitrary, and SubMod should be 0

*************************************************** ****************************/

INT8U I2C_Puts(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size)

{

  m_EnE2pWrite; //Allow EEPROM to read and write

  INT8U a[3];

  if (Size==0) return 1; // Check the length, an exception occurred during the receiving process

  a[0]=(SlaveAddr<<1); //Prepare slave address

  if (SubMod>2) SubMod = 2; //Check sub-address mode

 

  //Determine the sub-address

  switch (SubMod)

  {

  case 0:

    break;

  case 1:

    a[1]=(INT8U)(SubAddr);

    break;

  case 2:

    a[1]=(INT8U)(SubAddr >> 8);

    a[2]=(INT8U)(SubAddr);

    break;

  default:

    break;

  }


  //The master sends the slave address (a[0]), followed by the sub-address (if any) (a[1], a[2])

  I2C_Start();

  for (INT8U t=0;t<=SubMod;t++)

  {

    I2C_Write(a[t]);

    if (I2C_GetAck())

    {

      I2C_Stop();

      m_DiE2pWrite; //EEPROM read only

      return 1;

    }

  }

 

  //Host sends data

  while(Size--)

  {

    I2C_Write(*dat++);

    if (I2C_GetAck())

    {

      I2C_Stop();

      m_DiE2pWrite; //EEPROM read only

      return 1;

    }

  }

 

  //Sending completed, stop I2C bus, and return the result

  I2C_Stop();

  m_DiE2pWrite; //EEPROM read only

  return 0; //Send successfully

}


/****************************************************** *******************************

Function: I2C_Gets()

Function: I2C bus integrated receiving function, receiving multiple bytes of data from the slave

parameter:

SlaveAddr: slave address (7-bit pure address, excluding read/write bit, 0xxx xxxx)

SubAddr: slave subaddress

SubMod: Subaddress mode, 0 - no subaddress, 1 - single-byte subaddress, 2 - double-byte subaddress

*dat: save the received data

Size: The number of bytes of data

return:

0: Received successfully

1: An exception occurred during the receiving process

illustrate:

This function works well with all common I2C devices, whether or not they have subaddresses.

When the slave has no subaddress, the parameter SubAddr is arbitrary, and SubMod should be 0

*************************************************** ****************************/

INT8U I2C_Gets(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size)

{

  m_EnE2pWrite; //Allow EEPROM to read and write

  INT8U a[3];

  if(Size==0) return 1; //Check the length, an exception occurred during the receiving process

  a[0]=(SlaveAddr<<1); //Prepare slave address

  if(SubMod>2) SubMod=2; //Check sub-address mode

 

 //If it is a slave with a sub-address, the host must first send the slave address and sub-address

  if(SubMod!=0)

  {

    //Determine the sub-address

    if(SubMod==1)

    {

      a[1]=(INT8U)(SubAddr);

    }

    else

    {

      a[1]=(INT8U)(SubAddr>>8);

      a[2]=(INT8U)(SubAddr);

    }

    //Send the slave address write, then send the sub address

    I2C_Start();

    for(INT8U t=0;t<=SubMod;t++)

    {

      I2C_Write(a[t]);

      if(I2C_GetAck())

      {

        I2C_Stop();

        m_DiE2pWrite; //EEPROM read only

        return 1;

      }

    }

  }

 

  //The I2C_Start() here is a repeated start state for the slave with a sub-address

  //For a slave without a sub-address, it is a normal starting state

  I2C_Start();

  //Send slave address to read

  I2C_Write(a[0]+1);

  if(I2C_GetAck())

  {

I2C_Stop();

m_DiE2pWrite; //EEPROM read only

    return 1;

  }

 

  //Host receives data

  while(1)

  {

    *dat++ = I2C_Read();

    if(--Size==0)

    {

      I2C_PutAck(1);

      break;

    }

    I2C_PutAck(0);

  }

 

  //Receiving completed, stop I2C bus, and return the result

  I2C_Stop();

  m_DiE2pWrite; //EEPROM read only

  return 0;

}

 


/****************************************************** *******************************

Function: I2C_DigitalPot()

Function: I2C bus digital potentiometer sending program

parameter:

SlaveAddr: slave address (7-bit pure address, excluding read/write bit, 0xxx xxxx)

dat: data to be sent

return:

0: Send successfully

1: An exception occurred during the sending process

illustrate:

This function can be well adapted to common digital potentiometers, such as AD5245, AD5241

*************************************************** ****************************/

INT8U I2C_DigitalPot(INT8U SlaveAddr,INT8U dat)

{

  I2C_Start();

 

  //The host sends the slave address

  I2C_Write(SlaveAddr<<1);

  if(I2C_GetAck())

  {

    I2C_Stop();

    return 1;

  }

 

  //Host sends control word

  I2C_Write(0x00);

  if(I2C_GetAck())

  {

    I2C_Stop();

    return 1;

  }

 

  //Host sends data

  I2C_Write(dat);

  if(I2C_GetAck())

  {

    I2C_Stop();

    return 1;

  }

 

  //Receiving completed, stop I2C bus, and return the result

  I2C_Stop();

  return 0;

}


Example:

1. Write EEPROM, slave address 1010 000?, subaddress 0x00 (single byte), send data a (2 bytes)

 I2C_Puts (0x50,0, 1, &a, 2);

 

2. Read EEPROM, slave address 1010 000?, subaddress 0x00 (single byte), receive data b (2 bytes)

 I2C_Gets (0x50,0, 1, &b, 2);

 

3. Write digital potentiometer, address 0101 100?, write data 0xFF

 I2C_DigitalPot ( 0x2C, 0xFF);


I2C read and write EEPROM flow chart

Standard AVR microcontroller emulates the host program of I2C bus


Reference address:Standard AVR microcontroller emulates the host program of I2C bus

Previous article:IAR library file generation and usage
Next article:6. AVR timer/counter 1 --TC1 --input capture mode (capture external event mode)

Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号