标准AVR单片机模拟I2C总线的主机程序

发布者:ShiningSmile最新更新时间:2017-11-26 来源: eefocus关键字:AVR单片机  模拟I2C总线  主机程序 手机看文章 扫描二维码
随时随地手机看文章

.H文件预处理

 

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  //晶振频率,单位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

//假设SCL是端口PD6,SDA是端口PD7,WP是端口PD5

#define m_EnE2pWrite   PORTD&=~BIT5  //允许EEPROM读写

#define m_DiE2pWrite   PORTD|=BIT5    //EEPROM只读

#define m_I2CWritePort  DDRD|=BIT7    //I2C写时,SDA端口方向为输出

#define m_I2CReadPort  DDRD&=~BIT7   //I2C读时,SDA端口方向为输入

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

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

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

#define m_ClrSDA  PORTD&=~BIT7       //PD7=0,SDA清0

#define m_SDAIn  (PIND&BIT7)          //SDA读入 

#define m_I2C_Delay  m_delayus(5)      //I2C延时,不同器件可能不同

#define m_I2C_StopDelay  m_delayms(10)  //在下一次产生Start之前,总线空闲时间

 

 

 

void I2C_Start();//产生I2C总线的起始状态

void I2C_Write(INT8U dat);//向I2C总线写1个字节的数据

INT8U I2C_Read();//从从机读取1个字节的数据

INT8U I2C_GetAck();//读取从机应答位

void I2C_PutAck(INT8U ack);//主机产生应答位或非应答位

void I2C_Stop();//产生I2C总线的停止状态

INT8U I2C_Puts(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size);//I2C总线综合发送函数,向从机发送多个字节的数据

INT8U I2C_Gets(INT8U SlaveAddr,INT16U SubAddr,INT8U SubMod,INT8U *dat,INT16U Size);//I2C总线综合接收函数,从从机接收多个字节的数据

INT8U I2C_DigitalPot(INT8U SlaveAddr,INT8U dat);//I2C总线数字电位器发送程序


.C文件

 

void I2C_Start()

{

  m_I2CWritePort;  //端口方向为输出

  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;  //端口方向为输出

  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;  //端口方向为输入

  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;  //端口方向为输入

  //总线准备,接受应答

  m_SetSDA;  

  m_I2C_Delay;

  m_SetSCL;  

  m_I2C_Delay;

  if(m_SDAIn!=0)

    ack = 1;

  m_ClrSCL;

  m_I2C_Delay;

  return ack;

}


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

函数:I2C_PutAck()

功能:主机产生应答位或非应答位

参数:

ack=0:主机产生应答位; ack=1:主机产生非应答位

说明:

主机在接收完每一个字节的数据后,都应当产生应答位

主机在接收完最后一个字节的数据后,应当产生非应答位

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

void I2C_PutAck(INT8U ack)

{

  m_I2CWritePort;  //端口方向为输出

  if(ack==0)

  {

    m_ClrSDA;

  }

  else

  {

    m_SetSDA;

  }

  m_I2C_Delay;

  m_SetSCL;    

  m_I2C_Delay;

  m_ClrSCL;    

  m_I2C_Delay;

  m_SetSDA;

}

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

函数:I2C_Stop()

功能:产生I2C总线的停止状态

说明:

SCL处于高电平期间,当SDA出现上升沿时停止I2C总线

不论SDA和SCL处于什么电平状态,本函数总能正确产生停止状态

本函数执行后,I2C总线处于空闲状态

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

void I2C_Stop()

{

  m_I2CWritePort;  //端口方向为输出

  m_ClrSDA;  

  m_I2C_Delay;

  m_SetSCL;  

  m_I2C_Delay;

  m_SetSDA;

  m_I2C_StopDelay;

}


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

函数:I2C_Puts()

功能:I2C总线综合发送函数,向从机发送多个字节的数据

参数:

SlaveAddr:从机地址(7位纯地址,不含读写位,0xxx xxxx)

SubAddr:从机的子地址

SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址

*dat:要发送的数据

Size:数据的字节数

返回:

0:发送成功

1:在发送过程中出现异常

说明:

本函数能够很好地适应所有常见的I2C器件,不论其是否有子地址

当从机没有子地址时,参数SubAddr任意,而SubMod应当为0

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

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

{

  m_EnE2pWrite;     //允许EEPROM读写

  INT8U a[3];

  if (Size==0) return 1;  //检查长度,在接收过程中出现异常

  a[0]=(SlaveAddr<<1);  //准备从机地址

  if (SubMod>2) SubMod = 2;  //检查子地址模式

 

  //确定子地址

  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;

  }


  //主机发送从机地址(a[0]),接着发送子地址(如果有子地址的话)(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只读

      return 1;

    }

  }

 

  //主机发送数据

  while(Size--)

  {

    I2C_Write(*dat++);

    if (I2C_GetAck())

    {

      I2C_Stop();

      m_DiE2pWrite; //EEPROM只读

      return 1;

    }

  }

 

  //发送完毕,停止I2C总线,并返回结果

  I2C_Stop();

  m_DiE2pWrite; //EEPROM只读

  return 0;//发送成功

}


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

