?? i2c.cpp
字號:
#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;
//-----------------------------------------------------------------------
//寫完一個字節后進入這個入口,寫下一個字節
//-----------------------------------------------------------------------
case 0x18: //發送了slv+w,并接收到ACK
case 0x28: //發送I2C0DAT,并接收到ACK
can_restart = false;
if(cur_op->mode & I2C_WRITE)
{
//如果可以發送下一個字節
if(buf_pos < cur_op->buf_size)
{
DP_ISR(putchar('w'));
I2C0DAT = cur_op->buf_ptr[buf_pos++];
ClearFlag(IC_START | IC_SI); //清除 SI STA
//如果沒有字節可以發送,檢查是否需要發送停止字節
//并且當前的操作完畢, 需要調整cur_op
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -