STM32F10X的模拟IIC通讯
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通讯相关推荐
- STM32模拟IIC读取PCF8563
作者第一次开写博客,本着学习的态度,附上自己总结的代码,希望大家多多指点! 一.首先是对于PCF8563芯片的介绍与使用说明: PCF8563 是PHILIPS 公司推出的一款工业级内含I2C 总线接 ...
- STM32 软件模拟IIC
0.系列目录 STM32 软件模拟IIC STM32 使用DMP库处理MPU6050数据 STM32 MPU6050与匿名上位机通讯(V2.6版) 1.IIC通讯过程 SCL和SDA在空闲时候均为高电 ...
- STM32基于软件模拟IIC进行AHT21B温湿度采集
STM32基于软件模拟IIC进行AHT21B温湿度采集 一. IIC简介 1.1 IIC简介 1.2 实现方式 1.3 实现方式对比 二. AHT21B简介 2.1 简介 2.2 产品特点 2.3 外 ...
- 树莓派IIC通讯获取BMI08x IMU数据进行姿态解算,并通过UART/TCP在rviz上显示
截至2021年,树莓派出的最新款应该是Raspberry Pi 400,设计得跟键盘一样,很难想象到这是个树莓派,尤其是它的标语写的很好"你的下一个电脑,何必是电脑",不言而喻.反 ...
- ARM+SD2405 IIC_RTC驱动编写及IIC通讯协议
IIC通讯协议 协议简介 IIC(inter-integrated Circuit集成电路总线)总线支持设备之间的短距离通信,用于处理器和一些外围设备之间的接口,它需要两根信号线来完成信息交换,它是由 ...
- 模拟IIC读取SHT30温湿度传感器数据
文章目录 1 i2c.c 2 i2c.h 3 crc.c 4 crc.h 1 i2c.c /*** @brief SHT30温湿度传感器相关,使用模拟IIC进行数据的读取*/#include < ...
- 基于Stm32f103利用模拟iic驱动LM75A温度传感器
这两天一直在搞模拟iic,模拟iic相较于硬件iic的优势在于更稳定,io口的选择更灵活. 这次编写模拟iic驱动程序还是有点坎坷,其中模拟iic的时序不是难点,直接说我遇到的问题1.io口模式的选择 ...
- STM32之IIC通讯-SHT30温湿度采集
一.实验目的:通过单片机普通IO口,模拟IIC时序,掌握IIC通讯协议. 二.实验用到的主控芯片:STM32F103RCT6,IIC通讯引脚IIC_SDA:PC11,IIC_SCL:PC12 三.SH ...
- STM32F103C8T6模拟IIC控制4针0.96寸OLED显示屏
OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD).OLE ...
最新文章
- 2021,软科中国大学排名新鲜出炉!清华、北大、浙大位居前三!
- elasticsearch聚合操作——本质就是针对搜索后的结果使用桶bucket(允许嵌套)进行group by,统计下分组结果,包括min/max/avg...
- jQuery插件simplePagination的使用-踩坑记_03
- Coursera公开课笔记: 斯坦福大学机器学习第一课“引言(Introduction)”
- pythonide机制_强化vim打造python的IDE
- javaScript之数组Array
- mysql显示百分比例_显示值mysql的百分比
- 碧蓝航线维护服务器时间,碧蓝航线3月21日维护时间更新公告 新增与更改内容介绍...
- MessageBox函数
- ENVI5.3.1使用Landsat 8影像进行NDVI计算实例操作
- CSS3 transform 旋转、偏移、缩放属性,CSS3 filter 滤镜,实现图像特效
- tomcat 7配置ssl教程
- 【转】vc++MCF/C++/C中怎样让应用程序获得或以管理员权限运行 ,ShellExecuteEX编程 --- 获取管理员权限...
- 【hdu3183】A Magic Lamp(思维+st表(含模版))
- (Java)idea连接数据库
- 左耳朵耗子给出的学习指南
- CSS实现单行、多行文本溢出显示省略号(…)
- 追梦App系列博客——第一次会议总结
- android电子指南针,Android实现电子罗盘(指南针)方向传感器的应用
- element-ui message 显示重叠问题
热门文章
- 激光+电弧混合增材制造新工艺 | 哈工大团队取得重要研究进展
- 计算机应用基础(一),《计算机应用基础(一)》及参考答案
- spring boot +Mysql双端融合的教学过程管理系统小程序源码66431
- 如何下载旧版本的Eclipse
- 2018级河南省学业水平测试计算机,郑州市教育局关于2018级普通高中信息技术学业水平考试有关事宜的通知...
- 超级浏览器:防止精准识别从Canvas指纹开始
- matlab 正射影像,正射影像(DOM)的制作
- Cell Reports | 北大吴晓磊/聂勇组代谢分工微生物群落的组装机制
- 微信小程序前后端交互__完整的登录注册
- 算术运算符重载——普通函数重载算术运算符