STM8S207采用硬件I2C读写时,需要在读写函数间添加延时5ms,否则,程序会进入死循环。
程序测试可以通过,就是不明白为什么需要添加5ms延时?
i2c_ee.c
#include "i2c_ee.h"
#include "tim1.h"
//硬件I2C引脚: I2C_SCL-PE1,I2C_SDA-PE2
//函数功能:根据系统时钟,设置硬件I2C输出时钟频率为100KHz,自身地址为0xA0,并指定从机地址长度为7位
void I2C_EEInit(void)
{
u32 Input_Clock = 0x0;
Input_Clock = CLK_GetClockFreq();//获取系统时钟频率
Input_Clock = Input_Clock / 1000000;//单位为MHz
I2C_Cmd(ENABLE);//必须先启用硬件I2C功能模块
I2C_Init(I2C_Speed, I2C1_SLAVE_ADDRESS7, I2C_DUTYCYCLE_2,I2C_ACK_CURR, I2C_ADDMODE_7BIT, Input_Clock);
//设置硬件I2C输出时钟频率为100KHz
//设置硬件I2C自身地址为0xA0
//设置硬件I2C时钟占空比为2
//设置硬件I2C应答:对当前字节执行应答
//设置从机地址长度为7位,7-bit slave address (10-bit address not acknowledged)
//设置系统时钟,单位为MHz
}
//函数功能:将pBuffer所指向的存储数据写入EEPROM中地址为WriteAddr处
//写单一字节
void I2C_EE_ByteWrite(u8* pBuffer, u16 WriteAddr)
{
bool status=FALSE;
uint8_t tmpDeviceAddress;//EEPROM器件地址:1010+A2+A1+A0+R/W
uint8_t tmp;//方便计算中间量
delay_ms(5);//必须加延时,否则,硬件I2C会出错
tmp=(u8)(WriteAddr>>8);//读取"EEPROM高3位地址"
tmp=(u8)(tmp<<1);
if (WriteAddr>255) tmpDeviceAddress=EEPROM_DEVICE_ADDRESS+tmp;//地址在256~2048之间时,需要将高3位地址放在器件地址中的A2,A1,A0位置
else tmpDeviceAddress=EEPROM_DEVICE_ADDRESS;
I2C_GenerateSTART(ENABLE);//发送I2C起动条件,Send STRAT condition
status=FALSE;
while(status!=TRUE)//循环检测主机启动条件发送完成标志
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_START_SENT);
//读"主机启动条件发送完成标志"
//Test on EV5 and clear it
}
I2C_Send7bitAddress(tmpDeviceAddress, I2C_DIRECTION_TX);//发送器件写地址
//发送EEPROM写器件地址
//Send EEPROM address for write
status=FALSE;
while(status!=TRUE)//循环检测"主机发送EEPROM写器件地址结束标志"是否建立
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_ADDRESS_ACKED);
//读"主机发送EEPROM写器件地址结束标志",需要从机应答
//Test on EV6 and clear it
}
I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED);
//清除"写器件地址已被发送标志"
I2C_SendData((u8)(WriteAddr&0x00FF));//主机发送从机器件子地址低8位
status=FALSE;
while (status!=TRUE)//循环检测"主机发送从机器件子地址低8位完成标志"
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//读"主机发送从机器件子地址低8位完成标志"
//Test on EV8 and clear it
}
I2C_SendData(*pBuffer);
//主机将pBuffer所指向的存储数据发送给从机
//Send the byte to be written
status=FALSE;
while (status!=TRUE)
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//读"主机发送数据字节结束标志"
//Test on EV8 and clear it
}
I2C_GenerateSTOP(ENABLE);//主机发送I2C停止条件,Send STOP condition
}
//函数功能:将pBuffer[]中前count个字节写入EEPROM中,起始地址为firstaddress
//注意:支持页内写和跨页写,pBuffer[count],count必须小于等于Page_Byte_Size,才可使用;
void I2C_EE_PageWrite(unsigned char *pBuffer, u16 WriteAddr, u8 count)
{
unsigned char tmpOffsetAddress;
bool status;
unsigned char i;
uint8_t tmpDeviceAddress;//EEPROM器件地址:1010+A2+A1+A0+R/W
uint8_t tmp;//方便计算中间量
i=count;//记录有count个字节写入
tmpOffsetAddress=WriteAddr%Page_Byte_Size; //计算在当前页中的地址偏移量;
tmpOffsetAddress=Page_Byte_Size-tmpOffsetAddress; //计算距离下一页的边界有多少个字节;
if( tmpOffsetAddress<count )//若所写的字节要跨过一页,则执行下面;
{
delay_ms(5);//必须加延时,否则,硬件I2C会出错
tmp=(u8)(WriteAddr>>8);//读取"EEPROM高3位地址"
tmp=(u8)(tmp<<1);
if (WriteAddr>255) tmpDeviceAddress=EEPROM_DEVICE_ADDRESS+tmp;//地址在256~2048之间时,需要将高3位地址放在器件地址中的A2,A1,A0位置
else tmpDeviceAddress=EEPROM_DEVICE_ADDRESS;
status=TRUE;
while(status)//循环检测"硬件I2C总线忙标志"
{
status=I2C_GetFlagStatus(I2C_FLAG_BUSBUSY);//读"硬件I2C总线忙标志"
}
I2C_GenerateSTART(ENABLE);//发送I2C起动条件,Send STRAT condition
status=FALSE;
while(status!=TRUE)//循环检测主机启动条件发送完成标志
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_START_SENT);
//读"主机启动条件发送完成标志"
//Test on EV5 and clear it
}
I2C_Send7bitAddress(tmpDeviceAddress, I2C_DIRECTION_TX);//发送器件写地址
//发送EEPROM写器件地址
//Send EEPROM address for write
status=FALSE;
while(status!=TRUE)//循环检测"主机发送EEPROM写器件地址结束标志"是否建立
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_ADDRESS_ACKED);
//读"主机发送EEPROM写器件地址结束标志",需要从机应答
//Test on EV6 and clear it
}
I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED);
//清除"写器件地址已被发送标志"
I2C_SendData((u8)(WriteAddr&0x00FF));//主机发送从机器件子地址低8位
status=FALSE;
while (status!=TRUE)//循环检测"主机发送从机器件子地址低8位完成标志"
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//读"主机发送从机器件子地址低8位完成标志"
//Test on EV8 and clear it
}
for(i=0; i<tmpOffsetAddress; i++)
{
I2C_SendData(*pBuffer);
//主机将pBuffer所指向的存储数据发送给从机
//Send the byte to be written
status=FALSE;
while (status!=TRUE)
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//读"主机发送数据字节结束标志"
//Test on EV8 and clear it
}
pBuffer++;//Point to the next byte to be written
}
I2C_GenerateSTOP(ENABLE);//主机发送I2C停止条件,Send STOP condition
i=(count-tmpOffsetAddress); //计算还有多少个字没有写入EEPROM;
WriteAddr=WriteAddr+tmpOffsetAddress; //计算下一页的首地址;
}
if(i!=0)
{
delay_ms(5);//必须加延时,否则,硬件I2C会出错
tmp=(u8)(WriteAddr>>8);//读取"EEPROM高3位地址"
tmp=(u8)(tmp<<1);
if (WriteAddr>255) tmpDeviceAddress=EEPROM_DEVICE_ADDRESS+tmp;//地址在256~2048之间时,需要将高3位地址放在器件地址中的A2,A1,A0位置
else tmpDeviceAddress=EEPROM_DEVICE_ADDRESS;
status=TRUE;
while(status)//循环检测"硬件I2C总线忙标志"
{
status=I2C_GetFlagStatus(I2C_FLAG_BUSBUSY);//读"硬件I2C总线忙标志"
}
I2C_GenerateSTART(ENABLE);//发送I2C起动条件,Send STRAT condition
status=FALSE;
while(status!=TRUE)//循环检测主机启动条件发送完成标志
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_START_SENT);
//读"主机启动条件发送完成标志"
//Test on EV5 and clear it
}
I2C_Send7bitAddress(tmpDeviceAddress, I2C_DIRECTION_TX);//发送器件写地址
//发送EEPROM写器件地址
//Send EEPROM address for write
status=FALSE;
while(status!=TRUE)//循环检测"主机发送EEPROM写器件地址结束标志"是否建立
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_ADDRESS_ACKED);
//读"主机发送EEPROM写器件地址结束标志",需要从机应答
//Test on EV6 and clear it
}
I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED);
//清除"写器件地址已被发送标志"
I2C_SendData((u8)(WriteAddr&0x00FF));//主机发送从机器件子地址低8位
status=FALSE;
while (status!=TRUE)//循环检测"主机发送从机器件子地址低8位完成标志"
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//读"主机发送从机器件子地址低8位完成标志"
//Test on EV8 and clear it
}
for(; i!=0; i--)
{
I2C_SendData(*pBuffer);
//主机将pBuffer所指向的存储数据发送给从机
//Send the byte to be written
status=FALSE;
while (status!=TRUE)
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//读"主机发送数据字节结束标志"
//Test on EV8 and clear it
}
pBuffer++;//Point to the next byte to be written
}
I2C_GenerateSTOP(ENABLE);//主机发送I2C停止条件,Send STOP condition
}
}
//函数功能:从EEPROM中地址ReadAddr开始,连续读NumByteToRead个字节,保存到pBuffer[]中
void I2C_EE_BufferRead(u8* pBuffer, u16 ReadAddr, u8 NumByteToRead)
{
bool status;
uint8_t tmpDeviceAddress;//EEPROM器件地址:1010+A2+A1+A0+R/W
uint8_t tmp;//方便计算中间量
tmp=(u8)(ReadAddr>>8);
tmp=(u8)(tmp<<1);
if (ReadAddr>255) tmpDeviceAddress=EEPROM_DEVICE_ADDRESS+tmp;//地址在256~2048之间时,需要将高3位地址放在器件地址中的A2,A1,A0位置
else tmpDeviceAddress=EEPROM_DEVICE_ADDRESS;
delay_ms(5);//须加延时,否则硬件I2C会出错
status=TRUE;
while(status)//循环检测"硬件I2C总线忙标志"
{
status=I2C_GetFlagStatus(I2C_FLAG_BUSBUSY);//读"硬件I2C总线忙标志"
}
I2C_AcknowledgeConfig(I2C_ACK_CURR);//产生应答信号
I2C_GenerateSTART(ENABLE);//发送I2C起动条件,Send STRAT condition
status=FALSE;
while(status!=TRUE)//循环检测主机启动条件发送完成标志
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_START_SENT);
//读"主机启动条件发送完成标志"
//Test on EV5 and clear it
}
I2C_Send7bitAddress(tmpDeviceAddress, I2C_DIRECTION_TX);//发送器件写地址
//发送EEPROM写器件地址
//Send EEPROM address for write
status=FALSE;
while(status!=TRUE)//循环检测"主机发送EEPROM写器件地址结束标志"是否建立
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_ADDRESS_ACKED);
//读"主机发送EEPROM写器件地址结束标志",需要从机应答
//Test on EV6 and clear it
}
I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED);
//清除"写器件地址已被发送标志"
I2C_SendData((u8)(ReadAddr&0x00FF));//发送数据地址;
status=FALSE;
while (status!=TRUE)//循环检测"主机发送从机器件子地址低8位完成标志"
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED);
//读"主机发送从机器件子地址低8位完成标志"
//Test on EV8 and clear it
}
///发送重启条件
I2C_GenerateSTART(ENABLE);//发送I2C起动条件,Send STRAT condition
status=FALSE;
while(status!=TRUE)//循环检测主机启动条件发送完成标志
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_START_SENT);
//读"主机启动条件发送完成标志"
//Test on EV5 and clear it
}
I2C_Send7bitAddress(tmpDeviceAddress, I2C_DIRECTION_RX);//发送器件读地址
//发送EEPROM读器件地址
//Send EEPROM address for write
status=FALSE;
while(status!=TRUE)//循环检测"主机发送EEPROM读器件地址结束标志"是否建立
{
status = I2C_CheckEvent(I2C_EVENT_MASTER_ADDRESS_ACKED);
//读"主机发送EEPROM读器件地址结束标志",需要从机应答
//Test on EV6 and clear it
}
I2C_ClearFlag(I2C_FLAG_ADDRESSSENTMATCHED);
//清除"读器件地址已被发送标志"
while(NumByteToRead)
{
if(NumByteToRead == 1)//I2C读最后一个字节不需要应答
{
I2C_AcknowledgeConfig(I2C_ACK_NONE);//最后一个字节不产生应答信号
}
while (!I2C_CheckEvent(I2C_EVENT_MASTER_BYTE_RECEIVED));//等待数据接收完成
*pBuffer = I2C_ReceiveData(); //读出数据
pBuffer++; //读出数据缓存地址递加
NumByteToRead--; //接收数据数目减1
}
I2C_GenerateSTOP(ENABLE);
I2C_AcknowledgeConfig(I2C_ACK_CURR);
//设置硬件I2C应答:对当前字节执行应答,为下次访问I2C做准备
//Enable Acknowledgement to be ready for another reception
}
i2c_ee.h
#ifndef __I2C_EE_H
#define __I2C_EE_H
#include "stm8s.h"
//包含CPU头文件,使能int8_t,int16_t,int32_t,uint8_t,uint15_t,uint32_t,s8,s16,s32,u8,u16,u32,bool
//signed char,signed short,signed long,unsigned char,unsigned short,unsigned long
//FALSE,TRUE
//RESET,SET
//DISABLE,ENABLE
//ERROR,SUCCESS
#define I2C_Speed 100000 //I2C时钟为100KHz
#define I2C1_SLAVE_ADDRESS7 0xA0 //硬件I2C自身地址为0xA0
//AT24C01AN-10SU-2.7封装SOP8/2.7V~5.5V
//AT24C01AN-10SU-1.8封装SOP8/1.8V~5.5V
//AT24C16AN-10SU-2.7封装SOP8/2.7V~5.5V
//AT24C16AN-10SU-1.8封装SOP8/1.8V~5.5V
"EEPROM每页有多少个字节定义"开始
//本程序支持AT24C01,AT24C02,AT24C04,AT24C08.AT24C16
#define EEPROM_DEVICE_ADDRESS 0xA0 //EEPROM写器件地址
//地址在256~2048之间时,需要将高3位地址放在器件地址中
//#define EEPROM_Select 1 //AT24C01
#define EEPROM_Select 2 //AT24C02
//#define EEPROM_Select 3 //AT24C04
//#define EEPROM_Select 4 //AT24C08
//#define EEPROM_Select 6 //AT24C16
#if EEPROM_Select == 1 //AT24C01
#define Page_Byte_Size ((u8)8) //EEPROM每页只有8个字节
#elif EEPROM_Select == 2 //AT24C02
#define Page_Byte_Size ((u8)8) //EEPROM每页只有8个字节
#elif EEPROM_Select == 3 //AT24C04
#define Page_Byte_Size ((u8)16) //EEPROM每页只有16个字节
#elif EEPROM_Select == 4 //AT24C08
#define Page_Byte_Size ((u8)16) //EEPROM每页只有16个字节
#elif EEPROM_Select == 5 //AT24C16
#define Page_Byte_Size ((u8)16) //EEPROM每页只有16个字节
#endif
"EEPROM每页有多少个字节定义"结束
void I2C_EEInit(void);
void I2C_EE_ByteWrite(u8* pBuffer, u16 WriteAddr);
void I2C_EE_PageWrite(unsigned char *pBuffer, u16 firstaddress, u8 count);
void I2C_EE_BufferRead(u8* pBuffer, u16 ReadAddr, u8 NumByteToRead);
#endif /* __I2C_EE_H */
main.c
#include "stm8s.h"
//包含CPU头文件,使能int8_t,int16_t,int32_t,uint8_t,uint15_t,uint32_t,s8,s16,s32,u8,u16,u32,bool
//signed char,signed short,signed long,unsigned char,unsigned short,unsigned long
//FALSE,TRUE
//RESET,SET
//DISABLE,ENABLE
//ERROR,SUCCESS
#include "i2c_ee.h"
#include "uart.h"
#include "tim1.h"
u8 WriteEEPROMBuffer[8];
u8 ReadEEPROMBuffer[8];
int main(void)
{
u8 i;
CLK_HSIPrescalerConfig(CLK_PRESCALER_HSIDIV1);//配置HSI时钟分配器值为1分频,时钟到外围设备和核心为16MHz
USART_Configuration();
//UART1串口初始化:波特率115200,N81,无奇偶效验位,非同步模式,允许接收和发送,启用UART1
//USART1 config 115200 8-N-1
printf("\r\nCPU reaet!\r\n");
Tim1_Init();//设置TIM1中断周期为1ms,并允许TIM1中断
__enable_interrupt();
I2C_EEInit();//根据系统时钟,设置硬件I2C输出时钟频率为100KHz,自身地址为0xA0,并指定从机地址长度为7位
for(i=0;i<8;i++) WriteEEPROMBuffer[i]=i;
for(i=0;i<8;i++)
{
printf("\r\nWriteEEPROMBuffer[%u]=0x%02X",i,(u8)(WriteEEPROMBuffer[i]));
}
printf("\r\n");
I2C_EE_PageWrite(WriteEEPROMBuffer, 20, 8);
I2C_EE_BufferRead(ReadEEPROMBuffer, 20, 8);
for(i=0;i<8;i++)
{
printf("\r\nReadEEPROMBuffer[%u]=0x%02X",i,ReadEEPROMBuffer[i]);
}
printf("\r\n");
for(i=0;i<8;i++) WriteEEPROMBuffer[i]=(u8)(i+8);
for(i=0;i<8;i++)
{
printf("\r\nWriteEEPROMBuffer[%u]=0x%02X",i,(u8)(WriteEEPROMBuffer[i]));
}
printf("\r\n");
I2C_EE_PageWrite(WriteEEPROMBuffer, 28, 8);
I2C_EE_BufferRead(ReadEEPROMBuffer, 28, 8);
for(i=0;i<8;i++)
{
printf("\r\nReadEEPROMBuffer[%u]=0x%02X",i,ReadEEPROMBuffer[i]);
}
printf("\r\n");
for(i=0;i<8;i++) WriteEEPROMBuffer[i]=(u8)(i+16);
for(i=0;i<8;i++)
{
printf("\r\nWriteEEPROMBuffer[%u]=0x%02X",i,(u8)(WriteEEPROMBuffer[i]));
}
printf("\r\n");
for(i=0;i<8;i++)
I2C_EE_ByteWrite( WriteEEPROMBuffer+i,(u8)(36+i) );
I2C_EE_BufferRead(ReadEEPROMBuffer, 36, 8);
for(i=0;i<8;i++)
{
printf("\r\nReadEEPROMBuffer[%u]=0x%02X",i,ReadEEPROMBuffer[i]);
}
printf("\r\n");
while (1)
{
delay_ms(1000);
}
}