day18:I2C通信工程函数文件(包括:端口与引脚的定义、起始信号、终止信号、应答与非应答、读写输入函数等等I2C通信的支持函数)

相关知识:

 

工程结构:

(1)bsp_i2c_gpio.h

#ifndef __BSP_I2C_H__
#define __BSP_I2C_H__

#include "stm32f10x.h"

/* I2C的GPIO端口定义:SCL接PA2,SDA接PA3 */
#define I2C_SCL_GPIO_PORT			GPIOA					// A端口
#define I2C_SCL_GPIO_PIN			GPIO_Pin_2				// 引脚2
#define I2C_SCL_GPIO_CLK			RCC_APB2Periph_GPIOA	// 时钟:PA2挂在到APB2总线上

#define I2C_SDA_GPIO_PORT			GPIOA					// A端口
#define I2C_SDA_GPIO_PIN			GPIO_Pin_3				// 引脚3
#define I2C_SDA_GPIO_CLK			RCC_APB2Periph_GPIOA	// 时钟:PA3挂在到APB2总线上

/* EEPROM的引脚高低电平设置 */
#define EEPROM_I2C_SCL_1()			GPIO_SetBits(I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PIN)		// 设置SCL引脚为高电平
#define EEPROM_I2C_SCL_0()			GPIO_ResetBits(I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PIN)		// 设置SCL引脚为低电平
#define EEPROM_I2C_SDA_1()			GPIO_SetBits(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN)		// 设置SDA引脚为高电平
#define EEPROM_I2C_SDA_0()			GPIO_ResetBits(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN)		// 设置SDA引脚为低电平

/* STM32读取EEPROM设备的数据 */
#define EEPROM_I2C_SDA_READ()		GPIO_ReadInputDataBit(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN)


/* I2C的GPIO端口初始化 */
void I2C_GPIO_CONFIG(void);

/* I2C产生起始信号 */
void I2C_START(void);

/* I2C产生结束信号 */
void I2C_STOP(void);

/* 产生应答信号 */
void I2C_ASK(void);

/* STM32读EEPROM的数据时,EEPROM产生非应答信号 */
void I2C_NOASK(void);

/* 等待EEPROM的应答信号:应答置0,非应答置1 */
uint8_t I2C_WAIT_ASK(void);

/* STM32写一个字节数据到EEPROM */
void I2C_WRITE_BYTE(uint8_t data);

/* STM32读EEPROM的一个字节 */
uint8_t I2C_READ_BYTE(void);

#endif	/* __BSP_I2C_H__ */

(2)bsp_i2c_gpio.c

#include "./i2c/bsp_i2c_gpio.h"

/* 延迟时间 */
static void i2c_Delay(void)
{
	uint8_t i;

	/* 
	下面的时间是通过"逻辑分析仪"测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化
  
	循环次数为10时,SCL频率 = 205KHz 
	循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
	循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
	*/
	for (i = 0; i < 10; i++);
}

/* I2C的GPIO端口初始化 */
void I2C_GPIO_CONFIG(void)
{
	/* GPIO结构体 */
	GPIO_InitTypeDef GPIO_InitStructure;   
    
	/* 打开时钟 */
    RCC_APB2PeriphClockCmd(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK, ENABLE);
    
	/* 实例化SCL结构体 */
    GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PIN;     // SCL引脚     
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;	// 开漏输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   // I2C一般最大为400k/s,50MHZ足够了
    
	/* 初始化SCL */
	GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);	
	
	/* 实例化SDA结构体 */
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PIN;  	         
	
	/* 初始化SDA */
    GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);	
}

/* I2C产生起始信号:根据信号图来写 */
void I2C_START(void)
{
	EEPROM_I2C_SDA_1();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	EEPROM_I2C_SDA_0();
	i2c_Delay();
	EEPROM_I2C_SCL_0();
	i2c_Delay();
}

/* I2C产生结束信号:根据时序图来写 */
void I2C_STOP(void)
{
	EEPROM_I2C_SDA_0();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	EEPROM_I2C_SDA_1();
	i2c_Delay();
}

/* STM32读EEPROM的数据时,EEPROM产生应答信号 */
void I2C_ASK(void)
{
	EEPROM_I2C_SDA_0();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	EEPROM_I2C_SCL_0();
	i2c_Delay();

	/*
	释放SDA总线控制权,如果不置1,即拉成高阻态,则SDA为低电平0,
	因为SDA线为低电平比高电平优先,所以这条线一直都是低电平,
	那么其他EEPROM设备就无法接入SCL线
	*/
	EEPROM_I2C_SDA_1();
	i2c_Delay();
}

