📄 twi_1.c
字号:
/************************************************************************/
//I2C文件读写库函数V1.1
/* I2C读写程序,使用前要确保
1.中断向量号的正确
2.确保TWI的初始化
3.此版本文件是从twi.c派生出来的,读写缓存都不再单独分配,而是由应用函数和场合指定
4.此版本是速度优先版本,特点是速度快,但读写都是在编译时确定的,不是用指针方式寻址
5.没有通信超时退出
*/
/************************************************************************/
#include "twi.h"
// I2C标准波特率:
// 低速 100KHz
// 高速 400KHz
// I2C 状态和地址变量
static volatile eI2cStateType I2cState;
static unsigned char I2cDeviceAddrRW;
// 发送缓冲区
//static unsigned char I2cSendData[I2C_SEND_DATA_BUFFER_SIZE];
unsigned char *I2cSendData;
static unsigned int I2cSendDataIndex;
static unsigned int I2cSendDataLength;
// 接收缓冲区
//static unsigned char I2cReceiveData[I2C_RECEIVE_DATA_BUFFER_SIZE];
unsigned char *I2cReceiveData;
static unsigned int I2cReceiveDataIndex;
static unsigned int I2cReceiveDataLength;
unsigned char I2cComStatus; //通信状态变量
unsigned char localBuffer[] = "!!";
unsigned char localBufferLength = 0x20;
// 存放发送长度的地址
unsigned int* SlaveSendLength;
unsigned char EnFlex = 0; /*变长接收使能*/
unsigned char I2cNotAck = 0; /*如果在主机发送或接收时,SLA+R或者SLA+W,ACK未收到,则I2cNotAck=0xff,否则为0*/
//设置总线速率
void i2cSetBitrate(unsigned int bitrateKHz)
{
unsigned char bitrate_div;
// SCL freq = F_CPU/(16+2*TWBR))
#ifdef TWPS0
// 对于用速率分频的AVR (mega128)
// SCL freq = F_CPU/(16+2*TWBR*4^TWPS)
// set TWPS to zero
TWSR&=~(1<<TWPS0);
TWSR&=~(1<<TWPS1);
#endif
// 计算分频
bitrate_div = ((F_CPU/1000l)/bitrateKHz);
if(bitrate_div >= 16)
bitrate_div = (bitrate_div-16)/2;
TWBR = bitrate_div;
}
//总线初始化
void i2cInit(void)
{
// I2C总线使能
TWCR|=1<<TWEN;
// 状态设置
I2cState = I2C_IDLE;
// 开I2C中断和回应
TWCR|=1<<TWIE;
TWCR|=1<<TWEA;
}
/************************************************************************/
/* 设置本机从地址及是否响应广播
[in]:uchar deviceAddr,设备地址
[in]:uchar genCallEn,广播使能!0-使能,0-不使能 */
/************************************************************************/
void i2cSetLocalDeviceAddr(unsigned char deviceAddr, unsigned char genCallEn)
{
// 设置本机从地址 (从方式时)
TWAR=(deviceAddr&0xFE)|(genCallEn?1:0);
}
void i2cSendStart(void)
{
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT)|(1<<TWSTA);
}
void i2cSendStop(void)
{
// 发送停止条件,保持TWEA以便从接收
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT)|(1<<TWEA)|(1<<TWSTO);
}
void i2cWaitForComplete(void)
{
// 等待i2c 总线操作完成
while( !(TWCR&(1<<TWINT)) );
}
void i2cSendByte(unsigned char data)
{
// 装载数据到 TWDR
TWDR=data;
// 发送开始
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT);
}
void i2cReceiveByte(unsigned char ackFlag)
{
//开始通过 i2c 接收
if( ackFlag )
{
// ackFlag = TRUE: 数据接收后回应ACK
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT)|(1<<TWEA);
}
else
{
// ackFlag = FALSE: 数据接收后无回应
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT);
}
}
unsigned char i2cGetReceivedByte(void)
{
// 返回接收到的数据
return( TWDR );
}
unsigned char i2cGetStatus(void)
{
// 返回总线状态
return(TWSR);
}
unsigned char i2cMasterSend(unsigned char deviceAddr, unsigned int length)
{
unsigned char i;
// 等待总线准备完成
while(I2cState);
// 设置状态
I2cState = I2C_MASTER_TX;
// 准备数据
I2cDeviceAddrRW = (deviceAddr & 0xFE); // RW 为0: 写操作
I2cSendDataIndex = 0;
I2cSendDataLength = length;
// 发送开始条件
i2cSendStart();
while(I2cState);
return(I2cNotAck);
}
unsigned char i2cMasterReceive(unsigned char deviceAddr, unsigned int length)
{
unsigned char i;
// 等待总线准备完成
while(I2cState);
// 设置状态
I2cState = I2C_MASTER_RX;
// 保存数据
I2cDeviceAddrRW = (deviceAddr|0x01); // RW 为1 : 读操作
I2cReceiveDataIndex = 0;
if(length)
{
I2cReceiveDataLength = length;
EnFlex = 0;
}
else
{
I2cReceiveDataLength = 0; //如果此值在此不被清零,那么一旦它大于2就会使中断不能到达修改长度的分支
EnFlex = 0xff;
}
// 发送开始条件
i2cSendStart();
//等待数据准备好
while(I2cState);
return(I2cNotAck);
}
/************************************************************************/
/* 无中断的主机数据发送
*/
/************************************************************************/
unsigned char i2cMasterSendNI(unsigned char deviceAddr, unsigned char length, unsigned char* data)
{
unsigned char retval = I2C_OK;
// 关I2C中断
TWCR&=~(1<<TWIE);
// 发送开始条件
i2cSendStart();
i2cWaitForComplete();
// 发送器件写地址
i2cSendByte( deviceAddr & 0xFE );
i2cWaitForComplete();
// 检查器件是否可用
if( TWSR == TW_MT_SLA_ACK)
{
// 发送数据
while(length)
{
i2cSendByte( *data++ );
i2cWaitForComplete();
length--;
}
}
else
{
// 如未回应器件地址,停止发送,返回错误
retval = I2C_ERROR_NODEV;
}
// 发送停止条件,保持TWEA以便从接收
i2cSendStop();
while( !(TWCR&(1<<TWSTO)) );
// 开I2C中断
TWCR|=(1<<TWIE);
return retval;
}
/************************************************************************/
/* 无中断主机数据接收
*/
/************************************************************************/
unsigned char i2cMasterReceiveNI(unsigned char deviceAddr, unsigned char length, unsigned char *data)
{
unsigned char retval = I2C_OK;
// 关I2C中断
TWCR&=~(1<<TWIE);
//发送开始条件
i2cSendStart();
i2cWaitForComplete();
// 发送器件读地址
i2cSendByte( deviceAddr | 0x01 );
i2cWaitForComplete();
// 检查器件是否可用
if( TWSR == TW_MR_SLA_ACK)
{
// 接收数据并回应
while(length > 1)
{
i2cReceiveByte(TRUE);
i2cWaitForComplete();
*data++ = i2cGetReceivedByte();
length--;
}
// 接收数据无回应 (末位信号)
i2cReceiveByte(FALSE);
i2cWaitForComplete();
*data++ = i2cGetReceivedByte();
}
else
{
// 如未回应器件地址,停止发送,返回错误
retval = I2C_ERROR_NODEV;
}
// 发送停止条件,保持TWEA以便从接收
i2cSendStop();
// 开I2C中断
TWCR|=TWIE;
return retval;
}
eI2cStateType i2cGetState(void)
{
return I2cState;
}
// I2C (TWI) 中断服务程序
#pragma interrupt_handler twi_isr:iv_TWI
void twi_isr(void)
{
//读状态位
unsigned char status;
status = TWSR & TWSR_STATUS_MASK;
switch(status)
{
// 主方式
case TW_START: // 0x08: START 已发送
case TW_REP_START: // 0x10: 重复START 已发送
// 发送器件地址
i2cSendByte(I2cDeviceAddrRW);
I2cNotAck = 0;
break;
// 主发送,主接收状态码
case TW_MT_SLA_ACK: // 0x18: SLA+W 已发送;接收到ACK
case TW_MT_DATA_ACK: // 0x28: 数据已发送;接收到ACK
if(I2cSendDataIndex < I2cSendDataLength)
{
// 发送数据
i2cSendByte( *(I2cSendData+I2cSendDataIndex) );
I2cSendDataIndex++;
}
else
{
// 发送停止条件,保持TWEA以便从接收
i2cSendStop();
// 设置状态
I2cState = I2C_IDLE;
}
break;
case TW_MR_DATA_NACK: // 0x58: 接收到数据;NOT ACK 已返回
// 保存最终数据
*(I2cReceiveData+I2cReceiveDataIndex) = TWDR;
I2cReceiveDataIndex++;
//继续发送条件
case TW_MR_SLA_NACK: // 0x48: SLA+R 已发送,接收到NOT ACK
case TW_MT_SLA_NACK: // 0x20: SLA+W 已发送,接收到NOT ACK
case TW_MT_DATA_NACK: // 0x30: 数据已发送,接收到NOT ACK
// 发送停止条件,保持TWEA以便从接收
i2cSendStop();
// 设置状态
// if(status == TW_MR_SLA_NACK || status == TW_MT_SLA_NACK) /*此行有效则,数据发送过程中的模块断开不进行报错处理*/
I2cNotAck = 0xff;
I2cState = I2C_IDLE;
break;
case TW_MT_ARB_LOST: // 0x38: SLA+W 或数据的仲裁失败
// 释放总线
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT);
// 设置状态
I2cState = I2C_IDLE;
break;
case TW_MR_DATA_ACK: // 0x50: 接收到数据,ACK 已返回
// 保存接收到的数据位
*(I2cReceiveData+I2cReceiveDataIndex) = TWDR;
I2cReceiveDataIndex++;
// 检查是否接收完
case TW_MR_SLA_ACK: // 0x40: SLA+R 已发送,接收到ACK
if(I2cReceiveDataIndex < I2cReceiveDataLength )
{
i2cReceiveByte(TRUE);
}
else if(I2cReceiveDataIndex < 3 && EnFlex) //仅限于数据帧前两个字节是长度的情况
{
if(I2cReceiveDataIndex == 2)
{
I2cReceiveDataLength = *((unsigned int*)I2cReceiveData);
}
// 数据位将接收 , 回复 ACK (传送更多字节)
i2cReceiveByte(TRUE);
}
else
{
// 数据位将接收 , 回复 NACK (传送最后字节)
i2cReceiveByte(FALSE);
}
break;
// 从接收状态码
case TW_SR_SLA_ACK: // 0x60: 自己的SLA+W 已经被接收,ACK 已返回
case TW_SR_ARB_LOST_SLA_ACK:// 0x68: SLA+R/W 作为主机的仲裁失败;自己的SLA+W 已经被接收,ACK 已返回
case TW_SR_GCALL_ACK: // 0x70: 接收到广播地址,ACK 已返回
case TW_SR_ARB_LOST_GCALL_ACK: // 0x78: SLA+R/W 作为主机的仲裁失败;接收到广播地址,ACK 已返回
// 被选中为从写入 (数据将从主机接收)
// 设置状态
I2cState = I2C_SLAVE_RX;
// 缓冲准备
I2cReceiveDataIndex = 0;
// 接收数据,回应 ACK
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT)|(1<<TWEA);
break;
case TW_SR_DATA_ACK: // 0x80: 以前以自己的 SLA+W 被寻址;数据已经被接收,ACK 已返回
case TW_SR_GCALL_DATA_ACK: // 0x90: 以前以广播方式被寻址;数据已经被接收,ACK 已返回
*(I2cReceiveData+I2cReceiveDataIndex) = TWDR;
I2cReceiveDataIndex++;
//检查接收缓冲区状态
if(I2cReceiveDataIndex < SR_DATA_LENGTH_MAX)
{
// 接收数据,回应 ACK
i2cReceiveByte(TRUE);
}
else
{
// 接收数据,回应 NACK
i2cReceiveByte(FALSE);
}
break;
case TW_SR_DATA_NACK: // 0x88: 以前以自己的 SLA+W 被寻址;数据已经被接收,NOT ACK 已返回
case TW_SR_GCALL_DATA_NACK: // 0x98: 以前以广播方式被寻址;数据已经被接收,NOT ACK 已返回
// 接收数据,回应 NACK
i2cReceiveByte(FALSE);
break;
case TW_SR_STOP: // 0xA0: 在以从机工作时接收到STOP或重复START
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT)|(1<<TWEA);
// 设置状态
I2cState = I2C_IDLE;
SET_REC_COM;
break;
// 从发送
case TW_ST_SLA_ACK: // 0xA8: 自己的SLA+R 已经被接收,ACK 已返回
case TW_ST_ARB_LOST_SLA_ACK:// 0xB0: SLA+R/W 作为主机的仲裁失败;自己的SLA+R 已经被接收,ACK 已返回
// 被选中为从读出 (数据将从传回主机)
// 设置状态
I2cState = I2C_SLAVE_TX;
// 数据请求
I2cSendDataLength = *SlaveSendLength;
I2cSendDataIndex = 0;
//
case TW_ST_DATA_ACK: // 0xB8: TWDR 里数据已经发送,接收到ACK
// 发送数据位
TWDR = *(I2cSendData+I2cSendDataIndex);
I2cSendDataIndex++;
if(I2cSendDataIndex < I2cSendDataLength)
// 回应 ACK
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT)|(1<<TWEA);
else
// 回应 NACK
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT);
break;
case TW_ST_DATA_NACK: // 0xC0: TWDR 里数据已经发送接收到NOT ACK
case TW_ST_LAST_DATA: // 0xC8: TWDR 的一字节数据已经发送(TWAE = “0”);接收到ACK
// 全部完成
// 从方式开放
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT)|(1<<TWEA);
// 设置状态
I2cState = I2C_IDLE;
break;
case TW_NO_INFO: // 0xF8: 没有相关的状态信息;TWINT = “0”
// 无操作
break;
case TW_BUS_ERROR: // 0x00: 由于非法的START 或STOP 引起的总线错误
// 内部硬件复位,释放总线
TWCR=TWCR&TWCR_CMD_MASK|(1<<TWINT)|(1<<TWSTO)|(1<<TWEA);
// 设置状态
I2cState = I2C_IDLE;
break;
}
}
#define TARGET_ADDR 0xA0
//测试 (24xxyy 器件)
void testI2cMemory(void)
{
unsigned char i;
unsigned char txdata[66];
unsigned char rxdata[66];
txdata[0] = 0;
txdata[1] = 0;
for(i=0; i<16; i++)
txdata[2+i] = localBuffer[i];
// 发送地址和数据
i2cMasterSendNI(TARGET_ADDR, 18, txdata);
txdata[18] = 0;
// 发送地址
i2cMasterSendNI(TARGET_ADDR, 2, txdata);
// 接收数据
i2cMasterReceiveNI(TARGET_ADDR, 16, rxdata);
//
rxdata[16] = 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -