本文主要是介绍I2C学习笔记---STM32F091VC+I2C存储设备(AT24C04BN或FM24CL64B),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
前言:
常用的IIC类存储设备是以EEPROM和铁电为主。如:AT24C0xBN(x=2,4,8),这是ATMEL公司的EEPROM;FM24CL64B或者FM24CL16B,这是CYPRESS公司的铁电(F-RAM)。后续章节IIC程序是基于AT24C04BN和FM24CL64B这两款设备。
IIC时序简介:
起始信号:在SCL为高电平期间,SDA由高变低;
停止信号:在SCL为高电平期间,SDA由低变高。如下图所示:
应答信号:在8位数据信号传输完成之后,第9位就是应答信号。应答信号分为应答和非应答,应答为低电平,非应答为高电平,如下图所示:
IIC总线数据传输时序如下:
对于AT24C04BN与FM24CL64B在读写操作时,存在点差别。主要是因为FM24CL64B的内存64KB,寻址空间是0x0000~0xFFFFH,访问时需要先传输地址高字节,再传输地址低字节。
AT24C04BN写数据流程:START-->DEVICE ADDRESS(写)-->ACK-->WORD ADDRESS-->ACK-->DATA-->ACK-->STOP,其中ACK为从设备发送的应答信号(低电平),主机接收应答。
AT24C04BN读数据流程:START-->DEVICE ADDRESS(写)-->ACK-->WORD ADDRESS-->START-->DEVICE ADDRESS(读)-->ACK-->DATA-->NACK-->STOP。其中ACK是从设备发送的应答信号,低电平有效,NACK是主机发送的非应答信号,高电平有效。
FM24CL64B写数据流程:START-->Slave Address(写)-->ACK-->Address MSB-->ACK-->Address LSB-->ACK-->Data Byte-->ACK-->STOP。其中ACK是从机发送的应答信号,低电平有效。
FM24CL64B读数据流程:START-->Slave Address(写)-->ACK-->Address MSB-->ACK-->Address LSB-->ACK-->START-->Slave Address(读)-->ACK-->Data Byte-->NACK-->STOP。其中,ACK是从设备发送的应答信号,低电平有效,NACK是主机发送的非应答信号,高电平有效。
试验中,我使用单片机是STM32F091VC,程序中通过宏定义来兼容AT24C04BN和FM24CL64B两款I2C设备。#if 0默认使用AT24C04BN,#if 1则使用FM24CL64B。
#if 1
#define FM24CL64 1
#endif
以下是bsp_fram.h头文件代码:
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef _BSP_FRAM_H
#define _BSP_FRAM_H#include "main.h"#ifdef __cplusplus
extern "C" {
#endif
/* 定义读写SCL和SDA的宏,已增加代码的可移植性和可阅读性 */
/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define GPIO_PORT_I2C GPIOB /* GPIO端口 */
#define RCC_I2C_PORT RCC_AHBPeriph_GPIOB /* GPIO端口时钟 */
#define I2C_SCL_PIN GPIO_Pin_8 /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_PIN GPIO_Pin_9 /* 连接到SDA数据线的GPIO */#define I2C_SCL_H GPIO_SetBits(GPIO_PORT_I2C, I2C_SCL_PIN) /* SCL = 1 */
#define I2C_SCL_L GPIO_ResetBits(GPIO_PORT_I2C, I2C_SCL_PIN) /* SCL = 0 */#define I2C_SDA_H GPIO_SetBits(GPIO_PORT_I2C, I2C_SDA_PIN) /* SDA = 1 */
#define I2C_SDA_L GPIO_ResetBits(GPIO_PORT_I2C, I2C_SDA_PIN) /* SDA = 0 */#define I2C_SDA_READ() GPIO_ReadInputDataBit(GPIO_PORT_I2C, I2C_SDA_PIN)/* 读SDA口线状态 */#define I2C_SPEED_1K 5000 //根据处理器速度设置,这里处理器速度是72MHztypedef enum
{I2C_SUCCESS = 0,I2C_TIMEOUT,I2C_ERROR,
}I2C_StatusTypeDef;extern uint32_t i2c_speed; //I2C访问速度 = I2C_SPEED_1K / i2c_speed/* ---------------------------依照I2C协议编写的时序函数------------------------------*/
void BSP_I2C_Init(void); //初始化I2C的IO口
void I2C_Start(void); //发送I2C开始信号
void I2C_Stop(void); //发送I2C停止信号
uint8_t I2C_Wait_ACK(void); //I2C等待ACK信号
void I2C_ACK(void); //I2C发送ACK信号
void I2C_NACK(void); //I2C不发送ACK信号
void I2C_Send_Byte(uint8_t data); //I2C发送一个字节
uint8_t I2C_Read_Byte(uint8_t ack); //I2C读取一个字节
uint16_t I2C_SetSpeed(uint16_t speed);//设置I2C速度(1Kbps~400Kbps,speed单位,Kbps)/* ---------------------------以下部分是封装好的I2C读写函数--------------------------- *///具体到某一个器件,请仔细阅读器件规格书关于I2C部分的说明,因为某些器件在I2C的读写操作会
//有一些差异,下面的代码我们在绝大多数的I2C器件中,都是验证OK的!
I2C_StatusTypeDef I2C_WriteOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t Data);//向I2C从设备写入一个字节
I2C_StatusTypeDef I2C_WriteBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num);//向I2C从设备连续写入Num个字节
I2C_StatusTypeDef I2C_ReadOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t* Data);//从I2C从设备读取一个字节
I2C_StatusTypeDef I2C_ReadBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num);//从I2C设备连续读取Num个字节
I2C_StatusTypeDef I2C_WriteBit(uint8_t DevAddr, uint8_t DataAddr, uint8_t Bitx, uint8_t BitSet);#endif /* _bsp_FRAM_H */
以下是bsp_fram.c的c文件代码:
/* Includes ------------------------------------------------------------------*/
#include "stm32f0xx.h"
#include "main.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#if 1#define FM24CL64 1#endif
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
// I2C访问速度 = I2C_SPEED_1K / i2c_speed
uint32_t i2c_speed;
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*//*** @brief 模拟I2C接口初始化* @param None* @retval None* @note* SCL: PB8* SDA: PB9*/
void BSP_I2C_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHBPeriphClockCmd(RCC_I2C_PORT|RCC_AHBPeriph_GPIOD|RCC_AHBPeriph_GPIOE,ENABLE); /* 打开GPIO时钟 */GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; /* 输出 */GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN|I2C_SDA_PIN;GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);I2C_SetSpeed(100);//设置I2C访问速度为100Kbps
}void SDA_IN(void)
{GPIO_InitTypeDef GPIO_InitStructure;GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);
}
void SDA_OUT(void)
{GPIO_InitTypeDef GPIO_InitStructure;GPIO_StructInit(&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIO_PORT_I2C, &GPIO_InitStructure);
}/*** @brief 产生I2C起始信号* @param None* @retval None* @note* 请参考I2C通信协议,I2C起始信号:当SCL为高电平时,SDA由高变低* 如下图所示:方框部分表示I2C起始信号* _____ |* __|__ | | ___ ___ ___ ___ ___ ___ ___ ___ * SDA: | \__|____|_/ \/ \/ \/ \/ \/ \/ \/ \ /* | | | \___/\___/\___/\___/\___/\___/\___/\___/\___/* __|_____|_ | _ _ _ _ _ _ _ _ _* SCL: | | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_* |_____| | * start D7 D6 D5 D4 D3 D2 D1 D0 ACK*/
void I2C_Start(void)
{uint32_t i2c_delay = i2c_speed;SDA_OUT(); //SDA设置为输出I2C_SDA_H; //SDA: 高I2C_SCL_H; //SCL: 高i2c_delay = i2c_speed;//延时>4.7uswhile(i2c_delay--){}I2C_SDA_L; //当SCL为高电平时,SDA由高变低i2c_delay = i2c_speed;//延时>4uswhile(i2c_delay--){}I2C_SCL_L; //SCL变低,钳住I2C总线,准备发送或接收数据
}/*** @brief 产生I2C停止信号* @param None* @retval None* @note* 请参考I2C通信协议,I2C停止信号:当SCL为高电平时,SDA由低变高* 发送完STOP信号后,SCL和SDA都为高电平,即释放了I2C总线* 如下图所示:方框部分表示I2C起始信号* _____* ___ ___ ___ ___ | __|_* SDA: / \/ \/ \/ \ | / |* \___/\___/\___/\___/\______|_/ |* _ _ _ _ _ _|_____|_* SCL: / \__/ \__/ \__/ \__/ \__/ | |* |_____|* D3 D2 D1 D0 ACK stop*/
void I2C_Stop(void)
{uint32_t i2c_delay = i2c_speed;SDA_OUT(); //SDA设置为输出I2C_SDA_L; //SDA低电平I2C_SCL_H; //SCL高电平i2c_delay = i2c_speed;//延时>4uswhile(i2c_delay--){}I2C_SDA_H; //STOP:当SCL为高电平时,SDA由低变高i2c_delay = i2c_speed;while(i2c_delay--){}//延时>4.7us
}/*** @brief 等待ACK应答信号* @param None* @retval 1 - 未接收到应答信号ACK;0 - 接收到应答信号ACK* @note* 请参考I2C通信协议,检测ACK应答信号:当SCL为高电平时,读取SDA为低电平* 如下图所示:方框部分表示I2C起始信号* ________ _____* ___ ___ ___ ___ | _ | | __|_* SDA: / \/ \/ \/ \|/ \ | | / |* \___/\___/\___/\___/| \____|___|_/ |* _ _ _ _ | _____ | _|_____|* SCL: / \__/ \__/ \__/ \__|/ \_|_/ | |* |________| |_____|* D3 D2 D1 D0 ACK stop*/
uint8_t I2C_Wait_ACK(void)
{uint32_t i2c_delay = i2c_speed;uint8_t timeout = 0;SDA_IN(); //SDA设置为输入I2C_SDA_H; //SDA上拉输入I2C_SCL_H; //SCL设置为高电平i2c_delay = i2c_speed;while(i2c_delay--){}while(I2C_SDA_READ() == 1)//等待ACK{if(timeout++ > 250){I2C_Stop();return 1;}}I2C_SCL_L;//钳住I2C总线:时钟信号设为低电平return 0;
}/*** @brief 产生ACK应答信号* @param None* @retval None* @note* 请参考I2C通信协议,产生ACK应答信号: 在SDA为低电平时,SCL产生一个正脉冲* 如下图所示:方框部分表示I2C起始信号* _____ _____* ___ ___ ___ ___ | | | __|_* SDA: / \/ \/ \/ \| | | / |* \___/\___/\___/\___/|\____|___|_/ |* _ _ _ _ | _ | _|_____|_* SCL: / \__/ \__/ \__/ \__|_/ \_|_/ | |* |_____| |_____|* D3 D2 D1 D0 ACK stop*/
void I2C_ACK(void)
{uint32_t i2c_delay = i2c_speed;I2C_SCL_L; //低电平SDA_OUT(); //设置SDA为输出I2C_SDA_L; //ACK信号i2c_delay = i2c_speed;while(i2c_delay--){}//延时>4usI2C_SCL_H; //高电平i2c_delay = i2c_speed;while(i2c_delay--){}//延时>4usI2C_SCL_L; //钳住I2C总线:时钟信号设为低电平
}/*** @brief 产生非应答信号NACK* @param None* @retval None* @note* 请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲* 如下图所示:方框部分表示I2C起始信号* _____ ______* ___ ___ ___ ___ | ____|_ | __|_* SDA: / \/ \/ \/ \|/ | \ | / |* \___/\___/\___/\___/| | \_|__/ |* _ _ _ _ | _ | __|______|_* SCL: / \__/ \__/ \__/ \__|_/ \_|_/ | |* |_____| |______|* D3 D2 D1 D0 NACK stop*/
void I2C_NACK(void)
{uint32_t i2c_delay = i2c_speed;I2C_SCL_L; //低电平SDA_OUT(); //SDA设置为输出I2C_SDA_H; //NACK信号i2c_delay = i2c_speed;while(i2c_delay--){}//延时>4usI2C_SCL_H; //高电平i2c_delay = i2c_speed;while(i2c_delay--){}//延时>4usI2C_SCL_L; //钳住I2C总线:时钟信号设为低电平
}/*** @brief I2C发送一个字节* @param None* @retval None* @note* 请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲* 如下图所示:方框部分表示I2C起始信号** _____ |<------------I2C数据发送周期------------>|* __|__ | | ___ ___ ___ ___ ___ ___ ___ ___ | _ * SDA: | \__|____|_/ \/ \/ \/ \/ \/ \/ \/ \|/ * | | | \___/\___/\___/\___/\___/\___/\___/\___/|\_* __|_____|_ | _ _ _ _ _ _ _ _ | * SCL: | | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_* |_____| | |* start | D7 D6 D5 D4 D3 D2 D1 D0 |*/
void I2C_Send_Byte(uint8_t data)
{ uint8_t i = 0;uint32_t i2c_delay = i2c_speed;SDA_OUT(); //SDA设为输出I2C_SCL_L; //钳住I2C总线:SCL设为低电平for(i = 0; i < 8; i++){if(data&0x80)I2C_SDA_H; //高位先传else I2C_SDA_L;i2c_delay = i2c_speed;while(i2c_delay--){} //延时>4usI2C_SCL_H; //在SCL上产生一个正脉冲i2c_delay = i2c_speed;while(i2c_delay--){} //延时>4usI2C_SCL_L;i2c_delay = i2c_speed/3;while(i2c_delay--){} //延时>1usdata <<= 1; //右移一位}
}/*** @brief 从I2C读取一个字节* @param ack : 0 - NACK; 1 - ACK* @retval 接收到的数据* @note* 请参考I2C通信协议,产生ACK应答信号: 在SDA为高电平时,SCL产生一个正脉冲* 如下图所示:方框部分表示I2C起始信号** _____ |<------------I2C数据读取周期(ACK)------------>|* __|__ | | ___ ___ ___ ___ ___ ___ ___ ___ |* SDA: | \__|____|_/ \/ \/ \/ \/ \/ \/ \/ \ |* | | | \___/\___/\___/\___/\___/\___/\___/\___/\____|_* __|_____|_ | _ _ _ _ _ _ _ _ _ |* SCL: | | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_* |_____| | |* start | D7 D6 D5 D4 D3 D2 D1 D0 ACK** _____ |<------------I2C数据读取周期(NACK)----------->|* __|__ | | ___ ___ ___ ___ ___ ___ ___ ___ ____|_* SDA: | \__|____|_/ \/ \/ \/ \/ \/ \/ \/ \/ |* | | | \___/\___/\___/\___/\___/\___/\___/\___/ |* __|_____|_ | _ _ _ _ _ _ _ _ _ |* SCL: | | \__|__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \__/ \_|_* |_____| | |* start | D7 D6 D5 D4 D3 D2 D1 D0 NACK*/
uint8_t I2C_Read_Byte(uint8_t ack)
{uint8_t i, receive = 0x00;uint32_t i2c_delay = i2c_speed;I2C_SCL_L; //SCL低电平SDA_IN(); //SDA设置为输入for(i = 0; i < 8; i++){i2c_delay = i2c_speed;while(i2c_delay--);I2C_SCL_H; //高电平i2c_delay = i2c_speed;while(i2c_delay--);receive <<= 1;if(I2C_SDA_READ()) receive |= 1; //高位在前I2C_SCL_L;}if (ack == 0) I2C_NACK(); //发送NACKelse I2C_ACK(); //发送ACKreturn receive; //返回接收到的数据
}/*** @brief 设置I2C速度* @param speed : I2C速度,单位Kbps* @retval 返回设置前的I2C速度* @note I2C速度设置范围是: 1Kbps ~ 400Kbps*/
uint16_t I2C_SetSpeed(uint16_t speed)
{uint16_t temp;//I2C速度必须小于400Kbps,大于 1Kbpsif((speed > 400)|| (speed < 1)) return 0;temp = I2C_SPEED_1K / i2c_speed; //备份原来的i2c速度i2c_speed = I2C_SPEED_1K / speed; //设置新的i2c速度return temp; //返回设置前的i2c速度
}/* ---------------------------以下部分是封装好的I2C读写函数--------------------------- *///具体到某一个器件,请仔细阅读器件规格书关于I2C部分的说明,因为某些器件I2C的读写操作会
//有一些差异,下面的代码我们在绝大多数的I2C器件中,都是验证OK的!/*** @brief 向设备指定地址写入单一Byte数据* @param DevAddr : I2C从设备地址* @param DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)* @param Data : 写入的数据* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note * 1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写* 2 - 时序:* _______________________________________* | | | | | | | | |* Master:|S|DevAddr+W| |DataAddr| |Data| |P|* |_|_________|___|________|___|____|___|_|* _______________________________________* | | | | | | | | |* Slave: | | |ACK| |ACK| |ACK| |* |_|_________|___|________|___|____|___|_|*/
I2C_StatusTypeDef I2C_WriteOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t Data)
{I2C_Start(); //Master发送起始信号I2C_Send_Byte(DevAddr); //Master发送从设备地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#ifdef FM24CL64I2C_Send_Byte(DataAddr >> 8); //发送高8位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误I2C_Send_Byte(DataAddr); //发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#elseI2C_Send_Byte(DataAddr); //发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#endifI2C_Send_Byte(Data); //发送数据if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误I2C_Stop(); //发送停止信号return I2C_SUCCESS;
}/*** @brief 向设备指定地址连续写入数据(Burst写模式)* @param DevAddr : I2C从设备地址* @param DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)* 对于Burst模式,DataAddr一般是设备的FIFO,缓存,或存储设备的数据地址* @param *pData : 写入的数据首地址* @param Num : 连续写入的数据个数* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note * 1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写* 2 - 时序:* ____________________________________________________* | | | | | | | | | | | |* Master:|S|DevAddr+W| |DataAddr| |Data| |...|Data| |P|* |_|_________|___|________|___|____|___|___|____|___|_|* ____________________________________________________* | | | | | | | | | | | |* Slave: | | |ACK| |ACK| |ACK|...| |ACK| |* |_|_________|___|________|___|____|___|___|____|___|_|*/
I2C_StatusTypeDef I2C_WriteBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num)
{uint32_t i = 0;I2C_Start(); //Master发送起始信号I2C_Send_Byte(DevAddr); //Master发送从设备地址if(I2C_Wait_ACK()) return I2C_TIMEOUT; //等待ACK超时错误
#ifdef FM24CL64I2C_Send_Byte(DataAddr >> 8); //发送高8位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误I2C_Send_Byte(DataAddr); //发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#elseI2C_Send_Byte(DataAddr); //发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#endiffor(i = 0; i < Num; i++){I2C_Send_Byte(*(pData+i)); //发送数据if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误}I2C_Stop(); //发送停止信号return I2C_SUCCESS;
}/*** @brief 从指定设备读取1Byte数据* @param DevAddr : I2C从设备地址* @param DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)* @param *Data : 数据的存放地址* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note * 1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写* 2 - 时序:* _________________________________________________________* | | | | | | | | | | | |* Master:|S|DevAddr+W| |DataAddr| |S|DevAddr+R| | |NACK|P|* |_|_________|___|________|____|_|_________|___|____|____|_|* _________________________________________________________* | | | | | | | | | | | |* Slave: | | |ACK| |ACK | | |ACK|Data| | |* |_|_________|___|________|____|_|_________|___|____|____|_|*/
I2C_StatusTypeDef I2C_ReadOneByte(uint8_t DevAddr, uint8_t DataAddr, uint8_t* Data)
{I2C_Start(); //Master发送起始信号I2C_Send_Byte(DevAddr); //Master发送从设备地址if(I2C_Wait_ACK()) return I2C_TIMEOUT; //等待ACK超时错误
#ifdef FM24CL64I2C_Send_Byte(DataAddr >> 8); //发送高8位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误I2C_Send_Byte(DataAddr); //发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#elseI2C_Send_Byte(DataAddr); //发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#endifI2C_Start(); //Master发送起始信号I2C_Send_Byte(DevAddr+1); //Master发送从设备读地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误*Data = I2C_Read_Byte(0); //读数据,NACKI2C_Stop(); //发送停止信号return I2C_SUCCESS;
}/*** @brief 向设备指定地址连续写入数据(Burst写模式)* @param DevAddr : I2C从设备地址* @param DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)* 对于Burst模式,DataAddr一般是设备的FIFO,缓存,或存储设备的数据地址* @param *pData : 写入的数据首地址* @param Num : 连续写入的数据个数* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note * 1 - 设备地址DevAddr高7bit是固定的,最低为是读/写(R/W)位,1为读,0为写* 2 - 时序:* _____________________________________________________________________* | | | | | | | | | | | | | | |* Master:|S|DevAddr+W| |DataAddr| |S|DevAddr+R| | |ACK|...| |NACK|P|* |_|_________|___|________|___|_|_________|___|____|___|___|____|____|_|* _____________________________________________________________________* | | | | | | | | | | | | | | |* Slave: | | |ACK| |ACK| | |ACK|Data| |...|Data| | |* |_|_________|___|________|___|_|_________|___|____|___|___|____|____|_|*/
I2C_StatusTypeDef I2C_ReadBurst(uint8_t DevAddr, uint8_t DataAddr, uint8_t* pData, uint32_t Num)
{uint32_t i = 0;I2C_Start(); //Master发送起始信号I2C_Send_Byte(DevAddr); //Master发送从设备地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#ifdef FM24CL64I2C_Send_Byte(DataAddr >> 8); //发送高8位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误I2C_Send_Byte(DataAddr); //发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#elseI2C_Send_Byte(DataAddr); //发送低八位数据地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误
#endifI2C_Start(); //Master发送起始信号I2C_Send_Byte(DevAddr+1); //Master发送从设备读地址if(I2C_Wait_ACK()) return I2C_TIMEOUT;//等待ACK超时错误for(i = 0; i < (Num-1); i++){*(pData+i) = I2C_Read_Byte(1); //读数据,ACK}*(pData+i) = I2C_Read_Byte(0); //读数据,NACKI2C_Stop(); //发送停止信号return I2C_SUCCESS;
}/*** @brief 设置数据的某一位* @param DevAddr : I2C从设备地址* @param DataAddr: 需要访问的设备内地址(如寄存器地址,EEPROM地址等)* @param Bitx : 第几位* @param BitSet: 需要设置的值* @retval I2C访问的结果: I2C_SUCCESS / I2C_TIMEOUT / I2C_ERROR* @note */
I2C_StatusTypeDef I2C_WriteBit(uint8_t DevAddr, uint8_t DataAddr, uint8_t Bitx, uint8_t BitSet)
{I2C_StatusTypeDef status = I2C_ERROR;uint8_t tempdata = 0;status = I2C_ReadOneByte(DevAddr, DataAddr, &tempdata); //获取原有数据if(status != I2C_SUCCESS) return status; //I2C错误,则返回tempdata &= ~(1<<Bitx); //将要设定的位清零tempdata |= (BitSet<<Bitx); //设置指定的bitstatus = I2C_WriteOneByte(DevAddr, DataAddr, tempdata); //写入数据return status; //返回状态
}
这篇关于I2C学习笔记---STM32F091VC+I2C存储设备(AT24C04BN或FM24CL64B)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!