HAL库硬件I2C 阻塞,中断,DMA三种方法读写EEPROM
GPIO是开漏模式外部必须要上拉电阻
IIC_hard.c如下:
//iic_hard.c
#include "main.h"
#include "iic_hard.h"
#include "i2c.h"
#include "rs485.h"
#include "usart.h"
#include "stdio.h"
#define _DBUG_PRINTF_
#ifdef _DBUG_PRINTF_
#define DBUG_PRINTF(format, ...) printf("LINE-Error : %d "format"\r\n",__LINE__,##__VA_ARGS__)
#define DBUG_ERROR(format, ...) printf(format"\r\n",##__VA_ARGS__)
#else
#define DBUG_PRINTF(format, ...)
#define DBUG_ERROR(format, ...)
#endif
/*
写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。
对于24xx02,page size = 8
对于24xx04,page size = 16
简单的处理方法为:按字节写操作模式,每写1个字节,都发送地址
为了提高连续写的效率: 本函数采用page wirte操作。
I2C_MEMADD_SIZE_8BIT
I2C_MEMADD_SIZE_16BIT
24C16以下容量的地址为8位,24C32以上容量的地址为16位,
在调用读写函数时需要注意,选择I2C_MEMADD_SIZE_8BIT或者I2C_MEMADD_SIZE_16BIT
*/
#define hIIC hi2c1
#define hEEPROM_IIC I2C1
#define EE_DEV_WR I2C_7BIT_ADD_WRITE(EE_DEV_ADDR) /* 写控制bit */
#define EE_DEV_RD I2C_7BIT_ADD_READ(EE_DEV_ADDR) /* 读控制bit */
// #define EE_Delay() HAL_Delay(2)//阻塞
#define EE_Delay() HAL_Delay(5)//中断和DMA
//------------------------------------------------------------------------------------
// #define IIC_MEM_WRITE(W_Addr,W_buf,W_size) HAL_I2C_Mem_Write(&hIIC,EE_DEV_WR,W_Addr,I2C_MEMADD_SIZE_8BIT,W_buf, W_size,0xffff);
// #define IIC_MEM_WRITE(W_Addr,W_buf,W_size) HAL_I2C_Mem_Write_IT(&hIIC,EE_DEV_WR,W_Addr,I2C_MEMADD_SIZE_8BIT,W_buf, W_size);
#define IIC_MEM_WRITE(W_Addr,W_buf,W_size) HAL_I2C_Mem_Write_DMA(&hIIC,EE_DEV_WR,W_Addr,I2C_MEMADD_SIZE_8BIT,W_buf, W_size);
// #define IIC_MEM_READ(R_Addr,R_buf,R_size) HAL_I2C_Mem_Read(&hIIC,EE_DEV_RD,R_Addr,I2C_MEMADD_SIZE_8BIT,R_buf, R_size,0xffff)//阻塞
// #define IIC_MEM_READ(R_Addr,R_buf,R_size) HAL_I2C_Mem_Read_IT(&hIIC,EE_DEV_RD,R_Addr,I2C_MEMADD_SIZE_8BIT,R_buf, R_size); //中断
#define IIC_MEM_READ(R_Addr,R_buf,R_size) HAL_I2C_Mem_Read_DMA(&hIIC,EE_DEV_RD,R_Addr,I2C_MEMADD_SIZE_8BIT,R_buf, R_size); //中断DMA
//------------------------------------------------------------------------------------
void I2C_DMA_IS_BUSY(I2C_HandleTypeDef *hi2c);
uint8_t E2ROM_CheckOk(void)
{
if (HAL_I2C_Master_Transmit(&hIIC, EE_DEV_WR,0,1,0xFFFF ) == HAL_OK)
return 1;
else
return 0;
}
uint8_t EE_WaitStandState(HAL_StatusTypeDef IIC_status)
{
if (IIC_status != HAL_OK)
{
uint32_t IIC_error = HAL_I2C_GetError(&hIIC);
if(IIC_error){
DBUG_ERROR("IIC_error : 0X%0x\r\n",IIC_error);
return 0;
}
}
return 1;
}
/*
*********************************************************************************************************
* 函 数 名: E2ROM_ReadBytes
* 功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
* 形 参: usAddress : 起始地址
* usSize : 数据长度,单位为字节
* pReadBuf : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t E2ROM_ReadBytes(uint8_t *pReadBuf, uint16_t usAddress, uint16_t usSize)
{
uint16_t i;
int32_t iTime1, iTime2;
u8 IIC_status=0;
IIC_status = HAL_I2C_IsDeviceReady(&hIIC, EE_DEV_ADDR| I2C_RD, 10, 0xFFFF);
if (IIC_status != HAL_OK)
{
printf("HAL_I2C_IsDeviceReady: \r\n");
goto cmd_fail;
}
IIC_status = IIC_MEM_READ(usAddress,pReadBuf,usSize);
if (IIC_status != HAL_OK)
{
printf("HAL_I2C_Mem_Read_DMA: \r\n");
goto cmd_fail;
}
return 1;
cmd_fail:
printf("读eeprom出错!\r\n");
switch( IIC_status )
{
case HAL_OK:break;
case HAL_ERROR:printf("HAL_ERROR \r\n");return 0;
case HAL_BUSY:printf("HAL_BUSY \r\n");return 0;
case HAL_TIMEOUT:printf("HAL_TIMEOUT\r\n");return 0;
default:return 0;
}
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: E2ROM_WriteBytes
* 功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
* 形 参: usAddress : 起始地址
* usSize : 数据长度,单位为字节
* pWriteBuf : 存放读到的数据的缓冲区指针
* 返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t E2ROM_WriteBytes(uint8_t *pWriteBuf, uint16_t usAddress, uint16_t usSize)
{
uint16_t i,m;
uint16_t usAddr;
u8 IIC_status=0;
IIC_status = HAL_I2C_IsDeviceReady(&hIIC, EE_DEV_ADDR, 10, 0xFFFF);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
//-----------------------------------------------------------------------------------
uint8_t num_page=0,num_single=0,addr =0,count=0,temp=0;
addr =usAddress%EE_PAGE_SIZE;
count =EE_PAGE_SIZE-addr;
num_page =usSize/EE_PAGE_SIZE;
num_single =usSize%EE_PAGE_SIZE;
usAddr = usAddress;
// 当发送的地址是页开始
if(!addr)
{
if(!num_page)//小于1页
{
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, num_single);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
}
else//大于1页
{
while(num_page--)
{
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, EE_PAGE_SIZE);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
usAddr +=EE_PAGE_SIZE;
pWriteBuf +=EE_PAGE_SIZE;
EE_Delay();
I2C_DMA_IS_BUSY(&hIIC);
}
if(num_single !=0)
{
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, num_single);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
}
}
}
else//地址没有对齐到页
{
if(!num_page)//写入的小于1页
{
if(num_single>count)//数量大于1页的剩余数量
{
//先把1页写满
temp=num_single-count;
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, count);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
usAddr +=count;
pWriteBuf +=count;
EE_Delay();
//剩余的翻页写
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, temp);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
}
else
{
//写入的数量小于,写满1页的剩余数量
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, num_single);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
}
}
else
{
usSize -= count;
num_page =usSize/EE_PAGE_SIZE;
num_single =usSize%EE_PAGE_SIZE;
//写入的大于1页
// 先把开始地址所在的页写满
if(count)
{
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, count);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
usAddr +=count;
pWriteBuf +=count;
EE_Delay();
}
//剩下的和大于1页的相同
while(num_page--)
{
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, EE_PAGE_SIZE);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
usAddr +=EE_PAGE_SIZE;
pWriteBuf +=EE_PAGE_SIZE;
EE_Delay();
}
if(num_single )
{
IIC_status =IIC_MEM_WRITE(usAddr,pWriteBuf, num_single);
if (!EE_WaitStandState(IIC_status))
{
DBUG_PRINTF();
goto cmd_fail;
}
}
}
}
/* 命令执行成功 */
return 1;
cmd_fail: /* 命令执行失败*/
printf("写 eeprom 出错!\r\n");
switch( IIC_status )
{
case HAL_OK:break;
case HAL_ERROR:printf("HAL_ERROR \r\n");return 0;
case HAL_BUSY:printf("HAL_BUSY \r\n");return 0;
case HAL_TIMEOUT:printf("HAL_TIMEOUT\r\n");return 0;
default:return 0;
}
return 0;
}
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if(hIIC.Instance == hEEPROM_IIC)
{
}
}
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if(hIIC.Instance == hEEPROM_IIC)
{
}
}
void HAL_I2C_MemTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if(hIIC.Instance == hEEPROM_IIC)
{
uint8_t DMA_state=HAL_DMA_GetState(hi2c->hdmatx);
if(HAL_DMA_STATE_READY != DMA_state)
{
hi2c->hdmatx->State =HAL_DMA_STATE_READY;
}
}
}
void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
if(hIIC.Instance == hEEPROM_IIC)
{
uint8_t DMA_state=HAL_DMA_GetState(hi2c->hdmarx);
if(HAL_DMA_STATE_READY != DMA_state)
{
hi2c->hdmarx->State =HAL_DMA_STATE_READY;
}
}
}
void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
if(hIIC.Instance == hEEPROM_IIC)
{
}
}
void I2C_DMA_IS_BUSY(I2C_HandleTypeDef *hi2c)
{
uint8_t DMA_state=HAL_DMA_GetState(hi2c->hdmatx);
if(HAL_DMA_STATE_READY != DMA_state)
{
hi2c->hdmatx->State =HAL_DMA_STATE_READY;
}
}
//---------------------------------------------------------------------------------------------------------------
//测试程序
static uint8_t buf[EE_SIZE];
/*
*********************************************************************************************************
* 函 数 名: E2ROM_ReadTest
* 功能说明: 读串行EEPROM全部数据,并打印出来
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void E2ROM_ReadTest(void)
{
uint16_t i;
int32_t iTime1, iTime2;
u8 IIC_status=0;
IIC_status = HAL_I2C_IsDeviceReady(&hIIC, EE_DEV_ADDR| I2C_RD, 10, 0xFFFF);
if (IIC_status != HAL_OK)
{
printf("HAL_I2C_IsDeviceReady: \r\n");
goto cmd_fail;
}
/* 读EEPROM, 起始地址 = 0, 数据长度为 256 */
IIC_status = IIC_MEM_READ(0,buf,EE_SIZE);
if (IIC_status != HAL_OK)
{
printf("HAL_I2C_Mem_Read_DMA: \r\n");
goto cmd_fail;
}
else
{
printf("读eeprom成功,数据如下:\r\n");
}
/* 打印数据 */
for (i = 0; i < EE_SIZE; i++)
{
printf(" %02X", buf[i]);
if ((i & 31) == 31)
{
printf("\r\n"); /* 每行显示16字节数据 */
}
else if ((i & 31) == 15)
{
printf(" - ");
}
EE_Delay();
}
return;
cmd_fail:
printf("读eeprom出错!\r\n");
switch( IIC_status )
{
case HAL_OK:break;
case HAL_ERROR:printf("HAL_ERROR \r\n");return;
case HAL_BUSY:printf("HAL_BUSY \r\n");return;
case HAL_TIMEOUT:printf("HAL_TIMEOUT\r\n");return;
default:return;
}
return;
}
/*
*********************************************************************************************************
* 函 数 名: E2ROM_WriteTest
* 功能说明: 写串行EEPROM全部数据
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void E2ROM_WriteTest(void)
{
uint16_t i;
int32_t iTime1, iTime2;
/* 填充测试缓冲区 */
for (i = 0; i < EE_SIZE; i++)
{
buf[i] = 0;
}
/* 写EEPROM, 起始地址 = 0,数据长度为 EE_SIZE */
if (E2ROM_WriteBytes(buf, 0, EE_SIZE) == 0)
{
printf("写eeprom出错!\r\n");
return;
}
else
{
printf("写eeprom成功!\r\n");
}
}
/*
*********************************************************************************************************
* 函 数 名: ee_ReadTest
* 功能说明: 读串行EEPROM全部数据,并打印出来
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
static void E2ROM_Erase(void)
{
uint16_t i;
/* 填充缓冲区 */
for (i = 0; i < EE_SIZE; i++)
{
buf[i] = 0xFF;
}
/* 写EEPROM, 起始地址 = 0,数据长度为 EE_SIZE */
if (E2ROM_WriteBytes(buf, 0, EE_SIZE) == 0)
{
printf("擦除eeprom出错!\r\n");
return;
}
else
{
printf("擦除eeprom成功!\r\n");
}
}
static void E2ROM_DispMenu(void)
{
printf("\r\n------------------------------------------------\r\n");
printf("请选择操作命令:\r\n");
printf("1 - 读EEPROM (%d 字节)\r\n", EE_SIZE);
printf("2 - 写EEPROM (%d 字节,0x00-0xFF)\r\n", EE_SIZE);
printf("3 - 擦除EEPROM\r\n");
printf("4 - 从头开始写入\r\n");
printf("5 - 从指定位置写数据\r\n");
printf("6 - 显示命令提示\r\n");
}
void DemoEEPROM(void)
{
uint8_t cmd;
if (E2ROM_CheckOk() == 0)
{
/* 没有检测到EEPROM */
printf("没有检测到串行EEPROM!\r\n");
while (1); /* 停机 */
}
printf("已经检测到串行EEPROM : \r\n");
printf("型号: %s, 容量 = %dx2 字节, 页面大小 = %d\r\n", EE_MODEL_NAME, EE_SIZE, EE_PAGE_SIZE);
E2ROM_DispMenu(); /* 打印命令提示 */
while(1)
{
//cmd = getchar(); /* 从串口读入一个字符 (阻塞方式) */
// if (comGetChar(COM1, &cmd)) /* 从串口读入一个字符(非阻塞方式) */
RS485_RX();
if (HAL_OK == HAL_UART_Receive(&huart2,&cmd,1,0xFFFF)) /* 从串口读入一个字符(非阻塞方式) */
{
switch (cmd)
{
case '1':
printf("\r\n【1 - 读 EEPROM 测试】\r\n");
E2ROM_ReadTest(); /* 读EEPROM数据,并打印出来数据内容 */
break;
case '2':
printf("\r\n【2 - 写 EEPROM 测试】\r\n");
E2ROM_WriteTest(); /* 写EEPROM数据*/
break;
case '3':
printf("\r\n【3 - 擦除 EEPROM】\r\n");
E2ROM_Erase(); /* 写入全0xFF */
break;
case '4':
{
int num;
printf("\r\n 请输出写入数量: <100\r\n ");
scanf("%d",&num);
if(!num)break;
printf("\r\n");
printf("请输入 %d 个数据:",num);
getchar();
u8 dat[100];
for (size_t i = 0; i <num ; i++)
{
scanf("%d",(int*)&dat[i]);
}
// scanf("%*[^\n]%*c");
E2ROM_WriteBytes(dat,0,num);
printf("\r\n 写入完成");
printf("输入的是 \r\n");
for (size_t i = 0; i <num ; i++)
{
printf("%0X ",dat[i]);
}
printf("\n");
getchar();
}break;
case '5':
{
int num;
printf("\r\n 请输写入数量:");
scanf("%d",&num);
if(!num)break;
getchar();
int addr;
printf("\r\n 请输写入开始地址:");
scanf("%d",&addr);
getchar();
printf("\r 请输入 %d 个数据:",num);
u8 dat[258];
for (size_t i = 0; i <num ; i++)
{
scanf("%d",(int*)&dat[i]);
}
E2ROM_WriteBytes(dat,addr,num);
printf("\r\n 写入完成:");
getchar();
}break;
default:
E2ROM_DispMenu(); /* 无效命令,重新打印命令提示 */
break;
}
}
}
}
/*****************************************************************/
IIC_hard.h如下:
//iic_hard.h
#ifndef IIC_HARD_H
#define IIC_HARD_H
#include <stdlib.h>
extern void DemoEEPROM(void);
#endif
回调函数里面的判断和I2C_DMA_IS_BUSY()的作用是,解决用阻塞和DMA时出现BUSY问题
在main里面调用DemoEEPROM()
源码见源码