#include <stdio.h>
#include <string.h>
#include "..\Armos\ArmLib.h"
#include "..\Armos\ArmOS.h"
#include "I2C.h"
#define DP_ERR(x) x
#define DP_ISR(x)
#define MAX_RETRY_TO_SEND_START_STOP 10
//void SetSysLed(char on);
struct SI2COperation
{
uint8 mode;
uint8 slave_addr;
uint8 *buf_ptr;
int buf_size;
};
enum {I2C_START=0X01, I2C_STOP=0X02, I2C_READ=0X04, I2C_WRITE=0X08, I2C_APPEND_STOP=0X10};
class CI2COperation : public CAbsI2CBus
{
static void __irq I2CIRQSericeEntry();
static int buf_pos;
static struct SI2COperation *cur_op;
static struct SI2COperation operations[MAX_I2C_OPERATION_COUNT];
static int op_count;
static int op_pos;
static int send_start_stop_retry_count;
static bool can_restart ;
static bool continue_state;
static TI2CErrorCode error_code;
static bool GetNextOperation();
static void OnEndOfCurOperation_InIsr() ;
static void ReadNextByte();
static void StopOnError(TI2CErrorCode an_error_code, bool is_in_isr_and_set_event);
static bool CanSendStartStop(int state);
static HANDLE etEndOfOperations;
bool is_executed;
bool can_continue_read, can_continue_write;
uint32 start_time;
bool AddOperation(uint8 mode, uint8 slave_adder, uint8 *buf, int len);
public:
virtual bool AddOp_StartRead(uint8 slave_addr, __packed uint8 *buf, int len, bool append_stop);
virtual bool AddOp_StartWrite(uint8 slave_addr, __packed uint8 *buf, int len, bool append_stop);
virtual bool AddOp_ContinueRead(__packed uint8 *buf, int len, bool append_stop);
virtual bool AddOp_ContinueWrite(__packed uint8 *buf, int len, bool append_stop);
virtual bool AddOp_Stop();
virtual TI2CErrorCode Execute(uint32 time_out_in_ms = 20, bool block=true);
virtual TI2CErrorCode GetResult(uint32 time_out_in_ms = 20, bool block=true);
virtual bool Init();
};
int CI2COperation::buf_pos;
struct SI2COperation *CI2COperation::cur_op;
struct SI2COperation CI2COperation::operations[MAX_I2C_OPERATION_COUNT];
int CI2COperation::op_count;
int CI2COperation::op_pos;
bool CI2COperation::can_restart;
HANDLE CI2COperation::etEndOfOperations;
TI2CErrorCode CI2COperation::error_code;
int CI2COperation::send_start_stop_retry_count;
bool CI2COperation::continue_state;
// 7 6 5 4 3 2 1 0
// I2C0CONSET: - I2EN STA STO SI AA - -
#define IC_AA 0X04
#define IC_SI 0X08 //中断标志位
#define IC_STOP 0x10 //发送停止位
#define IC_START 0X20 //发送起始位
#define IC_EN 0X40
#define ClearFlag(x) I2C0CONCLR = x
#define SetFlag(x) I2C0CONSET = x
#define CheckFlag(x) (I2C0CONSET & (x))
void CI2COperation::OnEndOfCurOperation_InIsr()
{
if(cur_op->mode & I2C_APPEND_STOP)
{
//ClearFlag(IC_START | IC_SI); //清除 SI STA
SetFlag(IC_STOP); //设置 STOP, 并设置软件中断,使得下次能继续触发中断
ClearFlag(IC_START | IC_SI);
DP_ISR(printk(" STOP I2C "));
}
//如果没有下一个操作,就结束了
if(! GetNextOperation())
{
DP_ISR(printk(" stop isr, state=%x\n", I2C0STAT));
EnableIsr(ISN_I2C0,false);
//SetEventIsr(etEndOfOperations);
SetEventIsr(etEndOfOperations);
//SetSysLed(1);
}else
{
SetFlag(IC_SI);
}
}
void CI2COperation::ReadNextByte()
{
//连续读取字节时,如果ARM 最后发送了ACK,那么从设备可能会继续送出下一个字节
//这样就不能发送出停止条件,也就不能中途停止读取。
//因此如果已经是最后一个字节了,并且读取完了以后就要发送停止条件了,那么就不要发送确认
//另外,如果确认了当前字节,但是芯片还是要重新开始读取,那么只要发送起始条件,就同时先发送
//一个停止条件就可以了。
if((buf_pos == cur_op->buf_size -1) && (cur_op->mode & I2C_APPEND_STOP) != 0)
{
ClearFlag(IC_START | IC_SI | IC_AA); //I2C0CONSET = 0x04; //AA=1
}else
{
SetFlag(IC_AA); //I2C0CONSET = 0x04; //AA=1
ClearFlag(IC_START | IC_SI); //I2C0CONSET = 0x04; //AA=1
}
}
//遇到错误,发送停止条件,并且返回
void CI2COperation::StopOnError(TI2CErrorCode an_error_code, bool is_in_isr_and_set_event)
{
error_code = an_error_code;
ClearFlag(IC_START | IC_SI | IC_AA);
SetFlag(IC_STOP);
EnableIsr(ISN_I2C0,false);
if(is_in_isr_and_set_event)
{
SetEventIsr(etEndOfOperations);
// putchar('Q');
}
}
bool CI2COperation::CanSendStartStop(int state)
{
return (state == 0x18 || //SLA+W transmitted, ack received
state == 0x20 || //SLA+W transmitted, ack not received
state == 0x28 || //data transmitted, ack received
state == 0x30 || //data transmitted, ack not received
state == 0x38 || //arbitration lost
state == 0x48 ||
state == 0x58 ||
state == 0x88 ||
state == 0x98 ||
state == 0xa0 ||
state == 0xc0 ||
state == 0xc8 ||
state == 0xf8);
};
/*
调试信息的意义:
'S' - 发送起始条件
'P' - 发送停止条件
'W' - 发送写命令
'R' - 发送读命令
'r' - 读取一个字节
'w' - 写入一个字节
'x' - 没有得到ACK
'A' - 发送读命令后的第一个ACK
'M' - 多发送一个字节,然后才能发送停止条件
*/
//#define putchar(x)
void __irq CI2COperation::I2CIRQSericeEntry(void)
{
//-------------------------------------------------------------------------------
// cur_op 这个操作刚刚开始,buf_pos 被设置为-1.
// 此时可能发送起始或者停止条件。
// 如果需要发送起始条件,发送命令后若无响应可重新开始(例如EEPROM 正在编程),因此can_restart 设置为true
//-------------------------------------------------------------------------------
uint8 state = I2C0STAT & 0xF8;
if(buf_pos == -1)
{
DP_ISR(printk(" new:0x%x ",state));
buf_pos = 0;
//如果要发送起始条件
if(cur_op->mode & (I2C_START | I2C_STOP))
{
//这些状态都可以直接发送起始或者停止条件。
if(CanSendStartStop(state))
{
if((cur_op->mode & I2C_START) || state == 0xf8 || state == 0xa0)
{
SetFlag(IC_EN | IC_START | IC_STOP | IC_AA);//((cur_op->mode & I2C_STOP)? IC_STOP : 0));
ClearFlag(IC_SI);
DP_ISR(putchar('S'));
if((cur_op->mode & I2C_START) == 0)
buf_pos = -1;
}else
{
SetFlag(IC_EN | IC_STOP | IC_AA);//((cur_op->mode & I2C_STOP)? IC_STOP : 0));
ClearFlag(IC_SI);
OnEndOfCurOperation_InIsr();
DP_ISR(putchar('P'));
}
//必须多发送一个字节,然后才能发送停止条件。这是芯片内部的状态机不好!
}else
{
I2C0DAT = 0XFF; //Device addr:0xfe, read
ClearFlag(IC_SI | IC_AA | IC_START | IC_STOP);
buf_pos = -1; //下次还要继续判断是否可以发送起始、停止条件了
DP_ISR(putchar('M'));
send_start_stop_retry_count++;
if(send_start_stop_retry_count > MAX_RETRY_TO_SEND_START_STOP)
StopOnError(IE_CANNOT_SEND_START_STOP, true);
}
can_restart = true;
goto final;
}else
{
continue_state = true;
DP_ISR(printk(" continue state=%x\n", I2C0STAT));
}
}else
{
continue_state = false;
}
//-------------------------------------------------------------------------------
// cur_op 这个操作已经在进行中。如果已经在读写数据了,那么 can_restart 设置为false
// 如果读结束或者写结束就要检查下一个操作
// 如果遇到设备没有响应,则根据 can_restart 确定是否重试。
// 如果遇到错误则设置错误代码
//-------------------------------------------------------------------------------
switch (state)
{
//-----------------------------------------------------------------------
//发送起始条件后进入这个入口,然后发送读写命令
//-----------------------------------------------------------------------
case 0x08: //---------------- 起始条件
case 0x10: //----------------- 重复起始条件 ,如读操作
if(cur_op->mode & I2C_READ)
{
DP_ISR(putchar('R'));
I2C0DAT = cur_op->slave_addr | 1; //0:write, 1:read
}else if(cur_op->mode & I2C_WRITE)
{
DP_ISR(putchar('W'));
I2C0DAT = cur_op->slave_addr & 0xfe; //0:write, 1:read
}else
{
DP_ISR(putchar('E'));
OnEndOfCurOperation_InIsr();
}
ClearFlag(IC_START | IC_SI); //清除 SI STA
break;
//-----------------------------------------------------------------------