基于stm32f103的软件i2c协议

一.文章开头说明

        此软件i2c源码是在stm32f103芯片hal库的基础上实现的,其它芯片及库需要修改相应的底层驱动,如gpio口初始化,头文件相应的宏定义修改。

二.iic协议主要内容介绍

1.基础知识

        I2C 通讯协议(Inter-Integrated Circuit)是由Phiilps公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、 CAN等通讯协议的外部收发设备,现在被广泛地使用在系统内多个集成电路(IC)间的通讯。

        它是一个支持多设备的总线。“总线”指多个设备共用的信号线。在一个I2C通讯总线中,可连接多个I2C通讯设备,支持多个通讯。
        一个I2C总线只使用两条总线线路,一条双向串行数据线(SDA) ,一条串行时钟线 (SCL)。数据线即用来表示数据,时钟线用于数据收发同步。
        每个连接到总线的设备都有一个独立的地址,主机可以利用这个地址进行不同设备之间的访问。主机及多个通讯从机。


        特点:总线通过上拉电阻接到电源。当I2C设备空闲时,会输出高阻态,而当所有设备都空闲,都输出高阻态时,由上拉电阻把总线拉成高电平。
多个主机同时使用总线时,为了防止数据冲突,会利用仲裁方式决定由哪个设备占用总线。
具有三种传输模式:标准模式传输速率为100kbit/s ,快速模式为400kbit/s ,高速模式下可达 3.4Mbit/s,但目前大多I2C设备尚不支持高速模式。

2.工作时序介绍

1. 数据有效性

  IIC 的数据读取动作都在 SCL为高 时产生,SCL为低时是数据改变的时期。所以,传输数据的过程中,当SCL为高时,数据应当保持稳定,避免数据的采集出错。

2. 开始和结束信号

   开始信号(START/S): SCL为高时,SDA从高到低的跳变产生开始信号
   结束信号(STOP/P)  : SCL为高时,SDA从低到高的跳变产生结束信号

 3. 重复开始信号

   重复开始信号(ReSTART/Sr): 在结束时不给出STOP信号,而以一个时钟周期内再次给出开始信号作为替代。

 4. 主机写-从机收

  主机对从机发送数据时,主机对从机发送一个开始字节,然后即可一直发送数据。

5. 主机读-从机发


  主机对向从机读取数据时,方式同发送数据有所不同,要多一次通信过程。
  主机需要先向从机发送一次信号,告诉从机”我要读取数据“,然后重开一次通信,等待从机主动返回数据。

三.模拟iic的代码

源文件:

#include "myi2c.h"

/*说明:这个是基于stm32f103芯片的软件模拟iic源码,使用时需要注意先更改相应的引脚;
*		头文件也需要把gpio输入和输出相应的代码更改;
*		我的设置为:PB10->SCL  PB11->SDA
*/


/**********************************************************************************/
/*                                初始化部分                                      */
/*                           需要更改为对应的引脚                                 */
/**********************************************************************************/
/*
*funtion:初始化i2c的IO口
*/
void I2C_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10|GPIO_PIN_11, GPIO_PIN_SET);

  /*Configure GPIO pins : PB10 PB11 */
  GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/*设置相应数据的引脚为输出*/
void SDA_OUT()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	// PB11-SDA
	GPIO_InitStructure.Pin = GPIO_PIN_11;
	GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
	GPIO_InitStructure.Pull = GPIO_PULLUP;
	GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
}
/*设置相应数据的引脚为输入*/
void SDA_IN()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	// PB11-SDA
	GPIO_InitStructure.Pin = GPIO_PIN_11;
	GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
	GPIO_InitStructure.Pull = GPIO_PULLUP;
	HAL_GPIO_Init(GPIOB,&GPIO_InitStructure);
}

/*us级别延迟函数*/
void delay_us(uint32_t us)//主频72M
{
    uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
    while (delay--)
	{
		;
	}
}
/**********************************************************************************/
/*                                      END                                       */
/**********************************************************************************/


