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

📄 twi_1.c

📁 AVR单片机平台Atmega32+CH375A芯片读写U盘代码
💻 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 + -