栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

STM32硬件I2C

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

STM32硬件I2C

文章目录

前言一、原理图二、编程

1.写入数据2.接收数据3.代码


前言

本文记录的是硬件I2C通信过程
开发板:野火指南者STM32F103VET6
I2C设备:EEPROM(AT24C02) 2048个字节大小,只能存256个8位数。
开发软件:KEIL5


一、原理图


PB6是时钟线SCL
PB7是数据线SDA

二、编程 1.写入数据


2.接收数据

3.代码

main.c
代码如下(示例):

#include "stm32f10x.h"   // 相当于51单片机中的  #include 
#include "bsp_led.h"
#include "bsp_uart.h"
#include "bsp_i2c.h"

void Delay(unsigned int n) {
    unsigned int i,j;
    for(j = 0; j<5000; j++)
    {
        for(i = 0;i 

bsp_i2c.h

#ifndef __BSP_I2C_H__
#define __BSP_I2C_H__

#include "stm32f10x.h"
#include "bsp_uart.h"
#include "bsp_led.h"


#define      EEPROM_I2C_CLK_GPIO_PORT                     GPIOB
#define      EEPROM_I2C_CLK_GPIO_PIN                      GPIO_Pin_6
#define			 EEPROM_I2C_CLK_GPIO_CLK											RCC_APB2Periph_GPIOB
#define      EEPROM_I2C_CLK_APBxClkCmd              	 		RCC_APB2PeriphClockCmd


#define      EEPROM_I2C_SDA_GPIO_PORT                     GPIOB
#define      EEPROM_I2C_SDA_GPIO_PIN                      GPIO_Pin_7
#define			 EEPROM_I2C_SDA_GPIO_CLK											RCC_APB2Periph_GPIOB

#define      EEPROM_I2C_GPIO_APBxClkCmd              	 		RCC_APB2PeriphClockCmd


#define 		 EEPROM_I2C																		I2C1
#define			 EEPROM_I2C_CLK																RCC_APB1Periph_I2C1
#define 		 EEPROM_I2C_SPEED															400000
#define 		 EEPROM_I2C_OWN_ADDR													0x5f
#define			 EEPROM_I2C_APBxClkCmd												RCC_APB1PeriphClockCmd


#define  		 I2C_PAGE_SIZE																8 		
#define			 AT24C02_NUM_SIZE															256
#define 		 EEPROM_ADDR																	0xA0	

void I2C_EEPROM_Init(void);

void EEPROM_Byte_Write(uint8_t addr, uint8_t data);
void EEPROM_Read(uint8_t addr, uint8_t *data, uint16_t numByteToRead);
void EEPROM_WaitForWriteEnd(void);
void EEPROM_Page_Write(uint8_t addr, uint8_t* data, uint16_t numByteToWrite);
void I2C_EE_BufferWrite(uint8_t WriteAddr, uint8_t* pBuffer, uint16_t NumByteToWrite);
uint8_t I2C_eeprom_Test(void);

#endif


bsp_i2c.c

#include "bsp_i2c.h"

static void I2C_EE_GPIO_Config(void);
static void I2C_EE_Config(void);


void I2C_EEPROM_Init(void) {
	I2C_EE_GPIO_Config();
	
	I2C_EE_Config();
	return ;
}



static void I2C_EE_GPIO_Config(void) {
	GPIO_InitTypeDef GPIO_InitStruct;

	// 打开IIC GPIO的时钟
	EEPROM_I2C_GPIO_APBxClkCmd(EEPROM_I2C_CLK_GPIO_CLK|EEPROM_I2C_SDA_GPIO_CLK, ENABLE);

	// 将IIC SCL的GPIO配置为推挽复用模式
	GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_CLK_GPIO_PIN;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(EEPROM_I2C_CLK_GPIO_PORT, &GPIO_InitStruct);

  // 将IIC SDA的GPIO配置为浮空输入模式
	GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SDA_GPIO_PIN;
	GPIO_Init(EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStruct);
	return ;
}



static void I2C_EE_Config(void) {
	I2C_InitTypeDef I2C_InitStruct;
	
	EEPROM_I2C_APBxClkCmd(EEPROM_I2C_CLK, ENABLE);
	
	I2C_InitStruct.I2C_ClockSpeed = EEPROM_I2C_SPEED;
	I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStruct.I2C_DutyCycle =I2C_DutyCycle_2; //占空比2:1
	I2C_InitStruct.I2C_OwnAddress1 = EEPROM_I2C_OWN_ADDR;
	I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;	//使能应答
	I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;  //7位从机地址

	I2C_Init(EEPROM_I2C, &I2C_InitStruct);
	
	I2C_Cmd(EEPROM_I2C, ENABLE);
	return ;
}

//向EEPROM写入一个字节数据
void EEPROM_Byte_Write(uint8_t addr, uint8_t data) {
	//起始信号
	I2C_GenerateSTART(EEPROM_I2C, ENABLE);
	
	//检测EV5事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS );
	
	//发送7位地址
	I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Transmitter);
	
	//检测EV6事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR );
	
	//发送一开始在哪存数据的地址
	I2C_SendData(EEPROM_I2C, addr);

	//检测EV8事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR );
	
	//发送数据
	I2C_SendData(EEPROM_I2C, data);
	//检测EV8事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);
	//检测EV8_2事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR );
	
	//停止信号
	I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
	
	return ;
}


//向EEPROM写入一页数据(8个字节)
void EEPROM_Page_Write(uint8_t addr, uint8_t* data, uint16_t numByteToWrite) {
	//起始信号
	I2C_GenerateSTART(EEPROM_I2C, ENABLE);
	
	//检测EV5事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS );
	
	//发送7位地址
	I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Transmitter);
	
	//检测EV6事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR );
	
	//发送一开始在哪存数据的地址
	I2C_SendData(EEPROM_I2C, addr);

	//检测EV8事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR );
	
	while(numByteToWrite) {
		if(1 == numByteToWrite) {
			//发送数据
			I2C_SendData(EEPROM_I2C, *data);
			//检测EV8事件
			while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);
			//检测EV8_2事件
			while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR );
			break;
		}
		//发送数据
		I2C_SendData(EEPROM_I2C, *data);
		data++;
		numByteToWrite--;
		//检测EV8事件
		while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS);
	}
	
	//停止信号
	I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
	
	return ;
}