/**********************************************************************************/
/*                                iic协议部分                                     */
/**********************************************************************************/
/*iic起始信号*/
void IIC_Star(void)
{
	 SDA_OUT();     //sda线输出
	IIC_SDA_HIGH;	  	  
	IIC_SCL_HIGH;
	delay_us(2);
 	IIC_SDA_LOW;		//START:when CLK is high,DATA change form high to low 
	delay_us(2);
	IIC_SCL_LOW;		//钳住I2C总线,准备发送或接收数据 
}

/*iic停止信号*/
void IIC_Stop(void)
{
	SDA_OUT();
	IIC_SDA_LOW;	//STOP:when CLK is high DATA change form low to high
	IIC_SCL_HIGH;
	delay_us(2);
	IIC_SDA_HIGH;
	delay_us(2);
}

/*iic产生应答信号*/
//1:nack 0:ack
void IIC_ACK(uint8_t ack)
{
	SDA_OUT();
	IIC_SCL_LOW;			  //时钟线拉低,以便写数据
	if(ack) IIC_SDA_HIGH;    //写应答信号
	else    IIC_SDA_LOW; 
	IIC_SCL_HIGH;           //拉高时钟线
    delay_us(2);           //延时
    IIC_SCL_LOW;          //拉低时钟线
    delay_us(2);         //延时
}

/*iic等待应答信号*/
uint8_t IIC_Wait_Ack(void)
{
	uint16_t wait_time=0;
	SDA_IN();
	IIC_SCL_HIGH;delay_us(2);
	while(IIC_SDA_READ)
	{
		wait_time++;
		if(wait_time>=250)
		{
			IIC_Stop();
			return 1;
		}
	}
	IIC_SCL_LOW;
	return 0;
}

/*iic写一个字节数据*/
void IIC_Write_Byte(uint8_t Data)
{
	uint8_t t=0,bit;
	SDA_OUT();
	IIC_SCL_LOW;
	for(t=0;t<8;t++)
	{
		bit = Data&0x80;
		if(bit) IIC_SDA_HIGH;
		else IIC_SDA_LOW;
		Data<<=1;
		IIC_SCL_HIGH;           //拉高时钟线
		delay_us(2);         //延时
        IIC_SCL_LOW;           //拉低时钟线
        delay_us(2);         //延时
	}
	IIC_Wait_Ack();
}

/*iic读一个字节数据*/
uint8_t IIC_Read_Byte(void)
{
	uint8_t i;
    uint8_t dat = 0;
	SDA_IN();               //SDA设置为输入
    IIC_SDA_HIGH;              //使能内部上拉,准备读取数据,
    for (i=0; i<8; i++)     //8位计数器
    {
        dat <<= 1;
        IIC_SCL_HIGH;          //拉高时钟线
        delay_us(2);        //延时
		if(IIC_SDA_READ) dat+=1;               
        IIC_SCL_LOW;          //拉低时钟线
        delay_us(2);        //延时
    }
    return dat;
}
/**********************************************************************************/
/*                                      END                                       */
/**********************************************************************************/

头文件:

#ifndef _MYI2C_H_
#define _MYI2C_H_

/*下面为stmf103hal库相关的,其它库或者别的芯片根据需要更改
* 主要更改输入输出和读取数据的配置
*/
#include "gpio.h"

/*GPIO初始化*/
void I2C_GPIO_Init(void);

/*GPIO输出*/
#define IIC_SDA_HIGH GPIOB->ODR |=  (1 << 11) 
#define IIC_SDA_LOW  GPIOB->ODR &= ~(1 << 11) 
#define IIC_SCL_HIGH GPIOB->ODR |=  (1 << 10) 
#define IIC_SCL_LOW  GPIOB->ODR &= ~(1 << 10) 

/*GPIO读取数据*/
#define IIC_SDA_READ GPIOB->IDR &= (1<<11)



void IIC_Star(void);
void IIC_Stop(void);
void IIC_ACK(uint8_t ack);
uint8_t IIC_Wait_Ack(void);
void IIC_Write_Byte(uint8_t Data);
uint8_t IIC_Read_Byte(void);

#endif

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无問.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值