xiaob135 发表于 2014-5-11 15:41:54

发一个IO模拟I2C从机的代码吧

本帖最后由 xiaob135 于 2014-5-11 16:22 编辑

今天下了一天雨,无聊写点代码吧。
发现网上都是io口模拟i2c主机的代码,很少有模拟i2c从机的。所以写一个贡献出来。对于学习i2c的时序还是挺有帮助的。
需要两个带中断的io口,必须支持上升沿和下降沿中断。


typedef enum
{
                I2C_SLAVE_IDLE,
                I2C_SLAVE_ADD,//write iic add
                I2C_SLAVE_REG,//write the register add
                I2C_SLAVE_WRITE,//master write and slave read
                I2C_SLAVE_READ,//master read and slave write
                I2C_SLAVE_BUSY
}e_I2C_SLAVE_MODE;

static e_I2C_SLAVE_MODE                i2c_slave_mode = I2C_SLAVE_IDLE;
static unsigned char I2C_DATA_TEMP = 0;
static unsigned char i2c_slave_reg_p = 0;//the register add
static unsigned char i2c_slave_data_p = 0;//


void i2c_slave_scl_h( void )
{
        I2C_SDA_INT_EN();
        switch( i2c_slave_mode )
        {
                case I2C_SLAVE_ADD:
                case I2C_SLAVE_REG:
                case I2C_SLAVE_WRITE:
                        I2C_DATA_TEMP <<= 1;
                        if( I2C_SDA_IN() )
                                I2C_DATA_TEMP ++;
                        i2c_slave_data_p++;
                        break;               
                default:break;
        }
}

void i2c_slave_scl_l( void )
{
        I2C_SDA_INT_DIS();
       
        if( i2c_slave_data_p > 8 )
        {
                i2c_slave_data_p = 0;
                I2C_SDA_H();//end ack;
                return;
        }
       
        I2C_SCL_L();//slow the i2c speed
        switch( i2c_slave_mode )
        {
                case I2C_SLAVE_ADD:
                        if( i2c_slave_data_p == 8 )
                        {
                                        if( I2C_DATA_TEMP >> 1 == I2C_ADD )
                                        {
                                                I2C_SDA_L();//ack
                                                if( I2C_DATA_TEMP & 0x01 )//read
                                                {
                                                        i2c_slave_mode = I2C_SLAVE_READ;
                                                }
                                                else
                                                        i2c_slave_mode = I2C_SLAVE_REG;//write regster add
                                        }
                                        else
                                                i2c_slave_mode = I2C_SLAVE_BUSY;//nack
                        }
                        break;
                case I2C_SLAVE_REG:
                        if( i2c_slave_data_p == 8 )
                        {
                                        I2C_SDA_L();//ack
                                        i2c_slave_reg_p = I2C_DATA_TEMP;
                                        i2c_slave_mode = I2C_SLAVE_WRITE;
                        }
                        break;
                case I2C_SLAVE_WRITE:
                        if( i2c_slave_data_p == 8 )
                        {//
                                        if( I2C_SLAVE_WRITE_BYTE( i2c_slave_reg_p++, I2C_DATA_TEMP ) )
                                        {
                                                        I2C_SDA_L();
                                        }
                                        else
                                        {
                                                        i2c_slave_mode = I2C_SLAVE_BUSY;
                                        }
                        }
                        break;
                case I2C_SLAVE_READ:
                        if( i2c_slave_data_p < 8 )
                        {
                                        if( I2C_DATA_TEMP & 0x80 )//output a bit
                                                I2C_SDA_H();
                                        else
                                                I2C_SDA_L();               
                                        I2C_DATA_TEMP <<= 1;
                                        i2c_slave_data_p++;
                        }
                        else if( i2c_slave_data_p == 8 )
                        {
                                        if( I2C_SLAVE_READ_BYTE(i2c_slave_reg_p++, &I2C_DATA_TEMP) )
                                        {
                                                        I2C_SDA_L();
                                        }
                                        else
                                        {
                                                        i2c_slave_mode = I2C_SLAVE_BUSY;
                                        }
                        }
                        break;
                        default:break;
        }
        I2C_SCL_H();
}

