文章目录
- 一.概要
- 二.IIC总线基本概念
- 1.总体特征
- 2.通讯流程
- 三.EEPROM介绍
- 1.M24C08基本介绍
- 2.向M24C08写一个字节时序图
- 3.从M24C08读一个字节时序图
- 四.GPIO模拟IIC驱动M24C08读写
- 五.CubeMX工程源代码下载
- 六.讲解视频链接地址
- 七.小结
一.概要
IIC(Inter-Integrated Circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线,用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信,在小数据量场合使用,传输距离短,任意时刻只能有一个主机等特性。
二.IIC总线基本概念
I2C 总线,分别由SDA(串行数据线)和SCL(串行时钟线)及上拉电阻组成。
1.总体特征
2.通讯流程
三.EEPROM介绍
EEPROM( Electrically Erasable Programmable Read Only Memory )全称是电可擦除可编程只读存储器,是非易失存储器,可以访问到每个字节,容量比较小。目前的EEPROM一般是IIC接口或者SPI接口的。
1.M24C08基本介绍
M24C08是ST公司(意法半导体公司)的串行接口的EEPROM存储芯片,它的容量是8K bit。
芯片功能描述:
M24C08系列支持I2C,总线数据传送协议I2C,总线协议规定任何将数据传送到总线的器件作为发送器。任何从总线接收数据的器件为接收器;数据传送是由产生串行时钟和所有起始停止信号的主器件控制的。主器件和从器件都可以作为发送器或接收器,但由主器件控制传送数据(发送或接收)的模式。
芯片特性介绍:
1.两线串行接口(SDA、SCL)
2.有用于硬件数据保护的写保护引脚
3.自定时写入周期(5毫秒),因为内部有页缓冲区,向M24C08写入数据之后,还需要等待M24C08将缓冲区数据写入到内部EEPROM区域
4.数据保存可达100年
5.100万次擦写周期
6.高数据传送速率为400KHz、低速100KHZ和IIC总线兼容
7.16字节页写缓冲区
这个缓冲区大小与芯片具体型号有关: 8字节页(1K、2K)、16字节页(4K、8K、16K)
M24C08的E2(3脚)一般接地,E1,E0是容量大点的EEPROM才有,这样M24C08这个器件,写操作时候的地址就是0xA0,读操作时候的地址是0xA1。
2.向M24C08写一个字节时序图
写操作过程中只有ACK是M24C08发出的,其他信号都是单片机发出的。
3.从M24C08读一个字节时序图
读操作过程中ACK跟返回的数据是M24C08发出的,其他信号都是单片机发出的。
四.GPIO模拟IIC驱动M24C08读写
硬件准备:
STLINK接STM32F103C8T6小系统板,STLINK接电脑USB口,
STM32F103C8T6小系统板跟EEPROM模块连接:
板子G----模块GND
板子3.3–模块VCC
板子PB10—模块SCL
板子PB11—模块SDA
打开STM32CubeMX软件,新建工程
Part Number处输入STM32F103C8,再双击就创建新的工程
配置下载口引脚
配置外部晶振引脚
配置系统主频
配置工程文件名,保存路径,KEIL5工程输出方式
生成工程
用Keil5打开工程
添加代码
主要代码
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();//8M外部晶振,系统主频72M
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
EE_IIC_Init();//管脚初始化
EE_EE_IIC_SendByteToSlave(0xA0,0x00,0x55);//0地址写入0x55
HAL_Delay(10);//等待10ms
EE_IIC_ReadByteFromSlave(0xA0,0x00,&EEDATA);//从0地址读取内容存储到变量
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
#include "main.h"
#define EE_ADDR 0xa0
#define EE_SCL_PIN GPIO_PIN_10 //模拟IIC的SCL信号 1.修改引脚即可修改IIC接口
#define EE_SDA_PIN GPIO_PIN_11 //模拟IIC的SDA信号
void EE_SDA_IN(void) //配置成输入
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void EE_SDA_OUT(void)//配置成输出
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_11;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void EE_SCK_OUT(void) //配置成输出
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
#define EE_IIC_SCL(val) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10,val) //SCL 2.修改引脚即可修改IIC接口
#define EE_IIC_SDA(val) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11,val) //SDA
unsigned char EE_READ_SDA(void)
{
return HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_11);
}
/******************************************************************************
*函 数:void EE_IIC_Delay(void)
*功 能:IIC延时
*参 数:无
*返回值:无
*备 注: 移植时只需要将EE_IIC_Delay()换成自己的延时即可
*******************************************************************************/
void EE_IIC_Delay(uint8_t us)
{
for(int i = 0; i < 20; i++)
{
__asm("NOP");//core bus 160M 情况下大概IIC速率 400K
}
}
/******************************************************************************
*函 数:void IIC_Init(void)
*功 能:IIC初始化
*参 数:无
*返回值:无
*备 注:无
*******************************************************************************/
void EE_IIC_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
EE_SCK_OUT();
EE_SDA_OUT();
EE_IIC_SCL(1);
EE_IIC_SDA(1);
}
void EE_IIC_Start(void)
{
EE_SDA_OUT(); //sda线输出
EE_IIC_SDA(1);
EE_IIC_SCL(1);
EE_IIC_Delay(4);
EE_IIC_SDA(0); //START:when CLK is high,DATA change form high to low
EE_IIC_Delay(4);
EE_IIC_SCL(0); //钳住I2C总线,准备发送或接收数据
}
void EE_IIC_Stop(void)
{
EE_SDA_OUT(); //sda线输出
EE_IIC_SCL(0);
EE_IIC_SDA(0); //STOP:when CLK is high DATA change form low to high
EE_IIC_Delay(4);
EE_IIC_SCL(1);
EE_IIC_SDA(1); //发送I2C总线结束信号
EE_IIC_Delay(4);
}
uint8_t EE_IIC_WaitAck(void)
{
uint8_t ucErrTime=0;
EE_SDA_IN(); //SDA设置为输入 (从机给一个低电平做为应答)
EE_IIC_SDA(1);EE_IIC_Delay(1);
EE_IIC_SCL(1);EE_IIC_Delay(1);;
while(EE_READ_SDA())
{
ucErrTime++;
if(ucErrTime>250)
{
EE_IIC_Stop();
return 1;
}
}
EE_IIC_SCL(0); //时钟输出0
return 0;
}
void EE_IIC_Ack(void)
{
EE_IIC_SCL(0);
EE_SDA_OUT();
EE_IIC_SDA(0);
EE_IIC_Delay(1);
EE_IIC_SCL(1);
EE_IIC_Delay(2);
EE_IIC_SCL(0);
}
void EE_IIC_NAck(void)
{
EE_IIC_SCL(0);
EE_SDA_OUT();
EE_IIC_SDA(1);
EE_IIC_Delay(1);
EE_IIC_SCL(1);
EE_IIC_Delay(1);
EE_IIC_SCL(0);
}
void EE_IIC_SendByte(uint8_t data)
{
uint8_t t;
EE_SDA_OUT();
EE_IIC_SCL(0); //拉低时钟开始数据传输
for(t=0;t<8;t++)
{
EE_IIC_SDA((data&0x80)>>7);
EE_IIC_Delay(1);
EE_IIC_SCL(1);
data<<=1;
EE_IIC_Delay(1);
EE_IIC_SCL(0);
}
EE_IIC_Delay(1);
}
uint8_t EE_IIC_ReadByte(uint8_t ack)
{
uint8_t i,receive=0;
EE_SDA_IN(); //SDA设置为输入模式 等待接收从机返回数据
for(i=0;i<8;i++ )
{
EE_IIC_SCL(0);
EE_IIC_Delay(1);
EE_IIC_SCL(1);
receive<<=1;
if(EE_READ_SDA())receive++; //从机发送的电平
EE_IIC_Delay(1);
}
if(ack)
EE_IIC_Ack(); //发送ACK
else
EE_IIC_NAck(); //发送nACK
return receive;
}
uint8_t EE_IIC_ReadByteFromSlave(uint8_t I2C_Addr,uint8_t reg,uint8_t *buf)
{
EE_IIC_Start();
EE_IIC_SendByte(I2C_Addr); //发送从机地址
if(EE_IIC_WaitAck()) //如果从机未应答则数据发送失败
{
EE_IIC_Stop();
return 1;
}
EE_IIC_SendByte(reg); //发送寄存器地址
EE_IIC_WaitAck();
EE_IIC_Start();
EE_IIC_SendByte(I2C_Addr+1); //进入接收模式
EE_IIC_WaitAck();
*buf=EE_IIC_ReadByte(0);
EE_IIC_Stop(); //产生一个停止条件
return 0;
}
uint8_t EE_EE_IIC_SendByteToSlave(uint8_t I2C_Addr,uint8_t reg,uint8_t data)
{
EE_IIC_Start();
EE_IIC_SendByte(I2C_Addr); //发送从机地址
if(EE_IIC_WaitAck())
{
EE_IIC_Stop();
return 1; //从机地址写入失败
}
EE_IIC_SendByte(reg); //发送寄存器地址
EE_IIC_WaitAck();
EE_IIC_SendByte(data);
if(EE_IIC_WaitAck())
{
EE_IIC_Stop();
return 1; //数据写入失败
}
EE_IIC_Stop(); //产生一个停止条件
return 0;
}
五.CubeMX工程源代码下载
链接:https://pan.baidu.com/s/1MMYww6IgUNa3wPaN-pWqdw
提取码:d39f
如果链接失效,可以联系博主给最新链接
程序下载下来之后解压就行
六.讲解视频链接地址
IIC总线讲解
七.小结
学会使用模拟IIC,能适应各种平台,不管是STM32的单片机,还是其他牌子的单片机,都能很快实现对EEPROM等IIC总线的设备进行数据读写。