3398|0

849

帖子

0

TA的资源

纯净的硅(高级)

楼主
 

传说I+2+C=I2C [复制链接]

I2C的工程应用
本工程基于LM3S8962的I2C总线来模拟EEPROM(CAT24C02)传输协议的操作过程,系统简单实用,可以移植到所有的LM3Sxxx系列的MCU上。
在PROGRAM中,采用INT的方式进行数据传输过程的处理和实现,工程的功能实现都是在中断函数中进行的。
1.cat24c02是EEPROM芯片,从机地址为0x50,可存储256BYTE的数据,支持100KBIT/S或者400KBIT/S的速度。
2.cat24c02的总线协议
由于24c02是MEMORY DIVICE,所以存储从机子地址,即数据地址。所以无论是读还是写,主机首先要在发送“从机子地址+写”后的第一个字节发送“数据地址”。若主机此时要向24C02写入多个数据,那么此时便可以直接操作了,24C02会自增地址值;如果主机要向从机读多个数据,那个就必须重新发送起始信号,再发送“从机地址+读”后才能从24C02中读取数据。
 
主机向从机写数据采用突发发送时序:起始地址->从机地址/写控制命令->从机ACK->数据地址->从机ACK->发送数据->从机ACK->...->STOP
 
主机从从机读数据采用突发发送后突发读时序:起始地址->从机地址/写控制命令->从机ACK->数据地址->从机ACK->起始地址->从机地址/读控制命令->ACK->从机发送数据->主机ACK...->非应答->STOP
 
3.从机在数据传输过程中的实际操作
从机在接收或发送一个字节后,都会把从机原始状态寄存器I2CSRIS的RIS位置位。从机通过查询或者中断的方式检测到该位后,读取并判断I2CSCSR控制/状态寄存器的3个位FBR TREQ RREQ的状态,然后完成相应的操作,下面是这三个位的组合情况,除了下面的情况外,其它的组合情况是不存在的:
状态编号:1           2         3   
FBR:      1          0         0
TREQ:     0          0         1
RREQ:     1          1         0
状态描述:接收请求,查手册      接收请求      发送请求
主机操作从机的过程中完全都是根据上表来实现的。当主机向从机写数据时,从机首先读到状态1,接收到的是数据地址,将这个地址赋给一个数组下标或者是指针,之后每次都读到是状态2,读到的都是主机所写的数据,将这些数据存储到数组中,下标地址自动加1;当主机从从机读取数据时,从机会先读到状态1,读到的是数据地址,将这个地址赋给一个数组下标或者是指针,之后每次都读到是状态3,读到的都是从机所发送的数据,将这些数据存储到数组中,下标地址自动加1;
 
