149 lines
3.2 KiB
C
149 lines
3.2 KiB
C
|
|
#include "iic.h"
|
|||
|
|
|
|||
|
|
/* Software I2C delay (microseconds), based on SysTick */
|
|||
|
|
static void delayUs(uint16_t us)
|
|||
|
|
{
|
|||
|
|
uint32_t start = SysTick->VAL;
|
|||
|
|
uint32_t ticks = us * (SystemCoreClock / 1000000);
|
|||
|
|
|
|||
|
|
if (start < ticks) {
|
|||
|
|
while (SysTick->VAL > start);
|
|||
|
|
while (SysTick->VAL < (SysTick->LOAD - (ticks - start)));
|
|||
|
|
} else {
|
|||
|
|
while ((SysTick->VAL < start) && (SysTick->VAL >= (start - ticks)));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* I2C Interface Initialization */
|
|||
|
|
void iicInit(void)
|
|||
|
|
{
|
|||
|
|
GPIO_InitTypeDef GPIO_InitStruct;
|
|||
|
|
|
|||
|
|
__HAL_RCC_GPIOA_CLK_ENABLE();
|
|||
|
|
|
|||
|
|
/* SDA - PA12, open-drain output */
|
|||
|
|
GPIO_InitStruct.Pin = SDA_Pin;
|
|||
|
|
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
|
|||
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
|||
|
|
HAL_GPIO_Init(SDA_GPIO_Port, &GPIO_InitStruct);
|
|||
|
|
|
|||
|
|
/* SCL - PA11, push-pull output */
|
|||
|
|
GPIO_InitStruct.Pin = SCL_Pin;
|
|||
|
|
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
|||
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
|
|||
|
|
HAL_GPIO_Init(SCL_GPIO_Port, &GPIO_InitStruct);
|
|||
|
|
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
IIC_SDA_H;
|
|||
|
|
|
|||
|
|
/* 总线恢复:若上次运行在 I2C 传输中途被复位(烧录/看门狗/OTA 复位),
|
|||
|
|
* EEPROM 可能一直拉着 SDA 不放,导致后续通信卡死。
|
|||
|
|
* 检测 SDA 被拉低时,补 9 个 SCL 时钟把从机的当前字节推完,再发 STOP 释放总线 */
|
|||
|
|
if (READ_SDA == 0) {
|
|||
|
|
for (int i = 0; i < 9; i++) {
|
|||
|
|
IIC_SCL_L;
|
|||
|
|
delayUs(5);
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
delayUs(5);
|
|||
|
|
}
|
|||
|
|
iicStop();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* I2C Start Signal */
|
|||
|
|
void iicStart(void)
|
|||
|
|
{
|
|||
|
|
IIC_SDA_H;
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
delayUs(4);
|
|||
|
|
IIC_SDA_L; /* SCL high, SDA high->low = start */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* I2C Stop Signal */
|
|||
|
|
void iicStop(void)
|
|||
|
|
{
|
|||
|
|
IIC_SDA_L;
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
delayUs(4);
|
|||
|
|
IIC_SDA_H; /* SCL high, SDA low->high = stop */
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Wait for ACK: returns 0=OK, 1=timeout */
|
|||
|
|
uint8_t iicWaitAck(void)
|
|||
|
|
{
|
|||
|
|
unsigned char timeout = 0;
|
|||
|
|
|
|||
|
|
IIC_SDA_H;
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
|
|||
|
|
while (READ_SDA) {
|
|||
|
|
timeout++;
|
|||
|
|
if (timeout > 250) {
|
|||
|
|
iicStop();
|
|||
|
|
return 1;
|
|||
|
|
}
|
|||
|
|
delayUs(2);
|
|||
|
|
}
|
|||
|
|
IIC_SCL_L;
|
|||
|
|
delayUs(20);
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Send One Byte (MSB first), return ACK status: 0=ACK, 1=NACK/timeout */
|
|||
|
|
uint8_t iicSendByteAck(uint8_t txd)
|
|||
|
|
{
|
|||
|
|
unsigned char t;
|
|||
|
|
|
|||
|
|
IIC_SCL_L;
|
|||
|
|
for (t = 0; t < 8; t++) {
|
|||
|
|
IIC_SDA_OUT(((txd & 0x80) >> 7));
|
|||
|
|
txd <<= 1;
|
|||
|
|
delayUs(2);
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
delayUs(2);
|
|||
|
|
IIC_SCL_L;
|
|||
|
|
}
|
|||
|
|
return iicWaitAck();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Send One Byte (MSB first), legacy API without ACK return */
|
|||
|
|
void iicSendByte(uint8_t txd)
|
|||
|
|
{
|
|||
|
|
(void)iicSendByteAck(txd);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Read One Byte; ack=1 send ACK, ack=0 send NACK */
|
|||
|
|
unsigned char iicReadByte(uint8_t ack)
|
|||
|
|
{
|
|||
|
|
unsigned char i;
|
|||
|
|
unsigned char receive = 0;
|
|||
|
|
|
|||
|
|
IIC_SCL_L;
|
|||
|
|
delayUs(20);
|
|||
|
|
for (i = 0; i < 8; i++) {
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
delayUs(20);
|
|||
|
|
receive <<= 1;
|
|||
|
|
if (READ_SDA) receive++;
|
|||
|
|
delayUs(20);
|
|||
|
|
IIC_SCL_L;
|
|||
|
|
delayUs(20);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!ack) {
|
|||
|
|
/* NACK */
|
|||
|
|
IIC_SDA_H;
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
delayUs(2);
|
|||
|
|
IIC_SCL_L;
|
|||
|
|
} else {
|
|||
|
|
/* ACK */
|
|||
|
|
IIC_SDA_L;
|
|||
|
|
IIC_SCL_H;
|
|||
|
|
delayUs(2);
|
|||
|
|
IIC_SCL_L;
|
|||
|
|
IIC_SDA_H;
|
|||
|
|
}
|
|||
|
|
return receive;
|
|||
|
|
}
|