//从EEPROM读数据(在哪读,读到哪里去,读多少个字节)
void EEPROM_Read(uint8_t addr, uint8_t *data, uint16_t numByteToRead) {
	//起始信号
	I2C_GenerateSTART(EEPROM_I2C, ENABLE);
	
	//检测EV5事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) == ERROR );
	
	//发送7位地址+写方向
	I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Transmitter);
	
	//检测EV6事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR );
	
	//发送一开始在哪读数据的地址
	I2C_SendData(EEPROM_I2C, addr);
	
	//检测EV8事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR );
	
	//第二次起始信号
	I2C_GenerateSTART(EEPROM_I2C, ENABLE);
	//检测EV5事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) == ERROR );	
	//发送7位地址+读方向
	I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Receiver);
	//检测EV6事件
	while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR );	

	while(numByteToRead) {
	//如果为最后一个字节
		if(1 == numByteToRead) {
			I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE);	
		}		
		//检测EV7事件
		while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);
		//接收数据
		*data = I2C_ReceiveData(EEPROM_I2C);	
		data++;	
		numByteToRead--;
	}

	
	//停止信号
	I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
	
	//重新使能应答,以便下次通信
	I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE);
	
	return ;
}


void EEPROM_WaitForWriteEnd(void) {
	do {
		//产生起始信号
		I2C_GenerateSTART(EEPROM_I2C,ENABLE);
		while( I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_SB) == RESET );
		
		//发送设备地址
		I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
	}
	while( I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_ADDR) == RESET );	

	//EEPROM内部时序完成传输完成
	I2C_GenerateSTOP(EEPROM_I2C,ENABLE);	

}