下面是工程的实现代码:
#include "hw_memmap.h"
#include "hw_ints.h"
#include "hw_types.h"
#include "hw_i2c.h"
#include "gpio.h"
#include "sysctl.h"
#include "i2c.h"
#include "interrupt.h"
#include "I2CINT.H"
/*-----------------------------------------------------------------------------*/
#define LED_PORT GPIO_PORTB_BASE                              //数据判断口的定义
#define LED_PIN  GPIO_PIN_1                                   // 数据判断的LED指示
/******************************************************************
 - 功能描述:主函数
 - 隶属模块:------
 - 函数属性:------
 - 参数说明:void
 - 返回说明:嵌入式中的主函数的返回类型必须是Int型的
 - 函数说明:------
******************************************************************/
int main(void)
{
    unsigned char   WDATA[5] = {0xAA,0x55,0xAA,0x55,0xAA};       //主机向24C64写入的数据
    unsigned char   RDATA[5];                                   //主机从24C64读出的数据
    unsigned  long  ulIdx;
    // 设置晶振为系统时钟
    SysCtlClockSet(SYSCTL_SYSDIV_1 |
                   SYSCTL_USE_OSC  |
                   SYSCTL_OSC_MAIN |
                   SYSCTL_XTAL_6MHZ);  
    // 使能本例所使用的外设。
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIODirModeSet(LED_PORT, LED_PIN, GPIO_DIR_MODE_OUT);    
    I2CInit(400000, 1);       // 初始化I2C驱动   
    while(1)                  // 将一串数据写入EEPROM的前8个字节。
    {
     ISendStr(0xA0, 0x00, 1, WDATA, 5);         // 向24C02中写入数据
     Delays(5);                                 // 等待主机向24C02中写入数据延时。
     IRcvStr(0xA0, 0x00, 1, RDATA, 5);         // 从24C02中读出数据
     Delays(5);                                // 等待主机从24C02中读出数据延时。
     for(ulIdx = 0; ulIdx < 5; ulIdx++)
     {
          // 判断接收到的数据是否正确.
          if(WDATA[ulIdx] != RDATA[ulIdx])
          {
               //如果接收的数据出错,则点亮LED1指示.
              GPIOPinWrite(LED_PORT,LED_PIN,~LED_PIN);
              while(1);
          }
     }
        // 如果接收到的数据正确,则点亮LED2指示.
        GPIOPinWrite(LED_PORT,LED_PIN,~LED_PIN);
        Delays(3); 
        GPIOPinWrite(LED_PORT,LED_PIN,LED_PIN);
        while(1);
    }    
}
/*
---------------------------------------------------------------------------------------------------------
**************************************************************************************************
: I2CINT.c
** 功能说明 : Luminary硬件I2C中断方式软件包。
** 使用说明 :  主程序要配置好I2C总线接口(I2C引脚功能和I2C中断,并已使能I2C主模式)
***************************************************************************************************
---------------------------------------------------------------------------------------------------------
*/
#include "hw_ints.h"
#include "hw_memmap.h"
#include "hw_types.h"
#include "hw_i2c.h"
#include "gpio.h"
#include "sysctl.h"
#include "i2c.h"
#include "interrupt.h"
#define uchar   unsigned char
#define ulong   unsigned long
/*******************************************************************************************
** 中断中的状态
*******************************************************************************************/
#define STATE_IDLE         0     /* 总线空闲状态   */
#define STATE_WRITE_ONE    1     /* 写单个字节状态   */
#define STATE_WRITE_NEXT   2     /* 写下一个字节状态  */
#define STATE_WRITE_FINAL  3     /* 写最后一个字节状态  */
#define STATE_READ_ONE     4     /* 读单个字节状态   */
#define STATE_READ_FIRST   5     /* 读第一个字节状态  */
#define STATE_READ_NEXT    6     /* 读下一个字节状态  */
#define STATE_READ_FINAL   7     /* 读最后一个字节状态  */
#define STATE_READ_WAIT    8     /* 读字节等待状态   */
/*******************************************************************************************
** 全局变量定义
*******************************************************************************************/
static volatile uchar  I2C_sla;    /* I2C器件从地址      */
static volatile ulong I2C_suba;    /* I2C器件内部子地址     */
static volatile uchar  I2C_suba_num;   /* I2C子地址字节数     */
static volatile uchar  *I2C_buf;          /* 数据缓冲区指针       */
static volatile ulong   I2C_num;    /* 要读取/写入的数据个数    */
static volatile uchar   I2C_opt;     /* I2c读写操作,0为读操作,1为写操作 */
static volatile uchar  I2C_state = STATE_IDLE; /* 中断服务程序的当前状态     */

