一块闲置的NUCLEO-L552ZE-Q开发板需要驱动DS1307日历芯片,启用了硬件I2C,但尝试多次都没有读出数据,于是便启用模拟I2C。经过仔细调试,终于顺利地完成了对DS1307日历芯片的读写操作,下面是模拟I2C操作的时序图:
下面是模拟I2C及对DS1307读写操作的代码:
/******************************************************
*程 序 名:void SI2C_SDA_Dir(uint8_t dir)
*作 用:设置SDA引脚读写方向
*输入参数:dir: 0=写(输出) 1=读(输入)
*返回参数:无
******************************************************/
void SI2C_SDA_Dir(uint8_t dir) //设置SDA的读写模式
{
GPIO_InitTypeDef GPIO_InitStruct;
if(dir == 0){
GPIO_InitStruct.Pin = S_SDA_Pin; //DIO输出模式、弱上拉
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(S_SDA_GPIO_Port, &GPIO_InitStruct);
}
else{
GPIO_InitStruct.Pin = S_SDA_Pin; //DIO输入模式
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(S_SDA_GPIO_Port, &GPIO_InitStruct);
}
HAL_GPIO_WritePin(S_SDA_GPIO_Port,S_SDA_Pin,GPIO_PIN_SET);
}
void SI2C_Start(void)
{
SI2C_SDA_Dir(0);
SDA_SET();
SCL_SET();
Delay_us(1);
SDA_RESET();
Delay_us(1);
SCL_RESET();
Delay_us(3);
}
void SI2C_Stop(void)
{
SI2C_SDA_Dir(0);
SDA_RESET();
Delay_us(2);
SCL_SET();
Delay_us(1);
SDA_SET();
}
void SI2C_Ack(void)
{
SCL_RESET();
SI2C_SDA_Dir(0);
SDA_RESET();
Delay_us(1);
SCL_SET();
Delay_us(5);
SCL_RESET();
Delay_us(2);
}
void SI2C_NoAck(void)
{
SCL_RESET();
SDA_SET();
SI2C_SDA_Dir(0);
Delay_us(1);
SCL_SET();
Delay_us(5);
SCL_RESET();
}
uint8_t SI2C_IsAck(void)
{
uint8_t i=0;
SCL_RESET();
SI2C_SDA_Dir(0);
SDA_SET(); //释放数据线
SI2C_SDA_Dir(1);
SCL_SET();
Delay_us(1);
while(HAL_GPIO_ReadPin(S_SDA_GPIO_Port,S_SDA_Pin)){
i++;
if(i>250){
SI2C_Stop();
return 1;
}
}
SCL_RESET();
SI2C_SDA_Dir(0);
return 0;
}
/*****************************************************************************
* 函数名称:uint8_t SI2C_ReadByte(void)
* 函数功能:读取I2C设备数据
* 入口参数:无
* 出口参数:无
* 备 注:无
*****************************************************************************/
uint8_t SI2C_ReadByte(void)
{
uint8_t dat,i;
SI2C_SDA_Dir(1);
for(i=0; i<8; i++){
dat<<=1;
SCL_SET();
Delay_us(5);
dat|=HAL_GPIO_ReadPin(S_SDA_GPIO_Port,S_SDA_Pin);
SCL_RESET();
Delay_us(5);
}
return dat;
}
/*****************************************************************************
* 函数名称:uint8_t SI2C_WriteByte(uint8_t dat)
* 函数功能:向I2C写入8Bit数据,返回成功结果
* 入口参数:dat 要写入的数据
* 出口参数:返回 0=写成功,1=写失败
* 备 注:无
*****************************************************************************/
uint8_t SI2C_WriteByte(uint8_t dat)
{
uint8_t i;
SCL_RESET();
SI2C_SDA_Dir(0);
for(i=0; i<8; i++){
if((dat & 0x80)>>7)
SDA_SET();
else
SDA_RESET();
SCL_SET();
Delay_us(5);
SCL_RESET();
Delay_us(5);
dat<<=1;
}
return 0;
}
/******************************************************************************************
* 函数名称: DS1307_Read()
* 功能说明: 从DS1307地址addr开始获取size个字节的数据,获取的数据存储在全局变量DS_Buff中
* 输 入: uint8_t addr 获取数据从addr开始
* uint8_t size 要获取的数据个数(1~8)
* 输 出: ui08 0=RET_OK 成功从DS1307获取数据 1=RET_ERR 从DS1307获取数据过程中出现错误
******************************************************************************************/
uint8_t DS1307_Read(uint8_t addr,uint8_t size)
{
uint8_t i = 0;
SI2C_Start(); //产生起始信号
SI2C_WriteByte(addr); //发送DS1307芯片地址及读写位,0表示写
if(1 == SI2C_IsAck()) //检测DS1307是否有响应
{
SI2C_Stop(); //产生停止信号
return 2;
}
SI2C_WriteByte(0); //发送读取数据的起始地址
if(1 == SI2C_IsAck()) //检测DS1307是否有响应
{
SI2C_Stop(); //产生停止信号
return 3;
}
SI2C_Start(); //产生Repeated Start
SI2C_WriteByte(addr|1); //发送DS1307芯片地址及读写位,1表示读
if(1 == SI2C_IsAck()) //检测DS1307是否有响应
{
SI2C_Stop(); //产生停止信号
return 4;
}
for(i=0;size>0;i++,size--) //从addr处读取size个字节的数据
{
DS_Buff[i] = SI2C_ReadByte();
SI2C_Ack();
}
SI2C_NoAck(); //DS1307要求必须使用NOAck来结束数据读取
SI2C_Stop(); //产生停止信号
return 0;
}
/**********************************************************************************************
* 函数名称: DS1307_Write()
* 功能说明: 向DS1307地址addr开始写入size个字节的数据,将要写入的数据存储在全局变量DS_Buff中
* 输 入: uint8_t addr 数据被写入从addr开始的地址处
* uint8_t dat 数组
* uint8_t size 要设置的数据个数(1~8)
* 输 出: uint8_t 0=RET_OK 成功向DS1307设置数据 1=RET_ERR 向DS1307设置数据过程中出现错误
**********************************************************************************************/
uint8_t DS1307_Write(uint8_t addr,uint8_t *dat,uint8_t size)
{
uint8_t i = 0;
SI2C_Start(); //产生起始信号
SI2C_WriteByte(addr); //发送DS1307芯片地址及读写位,0表示写
if(1 == SI2C_IsAck()) //检测DS1307是否有响应
{
SI2C_Stop(); //产生停止信号
return 1;
}
for(i=0; i<size; i++)
{
SI2C_WriteByte(dat[i]);
if(1 == SI2C_IsAck()) //检测DS1307是否有响应
{
SI2C_Stop(); //产生停止信号
return 3;
}
}
SI2C_Stop(); //产生停止信号
return 0;
}
|
大佬,又解决了一个问题,👍
按说硬件IIC更好用一些啊?是不是配置的速度有问题?抓下波形看看啊
是的,下一步准备继续测试硬件I2C,充分发挥MCU的性能。