void i2c_slave_sda_h( void )
{
                if( I2C_SCL_IN() )
                {
                        I2C_SCL_INT_DIS();
                        i2c_slave_mode = I2C_SLAVE_IDLE;
                }
}

void i2c_slave_sda_l( void )
{
        switch( i2c_slave_mode )
        {
                case I2C_SLAVE_IDLE:
                        if( I2C_SCL_IN() )
                        {
                                I2C_SCL_INT_EN();
                                i2c_slave_mode = I2C_SLAVE_ADD;
                                i2c_slave_data_p = 0;
                        }
                        break;
                default:break;
        }
}

liyang0727 发表于 2014-5-11 15:57:28

nicemark

qiushui_007 发表于 2014-5-11 16:28:47

I2C从机, 在何处应用较多?

xiaob135 发表于 2014-5-11 16:32:08

qiushui_007 发表于 2014-5-11 16:28
I2C从机, 在何处应用较多?

单片机做从机的时候,或者两个单片机互相通信的时候。串口是字符型的通信,i2c是数据块型的通信,更方便一些。

jiezhi3200 发表于 2014-5-11 16:40:44

不错,赞一个。

596142041 发表于 2014-5-11 21:50:27

这思路不错啊!

xiaoguo_nihao 发表于 2014-5-11 21:58:16

谢谢分享,看上去很好。先收藏了

wintelboy 发表于 2014-5-11 23:46:59

太好了,对我正好有用!

fengyunyu 发表于 2014-5-12 06:10:50

xiaob135 发表于 2014-5-11 16:32
单片机做从机的时候,或者两个单片机互相通信的时候。串口是字符型的通信,i2c是数据块型的通信,更方便 ...

串口是字符型的通信,i2c是数据块型的通信,这个如何理解?

craigtao 发表于 2014-5-12 08:45:16

我记得以前学习51单片机的时候,就是模拟的,让人很不爽的就是i2c的时序了,当时是第一次接触,头晕,{:lol:}

Feeling_MCU 发表于 2014-5-12 09:16:46

这命名方式看得头大

xiaob135 发表于 2014-5-12 09:18:15

