IO模拟IIC通讯

1.STM32使用软件IIC原因

  • 由于STM32的硬件IIC会出现一定概率卡死等问题,为了系统更加稳定,少出BUG 。STM32的绝大多数开发者选择使用软件IIC 。软件IIC能让初学者更加容易理解IIC的工作原理,并且相对硬件IIC,软件IIC对不同平台的移植性更好。

2.IIC设备调试注意事项

  • 在调试IIC通讯设备时,尽量不要用杜邦线连接IIC设备,杜邦线接头不稳定,很多时候会出现设备卡死不应答。假如必须用杜邦线连接,请用较短等长杜邦线连接,必要时焊死杜邦线。如果出现设备有时不应答情况,且多次检查IIC通讯程序没问题,那就很大可能是接线不稳造成。经常移动设备,请考虑这点。

3.模拟IIC的基本函数

  • 起始信号:SCL在高电平时,SDA从高电平拉低。
  • 结束信号:SCL在高电平时,SDA从低电平拉高。


首先对GPIO的初始化,模拟IIC就用推挽模式就好

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_Initstruct.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_2;
GPIO_Initstruct.GPIO_Mode = GPIO_Mode_Out_PP;  //推挽模式
GPIO_Initstruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC,&GPIO_Initstruct);
GPIO_ResetBits(GPIOC,GPIO_Pin_0);
GPIO_ResetBits(GPIOC,GPIO_Pin_2);

后面函数要用到的宏定义在这给出,用宏定义可简化代码,增强可读性与可移植性。

#define SCL_H                    GPIO_SetBits(GPIOC,GPIO_Pin_0)
#define SCL_L                   GPIO_ResetBits(GPIOC,GPIO_Pin_0)#define SDA_H                   GPIO_SetBits(GPIOC,GPIO_Pin_2)
#define SDA_L                   GPIO_ResetBits(GPIOC,GPIO_Pin_2)#define MPU6050_SDA_Check_H()   {while(((GPIOC->ODR) & (1<<2))!=(1<<2));}//检测ODR寄存器查看IO输出状态
#define MPU6050_SDA_Check_L()   {while(((GPIOC->ODR) & (0<<2))!=(0<<2));}   #define MPU6050_SCL_Check_H()   {while(((GPIOC->ODR) & (1<<0))!=(1<<0));}
#define MPU6050_SCL_Check_L()   {while(((GPIOC->ODR) & (0<<0))!=(0<<0));}#define SDA_IN() {GPIO_InitTypeDef GPIO_Initstruct;\GPIO_Initstruct.GPIO_Pin = GPIO_Pin_2;\GPIO_Initstruct.GPIO_Mode = GPIO_Mode_IPU;\GPIO_Init(GPIOC,&GPIO_Initstruct);}#define SDA_OUT() {GPIO_InitTypeDef GPIO_Initstruct;\GPIO_Initstruct.GPIO_Pin = GPIO_Pin_2;\GPIO_Initstruct.GPIO_Mode = GPIO_Mode_Out_PP;\GPIO_Initstruct.GPIO_Speed = GPIO_Speed_50MHz;\GPIO_Init(GPIOC,&GPIO_Initstruct);}
#define IIC_PORT            GPIOC
#define IIC_SDA_Pin     GPIO_Pin_2

用宏定义封装后代码是不是更加通俗易懂,而且放在函数里时不冗杂。每个信号线都有相应的检测函数,保证在确定的电平后再做其他事。

起始信号函数

