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 的常用参数 如何增 ...
随机推荐
- GDB调试总结__1
该博客旨在分享IT技术心得和实际工作中遇到问题的解决方法,下面是新浪博客地址http://blog.sina.com.cn/qianyumolu,则为分享经济.行业趋势.心灵文章等,有兴趣的朋友能够踩 ...
- 对象-关系映射ORM(Object Relational Mapping)(转)
ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现 Hibernate在实现ORM功能的时候主要用到的文件有:映射类(*.java).映射文件(*.hbm.xml)和数据库配置文件 ...
- codevs 3044 矩形面积求并 (扫描线)
/* 之前一直偷懒离散化+暴力做着题 今天搞一下扫描线 自己按照线段树的一般写法写的有些问题 因为不用于以前的区间sum so 题解搬运者23333 Orz~ 去掉了打标记的过程 同时更新区间的时候先 ...
- setTimeout 方法用于在指定的毫秒数后调用函数或计算表达式
setTimeout 方法用于在指定的毫秒数后调用函数或计算表达式
- JAAS - Document
JAAS 参考文档: JAAS Reference Guide JAAS Authentication Tutorial JAAS Authorization Tutorial LoginModule ...
- Unity3D GUI学习之GUI窗口的使用
GUI还可以定义一个窗口,在窗口里面进行控件的添加: using UnityEngine; using System.Collections; public class getbutton : Mon ...
- java 字符串转int
//字符转整形 String aa = "23"; //int bb = Integer.parseInt(aa);//两种方式都是可以的 int bb = new Integer ...
- C#调用百度云存储接口上传文件
因前几日见园子里有人说可以把网站静态文件放在百度上,于是去百度开放平台看了看,发现之前那篇文章不是调的云存储接口啊... 于是自己写了个C#能调百度云存储的例子(百度云开放平台只提供php.java. ...
- IOS常用开源库
转自:http://www.csdn.net/article/2013-06-18/2815806-GitHub-iOS-open-source-projects-two/1 1. AFNetwork ...
- 安装freebsd9 出现 mountroot>怎么办
之前手欠把linux分区给删了想重装freebsd 重新进入的时候mbr提示grub信息 用PE把MBR删掉 之后再用freebsd光盘启动出现mountroot> 就用mountroot> ...