本帖最后由 zero949079783 于 2021-11-21 16:32 编辑
开发环境:VSCODE(gcc编译链)+STM32CubeMX(也可以使用HUAWEI-LiteOS-Studio) 。 代码:链接:https://pan.baidu.com/s/1uXfIR0GFQOBZPl1NfQP08w
提取码:6b0c
Master features 主模式特性
I2C Speed Mode: IIC模式设置 快速模式和标准模式。实际上也就是速率的选择。
I2C Clock Speed:I2C传输速率,默认为100KHz
Slave features 从模式特性
Clock No Stretch Mode: 时钟没有扩展模式
IIC时钟拉伸(Clock stretching)
clock stretching通过将SCL线拉低来暂停一个传输.直到释放SCL线为高电平,传输才继续进行.clock stretching是可选的,实际上大多数从设备不包括SCL驱动,所以它们不能stretch时钟.
Primary Address Length selection: 从设备地址长度 设置从设备的地址是7bit还是10bit 大部分为7bit
-Dual Address Acknowledged: 双地址确认
Primary slave address: 从设备初始地址
stm32f4xx_hal_i2c.c文件:
1、HAL_StatusTypeDef HAL_I2C_IsDeviceReady(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint32_t Trials, uint32_t Timeout) // Checks if target device is ready for communication.
eg:
//检查设备是否准备好,地址检查,次数超时
status=HAL_I2C_IsDeviceReady(&hi2c1, ADDRESS_W, 10, HAL_MAX_DELAY);
2、HAL_I2C_StateTypeDef HAL_I2C_GetState(I2C_HandleTypeDef *hi2c) //Return the I2C handle state. 总线工作状态
eg:
//检查总线是否准备好
flag=HAL_I2C_GetState(&hi2c1);
3、HAL_StatusTypeDef HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout) //Write an amount of data in blocking mode to a specific memory address
eg:
//发送写寄存器命令
HAL_I2C_Mem_Write(&hi2c1, ADDRESS_W, MPU_PWR_MGMT1_REG, 1, &pdata, 1, HAL_MAX_DELAY);
4、HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
// Read an amount of data in blocking mode from a specific memory address MemAddSize 只能填1or2,代表8位或者16位,size代表uint16_t。
eg:
//读取寄存器命令
HAL_I2C_Mem_Read(&hi2c1, ADDRESS_R, MPU_DEVICE_ID_REG, 1, &pdata, 1, HAL_MAX_DELAY);
#define I2C_MEMADD_SIZE_8BIT 0x00000001U ,上边的1代表的是8bit。
#define I2C_MEMADD_SIZE_16BIT 0x00000010U
HAL库硬件I2C写数据:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] AT24C02任意地址写入一个字节
* @param addr —— 写数据的地址(0-255)
* @param dat —— 存放写入数据的地址
* @param size :数据长度
* @retval 成功 —— HAL_OK
*/
uint32_t AT24C02_Write_Data(uint16_t addr,uint8_t dat)
{
HAL_StatusTypeDef status = HAL_OK;
status = HAL_I2C_Mem_Write(&hi2c1,AT24C02_READ_ADD,addr,I2C_MEMADD_SIZE_8BIT,&dat,1,100);
// if(status != HAL_OK)
// {
// /* Execute user timeout callback */
// }
while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY) //总线超时检测
{
}
/*****************************************************************
检查设备是否准备好,地址检查,次数超时 ,必须加入,否则总线会写进去数据
*****************************************************************/
while (HAL_I2C_IsDeviceReady(&hi2c1, AT24C02_READ_ADD, 300, 300) == HAL_TIMEOUT)
{
}
// /* Wait for the end of the transfer */
while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)//总线超时检测
{
}
return status;
}
HAL库硬件I2C读数据:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] AT24C02任意地址读多个字节数据
* @param addr —— 读数据的地址(0-255)
* @param read_buf —— 存放读取数据的地址
* @param size :数据长度
* @retval 成功 —— HAL_OK
*/
uint32_t AT24C02_Read_Byte(uint8_t addr, uint8_t *read_buf,uint16_t size)
{
HAL_StatusTypeDef status = HAL_OK;
status = HAL_I2C_Mem_Read(&hi2c1,AT24C02_READ_ADD,addr,I2C_MEMADD_SIZE_8BIT,read_buf,size,0xFFFF);
return status;
}
HAL库模拟IIC写数据:
void AT24C02_Write_Data(uint8_t Addr,uint8_t Data){
/* 第1步:发起I2C总线启动信号 */
I2C_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_Write_Byte(AT24C02_WRITE_ADD);
/* 第3步:等待ACK */
I2C_Wirte_Ack();
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
I2C_Write_Byte(Addr);
/* 第5步:等待ACK */
I2C_Wirte_Ack();
/* 第6步:开始写入数据 */
I2C_Write_Byte(Data);
/* 第7步:发送ACK */
I2C_Wirte_Ack();
/* 发送I2C总线停止信号 */
I2C_Stop();
delay_ms(5);
}
HAL库模拟IIC读数据:
uint8_t AT24C02_Read_Data(uint8_t Addr)
{
uint8_t Data;
/* 第1步:发起I2C总线启动信号 */
I2C_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_Write_Byte(AT24C02_WRITE_ADD);
/* 第3步:等待ACK */
I2C_Wirte_Ack();
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
I2C_Write_Byte(Addr);
/* 第5步:等待ACK */
I2C_Wirte_Ack();
/* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
I2C_Start();
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_Write_Byte(AT24C02_READ_ADD);
/* 第8步:发送ACK */
I2C_Wirte_Ack();
/* 第9步:读取数据 */
Data=I2C_Read_Data();
/* 第10步:发送ACK */
I2C_Wirte_Ack();
/* 发送I2C总线停止信号 */
I2C_Stop();
return Data;
}
标准库模拟IIC写数据:
void AT24C02_Write_Data(uint8_t Addr,uint8_t Data){
/* 第1步:发起I2C总线启动信号 */
I2C_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_Write_Byte(AT24C02_WRITE_ADD);/* 此处是写指令 */
/* 第3步:发送一个时钟,判断器件是否正确应答 */
I2C_Wirte_Ack();
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
I2C_Write_Byte(Addr);
/* 第5步:等待ACK */
I2C_Wirte_Ack();
/* 第6步:开始写入数据 */
I2C_Write_Byte(Data);
/* 第7步:发送ACK */
I2C_Wirte_Ack();
/* 命令执行成功,发送I2C总线停止信号 */
I2C_Stop();
//写完入后延时
delay_ms(5);
}
标准库模拟IIC读数据:
uint8_t AT24C02_Read_Data(uint8_t Addr)
{
uint8_t Data;
/* 第1步:发起I2C总线启动信号 */
I2C_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_Write_Byte(AT24C02_WRITE_ADD);
/* 第3步:等待ACK */
I2C_Wirte_Ack();
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
I2C_Write_Byte(Addr);
/* 第5步:等待ACK */
I2C_Wirte_Ack();
/* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
I2C_Start();
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_Write_Byte(AT24C02_READ_ADD);/* 此处是读指令 */
/* 第8步:发送ACK */
I2C_Wirte_Ack();
/* 第9步:读取数据 */
Data=I2C_Read_Data();
/* 第8步:发送ACK */
I2C_Wirte_Ack();
/* 命令执行成功,发送I2C总线停止信号 */
I2C_Stop();
return Data;
}
标准库硬件IIC写数据:
标准库硬件IIC读数据:
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] I2C读多个字节
* @param device_addr :设备地址
* @param addr :地址
* @param data :数据
* @param size :数据长度
* [url=home.php?mod=space&uid=2817080]@ARG[/url]
* @retval
*/
uint32_t I2C_Read_Data(uint8_t device_addr,uint8_t addr,uint8_t *data,uint16_t size){
//第一次起始信号
I2C_GenerateSTART(I2C1,ENABLE ); //起始信号
TimeOut = I2CTIME; //超时检测时间
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT ) == ERROR) //EV5事件
{
if(TimeOut-- == 0) // 防卡死 超时检测时间
{
return 0;
}
}
//EV5事件被检测到,发送设备地址
I2C_Send7bitAddress(I2C1,device_addr,I2C_Direction_Transmitter);//设备地址,写入模式
TimeOut = I2CTIME; //超时检测时间
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR) //写数据EV6事件
{
if(TimeOut-- == 0) // 防卡死 超时检测时间
{
return 0;
}
}
//EV6事件被检测到,发送要操作的存储单元地址
I2C_SendData(I2C1,addr); //地址
TimeOut = I2CTIME; //超时检测时间
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR) //写数据EV8事件
{
if(TimeOut-- == 0) // 防卡死 超时检测时间
{
return 0;
}
}
//第二次起始信号
I2C_GenerateSTART(I2C1,ENABLE ); //起始信号
TimeOut = I2CTIME; //超时检测时间
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT ) == ERROR) //EV5事件
{
if(TimeOut-- == 0) // 防卡死 超时检测时间
{
return 0;
}
}
//EV5事件被检测到,发送设备地址
I2C_Send7bitAddress(I2C1,EEPROM_I2C_ADDR,I2C_Direction_Receiver);//读入模式
while(size){
if(size==1){
//接收最后一个字节
I2C_AcknowledgeConfig(I2C1,DISABLE);
}
TimeOut = I2CTIME; //超时检测时间
while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED ) == ERROR) //读数据EV7事件
{
if(TimeOut-- == 0) // 防卡死 超时检测时间
{
return 0;
}
}
//EV7事件被检测到时,数据寄存器有新的有效数据
*data = I2C_ReceiveData(I2C1);
data++; //下一个数据
size--;
}
//数据传输完成
I2C_GenerateSTOP(I2C1,ENABLE);
//I2C_AcknowledgeConfig(I2C1,ENABLE); //重新配置ACK使能,以便下次通讯
return 1;
}
|
|