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 的常用参数 如何增 ...
随机推荐
- 京东手机webapp商城
http://bases.wanggou.com/mcoss/mportal/show?tabid=2&ptype=1&actid=1562&tpl=3&pi=1&am ...
- 让Hibernate生成的DDL脚本自动增加注释
我们知道可以通过Hibernate对象自动生成DDL建表语句,通过PowerDesigner工具可以反向工程生成数据字典,但是在生成的DDL中一直不能写上中文的注释,这就使我们生成的数据字典不具有可用 ...
- 2015华为机试——数字基root
题目描写叙述: 求整数的Root:给定正整数,求每位数字之和;假设和不是一位数,则反复; 输入:输入随意一个或多个整数 输出:输出各位数字之和,直到和为个位数为止(输入异常,则返回-1),多行,每行相 ...
- linux下杀死进程(kill)的N种方法 【转】
转自 http://blog.csdn.net/andy572633/article/details/7211546 首先,用ps查看进程,方法如下: $ ps -ef ……smx 182 ...
- 第二天——hibernate讲完了
hibernate 逐步优化 第一步 只按照步骤来提取的 jre包导入错误 第二步 继续封装,把增删改查提取出来,同时进行代码的封装 HQL语句 be stranger in the code .be ...
- 表达式:使用API创建表达式树(4)DynamicExpression
DynamicExpression:表示动态操作.这个网上可见的资料少得可怜,但想到MVC和第三方的动态语言能在NET运行.好奇的倒腾了下 先声明两个类(有相同的方法和字段,但不是继承于同一接口的类) ...
- 利用bat批量执行脚本文件
1.读取目录文件 利用bat 的for命令读取中的sql文件 for /r %%c in (0*.sql) do echo %%c %%c 相当于变量 in() 中的为循环的范围 此句的作用是显示当前 ...
- 在MVC中写Filter时经常filterContext无法代码提示HttpContext的方法和属性的原因
需要用System.Web.Abstractions.dll HttpContextBase是在System.Web.Abstractions下的,添加对System.Web.Abstractions ...
- webServices 执行流程,(我是菜鸟,我怕谁,仅代表个人理解,欢迎各位大神们指导,不和您的胃口,请默默离开!!)
二.上图仅仅代表个人理解,下面以代码方式解释一下. (1) strtus.xml <?xml version="1.0" encoding="UTF-8" ...
- linux 脚本编写基础(一)
1. Linux 脚本编写基础 1.1 语法基本介绍 1.1.1 开头 程序必须以下面的行开始(必须方在文件的第一行): #!/bin/sh 符号#!用来告诉系统它后面的参数是用来执行该文件的程序.在 ...