函数:I2C_Gets()

功能:I2C总线综合接收函数,从从机接收多个字节的数据

参数:

SlaveAddr:从机地址(7位纯地址,不含读写位,0xxx xxxx)

SubAddr:从机的子地址

SubMod:子地址模式,0-无子地址,1-单字节子地址,2-双字节子地址

*dat:保存接收到的数据

Size:数据的字节数

返回:

0:接收成功

1:在接收过程中出现异常

说明:

本函数能够很好地适应所有常见的I2C器件,不论其是否有子地址

当从机没有子地址时,参数SubAddr任意,而SubMod应当为0

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

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

{

  m_EnE2pWrite;     //允许EEPROM读写

  INT8U a[3];

  if(Size==0) return 1;//检查长度,在接收过程中出现异常

  a[0]=(SlaveAddr<<1);//准备从机地址

  if(SubMod>2) SubMod=2;//检查子地址模式

 

 //如果是有子地址的从机,则主机要先发送从机地址和子地址

  if(SubMod!=0)

  {

    //确定子地址

    if(SubMod==1)

    {

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

    }

    else

    {

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

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

    }

    //发送从机地址写,接着发送子地址

    I2C_Start();

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

    {

      I2C_Write(a[t]);

      if(I2C_GetAck())

      {

        I2C_Stop();

        m_DiE2pWrite; //EEPROM只读

        return 1;

      }

    }

  }

 

  //这里的I2C_Start()对于有子地址的从机是重复起始状态

  //对于无子地址的从机则是正常的起始状态

  I2C_Start();

  //发送从机地址读

  I2C_Write(a[0]+1);

  if(I2C_GetAck())

  {

I2C_Stop();

m_DiE2pWrite; //EEPROM只读

    return 1;

  }

 

  //主机接收数据

  while(1)

  {

    *dat++ = I2C_Read();

    if(--Size==0)

    {

      I2C_PutAck(1);

      break;

    }

    I2C_PutAck(0);

  }

 

  //接收完毕,停止I2C总线,并返回结果

  I2C_Stop();

  m_DiE2pWrite; //EEPROM只读

  return 0;

}

 


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

函数:I2C_DigitalPot()

功能:I2C总线数字电位器发送程序

参数:

SlaveAddr:从机地址(7位纯地址,不含读写位,0xxx xxxx)

dat:要发送的数据

返回:

0:发送成功

1:在发送过程中出现异常

说明:

本函数能够很好地适应常见的数字电位器,如AD5245,AD5241

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

INT8U I2C_DigitalPot(INT8U SlaveAddr,INT8U dat)

{

  I2C_Start();

 

  //主机发送从机地址

  I2C_Write(SlaveAddr<<1);

  if(I2C_GetAck())

  {

    I2C_Stop();

    return 1;

  }

 

  //主机发送控制字

  I2C_Write(0x00);

  if(I2C_GetAck())

  {

    I2C_Stop();

    return 1;

  }

 

  //主机发送数据

  I2C_Write(dat);

  if(I2C_GetAck())

  {

    I2C_Stop();

    return 1;

  }

 

  //接收完毕,停止I2C总线,并返回结果

  I2C_Stop();

  return 0;

}


使用范例:

1.  写EEPROM,从机地址1010 000?,子地址0x00(单字节),发送数据a (2字节)

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

 

2. 读EEPROM,从机地址1010 000?,子地址0x00(单字节),接受数据b (2字节)

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

 

3.  写数字电位器,地址0101 100?,写入数据0xFF

 I2C_DigitalPot ( 0x2C, 0xFF);


I2C读写EEPROM流程图

标准AVR单片机模拟I2C总线的主机程序


关键字:AVR单片机  模拟I2C总线  主机程序 引用地址:标准AVR单片机模拟I2C总线的主机程序

上一篇:IAR库文件生成及使用方法
下一篇:6. avr定时器/计数器1 --TC1 --输入捕捉模式 (捕获外部事件模式)

推荐阅读最新更新时间:2024-03-16 15:47

