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 的常用参数 如何增 ...
随机推荐
- Qt数据库sqlite总结
QSqlDatabase类实现了数据库连接的操作QSqlQuery类用来执行SQL语句QSqlRecord类封装数据库所有记录QSqlRelationalTableModelQSqlQueryMo ...
- Java基础知识强化之集合框架笔记34:List练习之集合的嵌套遍历
1. 需求: 我们班有学生,每一个学生是不是一个对象.所以我们可以使用一个集合表示我们班级的学生.ArrayList<Student> 但是呢,我们旁边是不是还有班级,每个班级是不是也是一 ...
- javascript新的原生态API
以下是最新的w3c标准的javascript,目前支持运行在firefox, chrome,IE9以上版本的浏览器 参考资料:https://developer.mozilla.org/ru/docs ...
- 9.26 noip模拟试题
魔术球问题弱化版(ball.c/.cpp/.pas) 题目描述 假设有 n 根柱子,现要按下述规则在这 n 根柱子中依次放入编号为 1,2,3,…的球. (1)每次只能在某根柱子的最上面放球. (2) ...
- asp.net mvc生命周期学习
ASP.NET MVC是一个扩展性非常强的框架,探究其生命周期对用Mock框架来模拟某些东西,达到单元测试效果,和开发扩展我们的程序是很好的. 生命周期1:创建routetable.把URL映射到ha ...
- angular调用WCF服务,读取文件夹下图片显示列表,下载另存为图片
读取文件夹下的文件 public string ReadImagesPaths() { string result = string.Empty; try { string path = System ...
- oracle中不曾熟悉的 to_char、to_number(未完待续)
十进制 十六进制88 58 用法一:Converts a HEX number to o FLOAT (转换一个十六进制数的浮标) SQL> sele ...
- office2010怎么激活
软件都是不断更新换代的,像我们使用最多的Microsoft Office软件,从最初的98,2000,2003,2007,到现在的2010.但是在最初安装Office软件时,都是未激活的.下面介绍的就 ...
- Windows程序设计 贪吃蛇c
看Windows程序有段时间了,终于动手写东西.贪吃蛇算是一个开始吧,下面的贪吃蛇很简单,也有很多地方需要修改,还有情况没有考虑QAQ 但这不是我的目的了... 思路很简单:建个链表储存蛇身节点即可. ...
- stat 的名字接口
File::stat - stat 的名字接口 名字为:dev, ino, mode, nlink, uid, gid, rdev, size, atime, mtime, ctime, blksiz ...