/* STM32读EEPROM的数据时,EEPROM产生非应答信号 */
void I2C_NO_ASK(void)
{
	EEPROM_I2C_SDA_1();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	EEPROM_I2C_SCL_0();
	i2c_Delay();
}

/* 等待EEPROM的应答信号:应答置0,非应答置1 */
uint8_t I2C_WAIT_ASK(void)
{
	uint8_t reply;
	
	// 释放SDA线的控制权
	EEPROM_I2C_SDA_1();
	
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	// 判断是应答信号还是非应答信号:看时序图
	if(EEPROM_I2C_SDA_READ()==1)		// 非应答
	{
		reply = 1;
	}
	else								// 应答
	{
		reply = 0;
	}
	
	// SCL拉低
	EEPROM_I2C_SCL_0();
	i2c_Delay();
	
	// 返回信号
	return reply;	
}

/* STM32写一个字节数据到EEPROM */
void I2C_WRITE_BYTE(uint8_t data)
{
	uint8_t i;
	
	// 每次发送一位,循环8次,将一个字节发送完,先发送最高位,后发送低位
	for(i=0; i<8; i++)
	{
		/*
		比如:data = 0101 0010,0x80 = 1000 0000,则:
		第一次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		data <<= 1;之后,data = 1010 0100
		第二次循环:data & 0x80 = 1000 0000,为1,执行:EEPROM_I2C_SDA_1();发送1
		data <<= 1;之后,data = 0100 1000
		第三次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		data <<= 1;之后,data = 1001 0000
		第四次循环:data & 0x80 = 1000 0000,为1,执行:EEPROM_I2C_SDA_1();发送1
		data <<= 1;之后,data = 0010 0000
		第五次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		data <<= 1;之后,data = 0100 0000
		第六次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		data <<= 1;之后,data = 1000 0000
		第七次循环:data & 0x80 = 1000 0000,为1,执行:EEPROM_I2C_SDA_1();发送1
		data <<= 1;之后,data = 0000 0000
		第八次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		经过八次循环之后,发送的数据为:0101 0010,就是一开始要发送的数据data的值
		*/
		if(data & 0x80)
		{
			EEPROM_I2C_SDA_1();
		}
		else
		{
			EEPROM_I2C_SDA_0();
		}
		// 每次都延迟,相当于事先将数据准备好,再产生时钟时序,这样数据发送稳定一点
		i2c_Delay();
		
		// 每次循环,SCL都产生一个0/1方波,表示在一个时钟周期内发送数据
		EEPROM_I2C_SCL_1();
		i2c_Delay();	
		EEPROM_I2C_SCL_0();
		i2c_Delay();
		
		// 将刚刚发送的位移掉
		data <<= 1;
		
		// 判断如果发送完数据了,就要释放SDA线的控制权
		if(i == 7)
		{
			EEPROM_I2C_SDA_1();
		}
	}
}

/* STM32读EEPROM的一个字节 */
uint8_t I2C_READ_BYTE(void)
{
	uint8_t i;
	uint8_t temp = 0;
	
	// 每次发送一位,循环8次,将一个字节发送完,先发送最高位,后发送低位
	for(i=0; i<8; i++)
	{
		// 在每次开始之前先将最后一位腾出位置
		temp <<= 1;
		
		// 时钟开始
		EEPROM_I2C_SCL_1();
		i2c_Delay();
		
		/* 在时钟为高电平时开始读取数据 */
		/* 分析过程和写过程类似 */
		if(EEPROM_I2C_SDA_READ() == 1)
		{
			temp += 1;
		}
		// 每次都延迟,相当于事先将数据准备好,再产生时钟时序,这样数据发送稳定一点
		i2c_Delay();	
	
		// 时钟结束
		EEPROM_I2C_SCL_0();
		i2c_Delay();
	}
	
	// 返回读取的数据
	return temp;
}

(3)main.c

/* 
I2C的初始化工程:
包括:端口与引脚的定义、起始信号、终止信号、应答与非应答、读写输入函数等等I2C通信的支持函数
*/

#include "stm32f10x.h"
#include "./i2c/bsp_i2c_gpio.h" 

int main(void)
{
	
}

 

posted @ 2018-06-09 00:29  半生戎马,共话桑麻、  阅读(957)  评论(0)    收藏  举报
levels of contents