void IIC_START()
{SDA_H;                     //SDA先拉高准备后面的下降沿SDA_Check_H();              //检查SDA是否拉高Systick_Delay_us(1);     //延时1us//先拉高SDA再拉高SCL避免信号影响!!!!!SCL_H;                       //SCL拉高SCL_Check_H();               //检查SCL是否拉高Systick_Delay_us(1);SDA_L;SDA_Check_L();Systick_Delay_us(1);SCL_L;                       //SCL拉低SCL_Check_L();               //SCL拉低检查Systick_Delay_us(1);}

结束信号函数

void IIC_STOP()
{SDA_L; //先拉低SDA为上升沿做准备,SCL此时要为低,防止误信号SDA_Check_L();Systick_Delay_us(1);SCL_H;SCL_Check_H();Systick_Delay_us(1);SDA_H;SDA_Check_H();Systick_Delay_us(1);SCL_L;    //获得想要信号后SCL先下降,避免产生误信号SCL_Check_L();Systick_Delay_us(1);SDA_L;SDA_Check_L();Systick_Delay_us(1);}

以上为IIC的起始信号与结束信号注意每个信号的SDA与SCL位置,防止产生误信号。导致通讯失败!!!在通讯时只有在SCL为高电平时才为有效电平,注意每个函数后面都要把SCL拉低,以防误信号。

写一个Byte函数

void IIC_Write_Byte(uint8_t Byte)
{uint8_t num;for(num = 0;num < 8;num++){if(Byte&0x80)//最高位是否为1{SDA_H;SDA_Check_H();Systick_Delay_us(1);SCL_H;SCL_Check_H();Systick_Delay_us(1);SCL_L;SCL_Check_L();Systick_Delay_us(1);}else{SDA_L;SDA_Check_L();Systick_Delay_us(1);SCL_H;SCL_Check_H();Systick_Delay_us(1);SCL_L;SCL_Check_L();Systick_Delay_us(1);}Byte <<= 1;  //数据是高位先行所以左移,注意左移符号后面要有“=”不然通讯失败,编译器还不报错}
}

读一个Byte函数

uint8_t IIC_Read_Byte()
{unsigned char num=0,temp=0,change=0;SDA_IN();                   //SDA为上拉输入模式for(num = 0;num < 8;num++){change=0;             //每次清0变量防止数据错误SCL_H;SCL_Check_H();if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_2) == 1)change = 1;            //不要习惯用++temp =(temp<<1)+change;  //更新数据SCL_L;                    //结束关闭SCL防止误数据SCL_Check_L();Systick_Delay_us(1);}return temp;
}

等待从设备响应函数

uint8_t Wait_ASK()
{uint16_t time = 1000; //为了防止程序卡死SDA_IN();             //SDA为上拉输入SCL_H;SCL_Check_H();while(GPIO_ReadInputDataBit(IIC_PORT,IIC_SDA_Pin)     != 0)//当低电平时设备响应,响应跳过下面条件{time --;if(time == 0){SDA_OUT();        //设备未响应,设置SDA为输出,return退出,防止卡死。调试时建议死循环return 0;}}SCL_L;SCL_Check_L();SDA_OUT();               //SDA为输出模式return 1;
}

主动发送响应信号让从机继续发送数据
主动发送响应函数(从机继续发数据)

void IIC_Send_ASC()
{IIC_SDA_OUT(); IIC_SDA_L;      //SDA拉低表示应答IIC_SDA_Check_L();IIC_SCL_H;IIC_SCL_Check_H();IIC_SCL_L;IIC_SCL_Check_L();
}

主动发送非应答信号终止从机的数据传输
非应答信号函数

void Send_NASC()
{SDA_OUT();SDA_H;       //SDA拉高表示非应答,结束数据传输SDA_Check_H();SCL_H;SCL_Check_H();SCL_L;SCL_Check_L();SDA_L;SDA_Check_L();
}

3.写寄存器与读寄存器

前面做好了几个基本函数,后面就可以根据前面的基本函数来组成IIC的数据基本格式,来写寄存器与读寄存器。

写寄存器函数

void IIC_Write_Reg(uint8_t addr,uint8_t Byte)
{IIC_START();               //起始信号IIC_Write_Byte(DEV_ADDR); //发送设备地址Wait_ASK();                 //等待从机响应IIC_Write_Byte(addr);       //写入寄存器地址Wait_ASK();                    //等待从机响应IIC_Write_Byte(Byte);       //写入要写数据Wait_ASK();                 //等待从机响应IIC_STOP();                 //发送停止信号
}

读寄存器函数

uint8_t IIC_Read_Reg(uint8_t addr)
{uint8_t temp;IIC_START();              //起始信号IIC_Write_Byte(DEV_ADDR); //发送设备地址Wait_ASK();                 //等待响应IIC_Write_Byte(addr);     //写入寄存器地址Wait_ASK();                    //等待响应IIC_START();              //再次起始信号IIC_Write_Byte(DEV_ADDR+1);    //发送设备地址,最后一位为1表示读。Wait_ASK();                   //等待响应temp = IIC_Read_Byte();      //读数据Send_NASC();               //发送非应答信号IIC_STOP();                    //发送停止信号return temp;                //返回数据
}

这里的设备地址默认为8位,最后一位默认为0也就是写,列如,厂商给你7位设备地址为0X68则在这的DEV_ADDR为左移1位,也就是0XD0.所以要定义DEV_ADDR为0XD0;
读寄存器和写寄存器的函数写好后建议各位测试一下,列如:

uint8_t temp;
IIC_Write_Reg(0XE0,0XB6);       //向0xE0写0XB6
temp = IIC_Read_Reg(0XE0);     //读取0XE0寄存器值

通过仿真看看读出值是否正确,不正确就检查IIC函数,不要纠结硬件问题。设备地址也是很容易出错的,多检查几次,设备地址若是7位要左移一位才是DEV_ADDR的真实地址。

结语

  • 学习通讯协议要用IO口模拟后印象才能深刻,简单用硬件通讯协议对整个通讯过程的理解不深,而且移植性较差。移植中要注意IIC的设备地址,SDA与SCL线的顺序,不然产生误信号会导致不能通讯,并且难以查找错误。

STM32F10X的模拟IIC通讯相关推荐

  1. STM32模拟IIC读取PCF8563

    作者第一次开写博客,本着学习的态度,附上自己总结的代码,希望大家多多指点! 一.首先是对于PCF8563芯片的介绍与使用说明: PCF8563 是PHILIPS 公司推出的一款工业级内含I2C 总线接 ...

  2. STM32 软件模拟IIC

    0.系列目录 STM32 软件模拟IIC STM32 使用DMP库处理MPU6050数据 STM32 MPU6050与匿名上位机通讯(V2.6版) 1.IIC通讯过程 SCL和SDA在空闲时候均为高电 ...

  3. STM32基于软件模拟IIC进行AHT21B温湿度采集

    STM32基于软件模拟IIC进行AHT21B温湿度采集 一. IIC简介 1.1 IIC简介 1.2 实现方式 1.3 实现方式对比 二. AHT21B简介 2.1 简介 2.2 产品特点 2.3 外 ...

  4. 树莓派IIC通讯获取BMI08x IMU数据进行姿态解算,并通过UART/TCP在rviz上显示

    截至2021年,树莓派出的最新款应该是Raspberry Pi 400,设计得跟键盘一样,很难想象到这是个树莓派,尤其是它的标语写的很好"你的下一个电脑,何必是电脑",不言而喻.反 ...

  5. ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议

    IIC通讯协议 协议简介 IIC(inter-integrated Circuit集成电路总线)总线支持设备之间的短距离通信,用于处理器和一些外围设备之间的接口,它需要两根信号线来完成信息交换,它是由 ...

  6. 模拟IIC读取SHT30温湿度传感器数据

    文章目录 1 i2c.c 2 i2c.h 3 crc.c 4 crc.h 1 i2c.c /*** @brief SHT30温湿度传感器相关,使用模拟IIC进行数据的读取*/#include < ...

  7. 基于Stm32f103利用模拟iic驱动LM75A温度传感器

    这两天一直在搞模拟iic,模拟iic相较于硬件iic的优势在于更稳定,io口的选择更灵活. 这次编写模拟iic驱动程序还是有点坎坷,其中模拟iic的时序不是难点,直接说我遇到的问题1.io口模式的选择 ...

  8. STM32之IIC通讯-SHT30温湿度采集

    一.实验目的:通过单片机普通IO口,模拟IIC时序,掌握IIC通讯协议. 二.实验用到的主控芯片:STM32F103RCT6,IIC通讯引脚IIC_SDA:PC11,IIC_SCL:PC12 三.SH ...

  9. STM32F103C8T6模拟IIC控制4针0.96寸OLED显示屏

    OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD).OLE ...

最新文章

  1. 2021,软科中国大学排名新鲜出炉!清华、北大、浙大位居前三!
  2. elasticsearch聚合操作——本质就是针对搜索后的结果使用桶bucket(允许嵌套)进行group by,统计下分组结果,包括min/max/avg...
  3. jQuery插件simplePagination的使用-踩坑记_03
  4. Coursera公开课笔记: 斯坦福大学机器学习第一课“引言(Introduction)”
  5. pythonide机制_强化vim打造python的IDE
  6. javaScript之数组Array
  7. mysql显示百分比例_显示值mysql的百分比
  8. 碧蓝航线维护服务器时间,碧蓝航线3月21日维护时间更新公告 新增与更改内容介绍...
  9. MessageBox函数
  10. ENVI5.3.1使用Landsat 8影像进行NDVI计算实例操作
  11. CSS3 transform 旋转、偏移、缩放属性,CSS3 filter 滤镜,实现图像特效
  12. tomcat 7配置ssl教程
  13. 【转】vc++MCF/C++/C中怎样让应用程序获得或以管理员权限运行 ,ShellExecuteEX编程 --- 获取管理员权限...
  14. 【hdu3183】A Magic Lamp(思维+st表(含模版))
  15. (Java)idea连接数据库
  16. 左耳朵耗子给出的学习指南
  17. CSS实现单行、多行文本溢出显示省略号(…)
  18. 追梦App系列博客——第一次会议总结
  19. android电子指南针,Android实现电子罗盘(指南针)方向传感器的应用
  20. element-ui message 显示重叠问题

热门文章

  1. 激光+电弧混合增材制造新工艺 | 哈工大团队取得重要研究进展
  2. 计算机应用基础(一),《计算机应用基础(一)》及参考答案
  3. spring boot +Mysql双端融合的教学过程管理系统小程序源码66431
  4. 如何下载旧版本的Eclipse
  5. 2018级河南省学业水平测试计算机,郑州市教育局关于2018级普通高中信息技术学业水平考试有关事宜的通知...
  6. 超级浏览器:防止精准识别从Canvas指纹开始
  7. matlab 正射影像,正射影像(DOM)的制作
  8. Cell Reports | 北大吴晓磊/聂勇组代谢分工微生物群落的组装机制
  9. 微信小程序前后端交互__完整的登录注册
  10. 算术运算符重载——普通函数重载算术运算符