/*
******************************************************************************************
** 函数名称: Delays()
** 函数功能: 延时
** 入口参数: dly
** 出口参数: 无
******************************************************************************************
*/
void Delays (ulong dly)
{
 int i,j;
 for (; dly>0; dly--)
 {
  for (i=0; i<150; i++)
   for (j=0; j<255; j++);
 }
}
/*
*********************************************************************************************************
** 函数名称:I2CInit()
** 函数功能:I2C接口初始化。
** 入口参数:spd  总线速度100K(参数值为100000)或400K(参数值为4000000)
**           pri  中断优先级0~7
** 出口参数:返回值为false时表示操作出错,为true时表示操作正确。
** 说明:
*********************************************************************************************************
*/
int I2CInit(ulong spd, uchar pri)
{
 if ((spd == 400000) || (spd == 100000))
 {
     // 使能所使用的外设
     SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C);
  SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
  
     // 配置相关引脚,以进行I2C操作
     GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_2 | GPIO_PIN_3);
     // 初始化I2C主机,设置主机为低速.
     if(spd == 400000)
      I2CMasterInit(I2C_MASTER_BASE, true);
  else
   I2CMasterInit(I2C_MASTER_BASE, false);
  
     // 使能处理器中断
     IntMasterEnable();
     
     // 使能I2C中断
     IntEnable(INT_I2C);
     // 使能I2C主机中断
     I2CMasterIntEnable(I2C_MASTER_BASE);
     
     // 设置I2C中断的优先级
     IntPrioritySet(INT_I2C, (pri << 5));
   
     return(true);
    }
    else
     return(false);
}
/*
*********************************************************************************************************
** 函数名称:ISendByte()
** 函数功能:向无子地址器件发送1字节数据。
** 入口参数:sla  器件地址
**           c   要发送的数据
** 出口参数:返回值为false时表示操作出错,为true时表示操作正确。
** 说明: 使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
*********************************************************************************************************
*/
int ISendByte(uchar sla, uchar c)
{
 I2C_sla = sla >> 1;
 I2C_buf = &c;
 I2C_state = STATE_WRITE_ONE;
 
 I2CMasterSlaveAddrSet(I2C_MASTER_BASE, I2C_sla, false);   // 主机写操作
 I2CMasterDataPut(I2C_MASTER_BASE, *I2C_buf);     // 写一个数据
    I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_SINGLE_SEND); // 启动单次发送
 
 while(I2C_state != STATE_IDLE);
 if(true == I2CMasterBusy(I2C_MASTER_BASE))
  return (false);
 else
  return (true);
}
/*
*********************************************************************************************************
** 函数名称 :ISendStr()
** 函数功能 :向有子地址器件任意地址开始写入N字节数据
** 入口参数 : sla   器件从地址
**    suba_type 子地址结构 0-8+X结构  1-单字节地址  2-双字节地址  3-三字节地址
**    suba  器件子地址
**    s   数据发送缓冲区指针
**    no   写入的个数
** 出口参数 : TRUE  操作成功
**    FALSE  操作失败
*********************************************************************************************************
*/
int ISendStr(uchar sla, ulong suba, uchar sub_type, uchar *s, ulong no)
{
 I2C_sla = sla >> 1;
 I2C_buf = s;
 I2C_num = no;
 I2C_suba = suba;
 
 switch(sub_type)
 {
  // 子地址为“8+x”类型
  case 0:
   I2C_sla = I2C_sla | ((suba >> 8) & 0x07);
   I2C_suba_num = 1;
   break; 
  // 子地址为1个字节  
  case 1:
   I2C_suba_num = 1; 
   break;
  // 子地址为2个字节
  case 2:
   I2C_suba_num = 2; 
   break;
  // 子地址为3个字节
  case 3:
   I2C_suba_num = 3; 
   break;
  default: break;
 }
 
 if((no == 1) && (I2C_suba_num == 1))
  I2C_state = STATE_WRITE_FINAL; // 单字节地址或8+x结构地址发送1个字节数据
 else
  I2C_state = STATE_WRITE_NEXT; // 多字节地址或发送多个字节数据 
      
 I2CMasterSlaveAddrSet(I2C_MASTER_BASE, I2C_sla, false);  // 主机写操作          
    I2C_suba_num--;
    I2CMasterDataPut(I2C_MASTER_BASE,
        (I2C_suba >> (8 * I2C_suba_num)));   // 写子地址高字节
    I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_START);// 突发写操作启动
   
 while(I2C_state != STATE_IDLE);
 if(true == I2CMasterBusy(I2C_MASTER_BASE))
  return (false);
 else
  return (true);
}
/*
*********************************************************************************************************
** 函数名称:ISendStrExt()
** 函数功能:向无子地址器件发送多字节数据。
** 入口参数:sla  器件地址
**           s   要发送的数据
**     no   发送的字节个数
** 出口参数:返回值为false时表示操作出错,为true时表示操作正确。
** 说明:  发送数据字节至少2个字节
*********************************************************************************************************
*/
int ISendStrExt(uchar sla, uchar *s, uchar no)
{
 I2C_sla = sla >> 1;
 I2C_buf = s;
 I2C_num = no;
 if (no > 1)        // 发送的字节参数过滤
 {
  if(no == 2)
   I2C_state = STATE_WRITE_FINAL;
  else
   I2C_state = STATE_WRITE_NEXT;
 
  I2CMasterSlaveAddrSet(I2C_MASTER_BASE, I2C_sla, false); // 主机写操作
     I2CMasterDataPut(I2C_MASTER_BASE, *I2C_buf++);
     I2C_num--;
     I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_START);// 突发写操作启动
       
  while(I2C_state != STATE_IDLE);
  if(true == I2CMasterBusy(I2C_MASTER_BASE))
   return (false);
  else
   return (true);
 }
 else
  return(false);
}
/*
*********************************************************************************************************
** 函数名称:IRcvByte()
** 函数功能:向无子地址器件读取1字节数据。
** 入口参数:sla  器件地址
**           c   接收数据的变量指针
** 出口参数:返回值为false时表示操作出错,为true时表示操作正确。
** 说明: 使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
*********************************************************************************************************
*/
int IRcvByte(uchar sla, uchar *c)
{
 I2C_sla = sla >> 1;
 I2C_buf = c;
 I2C_state = STATE_READ_WAIT;
 
 I2CMasterSlaveAddrSet(I2C_MASTER_BASE, I2C_sla, true); // 主机读操作
 I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);// 启动单次读取
 
 while(I2C_state != STATE_IDLE);
 if(true == I2CMasterBusy(I2C_MASTER_BASE))
  return (false);
 else
  return (true);
}
/*
*********************************************************************************************************
** 函数名称 :IRcvStr()
** 函数功能 :向有子地址器件读取N字节数据
** 入口参数 : sla   器件从地址
**    suba_type 子地址结构 0-8+X结构  1-单字节地址  2-双字节地址  3-三字节地址
**      suba  器件内部物理地址
**      *s   将要读取的数据的指针
**      no   将要读取的数据的个数
** 出口参数 : TRUE  操作成功
**      FALSE  操作失败
*********************************************************************************************************
*/
int IRcvStr(uchar sla, ulong suba, uchar sub_type, uchar *s, uchar no)
{
 I2C_sla = sla >> 1;
 I2C_buf = s;
 I2C_num = no;
 I2C_suba = suba;
 I2C_opt = 1;
 
 switch(sub_type)
 {
  // 子地址为“8+x”类型
  case 0:
   I2C_sla = I2C_sla | ((suba >> 8) & 0x07);
   I2C_suba_num = 1;
   break; 
  // 子地址为1个字节  
  case 1:
   I2C_suba_num = 1; 
   break;
  // 子地址为2个字节
  case 2:
   I2C_suba_num = 2; 
   break;
  // 子地址为3个字节
  case 3:
   I2C_suba_num = 3; 
   break;
  default: break;
 }
 
 if(I2C_suba_num == 1)
 {
  if(I2C_num == 1)
   I2C_state = STATE_READ_ONE;
  else
    I2C_state = STATE_READ_FIRST;
 }
 else
  I2C_state = STATE_WRITE_NEXT;
      
 I2CMasterSlaveAddrSet(I2C_MASTER_BASE, I2C_sla, false);  // 主机写操作          
    I2C_suba_num--;
    I2CMasterDataPut(I2C_MASTER_BASE,
        (I2C_suba >> (8 * I2C_suba_num)));   // 写子地址高字节
    I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_START);// 突发写操作启动
   
 while(I2C_state != STATE_IDLE);
 if(true == I2CMasterBusy(I2C_MASTER_BASE))
  return (false);
 else
  return (true);
}
/*
*********************************************************************************************************
** 函数名称:IRvcStrExt()
** 函数功能:向无子地址器件读取N字节数据。
** 入口参数:sla  器件地址
**           s   接收数据的变量指针
**    no   将要读取的数据的个数
** 出口参数:使用前要初始化好I2C引脚功能和I2C中断,并已使能I2C主模式
** 说明: 至少要读取2个字节
*********************************************************************************************************
*/
int IRvcStrExt(uchar sla, uchar *s, uchar no)
{
 I2C_sla = sla >> 1;
 I2C_buf = s;
 I2C_num = no;
 
 if(I2C_num > 1)
 {
  if(I2C_num == 2)
   I2C_state = STATE_READ_FINAL;
  else
   I2C_state = STATE_READ_NEXT;
   
  I2CMasterSlaveAddrSet(I2C_MASTER_BASE, I2C_sla, true); // 主机读操作
     I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_RECEIVE_START);// 突发读操作启动
 
  while(I2C_state != STATE_IDLE);
  if(true == I2CMasterBusy(I2C_MASTER_BASE))
   return (false);
  else
   return (true);
 }
 else
  return (false);
}