craigtao 发表于 2014-5-12 08:45
我记得以前学习51单片机的时候,就是模拟的,让人很不爽的就是i2c的时序了,当时是第一次接触,头晕,{:lol ...

你当时用的肯定是i2c的主模式。主模式和从模式的程序结构是完全不一样的。

fengyunyu 发表于 2014-5-12 09:18:44

串口是字符型的通信,i2c是数据块型的通信,这个如何理解?

xiaob135 发表于 2014-5-12 09:21:07

fengyunyu 发表于 2014-5-12 06:10
串口是字符型的通信,i2c是数据块型的通信,这个如何理解?

就是说从应用层的角度来讲,串口是一个字节一个字节的传送,需要自己编写一个通信协议。
i2c每次读取、写入都是先写地址,再写数据。这样应用层就可以直接进行数据交流,而不需要自己再写通信协议。

huaohui666666 发表于 2014-5-12 12:20:14

思路不错,MARK!

craigtao 发表于 2014-5-12 13:13:36

哦,是嘛。那看来这个是个不错的资料啊,。谢谢共享

xiaob135 发表于 2014-5-13 13:21:18

BUG修正版。在单片机上实际测试通过。

mhw 发表于 2014-5-13 13:35:25

不错。用在什么单片机上,主频多少,有没有测试过主机SCK最高速率多少?
去年做过一个产品,51单片机,处理起来很费时。最后没办法,检测到起始位后,关闭中断,直接用nop的方式来延时,直到通信结束退出。
主机SCK速率不能超过30K。

zhang3lu 发表于 2014-6-18 16:35:42

先顶了,楼主能不能发个应用方面的程序看看

tigerman520 发表于 2014-6-20 16:30:27

楼主测试过吗,有实际使用过吗?

BCE312 发表于 2014-7-19 09:45:41

很实用的程序,谢谢分享。

chenliangliang_ 发表于 2015-7-28 17:51:52

看着驱动很整洁,但是不知道怎么应用{:sweat:}

xld826 发表于 2015-7-29 09:32:53

不错,谢谢

chengyang79 发表于 2015-7-29 11:29:36

这么说主从机都可以不用带硬件IIC了么?完全软件化的IIC。可以自己弄和软件协议创造一个新的更实用的接口。

常春藤联盟 发表于 2015-10-12 16:23:55

好东西,先mark一下!{:titter:}

bajie 发表于 2015-12-16 07:08:16

楼主你好,我将你的代码移植到STM32L051K6上,发现配置为开漏输出和中断后,获取的I2C电平被衰减了,去掉上升沿下降沿触发后,I2C电平又恢复了,不知曾经遇到过这样的问题没有?

chwe1112 发表于 2016-2-17 13:55:00

正需要这个,感谢楼主分析,先分析一下

syj0925 发表于 2016-3-14 09:51:02

谢谢楼主的分享,但是没怎看懂,不知道楼主是怎么实现24lcxx中的:未收到停止位,又接收到一个起始位的情况。

arvin_zhang 发表于 2016-3-14 12:30:51

IO模拟I2C从机

huangqi412 发表于 2016-3-14 14:32:03

模拟从机还没见过

guyong2012 发表于 2016-3-15 15:05:44

谢谢分享,看上去很好。

andy_huang 发表于 2016-3-15 15:42:20

谢谢分享!

guo407214944 发表于 2016-3-24 10:03:46

        /* Connect EXTI Line2 to PA2 pin */

guo407214944 发表于 2016-3-24 10:06:47

        /* Connect EXTI Line2 to PA2 pin */        /* Connect EXTI Line3 to PA3 pin */你好,PA2,和PA3接口应该连接到哪里呢,连接主机SCL,SDA口么?看你代码,应该是PA6,PA7这2个脚连接主机的SCL和SDA,代码如下,                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 ;                 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                 GPIO_Init(GPIOA, &GPIO_InitStructure);                                I2C_SCL_H();                 I2C_SDA_H();。

guo407214944 发表于 2016-3-24 10:07:14

        /* Connect EXTI Line2 to PA2 pin */        /* Connect EXTI Line3 to PA3 pin */你好,PA2,和PA3接口应该连接到哪里呢,连接主机SCL,SDA口么?看你代码,应该是PA6,PA7这2个脚连接主机的SCL和SDA,代码如下,                GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 ;                 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;                 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;                 GPIO_Init(GPIOA, &GPIO_InitStructure);                                I2C_SCL_H();                 I2C_SDA_H();

online9930 发表于 2016-5-10 11:29:21

{:handshake:}楼主公德无量啊,之前我都从没有用过模拟从机,看了楼主的代码后觉得很棒。决定要试试了。

PANGKUN 发表于 2016-5-10 18:39:44

make!make!

贰梓 发表于 2016-5-10 19:03:46

这个必须顶一下,MAKE!

52HLX 发表于 2016-5-14 16:20:23

mark 。。学习

liufabing 发表于 2016-9-2 10:57:02

谢谢分享,用到一个触摸芯片程序中,用来输出按键状态数据.

shangyu60104 发表于 2016-9-2 18:21:09

很不错哦,测试看看

pcr386 发表于 2016-9-21 20:58:24

不错收藏了。

maxking 发表于 2017-1-19 21:02:41

请问楼主,这个设置为从机,另外一个单片机设置为主机与他通讯,是否可以读取他的内容了?

maxking 发表于 2017-1-20 15:09:07

楼主这个是32bit的是吗?

wanggoals 发表于 2017-1-20 15:24:23

这个不错,其实需要用到iic从机的时候建议还是用带从机模式的MCU,例如LPC812之类的芯片,操作比较简单。

lg05128018 发表于 2017-1-20 15:27:17

IIC 单片机做从设备,软件模拟需要配合中断脚

yufanyufan77 发表于 2018-3-28 14:20:32

shangyu60104 发表于 2016-9-2 18:21
很不错哦,测试看看

哥们,这个i2c从机程序怎么用啊

zhangliang3646 发表于 2018-3-28 14:30:51

yufanyufan77 发表于 2018-3-28 14:20
哥们,这个i2c从机程序怎么用啊

现在很多单片机带硬件I2C,建议还是硬件I2C的MCU。

zqy517 发表于 2018-3-28 21:44:54

很不错的,学习下、

tanguicaicai 发表于 2018-5-7 15:25:14

正好要用到 谢谢LZ 的奉献

zhcj66 发表于 2019-9-20 11:29:31

liufabing 发表于 2016-9-2 10:57
谢谢分享,用到一个触摸芯片程序中,用来输出按键状态数据.

楼上的模拟iic 从搞定了吗?

zhcj66 发表于 2019-9-20 11:31:05

tanguicaicai 发表于 2018-5-7 15:25
正好要用到 谢谢LZ 的奉献

楼上的在项目中开始应用了?

liufabing 发表于 2019-9-20 19:16:23

zhcj66 发表于 2019-9-20 11:29
楼上的模拟iic 从搞定了吗?

只是作了样品,没有量产.
程序是参考楼主的.

zhcj66 发表于 2019-9-21 08:53:40

liufabing 发表于 2019-9-20 19:16
只是作了样品,没有量产.
程序是参考楼主的.

最后有没有调通,我有点看不懂楼主的程序

liufabing 发表于 2019-9-21 09:05:16

zhcj66 发表于 2019-9-21 08:53
最后有没有调通,我有点看不懂楼主的程序

调通了,不过我那个是汇编,更难看懂.

zhcj66 发表于 2019-9-21 10:29:13

liufabing 发表于 2019-9-21 09:05
调通了,不过我那个是汇编,更难看懂.

c的没有做过?有没有听说谁用iic从 用到项目中稳定工作的?

汇编从模式的iic

;/*------------------------------------------------------------------*/
;/* --- STC MCU International Limited -------------------------------*/
;/* --- STC 1T Series MCU Simulate I2C Slave Demo -------------------*/
;/* --- Mobile: (86)13922805190 -------------------------------------*/
;/* --- Fax: 86-755-82944243 ----------------------------------------*/
;/* --- Tel: 86-755-82948412 ----------------------------------------*/
;/* --- Web: www.STCMCU.com -----------------------------------------*/
;/* If you want to use the program or the program referenced in the*/
;/* article, please specify in which data and procedures from STC    */
;/*------------------------------------------------------------------*/

SCL BIT P1.0
SDA BIT P1.1

;----------------------------

        ORG 0

RESET:
    SETB SCL
    SETB SDA
   
    CALL I2C_WAITSTART;等待起始信号
    CALL I2C_RXBYTE   ;接收地址数据
    CLR C
    CALL I2C_TXACK      ;回应ACK
    SETB C            ;读/写 IDATA
    RRC A               ;读/写位->C
    MOV R0,A            ;地址送入R0
    JC READDATA         ;C=1(读) C=0(写)
WRITEDATA:
    CALL I2C_RXBYTE   ;接收数据
    MOV @R0,A         ;写入IDATA
    INC R0            ;地址+1
    CLR C
    CALL I2C_TXACK      ;回应ACK
    CALL I2C_WAITSTOP   ;等待停止信号
    JMP RESET
   
READDATA:
    MOV A,@R0
    INC R0
    CALL I2C_TXBYTE   ;发送IDATA数据
    CALL I2C_RXACK      ;接收ACK
    CALL I2C_WAITSTOP   ;等待停止信号
    JMP RESET

;----------------------------
;等待起始信号
;----------------------------
I2C_WAITSTART:
    JNB SCL,$       ;等待时钟->高
    JB SDA,$      ;等待数据线下降沿
    JB SCL,$      ;等待时钟->低
    RET

;----------------------------
;等待结束信号
;----------------------------
I2C_WAITSTOP:
    JNB SCL,$       ;等待时钟->高
    JNB SDA,$       ;等待数据线上升沿
    RET

;----------------------------
;发送ACK/NAK信号
;----------------------------
I2C_TXACK:
    MOV SDA,C       ;送ACK数据
    JNB SCL,$       ;等待时钟->高
    JB SCL,$      ;等待时钟->低
    SETB SDA      ;发送完成
    RET

;----------------------------
;接收ACK/NAK信号
;----------------------------
I2C_RXACK:
    SETB SDA      ;准备读数据
    JNB SCL,$       ;等待时钟->高
    MOV C,SDA       ;读取ACK信号
    JB SCL,$      ;等待时钟->低
    RET

;----------------------------
;接收一字节数据
;----------------------------
I2C_RXBYTE:
    MOV R7,#8       ;8位计数
RXNEXT:
    JNB SCL,$       ;等待时钟->高
    MOV C,SDA       ;读取数据口
    RLC A         ;保存数据
    JB SCL,$      ;等待时钟->低
    DJNZ R7,RXNEXT;收下一位
    RET

;----------------------------
;发送一字节数据
;----------------------------
I2C_TXBYTE:
    MOV R7,#8       ;8位计数
TXNEXT:
    RLC A         ;移出数据位
    MOV SDA,C       ;数据送数据口
    JNB SCL,$       ;等待时钟->高
    JB SCL,$      ;等待时钟->低
    DJNZ R7,TXNEXT;送下一位
    RET

;----------------------------

        END

hsc0588 发表于 2019-10-17 16:42:43

zhcj66 发表于 2019-9-21 08:53
最后有没有调通,我有点看不懂楼主的程序

楼主的程序是有bug的,但楼主说验证通过了的,我就很纳闷了。{:lol:}

rqiang 发表于 2019-10-17 16:54:36

zhcj66 发表于 2019-9-21 10:29
c的没有做过?有没有听说谁用iic从 用到项目中稳定工作的?

汇编从模式的iic


STC这个DEMO一直在死等,难道不干其他事情吗

diego2003 发表于 2019-10-19 00:24:11

io模拟i2c从机

jim166783 发表于 2019-10-19 20:33:03

楼主 我在尝试不用中断 使用定时器的方式来实现 目前还未调通 不知道可不可行

ibmx311 发表于 2019-10-21 06:26:44

这个帖子让我很深刻,其中的关键词是楼主开贴的第一句。从此以后的很多年,我要写代码一定要等到天降大雨

dw_yoghourt 发表于 2019-12-22 21:12:35

在scl的下降沿中断中,开头的I2C_SCL_L();//slow the i2c speed和结尾的 I2C_SCL_H()是干嘛的,sck的速率不是主机决定的吗。

xyz.543 发表于 2019-12-23 03:14:59

这已超过五年以上的老帖又给挖出土了!呵呵~

zcf287 发表于 2021-3-25 14:48:29

mark!!!!!!

Wukupeng 发表于 2022-5-19 11:15:19

io模拟i2c从机

maxking 发表于 2022-11-4 09:39:06

楼主能否发个完整的例程上来,这个实在看不懂了。
页: [1]
查看完整版本: 发一个IO模拟I2C从机的代码吧