标签:str 上拉 无响应 reads == hat stopped end enable
有几个参考资料写得挺好的
一个是NXP出的I2C规范和用户手册,直接搜索UM10204 pdf即可
还有一个是ADI出的技术文章
https://www.analog.com/en/technical-articles/i2c-primer-what-is-i2c-part-1.html
https://www.analog.com/en/technical-articles/i2c-timing-definition-and-specification-guide-part-2.html
I2C使用两根信号线,一根SDA用于传输数据,一根SCL用于产生时钟
一条总线上可以有一个主设备和多个从设备,时钟由主设备控制,因此软件模拟I2C主设备较为容易
SCL和SDA在实际电路中需要配置成开漏/开集输出并使用上拉电阻,从而支持线与逻辑
STM32作为主设备时可配置开漏输出并使用GPIO自带的上拉功能。如果使用推挽输出,则可能会在与一些设备通信时产生异常,同时这种操作也不符合I2C规范
下图可以看到推挽输出具有输出高低电平的能力,同时切换速度较快;开漏输出只能输出低电平,其高电平靠上拉电阻实现,而它的上升时间也更长
先是几个比较明显的特点:
1.正常传输数据时,当SCL线处于低电平期时,SDA线上的电平允许变动
2.正常传输数据时,当SCL线处于高电平期时,SDA线上的电平不变
3.如果在SCL线的高电平期,SDA线由高电平向低电平跳变,则表示开始传输数据(START信号)
4.如果在SCL线的高电平期,SDA线由低电平向高电平跳变,则表示停止传输数据(STOP信号)
然后看一下具体时序要求
这里以普通I2C模式举例(SCL时钟频率为100kHz),其它模式可以查阅上表
在单片机实现时不需要太关注下降时间,但需要关注在SCL保持高电平时SDA下降沿之前的setup时间和hold时间。最小值分别为4.7us和4.0us
接着是STOP信号,可以看到STOP信号只有一个setup时间要求,最小值为4.0us
然而,在STOP信号和START信号之间会有一个buffer时间用于让其它设备检测总线状态,buffer时间最小值为4.7us,因此SDA上升沿之后至少要跟4.7us的延迟
图中可以看到在SDA稳定到SCL变为高电平之间有setup时间要求,同时在SDA跳变到下一个SCL上升沿之间也有setup时间要求,最小值均为250ns
需要注意的是,在SDA稳定且SCL处于高电平时会有一个hold时间要求,表格中给出的是至少300ns,但同时还要考虑到SCL的低电平时间和高电平时间也有最小值要求,分别是4.7us和4.0us
MSB在前,LSB在后,每次只能发送长度为8的字节,然后接收方会返回NACK/ACK作为回应
NACK(高电平)表示数据错误或者无响应(被外部上拉电阻拉高)
ACK(低电平)表示成功接收
START信号 -> 发送7位数据地址+1位写标识(HIGH) -> 从设备返回ACK -> 发送寄存器地址/命令 -> 从设备返回ACK -> STOP信号(部分设备不需要在这里发送STOP信号) -> START信号 -> 发送7位数据地址+1位读标识(LOW) -> 从设备返回ACK -> 从设备返回1个字节 -> 主设备发送ACK -> STOP信号
如果按照7位设备地址来计算此图对应的设备地址是0x29,将地址左移1位,或0得到0x52(写),或1得到0x53(读)
图中Ac是从设备发送的ACK,Am是主设备发送的ACK
START信号 -> 发送7位数据地址+1位写标识(HIGH) -> 从设备返回ACK -> 发送寄存器地址/命令 -> 从设备返回ACK -> 发送写入的字节 -> 从设备返回ACK -> STOP信号
如图
如图
使用HAL库编写,稍作修改也可移植到使用库函数开发的项目上
Delay_ticks()函数:
void Delay_ticks(uint32_t ticks)
{
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD;
told = SysTick->VAL;
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
tcnt += told - tnow;
else
tcnt += reload - tnow + told;
told = tnow;
if (tcnt >= ticks)
break;
}
}
}
头文件:
softi2c1.h
#ifndef _SOFTI2C1_H
#define _SOFTI2C1_H
#include "main.h"
#include "DELAY/delay.h"
#include "softi2c_common.h"
// ****** configuration start ******
#define SOFTI2C1_SCL_CLKEN() __HAL_RCC_GPIOB_CLK_ENABLE()
#define SOFTI2C1_SDA_CLKEN() __HAL_RCC_GPIOB_CLK_ENABLE()
#define SOFTI2C1_SCL_GPIO GPIOB
#define SOFTI2C1_SDA_GPIO GPIOB
#define SOFTI2C1_SCL_PINID 8
#define SOFTI2C1_SDA_PINID 9
#define SOFTI2C1_RETRYTIMES 3
// ****** configuration end ******
#define SOFTI2C1_SCL_PIN ((uint16_t)1u << SOFTI2C1_SCL_PINID)
#define SOFTI2C1_SDA_PIN ((uint16_t)1u << SOFTI2C1_SDA_PINID)
#define SOFTI2C1_SCL(__PINSTATE__) (SOFTI2C1_SCL_GPIO->BSRR = (uint32_t)SOFTI2C1_SCL_PIN << ((__PINSTATE__) ? (0u) : (16u)) )
#define SOFTI2C1_SDA(__PINSTATE__) (SOFTI2C1_SDA_GPIO->BSRR = (uint32_t)SOFTI2C1_SDA_PIN << ((__PINSTATE__) ? (0u) : (16u)) )
#define SOFTI2C1_READSDA() ((SOFTI2C1_SDA_GPIO->IDR >> SOFTI2C1_SDA_PINID) & 1u)
#define SOFTI2C1_SDA_IN() {SOFTI2C1_SDA_GPIO->MODER &= ~(3 << (SOFTI2C1_SDA_PINID * 2u)); SOFTI2C1_SDA_GPIO->MODER |= 0 << (SOFTI2C1_SDA_PINID * 2u);}
#define SOFTI2C1_SDA_OUT() {SOFTI2C1_SDA_GPIO->MODER &= ~(3 << (SOFTI2C1_SDA_PINID * 2u)); SOFTI2C1_SDA_GPIO->MODER |= 1 << (SOFTI2C1_SDA_PINID * 2u);}
// For most of the cases
void SoftI2C1_Init(uint32_t speed);
uint8_t SoftI2C1_SendAddr(uint16_t addr, uint8_t addrLen, uint8_t RorW);
uint8_t SoftI2C1_Read(uint16_t deviceAddr, uint8_t deviceAddrLen, uint8_t memAddr, uint8_t *dataBuf, uint32_t dataSize);
uint8_t SoftI2C1_Write(uint16_t deviceAddr, uint8_t deviceAddrLen, uint8_t memAddr, uint8_t *dataBuf, uint32_t dataSize);
// Low Layer
void SoftI2C1_Start(void);
void SoftI2C1_Stop(void);
void SoftI2C1_SendACK(uint8_t ACK);
uint8_t SoftI2C1_WaitACK(void);
void SoftI2C1_SendByte(uint8_t byte);
uint8_t SoftI2C1_ReadByte(void);
uint8_t SoftI2C1_SendByte_ACK(uint8_t byte, uint8_t handleACK);
uint8_t SoftI2C1_ReadByte_ACK(uint8_t ACK);
#endif
softi2c_common.h
#ifndef _SOFTI2C_COMMON_H
#define _SOFTI2C_COMMON_H
#define SI2C_ACK 0
#define SI2C_NACK 1
#define SI2C_WRITE 0
#define SI2C_READ 1
#define SI2C_ADDR_7b 0
#define SI2C_ADDR_10b 1
#endif
源码:
softi2c1.c
#include "softi2c1.h"
uint16_t SoftI2C1_delayTicks = 0;
void SoftI2C1_Init(uint32_t speed)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
SOFTI2C1_SCL_CLKEN();
SOFTI2C1_SDA_CLKEN();
SOFTI2C1_SCL(1);
SOFTI2C1_SDA(1);
GPIO_InitStruct.Pin = SOFTI2C1_SCL_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(SOFTI2C1_SCL_GPIO, &GPIO_InitStruct);
GPIO_InitStruct.Pin = SOFTI2C1_SDA_PIN;
HAL_GPIO_Init(SOFTI2C1_SDA_GPIO, &GPIO_InitStruct);
// 这里用系统主频来替换Delay_GetSYSFreq(),例如 72000000,168000000,480000000
SoftI2C1_delayTicks = Delay_GetSYSFreq() / speed / 2;
}
uint8_t SoftI2C1_SendAddr(uint16_t addr, uint8_t addrLen, uint8_t RorW)
{
uint8_t buf;
if (addrLen == SI2C_ADDR_7b)
{
buf = ((addr & 0x007F) << 1u) | RorW;
return SoftI2C1_SendByte_ACK(buf, SI2C_ACK);
}
else
{
// 111100XX with first 2 bits
buf = ((addr & 0x0003) << 1u) | RorW;
buf |= 0x78;
if (!SoftI2C1_SendByte_ACK(buf, SI2C_ACK))
return 0;
// the last 8 bits
buf = addr & 0x00FF;
return SoftI2C1_SendByte_ACK(buf, SI2C_ACK);
}
}
uint8_t SoftI2C1_Read(uint16_t deviceAddr, uint8_t deviceAddrLen, uint8_t memAddr, uint8_t *dataBuf, uint32_t dataSize)
{
uint32_t i;
SoftI2C1_Start();
if (!SoftI2C1_SendAddr(deviceAddr, deviceAddrLen, SI2C_WRITE))
return 0;
if (!SoftI2C1_SendByte_ACK(memAddr, SI2C_ACK))
return 0;
// SoftI2C1_Stop(); // A STOP signal is required on some devices.
SoftI2C1_Start();
if (!SoftI2C1_SendAddr(deviceAddr, deviceAddrLen, SI2C_READ))
return 0;
for (i = 0; i < dataSize; i++)
*(dataBuf + i) = SoftI2C1_ReadByte_ACK(SI2C_ACK);
SoftI2C1_Stop();
return 1;
}
uint8_t SoftI2C1_Write(uint16_t deviceAddr, uint8_t deviceAddrLen, uint8_t memAddr, uint8_t *dataBuf, uint32_t dataSize)
{
uint32_t i;
SoftI2C1_Start();
if (!SoftI2C1_SendAddr(deviceAddr, deviceAddrLen, SI2C_WRITE))
return 0;
if (!SoftI2C1_SendByte_ACK(memAddr, SI2C_ACK))
return 0;
for (i = 0; i < dataSize; i++)
if (!SoftI2C1_SendByte_ACK(*(dataBuf + i), SI2C_ACK))
return 0;
SoftI2C1_Stop();
return 1;
}
void SoftI2C1_Start(void)
{
SOFTI2C1_SDA_OUT();
SOFTI2C1_SCL(1);
SOFTI2C1_SDA(1);
Delay_ticks(SoftI2C1_delayTicks); // setup time
SOFTI2C1_SDA(0); // START: when CLK is high,DATA change form HIGH to LOW
Delay_ticks(SoftI2C1_delayTicks); // hold time
SOFTI2C1_SCL(0); // cannot be read
Delay_ticks(SoftI2C1_delayTicks);
}
void SoftI2C1_Stop(void)
{
SOFTI2C1_SDA_OUT();
SOFTI2C1_SCL(1);
SOFTI2C1_SDA(0);
Delay_ticks(SoftI2C1_delayTicks); // setup time
SOFTI2C1_SDA(1); // STOP: when CLK is high,DATA change form LOW to HIGH
Delay_ticks(SoftI2C1_delayTicks * 2); // hold time(not necessary in most of the situations) and buff time(necessary)
// when the transmition is stopped, the SCL should be high
}
void SoftI2C1_SendACK(uint8_t ACK) // 0:ACK 1:NACK
{
SOFTI2C1_SCL(0); // change start
SOFTI2C1_SDA_OUT();
SOFTI2C1_SDA(ACK);
Delay_ticks(SoftI2C1_delayTicks / 8); // data setup time
SOFTI2C1_SCL(1); // can be read
Delay_ticks(SoftI2C1_delayTicks); // hold
SOFTI2C1_SCL(0); // cannot be read
Delay_ticks(SoftI2C1_delayTicks); // data setup time & SCL_LOW & SMBus requirement
}
uint8_t SoftI2C1_WaitACK(void) // 0:ACK 1:NACK/No response
{
uint16_t waitTime = 0;
uint8_t result = 0;
SOFTI2C1_SDA_IN();
SOFTI2C1_SCL(1);
Delay_ticks(SoftI2C1_delayTicks / 8); // data setup time
result = SOFTI2C1_READSDA();
Delay_ticks(SoftI2C1_delayTicks);
SOFTI2C1_SCL(0);
Delay_ticks(SoftI2C1_delayTicks); // data setup time & SCL_LOW & SMBus requirement
if (result == SI2C_NACK)
SoftI2C1_Stop();
return result;
}
// 8 SCL clock, just send a byte
void SoftI2C1_SendByte(uint8_t byte) // barely send a byte
{
int8_t i;
SOFTI2C1_SDA_OUT();
SOFTI2C1_SCL(0);
for (i = 7; i >= 0; i--)
{
SOFTI2C1_SDA((byte >> i) & 1u);
Delay_ticks(SoftI2C1_delayTicks / 8); // data setup time
SOFTI2C1_SCL(1);
Delay_ticks(SoftI2C1_delayTicks); // data hold time
SOFTI2C1_SCL(0);
Delay_ticks(SoftI2C1_delayTicks); // data setup time & SCL_LOW & SMBus requirement
}
}
// 8 SCL clock, just read a byte
uint8_t SoftI2C1_ReadByte(void) // barely read a byte
{
uint8_t i, result = 0;
SOFTI2C1_SDA_IN();
for (i = 0; i < 8; i++)
{
SOFTI2C1_SCL(0);
Delay_ticks(SoftI2C1_delayTicks); // data setup time & SCL_LOW & SMBus requirement
SOFTI2C1_SCL(1);
result <<= 1;
Delay_ticks(SoftI2C1_delayTicks / 8); // data setup time
result |= SOFTI2C1_READSDA();
Delay_ticks(SoftI2C1_delayTicks); // data hold time
}
return result;
}
// 9 SCL clock, treat the 9th bit as ACK/NACK
// If SI2C_NACK is given, it will always return true(ignore ACK/NACK)
// If SI2C_ACK is given, it will retry SOFTI2C1_RETRYTIMES if no ACK is detected,
// and it will return whether the ACK is detected finally.
uint8_t SoftI2C1_SendByte_ACK(uint8_t byte, uint8_t handleACK) // handle ACK and retry
{
uint8_t i;
if (handleACK == SI2C_NACK)
{
SoftI2C1_SendByte(byte);
SOFTI2C1_SDA_IN();
SOFTI2C1_SCL(1);
Delay_ticks(SoftI2C1_delayTicks / 8); // data setup time
SOFTI2C1_SCL(1); // can be read
Delay_ticks(SoftI2C1_delayTicks); // hold
SOFTI2C1_SCL(0); // cannot be read
Delay_ticks(SoftI2C1_delayTicks); // data setup time & SCL_LOW & SMBus requirement
return 1;
}
else
{
for (i = 0; i < SOFTI2C1_RETRYTIMES; i++)
{
SoftI2C1_SendByte(byte);
if (SoftI2C1_WaitACK() == SI2C_ACK)
break;
}
return (i < SOFTI2C1_RETRYTIMES);
}
}
// 9 SCL clock, send ACK/NACK after data received
uint8_t SoftI2C1_ReadByte_ACK(uint8_t ACK)
{
uint8_t result;
result = SoftI2C1_ReadByte();
SoftI2C1_SendACK(ACK);
return result;
}
2021.01.30
标签:str 上拉 无响应 reads == hat stopped end enable
原文地址:https://www.cnblogs.com/wh201906/p/14350929.html