/*******************************************************************************************
** 函数名称 :I2C_ISR()
** 函数功能 :中断读写数据
** 入口参数 :无
** 出口参数 :无
*******************************************************************************************/
void I2C_ISR (void)
{
    I2CMasterIntClear(I2C_MASTER_BASE);  // 清除I2C中断标志
    switch(I2C_state)
    {
        // 空闲状态
        case STATE_IDLE:
        {
            break;
        }
        // 写完单个字节状态
        case STATE_WRITE_ONE:
        {
         I2C_state = STATE_IDLE;
         break;
        }
  // 写下一个数据
        case STATE_WRITE_NEXT:
        {
         // 将下一个字节写入寄存器
            if(I2C_suba_num != 0)
            {
             I2C_suba_num--;
             I2CMasterDataPut(I2C_MASTER_BASE,
                 (I2C_suba >> (8 * I2C_suba_num)));
             if((I2C_suba_num == 0) && I2C_opt == 1)
             {
              if(I2C_num == 1)
               I2C_state = STATE_READ_ONE; 
              else
               I2C_state = STATE_READ_FIRST; 
             }
             
            }           
   else
            {
              I2CMasterDataPut(I2C_MASTER_BASE, *I2C_buf++);
             I2C_num--;            
             if(I2C_num == 1)
             {
                 I2C_state = STATE_WRITE_FINAL;
             }         
            }
            // 继续执行块写操作(run=1)
            I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_CONT);
            break;
        }
        // 写最后一个数据
        case STATE_WRITE_FINAL:
        {
            I2CMasterDataPut(I2C_MASTER_BASE, *I2C_buf);
            I2C_num--;
            // 发送停止
            I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_BURST_SEND_FINISH);
   // 下一个状态为块写完成
            I2C_state= STATE_IDLE;
            break;
        }
 
   // 读取一个字节
        case STATE_READ_ONE:
        {
            I2CMasterSlaveAddrSet(I2C_MASTER_BASE, I2C_sla, true);
            I2CMasterControl(I2C_MASTER_BASE, I2C_MASTER_CMD_SINGLE_RECEIVE);
            I2C_state= STATE_READ_WAIT;
            break;
        }
  // 读取多个字节开始
        case STATE_READ_FIRST:
        {
            I2CMasterSlaveAddrSet(I2C_MASTER_BASE, I2C_sla, true);
            I2CMasterControl(I2C_MASTER_BASE,
                             I2C_MASTER_CMD_BURST_RECEIVE_START);
            if(I2C_num == 2)
                I2C_state = STATE_READ_FINAL;
            else
             I2C_state = STATE_READ_NEXT;
            break;
        }
  // 读取下一个字节
        case STATE_READ_NEXT:
        {
            *I2C_buf++ = I2CMasterDataGet(I2C_MASTER_BASE);
            I2C_num--;
            I2CMasterControl(I2C_MASTER_BASE,
                             I2C_MASTER_CMD_BURST_RECEIVE_CONT);
            if(I2C_num == 2)
                I2C_state = STATE_READ_FINAL;
            break;
        }
  // 读取最后一个字节
        case STATE_READ_FINAL:
        {
            *I2C_buf++ = I2CMasterDataGet(I2C_MASTER_BASE);
            I2C_num--;
            I2CMasterControl(I2C_MASTER_BASE,
                             I2C_MASTER_CMD_BURST_RECEIVE_FINISH);
            I2C_state= STATE_READ_WAIT;
            break;
        }
  // 等待读取一个字节
        case STATE_READ_WAIT:
        {
 
            *I2C_buf++  = I2CMasterDataGet(I2C_MASTER_BASE); // 读取数据
            I2C_num--;
            I2C_state= STATE_IDLE;
            break;
        }
    }
}
点赞 关注

回复
举报
您需要登录后才可以回帖 登录 | 注册

查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表