SHT11温湿度传感器AVR单片机程序
#include shtxx.h void shtxx_init(void) { shtxx_temp = shtxx_humi = 0; SHTXX_ SCK _LOW(); SHTXX_DAT_1(); shtxx_reconnect(); } void shtxx_reconnect(void) { SHTXX_DAT_1(); SHTXX_SCK_LOW(); for(uint8 i=0; i 9; i++) { SHTXX_SCK_HIGH(); SHTXX_SCK_LOW(); } SHTXX_START(); } uint8 shtxx_ SOF trst(voi
[单片机]
avr单片机模拟比较器初始化配置及说明
avr模拟比较器对正极 AIN0 的值与负极 AIN1 的值进行比较。当 AIN0 上的电压比负极 AIN1 上的电压要高时,模拟比较器的输出 ACO 即置位。比较器的输出可用来触发定时器 / 计 数器 1 的输入捕捉功能。此外,比较器还可触发自己专有的、独立的中断。用户可以选择 比较器是以上升沿、下降沿还是交替变化的边沿来触发中断。 /* 特殊功能 IO 寄存器- SFIOR 7 6 5 4 3 2 1 0 ADTS2 ADTS1 ADTS0 ACME PUD PSR2 PSR10 Bit 3 ACME: 模拟比较器多路复用器使能
[单片机]
AVR单片机基本硬件线路设计
话说AVR单片机可以裸跑,为什么?   其实,这句话是我自己给学校师弟师妹们讲解AVR时说的,其意思是说:AVR单片机可以不需要任何外部电路就可以跑起来。   不过,实际上,AVR单片机有时还是需要加些外部电路的,why,下面告诉您。。。   ● AVR复位电路的设计   与传统的51单片机相比,AVR单片机内置复位电路,并且在熔丝位里,可以控制复位时间,所以,AVR单片机可以不设外部上电复位电路,依然可以正常复位,稳定工作。   若是系统需要设置按键复位电路,那么注意,AVR单片机是低电平复位,如下图,设计按键复位电路:   ● AVR晶振电路的设计   与传统的51单片机相比,AVR单片机内置RC振荡电路。出厂时,未进
[单片机]
<font color='red'>AVR单片机</font>基本硬件线路设计
Arduino的互动产品平台创新设计
引言 Arduino是一系列基于单片机的人机互动产品开发平台,由于其具有高度的模块化特点,有时也叫它“电子积木”。它的硬件平台是开放的,任何人都可以在Arduino官方网站上下载最新的PCB设计进行复制。Arduino的硬件平台包括基于AVR单片机的主控制电路板,以及大量的各式输入/输出电子模块。输入/输出模块包括开关输入模块、温度压力传感器输入模块、超声测距传感器输入模块、各类显示输出模块、电机控制模块等,甚至还有以太网接入模块。由于Arduino具有丰富易用的模块,已经在各类机电创新设计比赛中得到广泛应用。在软件方面,Arduino有一个属于自己的基于Eclipse的IDE软件开发环境,开发语言采用类C++语言的高级语言,
[单片机]
AVR单片机定时器1 CTC模式A实验程序
/*AVR定时器1CTC模式A实验*/ #include iom16v.h #define uchar unsigned char #define uint unsigned int #define set_bit(a,b) a|=(1 b) #define clr_bit(a,b) a&=(1 b) #define get_bit(a,b) a&(1 b) uint i; #pragma interrupt_handler time1ctc_a_isr:7 void time1ctc_a_isr()//定时计数器1CTC中断A通道 { PORTC^=0x20;//输出方波T=8*2=16毫秒 } #pragma inter
[单片机]
AVR单片机驱动NOKIA3310的示例程序
#include mega48.h #include delay.h #include nokia3310.h void main(void) { PORTB&=209; DDRB|=46; //设置单片机的4个LCD引脚输出0 while(1) { lcd_init(); //lcd初始化 lcd_cls(); //清屏,光标回位 lcd_gotoxy(16,2); //光标定位到第16列,第1行(最上面是0行)
[单片机]
超实用的两种调试AVR单片机的方法
对于长期玩单片机的朋友都知道,程序运行BUG在所难免,重复性的调试过程永远是痛苦的,加入你对所使用的IDE应用还不是很熟悉,对于他的在线仿真调试工具使用不是很顺手,那么下边的方法可能会帮到你哦。 为了可以更好地帮助到哪些处于困难阶段的朋友,我特此针对于通用型单片机和AVR系列单片机的特点,总结了两种可以调试程序的方法,仅供于大家参考使用! If you have a better way, please leave a comment below. 方法一:通用方法:想知道程序执行到那里或者是不是到达了这里,可以在该处加上流水灯的闪烁程序。一目了然。代码实现方法如下: 开头定义一下 #define DEBUG_HE
[单片机]
8位共阳数码管74HC595芯片AVR单片机控制 proteus仿真及源码
刚入门AVR单片机一段时间,感觉资料很少,所以进度很慢,刚才百度到这里来,现上传一个数码管的程序来和大家分享,高手就跳过吧,初学可以下载来做参考 proteus仿真原理图: 单片机源程序: /* * smg8_avr.h * * Created: 2017/3/16 1:54:20 * Author: lyl */ #ifndef SMG8_AVR_H_ #define SMG8_AVR_H_ #include lyl_avr.h //数码管端口定义 #define smgPORT PORTA #define smgPORT_DDR DDRA #define DS PA5//串行数据输入端口 #define S
[单片机]
8位共阳数码管74HC595芯片<font color='red'>AVR单片机</font>控制 proteus仿真及源码
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved