[AVR单片机] 用AVR普通IO口模拟IIC

[复制链接]
11096|21
 楼主| 宇航3000 发表于 2011-5-19 00:31 | 显示全部楼层 |阅读模式
我现在想用普通IO口来模拟IIC协议,但我发现一个问题,因为IIC要不停的读写,那是不是每次都要改变IO口是输出还是输入啊,而51的单片机直接给IO口赋值就好了,不管是输入还是输出状态, 而AVR要先置为输入还是输出,如果是这样岂不是很麻烦,望高手指点一下
hotpower 发表于 2011-5-19 00:34 | 显示全部楼层
zhao9183 发表于 2011-5-19 22:48 | 显示全部楼层
呵呵,对啊,现成的
 楼主| 宇航3000 发表于 2011-5-20 11:38 | 显示全部楼层
我也想用现成的啊,但是我现在用的这个芯片不是完整的IIC协议啊, 读和写完全一样,只是用第一个字节的最后一位高和低来区分, 所以只能模拟一个了

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

×
andymmbbs 发表于 2011-5-20 11:44 | 显示全部楼层
要顶要顶的!!!!!!!!!!1
 楼主| 宇航3000 发表于 2011-5-20 14:48 | 显示全部楼层
多谢帮顶。
我现在用普通IO口模拟,还不成功呢
mugenwon 发表于 2011-5-21 20:28 | 显示全部楼层
今天就做了你要做的事情。以前用c来写了堆专门读IIC的代码,后来想修改来在AVR用,结果太麻烦了最后还是用硬件处理算了!
 楼主| 宇航3000 发表于 2011-5-21 23:09 | 显示全部楼层
今天就做了你要做的事情。以前用c来写了堆专门读IIC的代码,后来想修改来在AVR用,结果太麻烦了最后还是用硬件处理算了!
mugenwon 发表于 2011-5-21 20:28


最后成功了吗,还是最后也是用的AVR自己的TWI寄存器来实现的?
你认为麻烦在哪里了,我感觉AVR每次输入和输出都要重新来设置一下,不能直接读。很麻烦的
hotpower 发表于 2011-5-22 19:48 | 显示全部楼层
可以模拟,但要注意开漏
 楼主| 宇航3000 发表于 2011-5-23 10:02 | 显示全部楼层
可以模拟,但要注意开漏
hotpower 发表于 2011-5-22 19:48


怎么开漏呢, 是设置输入输出吗
 楼主| 宇航3000 发表于 2011-5-23 13:59 | 显示全部楼层
终于弄出来了,用IO口模拟的,但是最后一位不知道为什么总是为0呢
#include<avr/io.h>

#define uint unsigned int
#define uchar unsigned char
#define BIT(x) (1<<x)

//PORTA0=SCL
//PORTA1=SDA
void delay1(uint n)
{  uint i,j;
for(i=0;i<n;i++)
        for(j=0;j<367;j++);
}

void delay()
{  asm("NOP"); asm("NOP");asm("NOP"); }

void start()  //开始信号
{       
        PORTA|=BIT(1);
        delay();
        PORTA|=BIT(0);
        delay(); asm("NOP");asm("NOP");
        PORTA&=~BIT(1);
        delay(); asm("NOP");asm("NOP");
        PORTA&=~BIT(0);
    delay();

}

void stop()   //停止
{
        PORTA&=~BIT(1);
        delay();
        PORTA|=BIT(0);
        delay();asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        PORTA|=BIT(1);
        delay();asm("NOP");asm("NOP");asm("NOP");asm("NOP");
}

void respons()  //应答
{
        //uchar i;
        PORTA&=~BIT(1);
        delay();
        PORTA|=BIT(0);
        delay();asm("NOP");asm("NOP");asm("NOP");asm("NOP");
        PORTA&=~BIT(0);
        delay();

}

/*
void respons_send()  //应答
{
        uchar i;
        PORTA|=BIT(0);
        delay();
       
        while(((PORA&BIT(1))==1)&&(i<250))i++;
        PORTA&=~BIT(0);
        delay();
}
*/

void init()
{
        PORTA|=BIT(1);
        delay();
        PORTA|=BIT(0);
        delay();
}

void write_byte(uchar date)
{
        uchar i,temp;
       
  for(i=0;i<8;i++)  //要传送的数据长度为8位
    {
     if((date<<i)&0x80)
         PORTA|=BIT(1);  //判断发送位
       else  PORTA&=~BIT(1);               
      delay();
     PORTA|=BIT(0);               //置时钟线为高,通知被控器开始接收数据位
     delay(); delay();           //在此期间取走数据
     PORTA&=~BIT(0);
         delay(); delay();   
    }

    PORTA&=~BIT(0);
        delay();
        PORTA|=BIT(1);
        delay();
}

uchar read_byte()
{
        uchar i,k,temp=0;
        PORTA&=~BIT(0);
        delay();
        PORTA|=BIT(1);   //释放数据线SDA
        delay();

        for(i=0;i<8;i++)
        {
                PORTA|=BIT(0);
                delay();
                DDRA&=~BIT(1);
                PORTA|=BIT(1);
                       
                delay();
                temp=(PINA&0x02);
                k=((k<<1)|temp);
        //        DDRA|=BIT(1);

                PORTA&=~BIT(0);
                delay();       
        }

        DDRA|=BIT(1);
        delay();

        return k;
}

void write_add(uchar address,uchar date)
{
        start();
        write_byte(0x56);
        respons();
        write_byte(address);
        respons();
        write_byte(date);
        respons();
        stop();
}

uchar read_add(uchar address)
{
        uchar date;
        start();
        write_byte(0x57);
        respons();
        write_byte(address);
        respons();
        date=read_byte();
        stop();
        return date;
}

main()
{
    DDRA=0xff;
        DDRB=0xFF;


    init();
        write_add(0x03,0x3f);
        delay();
        write_add(0x00,0x41);
    delay(1);
        write_add(0x08,0xab);
        delay1(10);

        PORTB=read_add(0x08);
        delay();
//        PORTB=0XFF;

        delay();

        while(1);
}
 楼主| 宇航3000 发表于 2011-5-23 22:35 | 显示全部楼层
为什么最后一位会丢失呢,比如我写入的是0xab , 读出来就变成了0xaa , 百思不得其解
 楼主| 宇航3000 发表于 2011-5-23 23:47 | 显示全部楼层
自己搞定了,找到是哪条语句存在问题了, temp=(PINA&0x02);
                k=((k<<1)|temp);

应该改成这样: temp=(PINA&0x02);   // 读进SDA的值即PA1口
                if(temp)            // 如果读到的高电平即为真
                k=((k<<1)|0x01);    // 则将上一次的K左移一位,地位补1
                else
                k=k<<1;              // 否则地位补0
Light_David 发表于 2011-5-25 01:57 | 显示全部楼层
看到AVR的位操作,有种说不出的感觉...:@
hotpower大师所说的软I2C开漏那是一定要有滴
端口寄存器永久设置为0,用方向寄存器当端口寄存器发送数据不就OVER了...
hotpower 发表于 2011-5-26 15:56 | 显示全部楼层
楼上说对了。真双向模拟io时,要用方向做0和1的控制的,输出要恒为0的。
不知楼主明白否?
 楼主| 宇航3000 发表于 2011-5-29 22:52 | 显示全部楼层
楼上说对了。真双向模拟io时,要用方向做0和1的控制的,输出要恒为0的。
不知楼主明白否?
hotpower 发表于 2011-5-26 15:56

不是太明白~~

不过我看了一个论坛有这样写:

模拟OC结构的IIC总线的技巧:

虽然AVR大多带有硬件IIC接口,但也有需要使用软件模拟IIC的情况

可以通过使用外部上拉电阻+控制DDRx的方法来实现OC结构的IIC总线。
          IIC的速度跟上拉电阻有关,内部的上拉电阻阻值较大(Rup=20K~50K),只能用于低速的场合
          #define SDA     0    //PC0
          #define SCL     1    //PC1
          (
程序初始化设定  SDA和SCL都是 PORT=0,DDR=0)
          #define  SDA_0()   DDRA|=(1<<SDA)    //输出低电平

          #define  SDA_1()   DDRA&=~(1<<SDA)   //输入,外部电阻上拉为高电平
          #define  SCL_0()   DDRA|=(1<<SCL)    //输出低电平
          #define  SCL_1()   DDRA&=~(1<<SCL)   //输入,外部电阻上拉为高电平

          使用上面的SDA_0()/SDA_1()/SCL_0()/SCL_1()宏即可,直观,而且效率跟汇编是一样的
yangganglone 发表于 2013-12-19 13:24 | 显示全部楼层
宇航3000 发表于 2011-5-23 23:47
自己搞定了,找到是哪条语句存在问题了, temp=(PINA&0x02);
                k=((k

您好:我现在也在用avr做软件模拟iic,但是遇到了点麻烦,想向您请教一下,我的qq是971947289
yls0221 发表于 2014-4-24 10:20 | 显示全部楼层
学习一下
lantian510 发表于 2014-5-5 19:33 | 显示全部楼层
把51的I2C程序拿过来,改一下就可以呢
perry_peng 发表于 2014-5-6 08:35 | 显示全部楼层
宇航3000 发表于 2011-5-20 11:38
我也想用现成的啊,但是我现在用的这个芯片不是完整的IIC协议啊, 读和写完全一样,只是用第一个字节的最后 ...

I2C也是用地址的最低位来决定是读或写。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

30

主题

165

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部