I2C的主机从机模拟
好久没有在csdn上面做笔记了,主要是最近琐碎的事情太多,乱七八糟的事情让自己不能坚定下来做自己喜欢做的事情。上了星期花了两天的时间模拟了I2C的主机和从机通信。一般都是主机模拟,从机直接用硬件I2C的,但是由于所谓的项目里面没有I2C,但是要用到I2C了,因此就不得不用I/O口去模拟I2C了。
1、I2C协议
I2C的协议相信网上已经有很多资料了,这里就不做详细介绍,只做简单说明即可。
a、I2C协议有两根总线:SDA和SCL。SDA为数据线,而SCL就是主机的时钟线。
b、I2C是主机控制从机,时钟线只能主机改变。
c、每个从机都有唯一的地址,主机通过发送从机地址来选择从机。
d、I2C开始信号:SCL为高电平的时候,SDA由高电平向低电平跳变。
e、I2C结束信号:SCL为高电平的时候,SDA由低电平向高电平跳变。
f、主机传输信号的时候,SCL为高电平的时候,传输信号,SCL为低电平的时候改变信号。
g、主机接收信号的时候,SCL为高电平的时候,接收信号。
2、如果用I/O模拟I2C的时候,一定要记住,是主机控制从机,从机根据主机SCL信号的改变而改变。
3、主机代码:
/**************************************************************************** I2C模拟条件: 1、HOST先发地址和控制命令给SLAVE; 2、地址和控制命令占一个字节; 3、字节格式: 7~2 1 0 地址 单/多字节(0/1) 读/写(1/0) 4、发送多字节时候,第一个字节是地址和控制命令、第二个字节是长度、接下来是数据 5、发送多字节时候,第一个字节是地址和控制命令、第二个字节是要发送的 ******************************************************************************/ #include "ioCC1110.h" #include "hal.h" #define SCL P1_2 #define SDA P1_3 #define IN 0 #define OUT 1 BYTE ACK_Flag = ; BYTE I2C_count;//计数器 BYTE receive_slave[] = {0x00}; //接收从机的字节 BYTE send_slave[] = {0xaa,0x55,0xbb,0x55,0xaa}; //发送字节给从机 /*初始化I2C*/ void SDA_(BYTE input) { ) //SDA输出,p1.3 P1DIR |= 0X08; else P1DIR &= 0XF7; //SDA输入,p1.3 } void SCL_(BYTE input) { ) //SCL输出,P1.2 P1DIR |= 0X04; else //SCL输入,P1.2 P1DIR &= 0XFB; } /*启动I2C工作*/ void START_I2C(void) { SDA = ; SCL = ; // Delay_us(20); //这个没有多大影响,可以不要 SCL = ; Delay_us(); //最开始50,5us太短了,不能判断,10us可以。 SDA = ; Delay_us(); //最开始50, SCL = ; Delay_us(); //最开始50,这个延时和上面的延时可以不要,但是为了SLAVE有足够时间退出中断,就加上 } /*停止I2C工作*/ void STOP_I2C(void) { // SDA_OUT; SDA = ; Delay_us();; SCL = ; Delay_us();; SDA = ; Delay_us();; SCL = ; Delay_us();; } /*收到从器件的ACK帧,用于写完一个字节后检查*/ void Receive_SLAVE_ACK(void) { SCL = ; // Delay_us(50); //这里没有必要 SDA = ; SDA_(IN); SCL = ; Delay_us(); //15us短了,经常出错 == SDA) // 若SDA=1表明非应答,置位非应答标志ACK_Flag ACK_Flag = ; SDA_(OUT); SCL = ; // Delay_us(50); //这里也没有必要 } /*主器件往从器件里写一个字节*/ void WriteByte(BYTE writedata) { //SDA_OUT; SCL = ; //SCL为低电平的时候可以改变数据状态 ;i<;i++){ )&0x01) == 0x01){//先写最高位 SDA = ; } else{ SDA = ; } // Delay_us(10); //这个可以不要 SCL = ; Delay_us(); //这个是等待SLAVE进入中断并接收数据,退出中断,15us不行,要20us writedata = writedata << ;//写完一位后将低位移到高位 SCL = ; // Delay_us(50); //这个也可以不要 } // Delay_us(50); //这个其实可以不要 SCL = ; } /*主器件从从器件里面读取一个字节*/ BYTE ReadByte(void) { BYTE TempData = ; SCL = ; ;i<;i++){ SDA = ; SDA_(IN); // Delay_us(10); //这个没有什么影响 SCL = ; Delay_us(); //这个时间不能太短,不然的话就会读错1位 TempData <<= ; == SDA) TempData |= 0x01; else TempData |= 0x00; SCL = ; } SCL = ; SDA_(OUT); // Delay_us(50); return (TempData); } void I2C_Bytes_Test(void) { /***********************写单字节正常*****************************/ #if 0 START_I2C(); WriteByte(0xa4);//写单字节的命令 Receive_SLAVE_ACK(); ){ return; } WriteByte(0xaa); Receive_SLAVE_ACK(); ){ return; } STOP_I2C(); #endif /***********************************************************/ /*****************************写多字节********************/ #if 0 START_I2C(); WriteByte(0xa6);//写字多节的命令 Receive_SLAVE_ACK(); ){ return; } WriteByte(0x05);//写多字节长度 Receive_SLAVE_ACK(); ){ return; } ;i<;i++){ //开始写多字节 WriteByte(send_slave[i]); Receive_SLAVE_ACK(); ){ return; } } STOP_I2C(); #endif /**********************************************************/ /*****************I2C读正常**************************/ START_I2C(); WriteByte(0xa5); Receive_SLAVE_ACK(); ){ return; } WriteByte(0x03); //读的长度 Receive_SLAVE_ACK(); ){ return; } ;i<;i++){ receive_slave[i] = ReadByte(); Receive_SLAVE_ACK(); ) return; // UART1_Send_BYTE(receive_slave[i]); } STOP_I2C(); LED0 = ; UART1_Send_String(receive_slave,); /**********************************************************/ }
4、从机是在中断里面接收的,每次SCL上升沿的时候进入中断。代码:
#define START_STATE 0 //开始 #define CONTROL_STATE 1 //控制命令 #define ACK_STATE 2 //ACK应答 #define NOACK_STATE 3 //非ACK应答 #define WRITE_STATE 4 //写从机 #define READ_STATE 5 //读从机 #define STOP_STATE 6 //停止 BYTE STATE = ; BYTE address = ; //接收到的从机地址 BYTE count = ; //接收到一位计数,产生一个字节的计数 BYTE receive_BYTE; //从机接收主机单个字节 BYTE receive_buf[] = {0x00}; //从机接收主机数据的buffer BYTE write_buf[] = {0x47,0x55,0x11};//从机发送数据给主机的buffer BYTE receive_len = ; //从接接收主机多字节时的长度 BYTE send_len = ; //从机要发送给主机字节的长度 BYTE write_end = ; //主机是否对从机写完 BYTE read_end = ; //主机是否接收完从机发送的数据 BYTE length_ACK = ; //从机是否接收到了要发送数据给主机的长度 BYTE read_num = ; BYTE Temp = ; void PORT1_InterruptInit(void) { EA = ; P1DIR &= 0XFB;//P1.2输入,scl IEN2 |= 0X10;//P1中断使能,scl P1IEN |= 0x04;//P1.2中断使能,scl PICTL &= ~0X02; //P1上升沿中断,0:rising edge P1IFG &= ~0x04; } #pragma vector = P1INT_VECTOR __interrupt void P1_ISR(void) { /* if(P1IFG>0) //按键中断 { P1IFG = 0; LED1 = ~LED1; } P1IF = 0; //清中断标志 */ ) { P1IFG = ; switch(STATE){ case START_STATE: SDA_(IN); if(SDA){ while(SDA); while(SCL); STATE = CONTROL_STATE; } break; case CONTROL_STATE: address <<= ; == SDA) address |= 0x01; else address |= 0x00; count++; == count){ count = ; if((address & 0xfc) == 0xa4){ STATE = ACK_STATE; } else STATE = NOACK_STATE; } break; case ACK_STATE: SDA_(OUT);//SDA设置为输出 SDA = ; ) || (read_end == ) ){ //已经读完或者写完 STATE = STOP_STATE; write_end = ; read_end = ; } else{ if((address & 0x01) == 0x00) //主机写SLAVE STATE = WRITE_STATE; else{ STATE = READ_STATE; // UART1_Send_BYTE(STATE); } } break; case NOACK_STATE: SDA_(OUT); SDA = ; address = ; STATE = START_STATE; break; case WRITE_STATE: //主机写从机 SDA_(IN); //这里将SDA置为输入,是因为发送ACK时候置为输出了 if((address & 0x02) == 0x00){ //写单字节 receive_BYTE <<= ; == SDA) receive_BYTE |= 0X01; else receive_BYTE |= 0X00; count++; == count){ STATE = ACK_STATE; // UART1_Send_BYTE(receive_BYTE); count = ; write_end = ; } } else{ receive_buf[receive_len] <<= ; == SDA) receive_buf[receive_len] |= 0x01; else receive_buf[receive_len] |= 0x00; count++; == count){ //接收到了8个字节 count = ; receive_len++; UART1_Send_BYTE(receive_buf[receive_len-]); ]+)){ //这里+1是因为要先写长度 write_end = ; receive_len = ; } STATE = ACK_STATE; } } break; case READ_STATE: //主机读从机 if(!length_ACK){ //主机发送过从机长度 SDA_(IN); send_len <<= ; == SDA) send_len |= 0x01; else send_len |= 0x00; count++; == count){ length_ACK = ; //主机发送长度给从机 LED0 = ; // UART1_Send_BYTE(send_len); count = ; STATE = ACK_STATE; } } else{ SDA_(OUT); Temp = write_buf[read_num]; Temp <<= count; UART1_Send_BYTE(Temp); if((Temp & 0x80) == 0x80) SDA = ; else SDA = ; count++; ){ //移了7位,正好读一个字节 count = ; read_num++; if(read_num >= send_len){//读完了所有数据 read_num = ; read_end = ; length_ACK = ; //将接收长度置0 } STATE = ACK_STATE; } } break; case STOP_STATE: SDA_(IN); while(!SDA); address = ; // receive_BYTE = 0; receive_len = ; send_len = ; LED1 = ~LED1; STATE = START_STATE; break; default: break; } } P1IF = ; }
I2C的主机从机模拟的更多相关文章
- 如何通过WiFi来进行Android的真机模拟
我们知道,在使用模拟机模拟的时候会出现较多的问题,所以如果有一部Android手机的话进行真机模拟是极好的. 准备工作: 第一种方法:使用数据线,具体操作百度.略(非WIFI操作的真机模拟) 第二方法 ...
- I2C总线协议的软件模拟实现方法
I2C总线协议的软件模拟实现方法 在上一篇博客中已经讲过I2C总线通信协议,本文讲述I2C总线协议的软件模拟实现方法. 1. 简述 所谓的I2C总线协议的软件模拟实现方法,就是用软件控制GPIO的输入 ...
- MS10-087微软OFFICE漏洞【参考拿机模拟】
[声明:以下测试仅仅为了学习用途,模仿尝试与博主无关] 工 具:metasploit 目标机:windows xp sp3 步骤: 1.使用msf创建特殊代码的doc文档 命令: msfconso ...
- vb上位机模拟电压监测系统
vb作为一种古老的语言,在工作中已经用不到了,但这门语言也是我在校期间研究比较多的一种,基本的通讯,数据库,界面等模块已经比较了解,马上要进单位实习了,研究的是电机的变频器,软件这块,希望在以后的工作 ...
- XCode请求数据中接收类型的后台与前台处理(本机模拟)
Xcode报错问题如下: 解决办法如下: 0x1 ->请求数据时加上缺少的类型 AFHTTPSessionManager *manager = [selfAFHTTPSessionMan ...
- 街机模拟.samsho2
1.sdyrugal https://www.douyu.com/599516 2.红中哥:西瓜视频 搜索 "红中哥游戏解说" 我搜索到的地址:https://live.ixig ...
- redis的主从复制,以及使用sentinel自动处理主机宕机问题,集群
以下部分想看懂得有一定的redis基础,且步骤是连贯的,错一步都不行.redis运行多个实例,不懂得自行百度. 1. redis主从同步 原理: 从服务器向主服务器发送 SYNC 命令. 接到 SYN ...
- I2C总线以及GPIO模拟I2C
·I2C总线的一些特征: 1. 只要求两条总线,一条串行数据线(SDA),一条串行时钟线(SCL) 2. 两个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机系统软件设定的地址:主机可 ...
- MySQL 系列(四) 主从复制、读写分离、模拟宕机、备份恢复方案生产环境实战
本章内容: 主从复制 简介原理 备份主库及恢复从库,配置从库生效 读写分离 如果主宕机了,怎么办? 双主的情况 MySQL 备份及恢复方案 备份单个及多个数据库 mysqldump 的常用参数 如何增 ...
随机推荐
- HUNNU--湖师大--11409--Skill
Skill Yasser is an Egyptian coach; he will be organizing a training camp in Jordan. At the end of ca ...
- linux安装总结(亲测)
一:磁盘分区情况 NTFS,FAT32 这是两种常用的磁盘格式 windows7自带的磁盘工具也可以分出上面的格式,用软件也可以.但是FAT32最大的复制文件进去的限度时4G. EXT3只能用软件才能 ...
- [转] npm 模块安装机制简介
npm 是 Node 的模块管理器,功能极其强大.它是 Node 获得成功的重要原因之一. 正因为有了npm,我们只要一行命令,就能安装别人写好的模块 . $ npm install 本文介绍 npm ...
- 一个很简单的SqlServer生成常用C#语句工具的诞生
前言: 这个文章只要是记录一下,这个工具的诞生过程.作用.其中的技术实在是太简单可以说没有什么技术~主要是锻炼一下写文章的能力! 正文: 在开发项目的时,常常会要维护或变更一些老项目,涉及到简单的几张 ...
- ssh连接超时问题解决方案
方法一: 1.设置服务器向SSH客户端连接会话发送频率和时间 #vi /etc/ssh/sshd_config,添加如下两行ClientAliveInterval 60ClientAliveCount ...
- Socket.IO 概述
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/3826251.html ...
- 关于“SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatasource' 的访问 ”
原因:在从远程服务器复制数据到本地时出现“SQL Server 阻止了对组件 'Ad Hoc Distributed Queries' 的 STATEMENT'OpenRowset/OpenDatas ...
- 利用抽象、多态实现无反射的绿色环保ORM框架
最近一直在忙新公司的基础库建设,对系统架构.开发框架及快速开发平台的设计实施都积累了一定的实践经验. 一般的中小型的软件开发公司,如果按照技术储备来衡量软件项目的技术含量的评定依据是可行的.但如果光是 ...
- godaddy_关于产品退款
You're chatting with Danny.Danny - Thank you for contacting live chat. My name is Danny. How can I a ...
- Sprite Kit教程:初学者
作者:Ray Wenderlich 原文出处:点击打开链接 http://www.raywenderlich.com/42699/spritekit-tutorial-for-beginners 转自 ...