STM32 通过I2C 读写EEPR0M AT24C02
一、I2C简介两线式串行总线属于同步通信共用主机时钟SCL每个连接到I2C总线上的设备都有一个唯一的地址SCL、SDA均需要接上拉电阻设备空闲均输出高电平传输速率标准模式下100Kbit/sI2C协议I2C 的协议定义了通信的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。在SCL高电平时数据有效3.每次数据传输都以字节为单位每次传输的字节数不受限制。4.起始信号SCL高电平时SDA产生下降沿5.停止信号SCL高电平时SDA产生上升沿6.应答信号传输数据的第九位时由从机拉低SDA,在SCL高电平时读SDA如果是低电平就表示应答每个字节必须保证为8位长度每传输一帧为9位 8位1位应答数据传输时先传输最高位寻址字节是起始信号后的第一个字节1要针对哪个从机 2读还是写从机地址分为固定部分可编程部分举例读取一个字节的流程1~11完全和c这种组合方式一致如写数据的过程和a这种组合方式一致二、EEPR0M AT24C02I2C接口介绍硬件介绍使用软件模拟I2C时序。这里设计的上拉电阻为10K:三、工程实现新建文件夹及文件iic.h#ifndef __IIC_H #define __IIC_H #include system.h /* IIC_SCL时钟端口、引脚定义 */ #define IIC_SCL_PORT GPIOB #define IIC_SCL_PIN (GPIO_Pin_10) #define IIC_SCL_PORT_RCC RCC_APB2Periph_GPIOB //为了使能端口时钟 /* IIC_SDA时钟端口、引脚定义 */ #define IIC_SDA_PORT GPIOB #define IIC_SDA_PIN (GPIO_Pin_11) #define IIC_SDA_PORT_RCC RCC_APB2Periph_GPIOB //IO操作函数 使用位带操作 #define IIC_SCL PBout(10) //SCL #define IIC_SDA PBout(11) //SDA #define READ_SDA PBin(11) //输入SDA /* IIC所有操作函数 */ void IIC_Init(void); //初始化IIC的IO口 void IIC_Start(void); //发送IIC开始信号 void IIC_Stop(void); //发送IIC停止信号 void IIC_Send_Byte(u8 txd); //IIC发送一个字节 u8 IIC_Read_Byte(u8 ack); //IIC读取一个字节 u8 IIC_Wait_Ack(void); //IIC等待ACK信号 void IIC_Ack(void); //IIC发送ACK信号 void IIC_NAck(void); //IIC不发送ACK信号,不应答 #endifiic.c#include iic.h #include SysTick.h //需要使用delay_us /* IIC所有操作函数 */ void IIC_Init(void) //初始化IIC的IO口 { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(IIC_SCL_PORT_RCC|IIC_SDA_PORT_RCC, ENABLE); //使能GPIOB端口时钟 GPIO_InitStructure.GPIO_Pin IIC_SCL_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; //配置为推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(IIC_SCL_PORT, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin IIC_SDA_PIN; GPIO_Init(IIC_SDA_PORT, GPIO_InitStructure); //总线开始为空闲状态 IIC_SCL 1; IIC_SDA 1; } /******************************************************************************* * 函 数 名 : SDA_OUT * 函数功能 : SDA输出配置 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void SDA_OUT(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin IIC_SDA_PIN; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_PP; //输出时配置为推挽输出 GPIO_Init(IIC_SDA_PORT, GPIO_InitStructure); } /******************************************************************************* * 函 数 名 : SDA_IN * 函数功能 : SDA输入配置 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void SDA_IN(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin IIC_SDA_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPU; //配置为上拉输入 GPIO_Init(IIC_SDA_PORT, GPIO_InitStructure); } void IIC_Start(void) //发送IIC开始信号 { SDA_OUT(); IIC_SDA 1; IIC_SCL 1; delay_us(5); IIC_SDA 0; //START:when CLK is high,DATA change form high to low 此处为起始信号 delay_us(6); IIC_SCL 0; //钳住I2C总线准备发送或接收数据 } void IIC_Stop(void) //发送IIC停止信号 { SDA_OUT(); IIC_SCL 0; IIC_SDA 0; //STOP:when CLK is high DATA change form low to high IIC_SCL 1; delay_us(6); IIC_SDA 1; //发送I2C总线结束信号 delay_us(6); } /******************************************************************************* * 函 数 名 : IIC_Wait_Ack * 函数功能 : 主机等待应答信号到来 * 输 入 : 无 * 输 出 : 1接收应答失败 0接收应答成功 *******************************************************************************/ u8 IIC_Wait_Ack(void) { u8 tempTime 0; IIC_SDA 1; //释放控制权 delay_us(1); SDA_IN(); //SDA设置为输入 IIC_SCL 1; //高电平开始检查应答信号 delay_us(1); while(READ_SDA) //如果读到被拉低则表示有应答 { tempTime; if(tempTime 250) { IIC_Stop(); return 1; //表示无应答 } } IIC_SCL 0; //时钟输出0 return 0; } /******************************************************************************* * 函 数 名 : IIC_Ack * 函数功能 : 主机产生ACK应答 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void IIC_Ack(void) { IIC_SCL 0; SDA_OUT(); IIC_SDA 0; delay_us(2); IIC_SCL 1; //时钟有效了延时一会后把IIC_SCL拉低表示应答信号 delay_us(5); IIC_SCL 0; } /******************************************************************************* * 函 数 名 : IIC_NAck * 函数功能 : 主机产生NACK非应答 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void IIC_NAck(void) { IIC_SCL 0; SDA_OUT(); IIC_SDA 1; delay_us(2); IIC_SCL 1; delay_us(5); IIC_SCL 0; } /******************************************************************************* * 函 数 名 : IIC_Send_Byte * 函数功能 : IIC发送一个字节 * 输 入 : txd发送一个字节 * 输 出 : 无 *******************************************************************************/ void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL 0;//拉低时钟开始数据传输 for(t 0; t 8; t) { if((txd0x80) 0) //0x80 1000 0000 判断8位最高位是1还是0下一步是发送 IIC_SDA 1; else IIC_SDA 0; txd1; //将低位移到最高位等待下一位的发送 delay_us(2); //对TEA5767这三个延时都是必须的数据保持 IIC_SCL 1; //数据开始有效此时不能被更改 delay_us(2); IIC_SCL 0; //时钟为低电平数据失效 delay_us(2); } } /******************************************************************************* * 函 数 名 : IIC_Read_Byte * 函数功能 : IIC读一个字节 * 输 入 : ack1时发送ACKack0发送nACK * 输 出 : 应答或非应答 *******************************************************************************/ u8 IIC_Read_Byte(u8 ack) { u8 i, receive 0; SDA_IN(); //SDA设置为输入 for(i 0; i 8; i) { IIC_SCL 0; delay_us(2); IIC_SCL 1; //时钟有效开始读取IIC_SDA上的数据 receive1; //i 0时该左移无效 if(READ_SDA) receive; delay_us(1); } if(!ack) IIC_NAck(); //发送nACK else IIC_Ack(); //发送ACK return receive; }AT24Cxx.h#ifndef __AT24CXX_H #define __AT24CXX_H #include system.h #include iic.h #define AT24C01 127 #define AT24C02 255 //我们PZ6806L是这个 #define AT24C04 511 #define AT24C08 1023 #define AT24C16 2047 #define AT24C32 4095 #define AT24C64 8191 #define AT24C128 16383 #define AT24C256 32767 //开发板使用的是AT24C02所以定义EE_TYPE为AT24C02 #define EE_TYPE AT24C02 u8 AT24CXX_ReadOneByte(u16 ReadAddr); //指定地址读取一个字节 void AT24CXX_WriteOneByte(u16 WriteAddr, u8 DataToWrite); //指定地址写入一个字节 void AT24CXX_WriteLenByte(u16 WriteAddr, u32 DataToWrite,u8 Len); //指定地址开始写入指定长度的数据 u32 AT24CXX_ReadLenByte(u16 ReadAddr, u8 Len); //指定地址开始读取指定长度数据 void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer, u16 NumToWrite); //从指定地址开始写入指定长度的数据 void AT24CXX_Read(u16 ReadAddr, u8 *pBuffer, u16 NumToRead); //从指定地址开始读出指定长度的数据 u8 AT24CXX_Check(void); //检查器件 void AT24CXX_Init(void); //初始化IIC #endifAT24Cxx.c#include AT24Cxx.h #include SysTick.h /******************************************************************************* * 函 数 名 : AT24CXX_Init * 函数功能 : AT24CXX初始化 * 输 入 : 无 * 输 出 : 无 *******************************************************************************/ void AT24CXX_Init(void) { IIC_Init(); //IIC初始化 } /******************************************************************************* * 函 数 名 : AT24CXX_ReadOneByte * 函数功能 : 在AT24CXX指定地址读出一个数据 * 输 入 : ReadAddr:开始读数的地址 * 输 出 : 读到的数据 *******************************************************************************/ u8 AT24CXX_ReadOneByte(u16 ReadAddr) { u8 temp 0; //1.开始 IIC_Start(); if(EE_TYPE AT24C16) { IIC_Send_Byte(0xA0); //发送写命令 IIC_Wait_Ack(); IIC_Send_Byte(ReadAddr 8);//发送高地址 } else { // 对于AT24C02来讲ReadAddr范围0~255最大为255 //2.从机地址(写) IIC_Send_Byte(0xA0 ((ReadAddr/256) 1)); //发送器件地址0xA0,写数据 0xA0 ((ReadAddr/256) 1) 0xA0 } //3.等待从机应答信号 IIC_Wait_Ack(); //4.发送存储器的低地址(可以理解为数据) IIC_Send_Byte(ReadAddr % 256); //发送存储器的低地址注意和器件地址的区别 //5.等待从机应答信号 IIC_Wait_Ack(); //6.开始 IIC_Start(); //7.从机地址(读) IIC_Send_Byte(0xA1); //读数据 //8.等待从机应答信号 IIC_Wait_Ack(); //9.从机数据有效可以读取 //10.主机发送不应答信号在函数里实现 temp IIC_Read_Byte(0); //参数0表示不需要应答由主机发送 //11.停止 IIC_Stop(); //产生一个停止条件 return temp; } /******************************************************************************* * 函 数 名 : AT24CXX_WriteOneByte * 函数功能 : 在AT24CXX指定地址写入一个数据 * 输 入 : WriteAddr :写入数据的目的地址 DataToWrite:要写入的数据 * 输 出 : 无 *******************************************************************************/ void AT24CXX_WriteOneByte(u16 WriteAddr, u8 DataToWrite) { //1. IIC_Start(); if(EE_TYPE AT24C16) { IIC_Send_Byte(0xA0); //发送写命令 IIC_Wait_Ack(); IIC_Send_Byte(WriteAddr 8); //发送高地址 } else { //2. IIC_Send_Byte(0xA0 ((WriteAddr/256) 1)); //发送器件地址0XA0,写数据,最低位为0 } //3. IIC_Wait_Ack(); //4. IIC_Send_Byte(WriteAddr % 256); //发送存储器低地址AT24C02为0~255 //5. IIC_Wait_Ack(); //6. IIC_Send_Byte(DataToWrite); //发送字节需要保存的数据8位 //7. IIC_Wait_Ack(); //8. IIC_Stop(); //产生一个停止条件 delay_ms(10); } /******************************************************************************* * 函 数 名 : AT24CXX_WriteLenByte * 函数功能 : 在AT24CXX里面的指定地址开始写入长度为Len的数据 用于写入16bit或者32bit的数据 * 输 入 : WriteAddr :写入数据的目的地址 DataToWrite:要写入的数据 Len :要写入数据的长度2,4 * 输 出 : 无 *******************************************************************************/ void AT24CXX_WriteLenByte(u16 WriteAddr, u32 DataToWrite, u8 Len) { u8 t; for(t 0; t Len; t) { AT24CXX_WriteOneByte(WriteAddr t, (DataToWrite (8 * t)) 0xff); } } /******************************************************************************* * 函 数 名 : AT24CXX_ReadLenByte * 函数功能 : 在AT24CXX里面的指定地址开始读出长度为Len的数据 用于读出16bit或者32bit的数据 * 输 入 : ReadAddr :开始读出的地址 Len :要读出数据的长度2,4 * 输 出 : 读取的数据 *******************************************************************************/ u32 AT24CXX_ReadLenByte(u16 ReadAddr, u8 Len) { u8 t; u32 temp 0; for(t 0; t Len; t) { temp 8; temp AT24CXX_ReadOneByte(ReadAddr Len - t -1); } return temp; } /******************************************************************************* * 函 数 名 : AT24CXX_Check * 函数功能 : 检查AT24CXX是否正常(通过测试能否读写来检查) * 输 入 : 无 * 输 出 : 1:检测失败0:检测成功 *******************************************************************************/ u8 AT24CXX_Check(void) { u8 temp; tempAT24CXX_ReadOneByte(255); //避免每次开机都写AT24CXX if(temp 0x36) return 0; else//排除第一次初始化的情况 { AT24CXX_WriteOneByte(255, 0x36); temp AT24CXX_ReadOneByte(255); if(temp 0x36) return 0; } return 1; } /******************************************************************************* * 函 数 名 : AT24CXX_Read * 函数功能 : 在AT24CXX里面的指定地址开始读出指定个数的数据 * 输 入 : ReadAddr :开始读出的地址 对24c02为0~255 pBuffer :数据数组首地址 NumToRead:要读出数据的个数 * 输 出 : 无 *******************************************************************************/ void AT24CXX_Read(u16 ReadAddr, u8 *pBuffer, u16 NumToRead) { while(NumToRead) { *pBuffer AT24CXX_ReadOneByte(ReadAddr); NumToRead--; } } /******************************************************************************* * 函 数 名 : AT24CXX_Write * 函数功能 : 在AT24CXX里面的指定地址开始写入指定个数的数据 * 输 入 : WriteAddr :开始写入的地址 对24c02为0~255 pBuffer :数据数组首地址 NumToRead:要读出数据的个数 * 输 出 : 无 *******************************************************************************/ void AT24CXX_Write(u16 WriteAddr, u8 *pBuffer, u16 NumToWrite) { while(NumToWrite--) { AT24CXX_WriteOneByte(WriteAddr, *pBuffer); WriteAddr; pBuffer; } }main.c#include system.h #include SysTick.h #include led.h #include usart.h #include key.h #include AT24Cxx.h int main() { u8 i0; u8 key; u8 k0; SysTick_Init(72); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组 LED_Init(); USART1_Init(9600); KEY_Init(); AT24CXX_Init(); while(AT24CXX_Check()) //检测AT24C02是否正常 { printf(AT24C02检测不正常! \r\n); delay_ms(500); } printf(AT24C02检测正常! \r\n); while(1) { key KEY_Scan(0); if(key KEY_UP) { k; if(k 255) { k 255; } AT24CXX_WriteOneByte(0, k); printf(写入的数据是%d\r\n, k); } if(key KEY_DOWN) { k AT24CXX_ReadOneByte(0); printf(读取的数据是%d\r\n, k); } i; if(i%20 0) { led1 !led1; } delay_ms(10); } }iic.x文件里注要实现标准iic的起始信号、停止信号等时序为了通用以AT24Cxx.x文件主要实现iic具体器件的读写要求如1字节从机器件地址和写 1字节从机操作地址 1字节要写的数据实验现象
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2576386.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!