I2C学习笔记---STM32F091VC+I2C存储设备(AT24C04BN或FM24CL64B)

2024-02-25 16:20

本文主要是介绍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)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/746018

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件