⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 i2c.cpp

📁 I2C总线在多机通信中的应用介绍了I2C总线的结构、工作原理、数据传输方式
💻 CPP
📖 第 1 页 / 共 2 页
字号:
#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 + -