SmartOS/I2C.cpp

527 lines
9.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "I2C.h"
//static I2C_TypeDef* const g_I2Cs[] = I2CS;
//static const Pin g_I2C_Pins_Map[][2] = I2C_PINS_FULLREMAP;
I2C::I2C()
{
Speed = 10000;
Retry = 200;
Error = 0;
Address = 0x00;
SubWidth= 0;
Opened = false;
}
I2C::~I2C()
{
Close();
}
void I2C::Open()
{
if(Opened) return;
OnOpen();
Opened = true;
}
void I2C::Close()
{
if(!Opened) return;
OnClose();
Opened = false;
}
bool I2C::SendAddress(int addr, bool tx)
{
// 发送设备地址
WriteByte(Address);
if(!WaitAck()) return false;
return SendSubAddr(addr);
}
bool I2C::SendSubAddr(int addr)
{
// 发送子地址
if(SubWidth > 0)
{
// 逐字节发送
for(int k=SubWidth-1; k>=0; k--)
{
WriteByte(addr >> (k << 3));
if(!WaitAck()) return false;
}
}
return true;
}
// 新会话向指定地址写入多个字节
bool I2C::Write(int addr, byte* buf, uint len)
{
Open();
I2CScope ics(this);
// 发送设备地址
if(!SendAddress(addr)) return false;
for(int i=0; i<len; i++)
{
WriteByte(*buf++);
if(!WaitAck()) return false;
}
return true;
}
// 新会话从指定地址读取多个字节
uint I2C::Read(int addr, byte* buf, uint len)
{
Open();
I2CScope ics(this);
// 发送设备地址
if(!SendAddress(addr)) return false;
uint rs = 0;
for(int i=0; i<len; i++)
{
*buf++ = ReadByte();
rs++;
Ack(i < len - 1); // 最后一次不需要发送Ack
}
return rs;
}
HardI2C::HardI2C(I2C_TypeDef* iic, uint speedHz ) : I2C()
{
assert_param(iic);
I2C_TypeDef* g_I2Cs[] = I2CS;
_index = 0xFF;
for(int i=0; i<ArrayLength(g_I2Cs); i++)
{
if(g_I2Cs[i] == iic)
{
_index = i;
break;
}
}
Init(_index, speedHz);
}
HardI2C::HardI2C(byte index, uint speedHz) : I2C()
{
Init(index, speedHz);
}
void HardI2C::Init(byte index, uint speedHz)
{
_index = index;
I2C_TypeDef* g_I2Cs[] = I2CS;
assert_param(_index < ArrayLength(g_I2Cs));
_IIC = g_I2Cs[_index];
SCL.OpenDrain = true;
SDA.OpenDrain = true;
Pin pins[][2] = I2C_PINS;
SCL.Set(pins[_index][0]).Config();
SDA.Set(pins[_index][1]).Config();
Speed = speedHz;
debug_printf("HardI2C%d %dHz \r\n", _index + 1, speedHz);
}
HardI2C::~HardI2C()
{
Close();
}
void HardI2C::SetPin(Pin scl , Pin sda )
{
SCL.Set(scl).Config();
SDA.Set(sda).Config();
}
void HardI2C::GetPin(Pin* scl , Pin* sda )
{
if(scl) *scl = SCL._Pin;
if(sda) *sda = SDA._Pin;
}
void HardI2C::OnOpen()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 << _index, ENABLE);
#ifdef STM32F0
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
#endif
//RCC_APB1PeriphResetCmd(sEE_I2C_CLK, ENABLE);
//RCC_APB1PeriphResetCmd(sEE_I2C_CLK, DISABLE);
#ifdef STM32F0
// 都在GPIO_AF_0分组内
SCL.AFConfig(GPIO_AF_0);
SDA.AFConfig(GPIO_AF_0);
#elif defined(STM32F4)
byte afs[] = { GPIO_AF_I2C1, GPIO_AF_I2C2, GPIO_AF_I2C3 };
SCL.AFConfig(afs[_index]);
SDA.AFConfig(afs[_index]);
#endif
SCL.Config(true);
SDA.Config(true);
I2C_InitTypeDef i2c;
I2C_StructInit(&i2c);
I2C_DeInit(_IIC); // 复位
// I2C_Timing
// I2C_AnalogFilter
// I2C_DigitalFilter
// I2C_Mode
// I2C_OwnAddress1
// I2C_Ack
// I2C_AcknowledgedAddress
/*i2c.I2C_AnalogFilter = I2C_AnalogFilter_Disable; // 关闭滤波器
// i2c.I2C_DigitalFilter // 数字滤波器 不知道怎么设置 跟 cr1 的8-11位有关
i2c.I2C_Mode =I2C_Mode_I2C; // IIC 模式
// i2c.I2C_OwnAddress1
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
if(addressLen == ADDR_LEN_10)
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_10bit;*/
i2c.I2C_Mode = I2C_Mode_I2C; //设置I2C接口模式
#ifndef STM32F0
i2c.I2C_DutyCycle = I2C_DutyCycle_2; //设置I2C接口的高低电平周期
#endif
i2c.I2C_OwnAddress1 = Address; //设置I2C接口的主机地址。从设备需要设定的主机地址而对于主机而言这里无所谓
i2c.I2C_Ack = I2C_Ack_Enable; //设置是否开启ACK响应
i2c.I2C_ClockSpeed = Speed; //速度
if(Address > 0xFF)
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_10bit;
else
i2c.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Cmd(_IIC, ENABLE);
I2C_Init(_IIC, &i2c);
}
void HardI2C::OnClose()
{
// sEE_I2C Peripheral Disable
I2C_Cmd(_IIC, DISABLE);
// sEE_I2C DeInit
I2C_DeInit(_IIC);
// sEE_I2C Periph clock disable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1 << _index, DISABLE);
SCL.Config(false);
SDA.Config(false);
}
void HardI2C::Start()
{
// 允许1字节1应答模式
I2C_AcknowledgeConfig(_IIC, ENABLE);
I2C_GenerateSTART(_IIC, ENABLE);
_Event = I2C_EVENT_MASTER_MODE_SELECT;
WaitAck();
}
void HardI2C::Stop()
{
// 最后一位后要关闭应答
I2C_AcknowledgeConfig(_IIC, DISABLE);
// 产生结束信号
I2C_GenerateSTOP(_IIC, ENABLE);
}
void HardI2C::Ack(bool ack)
{
I2C_AcknowledgeConfig(_IIC, ack ? ENABLE : DISABLE);
}
bool HardI2C::WaitAck(int retry)
{
if(!retry) retry = Retry;
while(!I2C_CheckEvent(_IIC, _Event));
{
if(--retry <= 0) return ++Error; // 超时处理
}
return retry > 0;
}
bool HardI2C::SendAddress(int addr, bool tx)
{
if(tx)
{
// 向设备发送设备地址
I2C_Send7bitAddress(_IIC, Address, I2C_Direction_Transmitter);
_Event = I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED;
if(!WaitAck()) return false;
_Event = I2C_EVENT_MASTER_BYTE_TRANSMITTED;
return I2C::SendSubAddr(addr);
}
else
{
// 如果有子地址,需要先发送子地址。而子地址作为数据发送,需要首先设置为发送模式
if(SubWidth > 0)
{
if(!SendAddress(addr, true)) return false;
// 重启会话
Start();
}
// 发送读地址
I2C_Send7bitAddress(_IIC, Address, I2C_Direction_Receiver);
_Event = I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED;
if(!WaitAck()) return false;
return true;
}
}
void HardI2C::WriteByte(byte dat)
{
_Event = I2C_EVENT_MASTER_BYTE_TRANSMITTED;
I2C_SendData(_IIC, dat);
}
byte HardI2C::ReadByte()
{
_Event = I2C_EVENT_MASTER_BYTE_RECEIVED;
return I2C_ReceiveData(_IIC);
}
/*// 新会话向指定地址写入多个字节
bool HardI2C::Write(int addr, byte* buf, uint len)
{
Open();
I2CScope ics(this);
_Event = I2C_EVENT_MASTER_MODE_SELECT;
if(!WaitAck()) return false;
// 发送设备地址
SendAddress(addr, true);
_Event = I2C_EVENT_MASTER_BYTE_TRANSMITTED;
//发送数据
while(len--){
I2C_SendData(_IIC, *buf++);
if(!WaitAck()) return false;
}
return true;
}
// 新会话从指定地址读取多个字节
uint HardI2C::Read(int addr, byte* buf, uint len)
{
_Event = I2C_FLAG_BUSY;
if(!WaitAck()) return false;
I2CScope ics(this);
_Event = I2C_EVENT_MASTER_MODE_SELECT;
if(!WaitAck()) return false;
// 发送设备地址
SendAddress(addr, false);
//读取数据
_Event = I2C_EVENT_MASTER_BYTE_RECEIVED;
while (len)
{
if(len==1)
{
I2C_AcknowledgeConfig(_IIC, DISABLE);
I2C_GenerateSTOP(_IIC, ENABLE);
}
if(!WaitAck()) return false;
*buf++ = I2C_ReceiveData(_IIC);
len--;
}
I2C_AcknowledgeConfig(_IIC, ENABLE);
return 0;
}*/
SoftI2C::SoftI2C(uint speedHz) : I2C()
{
Speed = speedHz;
_delay = Sys.Clock / speedHz;
Retry = 200;
Error = 0;
Address = 0x00;
}
SoftI2C::~SoftI2C()
{
Close();
}
void SoftI2C::SetPin(Pin scl , Pin sda )
{
SCL.Set(scl);
SDA.Set(sda);
}
void SoftI2C::GetPin(Pin* scl , Pin* sda )
{
if(scl) *scl = SCL._Pin;
if(sda) *sda = SDA._Pin;
}
void SoftI2C::OnOpen()
{
assert_param2(!SCL.Empty() && !SDA.Empty(), "未设置I2C引脚");
debug_printf("I2C::Open Address=0x%02X \r\n", Address);
// 开漏输出
SCL.OpenDrain = true;
SDA.OpenDrain = true;
SCL.Config(true);
SDA.Config(true);
}
void SoftI2C::OnClose()
{
SCL.Config(false);
SDA.Config(false);
}
/*
sda ----____
scl ___--------____
*/
void SoftI2C::Start()
{
SDA = true; //发送起始条件的数据信号
__nop();
__nop();
__nop();
SCL = true; //起始条件建立时间大于4.7us,延时
Sys.Delay(4);
SDA = false; //发送起始信号
Sys.Delay(4);
SCL = false; //钳住I2C总线准备发送或接收数据
__nop();
__nop();
__nop();
__nop();
}
/*
sda ____----
scl ____----
*/
void SoftI2C::Stop()
{
SCL = false; //发送结束条件的时钟信号
SDA = false; //发送结束条件的数据信号
Sys.Delay(4);
SCL = true; //结束条件建立时间大于4μ
SDA = true; //发送I2C总线结束信号
Sys.Delay(4);
}
// 等待Ack
bool SoftI2C::WaitAck(int retry)
{
SDA = true;
Sys.Delay(1);
SCL = true;
Sys.Delay(1);
// 等待SDA低电平
if(!retry) retry = Retry;
while(SDA)
{
if(retry-- <= 0)
{
Stop();
return false;
}
}
SCL = false;
return true;
}
// 发送Ack
void SoftI2C::Ack(bool ack)
{
SCL = false; //时钟低电平周期大于4μ
SDA = !ack;
Sys.Delay(2);
SCL = true; //清时钟线钳住I2C总线以便继续接收
Sys.Delay(2);
SCL = false;
}
void SoftI2C::WriteByte(byte dat)
{
SCL = false; //拉低时钟开始数据传输
for(int i=0; i<8; i++) //要传送的数据长度为8位
{
SDA = (dat & 0x80) >> 7; //判断发送位
dat <<= 1;
Sys.Delay(2);
SCL = true; //置时钟线为高,通知被控器开始接收数据位
Sys.Delay(2);
SCL = false;
Sys.Delay(2);
}
}
byte SoftI2C::ReadByte()
{
byte rs = 0;
SDA = true; // 开
for(int i=0; i<8; i++)
{
SCL = false; // 置时钟线为低,准备接收数据位
Sys.Delay(2);
SCL = true; // 置时钟线为高使数据线上数据有效
rs = rs << 1;
if(SDA) rs++; //读数据位,接收的数据位放入retc中
Sys.Delay(1);
}
return rs;
}