硬件平台:STM32F103C8T6 LM75A
连线:使用PB6作为SCL PB7作为SDA 分别与LM75A连接
参考资料:LM75A数据手册
代码如下
模拟I2C
I2C.h
/*
软件模拟I2C
*/
#ifndef _I2C_H
#define _I2C_H
#include "sys.h"
#include "delay.h"
#include "usart.h"
/*
读写标志位
*/
#define Write_Flag 0
#define Read_Flag 1
/*
PB7->SDA
PB6->SCL
*/
#define SDA_Port GPIOB
#define SDA_Pin GPIO_Pin_7
#define SCL_Port GPIOB
#define SCL_Pin GPIO_Pin_6
/*
定义SDA为输出模式
*/
void SDA_Mode_Out(void);
/*
定义SDA为输入模式
*/
void SDA_Mode_In(void);
/*
位带操作SDA,SCL高低电平以及读取SDA电平
*/
#define SCL_High PBout(6)=1
#define SCL_Low PBout(6)=0
#define SDA_Out_High PBout(7)=1
#define SDA_Out_Low PBout(7)=0
#define SDA_Input PBin(7)
/*
I2C基本时序电平
*/
void MyI2C_Init(void);//I2C初始化
void MyI2C_Start(void);//I2C开始信号
void MyI2C_Stop(void);//I2C停止信号
void MyI2C_Ack(void);//I2C应答信号
void MyI2C_NAck(void);//I2C非应答信号
unsigned char MyI2C_WaitAck(void);//等待应答信号
void MyI2C_WriteByte(unsigned char dat);//写数据
unsigned char MyI2C_ReadByte(void);//读数据
/*
从机通信时序
*/
void MyI2C_ReadBufAddr(unsigned char SlaveAddr, unsigned char RegAddr,unsigned char *dat,unsigned char len);
void MyI2C_ReadByteAddr(unsigned char SlaveAddr, unsigned char RegAddr,unsigned char *dat);
void MyI2C_SendByteAddr(unsigned char SlaveAddr, unsigned char RegAddr, unsigned char dat);
void MyI2C_SendBufAddr(unsigned char SlaveAddr, unsigned char RegAddr, unsigned char *dat,unsigned char len);
#endif
I2C.c
#include "i2c.h"
void MyI2C_Init(void){//I2C初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);//GPIOB时钟开启
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = SDA_Pin|SCL_Pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);//初始化SDA
GPIO_SetBits(GPIOB,SDA_Pin|SCL_Pin);//I2C总线默认为高电平
}
/*
SDA为输出
*/
void SDA_Mode_Out(void){
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = SDA_Pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
/*
SDA为输入
*/
void SDA_Mode_In(void){
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = SDA_Pin;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStruct);
}
/*
I2C开始信号 SCL SDA为输出模式 SDA SCL均为高电平
然后SCL高时,SDA由高到低转变
*/
void MyI2C_Start(void){//I2C开始信号
SDA_Mode_Out();//SDA为输出
SCL_High;
delay_us(5);
SDA_Out_Low;//SDA由高向低,产生开始信号
delay_us(5);
SCL_Low;
}
/*
I2C停止信号 SCL SDA为输出模式
然后SCL高时,SDA由高到低转变
*/
void MyI2C_Stop(void){//I2C停止信号
SDA_Mode_Out();//SDA为输出
SDA_Out_Low;
delay_us(5);
SCL_High;
delay_us(5);
SDA_Out_High;
}
void MyI2C_Ack(void){//I2C应答信号
SDA_Mode_Out();
SDA_Out_Low;
SCL_High;
delay_us(4);
SCL_Low;
delay_us(4);
}
void MyI2C_NAck(void){//I2C非应答信号
SDA_Mode_Out();
SDA_Out_High;
SCL_High;
delay_us(4);
SCL_Low;
delay_us(4);
}
unsigned char MyI2C_WaitAck(void){//等待应答信号,就是检测SDA是否被拉低
unsigned char time = 0;
SDA_Mode_In();//SDA输入模式
SCL_High;
delay_us(4);
while(SDA_Input){
time++;
if(time>250) {
MyI2C_Stop();//判断是否超时
return 1;//非应答
}
}
SCL_Low;
return 0;//应答
}
void MyI2C_WriteByte(unsigned char dat){//写入数据
unsigned char i=0;
SCL_Low;
delay_us(4);
//1字节有8位,每次发送一个位
//MSB 先发最高有效位
//每次发送数据,左移
for(i=0;i<8;i++){
SDA_Mode_Out();//发送数据,为输出模式
if((dat<<i)&0x80) SDA_Out_High;
else SDA_Out_Low;//SCL低电平期间,SDA状态改变
SCL_High;//SCL拉高,发送SDA
delay_us(4);
SCL_Low;//允许SDA改变状态
delay_us(4);
}
}
unsigned char MyI2C_ReadByte(void){//读取数据
unsigned char dat = 0;
unsigned char i = 0;
for(i=0;i<8;i++){
SDA_Mode_In();//切换为输入模式,准备读取数据
SCL_High;
delay_us(4);
dat<<=1;
if(SDA_Input) dat |= 0x01;
SCL_Low;
delay_us(4);
}
return dat;
}
/*
从从机指定地址中读取多个数据
参数分别为从机地址 寄存器地址 读取出的数据(指针传出,推荐传入数组名) 数据长度
*/
void MyI2C_ReadBufAddr(unsigned char SlaveAddr, unsigned char RegAddr,unsigned char *dat,unsigned char len){
unsigned char i = 0;
MyI2C_Start();
MyI2C_WriteByte(SlaveAddr|Write_Flag);
MyI2C_WaitAck();
MyI2C_WriteByte(RegAddr);
MyI2C_WaitAck();
MyI2C_Start();
MyI2C_WriteByte(SlaveAddr|Read_Flag);
MyI2C_WaitAck();
for(i=0;i<len;i++){
dat[i] = MyI2C_ReadByte();
if(i == len) MyI2C_NAck();
else MyI2C_Ack();
}
MyI2C_Stop();
}
/*
从从机指定地址中读取1个数据
参数分别为从机地址 寄存器地址 读取出的数据
*/
void MyI2C_ReadByteAddr(unsigned char SlaveAddr, unsigned char RegAddr,unsigned char *dat){
MyI2C_Start();
MyI2C_WriteByte(SlaveAddr|Write_Flag);
MyI2C_WaitAck();
MyI2C_WriteByte(RegAddr);
MyI2C_WaitAck();
MyI2C_Start();
MyI2C_WriteByte(SlaveAddr|Read_Flag);
MyI2C_WaitAck();
*dat = MyI2C_ReadByte();
MyI2C_NAck();
MyI2C_Stop();
}
/*
向从机指定地址发送单个字节数据
*/
void MyI2C_SendByteAddr(unsigned char SlaveAddr, unsigned char RegAddr, unsigned char dat){
MyI2C_Start();
MyI2C_WriteByte(SlaveAddr|Write_Flag);
MyI2C_WaitAck();
MyI2C_WriteByte(RegAddr);
MyI2C_WaitAck();
MyI2C_WriteByte(dat);
MyI2C_WaitAck();
MyI2C_Stop();
}
/*
向从机指定地址发送多个字节数据
*/
void MyI2C_SendBufAddr(unsigned char SlaveAddr, unsigned char RegAddr, unsigned char *dat,unsigned char len){
unsigned char i=0;
MyI2C_Start();
MyI2C_WriteByte(SlaveAddr|Write_Flag);
MyI2C_WaitAck();
MyI2C_WriteByte(RegAddr);
MyI2C_WaitAck();
for(i=0;i<len;i++){
MyI2C_WriteByte(dat[i]);
MyI2C_WaitAck();
}
MyI2C_Stop();
}
2.LM75A
LM75A.h
#ifndef _LM75A_H
#define _LM75A_H
#include "sys.h"
#include "i2c.h"
#include "delay.h"
#define LM75a_Address 0X9E
#define Write_Flag 0
#define Read_Flag 1
void LM75a_GetTemp(float *data);
void LM75a_Init(void);
#endif
2.LM75A.c
void LM75a_Init(void){
MyI2C_Start();
MyI2C_WriteByte(0x9E);
MyI2C_WaitAck();
MyI2C_WriteByte(0x00);
MyI2C_WaitAck();
MyI2C_Stop();
delay_ms(100);//100ms延时
}
void LM75a_GetTemp(float *data){
int data_temp = 0;
unsigned char test[2];
MyI2C_ReadBufAddr(0x9E,0x00,test,2);
data_temp = (test[0]<<3)|(test[1]>>5);
if(data_temp & 0x400){ //判断D10来确定温度正负 0位正 1为负
*data = (~data_temp+1)*(-0.125f);//去反+1为补码
}
else{
*data = data_temp*0.125f;
}
}
3.main.c文件
main.c
#include "stm32f10x.h" //STM32头文件
#include "sys.h"
#include "i2c.h"
#include "usart.h"
#include "LM75A.h"
float Temp;
unsigned char Test[2];
int main (void){//主程序
RCC_Configuration(); //时钟设置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Usart1_Init(115200);
MyI2C_Init();
LM75a_Init();
while(1){
LM75a_GetTemp(&Temp);
printf("temp = %.2f\n",Temp);
delay_ms(200);
}
}
4.实验效果
1.串口
2.逻辑分析仪