STM32 Cube MX 之hal库软件模拟IIC 可直接移植使用

此为软件模拟IIC,可以直接移植到HAL库使用。.h文件需要自己做函数声明这里就不再放出,如有问题大家可以讨论。

使用的时候只需要更改SDA 和SCL引脚的宏定义就可以移植使用,当然IIC协议其实就是根据IIC的时序图编写代码,主要内容就是包括开始信号,停止信号以及发送数据的格式、接收数据和数据的应答。

此IIC代码可以供所有的IIC模块使用,包括IIC必须要的几个基本功能代码中都已经给出。下面开始来介绍IIC协议。

IIC协议具体内容

写数据 :主机给从机发 从机只收 SDA为输出模式

读数据: 从机给主机发 主机收 SDA为输入模式

IIC具体内容:(只规定了开始信号 停止信号 8位数据(发8位收一位ACK) ACK应答(低电位有效) 具体使用IIC模块要看模块数据的发送协议) 时钟线为高电平期间数据有效 ACK应答的时候SDA的模式为输入模式(因为要判断SDA的电平状态)

应答:

MCU一次只能和IIC总线的一个从机进行通信

SDA数据线 SCLK时钟线

具体代码如下:包括IIC开始 停止 应答 发送 接收数据的几种模式

#include "iic.h"
#include "delay.h"
#include "gpio.h"
/**********************************************************
	ST芯片单片机HAL库之IIC软件模拟 
	作者:十一
**********************************************************/
/**********************************************************
   1.IIC软件模拟   使用HAL库时
   2.需要STM32CubeMX配置初始化的相关引脚为GPIO模式 SDA SCL初始状态下都是输出 推挽 上拉模式 
   4.初始状态下SDA 与 SCL要给高电平 使用高低电平转换时之间要有明显的us级延时
**********************************************************/
static GPIO_InitTypeDef GPIO_InitStruct;
/**********************************************************
1.引脚配置 宏定义用IF语句 
2.给引脚电平必须要给输出模式
3.SCL一直都是输出模式(输出时钟肯定是输出模式)
4.宏定义绑定引脚SDA与SCL   SDA PB7    SCL PB6
**********************************************************/
#define SCL_Type     GPIOB
#define SDA_Type     GPIOB

#define SCL_GPIO    GPIO_PIN_6
#define SDA_GPIO    GPIO_PIN_7
//设置输出高低电平模式
#define SDA_OUT(X)   if(X) \
					 HAL_GPIO_WritePin(SDA_Type, SDA_GPIO, GPIO_PIN_SET); \
				     else  \
					 HAL_GPIO_WritePin(SDA_Type, SDA_GPIO, GPIO_PIN_RESET);

#define SCL_OUT(X)   if(X) \
					 HAL_GPIO_WritePin(SCL_Type, SCL_GPIO, GPIO_PIN_SET); \
				     else  \
					 HAL_GPIO_WritePin(SCL_Type, SCL_GPIO, GPIO_PIN_RESET);    
                                     
#define SDA_IN		 HAL_GPIO_ReadPin(SDA_Type,SDA_GPIO)//只有输入模式才能读取电平状态

/*****************************************
  SDA引脚转变为 OUT输出模式(输出模式给停止 开始信号) 
******************************************/
void IIC_SDA_Mode_OUT(void)
{
  GPIO_InitStruct.Pin = SDA_GPIO;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(SDA_Type, &GPIO_InitStruct);
}
/*****************************************
  SDA引脚转变为 输入模式(输入模式传输具体的数据) 
******************************************/
void IIC_SDA_Mode_IN(void)
{
  GPIO_InitStruct.Pin = SDA_GPIO;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(SDA_Type, &GPIO_InitStruct);
}
/*****************************************
  IIC开始信号
******************************************/
void IIC_Start(void)//IIC开始信号
{
	//设置为输出模式
	IIC_SDA_Mode_OUT();
	
	//空闲状态两个引脚是高电平
	SDA_OUT(1);
	SCL_OUT(1) ;
	delay_us(5);
	
	//拉低数据线
	SDA_OUT(0);
	delay_us(5);
	
	//再拉低时钟线
	SCL_OUT(0) ;
	delay_us(5);
}
//IIC停止信号
void IIC_Stop(void)
{
	//设置为输出模式
	IIC_SDA_Mode_OUT();

	//拉低
	SDA_OUT(0);
	SCL_OUT(0);
	delay_us(5);
	
	//时钟线先拉高
	SCL_OUT(1);
	delay_us(5);
	
	//再把数据线拉高
	SDA_OUT(1);
	delay_us(5);
}
void IIC_Send_Byte(uint8_t d)//主机发送8位数据给从机MSB 高位先发
{
    uint8_t i = 0;
    	//设置为输出模式
	IIC_SDA_Mode_OUT();
	
	SDA_OUT(0);
	SCL_OUT(0);
	delay_us(5);
	for(i=0;i<8;i++)
	{
		if(d & (0x1<<(7-i)))//表示数据是1
			SDA_OUT(1)
		else
			SDA_OUT(0);
		
		delay_us(5);
		SCL_OUT(1);//拉高时钟线,告诉对方你可以读了
		
		delay_us(5);
		SCL_OUT(0);//拉低时钟线,告诉对方你暂时别读,我在准备数据
	}

}
uint8_t IIC_Wait_Ack(void)//等待从机给主机应答或者不应答
{
	uint8_t ack = 0;
	//设置为输入模式
	IIC_SDA_Mode_IN();
	
	//时钟线拉高,时钟线为高电平期间,不管是数据还是ack都是有效的
	SCL_OUT(1);
	delay_us(5);
	
	if( SDA_IN  == 1)
		ack = 1;//无效ACK,就是无效应答
	else
		ack = 0;//有效ACK,就是有效应答
	
	SCL_OUT(0);
    
	delay_us(5);
	return ack;
}
uint8_t  IIC_Read_Byte(void)//从机发送8位数据给主机
{
	uint8_t i =0;
	uint8_t data = 0;
	//设置为输入模式
	IIC_SDA_Mode_IN();
	//先拉低时钟线,准备数据
	SCL_OUT(0);
	delay_us(5);
	
	for(i=0;i<8;i++)
	{
		SCL_OUT(1);//时钟线为高电平期间数据才是有效的
		delay_us(5);
		if(SDA_IN == 1)
			data |= (0x1<<(7-i));//数据就是1
		else
			data &= ~(0x1<<(7-i));//数据就是0
		
		SCL_OUT (0);//告诉对方此时准备数据,先别读写
		delay_us(5);
	}
	return data;
}
void IIC_Ack(uint8_t ack)//主机发送应答或者不应答给从机
{
	//设置为输出模式
	IIC_SDA_Mode_OUT();
	
	SDA_OUT(0);
	SCL_OUT(0);
	delay_us(5);
	
	SDA_OUT(ack) ;//发送高/低电平--->发送不应答/应答
	delay_us(5);
	
	SCL_OUT(1);//告诉从机我已经准备好数据,你可以读取了
	delay_us(5);
	
	SCL_OUT (0);//拉低时钟线,发送ack结束
	delay_us(5);
}
  • 20
    点赞
  • 109
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值