void I2C_EE_BufferWrite(uint8_t WriteAddr, uint8_t* pBuffer, uint16_t NumByteToWrite) {
	uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
	
	//Addr是用来检测是否能被8整除
	Addr = WriteAddr % I2C_PAGE_SIZE;

	count = I2C_PAGE_SIZE - Addr; //第一页要写入多少个字节	
	
	//NumOfPage是记录有多少页
	NumOfPage = NumByteToWrite / I2C_PAGE_SIZE;
	
	//NumOfSingle记录最后一页需要写入多少个字节
	NumOfSingle = NumByteToWrite % I2C_PAGE_SIZE;

	if( 0 == Addr ) {	//如果字节总数对齐字节8
		if( 0 == NumOfPage) {					//如果一页未满
			EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle);
			EEPROM_WaitForWriteEnd();//等待写入完成
		} else {//如果不止一页,而且对齐字节8
			while(NumOfPage--) {
				EEPROM_Page_Write(WriteAddr, pBuffer, I2C_PAGE_SIZE);
				EEPROM_WaitForWriteEnd();//等待写入完成
				WriteAddr += I2C_PAGE_SIZE;
				pBuffer += I2C_PAGE_SIZE;	
			}
			if(0 == NumOfPage && NumOfSingle != 0) {
				EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle);
				EEPROM_WaitForWriteEnd();//等待写入完成
			}
		}
	} else {	//如果字节总数不对齐字节8
		if( 0 == NumOfPage ) { //如果一页未满
			EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle);
			EEPROM_WaitForWriteEnd();//等待写入完成
		} else { //如果不止一页,而且对齐字节8
			NumByteToWrite = NumByteToWrite - count;	//减去第一页后,还剩多少个字节
			NumOfPage =  NumByteToWrite / I2C_PAGE_SIZE;	//第一页后面后有多少页
			NumOfSingle = NumByteToWrite % I2C_PAGE_SIZE;//最后一页有多少个
			
			if(count != 0) {
				EEPROM_Page_Write(WriteAddr, pBuffer, count);
				EEPROM_WaitForWriteEnd();//等待写入完成
				WriteAddr = WriteAddr + count;
				pBuffer = pBuffer + count;
			}
			
			while(NumOfPage != 0) {
				EEPROM_Page_Write(WriteAddr, pBuffer, I2C_PAGE_SIZE);
				EEPROM_WaitForWriteEnd();//等待写入完成
				--NumOfPage;
				WriteAddr = WriteAddr + I2C_PAGE_SIZE;
				pBuffer = pBuffer + I2C_PAGE_SIZE;
			}
			
			if(NumOfSingle != 0) {
				EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle);
				EEPROM_WaitForWriteEnd();//等待写入完成			
			}	
		}
	}
	return ;
}

uint8_t I2C_eeprom_Test(void) {
	uint16_t i;
	uint8_t read_buf[AT24C02_NUM_SIZE] = {0};
	uint8_t write_buf[AT24C02_NUM_SIZE] = {0};
	
	printf("成功写入!nr");
	for(i = 0; i < AT24C02_NUM_SIZE; i++) {
		write_buf[i] = i;
		printf("%3d ", write_buf[i]);
		if( (i & 15) == 15) {
			printf("nr");
		}	
	}
	
	I2C_EE_BufferWrite(0, write_buf, AT24C02_NUM_SIZE);
	
	EEPROM_Read(0, read_buf, AT24C02_NUM_SIZE);
	
	printf("成功读取!nr");
	for(i = 5; i < AT24C02_NUM_SIZE; i++) {
		printf("%3d ", read_buf[i]);
		if( (i & 15) == 15) {
			printf("nr");
		}	
	}

	for(i = 0; i < AT24C02_NUM_SIZE; i++) {
		if( read_buf[i] != write_buf[i]) {
			return 1;
		}
	}
	return 0;
}

结果如图所示:

如果刚开始写入的地址的数不对齐8,同样也是可以写入和读取的。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/767727.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号