打印
[新手园地]

HOT大叔NUC120助学板第七贴----I2C(查询方式)

[复制链接]
8222|25
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
菜鸟玩I2C查询方式完成单字节写及随机地址读操作,望各位多多交流,多多指教!

源代码:
 
/**************************************************
** 文件名称:NUC120_HOT_I2C.c
** 文件说明:NUC120助学板练习程序
** 创建日期:2011-04-11
** 修改日期:
** 备    注:I2C查询方式完成单字节写及随机地址读操作
**************************************************/
#include <stdio.h>
#include "NUC1xx.h"
#include "Driver\DrvGPIO.h"
#include "Driver\DrvSYS.h"
#include "Driver\DrvUART.h"
#include "Driver\DrvI2C.h"

uint8_t  Run_Led = 4;    //2----LED1  3----LED2   4----LED3  5----LED4
volatile uint8_t Receive_Data = 0;
volatile uint8_t IsStart = FALSE;
/***************
**  函数声明  **
***************/
void Init_System (void);
void Init_Uart (void);
void UART_INT_HANDLE(uint32_t u32IntStatus);
void I2C_WriteByte ( uint32_t address,uint8_t data );
uint8_t I2C_ReadByte ( uint32_t address );


/*****************************
** Name:      UART_INT_HANDLE
** Function:  UART Callback function
** Input:     u32IntStatus
** OutPut:    None
** Data:      2011-03-17
** Note:      
****************************/
void UART_INT_HANDLE(uint32_t u32IntStatus)
{
  uint8_t bInChar[1]={0xFF};
if(u32IntStatus & DRVUART_RDAINT)
{
  /* Get all the input characters */
  while(UART0->ISR.RDA_IF==1)
  {
   /* Get the character from UART Buffer */
   DrvUART_Read(UART_PORT0,bInChar,1);
   if (IsStart!=TRUE)
   {
    IsStart = TRUE;
    Receive_Data = bInChar[0];
   }
  }
}
}
/*****************************
** Name:      Init_System
** Function:  系统初始化函数
** Input:      None
** OutPut:     None
** Data:       2011-03-17
** Note:      
****************************/
void Init_System(void)
{
/* Unlock the locked registers before access */
    UNLOCKREG(x); //寄存器锁定键地址寄存器(RegLockAddr) :有些系统控制寄存器需要被保护起来,以防止误操作而影响芯片运行,
        //这些寄存器在上电复位到用户解锁定之前是锁定的。用户可以连续依次写入“59h”, “16h” “88h”到0x5000_0100解锁定.
/* Enable the 12MHz oscillator oscillation */
DrvSYS_SetOscCtrl(E_SYS_XTL12M, 1);        //SYSCLK->PWRCON.XTL12M_EN = 1;
/* Waiting for 12M Xtal stable */
//while (DrvSYS_GetChipClockSourceStatus(E_SYS_XTL12M) != 1);  //SYSCLK->CLKSTATUS.XTL12M_STB
/*eClkSrc  - [in]  E_SYS_XTL12M / E_SYS_XTL32K / E_SYS_OSC22M / E_SYS_OSC10K / E_SYS_PLL    */
// Note: Only some of NuMicro NUC100 Series support this function.
DrvSYS_Delay(5000);
LOCKREG(x);
//向“0x5000_0100”写入任何值,就可以重锁保护寄存器
}
/*****************************
** Name:      Init_Uart
** Function:  UART初始化函数
** Input:      None
** OutPut:     None
** Data:       2011-03-17
** Note:      
****************************/
void Init_Uart(void)
{
STR_UART_T param;
/*
声明 UART设置的结构体 位于DRVUART.H
结构体如下
typedef struct DRVUART_STRUCT
{
  uint32_t            u32BaudRate;   
  E_DATABITS_SETTINS  u8cDataBits;   
  E_STOPBITS_SETTINS  u8cStopBits;   
  E_PARITY_SETTINS  u8cParity;     
  E_FIFO_SETTINGS     u8cRxTriggerLevel;   
  uint8_t             u8TimeOut ;     
      }STR_UART_T;
*/
DrvSYS_SelectIPClockSource(E_SYS_UART_CLKSRC,0);      //使能UART时钟
//SYSCLK->CLKSEL1.UART_S = 0;  //UART时钟源选择. 00 =外部12MHz 晶振 01 = PLL 1x =内部 22MHz 振荡器
DrvGPIO_InitFunction(E_FUNC_UART0);          //GPB_MFP0-1-2-3置位 GPIO使能UART功能
//outpw(&SYS->GPBMFP, inpw(&SYS->GPBMFP) | (0xF<<0));
param.u32BaudRate        = 115200;       //  波特率
param.u8cDataBits        = DRVUART_DATABITS_8;    //  数据位
param.u8cStopBits        = DRVUART_STOPBITS_1;    //  停止位
param.u8cParity          = DRVUART_PARITY_NONE;    //  校验位
param.u8cRxTriggerLevel  = DRVUART_FIFO_1BYTES;    //  FIFO存储深度 1 字节
param.u8TimeOut          = 0;        //  FIFO超时设定
/* Set UART Configuration */
  if(DrvUART_Open(UART_PORT0,&param) != E_SUCCESS)   //  串口开启、结构体整体赋值
  printf("UART0 open failed\n");      
/* u32Port -[in] UART Channel:  UART_PORT0 / UART_PORT1 /UART_PORT2 */
/* sParam  -[in] the struct parameter to configure UART    */   
/* Enable Interrupt and install the call back function */
DrvUART_EnableInt(UART_PORT0, DRVUART_RDAINT,UART_INT_HANDLE);
/*u32Port     -[in] UART Channel:  UART_PORT0 / UART_PORT1 / UART_PORT2                   */
/*u32InterruptFlag -[in] DRVUART_LININT/DRVUART_WAKEUPINT/DRVUART_BUFERRINT/DRVUART_RLSINT     */
/*          DRVUART_MOSINT/DRVUART_THREINT/DRVUART_RDAINT/DRVUART_TOUTINT   */
/*pfncallback      -[in] A function pointer for callback function                              */
}
int main (void)
{
uint8_t test = 250;
uint8_t EEPROM_Data = 0;

Init_System();

Init_Uart();
DrvGPIO_InitFunction(E_FUNC_I2C1);  
DrvGPIO_Open(E_GPA,Run_Led, E_IO_OUTPUT);      //程序运行指示
DrvGPIO_ClrBit(E_GPA,Run_Led);
printf("\n");
printf("/*==========================\n");
printf("======菜农 %d 助学计划======\n",test);
printf("========NUC120助学板========\n");
printf("======程序参考新唐BSP库======\n");
printf("=======2011年04月11日=======\n");
printf("========I2C(查询)实验=======\n");
printf("I2C查询方式完成单字节写及随机地址读操作\n");
printf("'R/r'为读指令、'U/u'为加1并存储指令、'D/d'为减1并存储指令\n");
printf("===========================/\n");
EEPROM_Data = I2C_ReadByte(0x00);
printf("AT24C16地址0的内容为:0x%x!\n",EEPROM_Data);
printf("====请输入字符开始测试!===\n");
printf("==========================*/\n");
    while(1)
    {
        if (IsStart)
        {
         switch (Receive_Data)
         {
          case 'R':
    case 'r':
     EEPROM_Data = I2C_ReadByte(0x00);
     printf("AT24C16地址0的内容为:0x%x!\n",EEPROM_Data);
     break;
    case 'U':
    case 'u':
     I2C_WriteByte(0x00,++EEPROM_Data);
     printf("AT24C16地址0的内容加1成功!\n");
     break;
    case 'D':
    case 'd':
     I2C_WriteByte(0x00,--EEPROM_Data);
     printf("AT24C16地址0的内容减1成功!\n");
     break;
    default:
     printf("请确认您输入的指令是否合法!\n");
   
         }
         IsStart = FALSE;
        }
    }
}
/*****************************
** Name:      I2C_WriteByte
** Function:  I2C向写单字节函数
** Input:     uint32_t address,uint8_t data
** OutPut:    None
** Data:      2011-04-11
** Note:      
****************************/
void I2C_WriteByte ( uint32_t address,uint8_t data )
{
/*
在字节写模式下,主器件发送起始命令和从器件地址信息(R/W 位置 0)给从器件,
主器件在收到从器件产生应答信号后,主器件发送1个8位字节地址写入AT24C01/
02/04/08/16 的地址指针,对于 AT24C31/64/128/256 来说,所不同的是主器件
发送两个8位地址字写入AT24C32/64/128/256的地址指针。主器件在收到从器件的
另一个应答信号后,再发送数据到被寻址的存储单元。AT24Cxx 再次应答,并在
主器件产生停止信号后开始内部数据的擦写,在内部擦写过程中,AT24Cxx 不再
应答主器件的任何请求。
*/
uint32_t i;

DrvI2C_Open(I2C_PORT1, 50000);  //打开I2C1功能,并配置 I2C总线时钟为50KHZ
/* Parameters:port      - [in]  I2C_PORT0 / I2C_PORT1        */
/*           u32BusClock   - [in]  I2C bus clock frequency (Hz)      */

DrvI2C_Ctrl(I2C_PORT1, 1, 0, 0, 0); //设定I2C控制比特   START
/* Parameters:port   - [in]  I2C_PORT0 / I2C_PORT1       */
/*            start   - [in]  1:Enable / 0:Disable       */
/*            stop   - [in]  1:Enable / 0:Disable       */
/*            intFlag   - [in]  Wrtie '1' to clear this flag */
/*            ack   - [in]  1:Enable / 0:Disable       */
while (I2C1->CON.SI == 0);   //查询中断标志位 SI
I2C1->DATA = 0xA0;     //发送写命令
    DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 0); //清标志位 SI
    while( I2C1->CON.SI == 0 );      //查询中断标志位 SI

I2C1->DATA = address&0XFF;   //发送地址
DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 1); //清标志位 SI 并使能应答
while( I2C1->CON.SI == 0 );   //查询中断标志位 SI

I2C1->DATA = data;     //发送待写内容
DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 1); //清标志位 SI 并使能应答
while( I2C1->CON.SI == 0 );   //查询中断标志位 SI

DrvI2C_Ctrl(I2C_PORT1, 0, 1, 1, 0); //清标志位 SI 并STOP

for(i=0;i<60;i++);
DrvI2C_Close(I2C_PORT1);
   
for(i=0;i<6000;i++);
for(i=0;i<6000;i++);
}
/*****************************
** Name:      I2C_ReadByte
** Function:  I2C向读单字节函数
** Input:     uint32_t address,uint8_t data
** OutPut:    None
** Data:      2011-04-11
** Note:      
****************************/
uint8_t I2C_ReadByte ( uint32_t address )
{
/*
随机读操作允许主器件对寄存器的任意字节进行读操作,主器件
首先通过发送起始信号、从器件地址和它想读取的字节数据的地
址执行一个伪写操作。在AT24Cxx 应答之后,主器件重新发送起
始信号和从器件地址,此时R/W 位置1,AT24CXX 响应并发送应答
信号,然后输出所要求的一个 8位字节数据,主器件不发送应答
信号但产生一个停止信号。
*/
uint8_t TEMP;

DrvI2C_Open(I2C_PORT1, 50000);

    DrvI2C_Ctrl(I2C_PORT1, 1, 0, 1, 0);   //设定I2C控制比特   START并清中断标志
while (I2C1->CON.SI == 0);    //查询中断标志位 SI
  
I2C1->DATA = 0XA0;      //发送写命令
    DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 0);     //清标志位 SI
    while( I2C1->CON.SI == 0 );      //查询中断标志位 SI
   
I2C1->DATA = address&0xFF;    //发送地址
DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 1);     //清标志位 SI 并使能应答
while( I2C1->CON.SI == 0 );       //查询中断标志位 SI

DrvI2C_Ctrl(I2C_PORT1, 1, 0, 1, 0);     //设定I2C控制比特   START并清中断标志
while( I2C1->CON.SI == 0 );       //查询中断标志位 SI

I2C1->DATA = 0XA1;      //发送读命令
DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 1);     //清标志位 SI 并使能应答
    while( I2C1->CON.SI == 0 );       //查询中断标志位 SI
//I2C1->DATA = 0xFF;     //通过操作数据寄存器配合控制位的设置启动一次新的I2C操作(此句加与不加未影响到结果)
/*"Software should load the data byte (to be transmitted)into I2DAT before new I2CON setting is done." 手册中该句怎么解释?*/
DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 0);     //清标志位 SI
while( I2C1->CON.SI == 0 );       //查询中断标志位 SI
TEMP= I2C1->DATA;      //读数据
  
  DrvI2C_Ctrl(I2C_PORT1, 0, 1, 1, 0);    //清标志位 SI 并STOP
DrvI2C_Close(I2C_PORT1);
return TEMP;
}


工程结构:


串口调试截图:


工程文件:
NUC120_HOT_I2C.rar (1.68 MB)

一点疑问:

相关帖子

沙发
lanrongye| | 2011-4-11 17:15 | 只看该作者
我坐上传说中的沙发

使用特权

评论回复
板凳
wzywzy12| | 2011-4-11 17:35 | 只看该作者
//我的理解是这样的
//发送读命令
I2C1->DATA = 0xA1;//本句先把数据写进相应的寄存器,但不发送数据到总线
DrvI2C_Ctrl(I2C_PORT1, 0, 0, 1, 1);//清SI并要求应答,I2CON内容发生变化,启动发送到总线
while( I2C1->CON.SI == 0 );//直到发送完成

使用特权

评论回复
地板
wzywzy12| | 2011-4-11 17:53 | 只看该作者
还有一点,我觉得下面这句要加的。在接收数据时候起拉高DATA线的作用,才能读到从机发来数据。
//I2C1->DATA = 0xFF;     //通过操作数据寄存器配合控制位的设置启动一次新的I2C操作(此句加与不加未影响到结果)

使用特权

评论回复
5
hotpower| | 2011-4-11 18:38 | 只看该作者
还有一点,我觉得下面这句要加的。在接收数据时候起拉高DATA线的作用,才能读到从机发来数据。
//I2C1->DATA = 0xFF;     //通过操作数据寄存器配合控制位的设置启动一次新的I2C操作(此句加与不加未影响到结果) ...
wzywzy12 发表于 2011-4-11 17:53


如果用准双向IO的理解是对的,但这是硬件控制器,不应该涉及这些。

使用特权

评论回复
6
hotpower| | 2011-4-11 18:38 | 只看该作者
void I2cObj::Start(void)
{
Busy = true;
State = I2C_START;//主机准备发送启始位
Count = 0;//发送数据个数
SystickCount = 0;//清除节拍计数器
I2C1s.CON.Regs &= ~((1 << I2C_CON_STO)
| (1 << I2C_CON_AA));
I2C1s.CON.Regs |= (1 << I2C_CON_EI)
| (1 << I2C_CON_ENSI)
| (1 << I2C_CON_SI);
I2C1s.CON.Bits.STA = 1;
Wait();
}

void I2cObj::REStart(void)
{
Busy = true;
State = I2C_REP_START;//主机准备发送启始位
Count = 0;//发送数据个数
I2C1s.CON.Regs |= (1 << I2C_CON_STA) | (1 << I2C_CON_SI);
}

void I2cObj::Stop(void)
{
Busy = false;
State = I2C_BUS_OK;//通讯成功
I2C1s.CON.Regs |= (1 << I2C_CON_STO) | (1 << I2C_CON_SI);
}

void I2cObj::Exit(void)
{
Busy = false;
State = I2C_BUS_ERROR;//通讯失败
I2C1s.CON.Bits.SI = 1;//清除中断标志
}

void I2cObj::Wait(void)
{
SystickCount = 0;//清除节拍计数器
// while (Busy && SystickCount < 10);//100mS超时
while (SystickCount < 10);//100mS超时
}

void I2cObj::WriteWait(void)
{
SystickCount = 0;//清除节拍计数器
while (SystickCount < 10);//延时100mS等待写入完成
}

//I2C回调函数(静态成员函数)
void I2cObj::Exec(void)
{
NU_REG I2cStatus;
I2cStatus = I2C1s.STATUS.Regs & 0xf8;
switch(I2cStatus) {
// switch(I2C1s.STATUS.Regs & 0xf8) {
case I2C_START://主机收到自己发送的开始信号
if (I2c.State == I2C_START) {//本次中断应该接收TW_START信号
I2C1s.DATA.Regs = I2c.SubAddr & 0xfe;//发送子机地址(写)
I2C1s.CON.Bits.STA = 0;//此位必须清除,否则死机
I2C1s.CON.Bits.SI = 1;//清除中断标志
I2c.State = I2C_MT_SLA_ACK;//Status下次I2C_MT_SLA_ACK
}
else I2c.Exit();//通讯失败
break;
case I2C_REP_START://主机收到自己发送的重新开始信号
if (I2c.State == I2C_REP_START) {//本次中断应该接收TW_RESTART信号
I2C1s.DATA.Regs = I2c.SubAddr | 0x01;//发送子机地址(读)
I2c.State = I2C_MR_SLA_ACK;//Status下次I2C_MR_SLA_ACK
I2C1s.CON.Bits.STA = 0;//此位必须清除,否则读不出数据
I2C1s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
}
else I2c.Exit();//通讯失败
break;
case I2C_MT_SLA_ACK://主发机接收到从机的地址应答信号后发送命令
if (I2c.State == I2C_MT_SLA_ACK) {//本次中断应该接收TW_MT_SLA_ACK信号
I2c.State = I2C_MT_DATA_ACK;//Status下次应该收TW_MT_DATA_ACK
I2C1s.DATA.Regs = I2c.SubComm;//发送子机命令
I2C1s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
}
else I2c.Exit();//通讯失败
break;
case I2C_MR_SLA_ACK://主收机接收到从机的地址应答信号
if ((I2c.State == I2C_MR_SLA_ACK) && I2c.SubCount) {//本次中断应该接收TW_MR_SLA_ACK信号
I2c.State = I2C_MR_DATA_ACK;//Status下次应该收TW_MR_DATA_ACK
// I2C1s.DATA.Regs = 0XFF;//
I2C1s.CON.Bits.SI = 1;//清除中断标志
}
else I2c.Exit();//通讯失败
break;
case I2C_MT_DATA_ACK://主收机接收到从机的数据应答信号
if ((I2c.State == I2C_MT_DATA_ACK) && (I2c.Count < I2c.MainCount)) {//本次中断应该接收TW_MT_DATA_ACK信号
I2C1s.DATA.Regs = I2c.TxBuffer[I2c.Count ++];//发送子机数据
I2C1s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);
}
else {
if ((I2c.State == I2C_MT_DATA_ACK) && (I2c.Count == I2c.MainCount) && (I2c.SubAddr & 1)) {//本次中断应该接收TW_MT_DATA_ACK信号
I2c.REStart();//
}
else I2c.Stop();//通讯成功
}
break;
case I2C_MR_DATA_ACK:
if ((I2c.State == I2C_MR_DATA_ACK) && (I2c.Count < I2c.SubCount)) {
I2c.RxBuffer[I2c.Count ++] = I2C1s.DATA.Regs;//接收子机数据
if (I2c.Count < I2c.SubCount) {
I2C1s.CON.Regs |= (1 << I2C_CON_SI) | (1 << I2C_CON_AA);//主机转入接收状态
}
else {
I2C1s.CON.Bits.AA = 0;//清除ACK标志
I2C1s.CON.Bits.SI = 1;//清除中断标志
I2c.State = I2C_MR_DATA_NACK;//下次进入I2C_MR_DATA_NACK,接收数据准备完成
}
}
else I2c.Exit();//通讯失败
break;
case I2C_MR_DATA_NACK://数据接收结束
if ((I2c.State == I2C_MR_DATA_NACK) && (I2c.Count == I2c.SubCount)) {
I2c.Stop();//通讯成功
}
else I2c.Exit();//通讯失败
break;
// case I2C_MT_DATA_NACK:
// Exit();//通讯失败
// break;
default:
I2c.Exit();//通讯失败
}
}

使用特权

评论回复
7
hotpower| | 2011-4-11 18:40 | 只看该作者
 
unsigned char I2cObj::ReadByte(unsigned int Address, unsigned char &Data)
{
SubAddr |= 0x01;
MainCount = 0;//发送0个数据(只读)
//本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
SubComm = Address;//读出地址
SubCount = 1;//接收1个数据
Start();//启动I2C模块
if (State == I2C_BUS_OK) {//通讯成功
  Data = RxBuffer[0];//从接收缓冲区取出一个字节
}
return State;//(读出数据在RxBuffer[0]~RxBuffer[15])
}
unsigned char I2cObj::ReadBuffer(unsigned int Address, unsigned char *Data, unsigned int Cnt)
{
SubAddr |= 0x01;
MainCount = 0;//发送0个数据(只读)
//本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
SubComm = Address;//读出地址
SubCount = (Cnt <= sizeof(RxBuffer)) ? Cnt : sizeof(RxBuffer);//接收Cnt个数据
Start();//启动I2C模块
if (State == I2C_BUS_OK) {//通讯成功
  for (int i = 0; i < SubCount; i ++) Data[i] = RxBuffer[i];//从接收缓冲区取出Cnt个字节
}
return State;//(读出数据在RxBuffer[0]~RxBuffer[15])
}
unsigned char I2cObj::WriteByte(unsigned int Address, unsigned char Data)
{
SubAddr &= 0xfe;
MainCount = 1;//发送1个数据
//本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
SubComm = Address;//写入地址
TxBuffer[0] = Data;//写入1个数据到发送缓冲区
SubCount = 0;//接收0个数据
Start();//启动I2C模块
WriteWait();//延时100mS等待写入完成
return State;
}
unsigned char I2cObj::WriteBuffer(unsigned int Address, unsigned char *Data, unsigned int Cnt)
{
SubAddr &= 0xfe;
MainCount = (Cnt <= sizeof(TxBuffer)) ? Cnt : sizeof(TxBuffer);//发送Cnt个数据
//本程序为通用I2C,故发送器件后一般为命令,对EEPROM来说,命令实际是EEPROM地址
SubComm = Address;//写入地址
for (int i = 0; i < MainCount; i ++) TxBuffer[i] = Data[i];//写入Cnt个数据到发送缓冲区
SubCount = 0;//接收0个数据
Start();//启动I2C模块
WriteWait();//延时100mS等待写入完成
return State;
}

使用特权

评论回复
8
hotpower| | 2011-4-11 18:48 | 只看该作者
本帖最后由 hotpower 于 2011-4-11 18:49 编辑

PIC24系列的I2C(注意里面的读写回调)
 
#include "i2c.h"
_PERSISTENT volatile I2CREGS I2CRegs;
_PERSISTENT volatile I2CBITS I2CBits;
void I2cInit(void)
{
unsigned int i;
// TRIS_WP   = PORTOUTMODE;//定义WP为输出IO
TRIS_SCL1 = PORTOUTMODE;//定义SCL为输出IO
TRIS_SDA1 = PORTINPUTMODE;//定义SDA为输出入IO
ODC_SCL1 = 1;//OC输出
ODC_SDA1 = 1;//OC输出
// WP = 1;//写保护
//    I2CRegs.MaxCount = 0x200;//8KByte
    I2CRegs.MaxCount = 256;//256Byte
I2CRegs.I2CAddr = 0xb2;//器件地址
I2CRegs.Command = 0;//EEPROM读写地址命令
I2CRegs.TxCount = 0;//发送数据字节个数
I2CRegs.RxCount = 0;//接收数据字节个数
for (i = 0; i < sizeof(I2CRegs.TxBuffer); i ++)
{
  I2CRegs.TxBuffer = 0;//发送缓冲区清零
}
for (i = 0; i < sizeof(I2CRegs.RxBuffer); i ++)
{
  I2CRegs.RxBuffer = 0;//接收缓冲区清零
}
I2C1CON = 0;
// I2C1CONbits.A10M = 0;//7位地址模式
I2C1CONbits.SCLREL = 1;
I2C1MSK = 0;
I2C1STAT = 0;
_MI2C1IF = 0;
_SI2C1IF = 0;
I2C1BRG = (FCY / (2 * I2CBAUD)) - 1;//波特率计算
/*------------------------------------------------------------------------
    定义I2C串口2中断优先级位1111)
-------------------------------------------------------------------------*/
IPC4bits.MI2C1P0 = 1;
IPC4bits.MI2C1P1 = 1;
IPC4bits.MI2C1P2 = 1;
I2C1CONbits.I2CEN = 1;//允许I2C功能
_MI2C1IE = 1;//允许主设备中断
// I2cStop();
}
/*------------------------------------------------------------------
EEPROM读块函数(只能在回调函数I2CReadCallBack中得到读出的数据)
-------------------------------------------------------------------*/
void I2CTest(void)
{
I2CRegs.TxCount = 19;
I2CRegs.RxCount = 20;
I2CRegs.Command = '$';
I2CRegs.I2CAddr |= 1;//0xa3
I2cStart();
}
void I2CReadBuffers(unsigned int Command, unsigned char ReadBuffer[], unsigned int ReadSize)
{
if (ReadSize && (ReadSize <= 256))
{
  I2CRegs.TxCount = 0;
  I2CRegs.RxCount = ReadSize;
  I2CRegs.Command = Command;
  I2CRegs.I2CAddr |= 1;//0xa3
  I2cStart();
}
}
/*------------------------------------------------------------------
EEPROM写块函数
-------------------------------------------------------------------*/
void I2CWriteBuffers(unsigned int Command, unsigned char WriteBuffer[], unsigned int WriteSize)
{
if (WriteSize && (WriteSize <= 16))
{
  I2CRegs.TxCount = WriteSize;
  I2CRegs.RxCount = 0;
  I2CRegs.Command = Command;
  I2CRegs.I2CAddr &= 0xfe;//0xa0
  I2cStart();
}
}
/*------------------------------------------------------------------
用户读回调函数
-------------------------------------------------------------------*/
void I2CReadCallBack(void)
{
I2CBits.ReadFlag = 0;
I2CRegs.I2CAddr ^= 0x10;
I2CTest();
}
/*------------------------------------------------------------------
用户写回调函数
-------------------------------------------------------------------*/
void I2CWriteCallBack(void)
{
I2CBits.WriteFlag = 0;
}

/*------------------------------------------------------------------
EEPROM读写启动函数
-------------------------------------------------------------------*/
void I2cStart(void)
{
/*------------------------------------------------------------------------
//本程序在状态I2C_MT_ADDRL_ACK下进行瞬间打开,也可在此打开,不过安全不好
if (I2CRegs.TxCount)//需要写入字节
{
  WP = 0;//不写保护
}
else
{
  WP = 1;//写保护
}
--------------------------------------------------------------------------*/
I2C1STATbits.IWCOL = 0;
    I2CBits.BusyFlag = 1;
    I2CRegs.State = I2C_START;//主机准备发送启始位
    I2CRegs.Count = 0;//发送数据个数
I2CBits.I2CFlag = 0;
I2C1CONbits.SEN = 1;//发送Start信号
}
/*------------------------------------------------------------------
EEPROM读再启动函数
-------------------------------------------------------------------*/
void I2cReStart(void)
{
I2C1STATbits.IWCOL = 0;
    I2CBits.BusyFlag = 1;
    I2CRegs.State = I2C_REP_START;//主机准备发送重新启始位
    I2CRegs.Count = 0;//发送数据个数
I2C1CONbits.RSEN = 1;//发送ReStart信号
I2C1CONbits.ACKEN = 0;
}
/*------------------------------------------------------------------
EEPROM读写正确停止函数
-------------------------------------------------------------------*/
void I2cStop(void)
{
I2C1STATbits.IWCOL = 0;
    I2CBits.BusyFlag = 0;
I2CRegs.State = I2C_SUCCEEDED;//通讯成功
I2C1CONbits.PEN = 1;//发送Stop信号
// WP = 1;//写保护
}

/*------------------------------------------------------------------
EEPROM读写错误退出函数
-------------------------------------------------------------------*/
void I2cExit(void)
{
I2C1STATbits.IWCOL = 0;
    I2CBits.BusyFlag = 0;
I2CRegs.State = I2C_FAILED;
I2C1CONbits.PEN = 1;//发送Stop信号
// WP = 1;//写保护
}
/*------------------------------------------------------------------
  EEPROM读写中断事件处理函数(说明见文件头部)
-------------------------------------------------------------------*/
void I2CExec(void)
{
if (I2C1STATbits.S)//收到Start过信号
{
  switch (I2CRegs.State)
  {
   case I2C_START://收到Start信号
    I2C1TRN = I2CRegs.I2CAddr & 0xfe;//发送器件写地址(通知从机只能听)
    I2CRegs.State = I2C_MT_SLA_ACK;//下次应该接收器件写地址应答信号
    break;
   case I2C_MT_SLA_ACK://收到器件写地址应答信号
    if (!I2C1STATbits.ACKSTAT)//收到Ack信号
    {
     if (I2CRegs.MaxCount > 0x100)//EEPROM容量超过256个字节,EEPROM地址需要两次发送
     {
      I2C1TRN = I2CRegs.Command >> 8;//发送EEPROM写高8位地址
      I2CRegs.State = I2C_MT_ADDRH_ACK;//下次应该接收EEPROM写高8位地址应答信号
     }
     else//小容量只需一次发送!!!
     {
      I2C1TRN = I2CRegs.Command;//发送EEPROM写低8位地址
      I2CRegs.State = I2C_MT_ADDRL_ACK;//下次应该接收EEPROM写低8位地址应答信号
      I2CRegs.Count = 0;//清空发送缓冲计数器
     }
    }
    else//收到NAck信号
    {
     I2cExit();//错误的ACK信号
    }
    break;
   case I2C_MT_ADDRH_ACK://收到EEPROM写高8位地址应答信号
    if (!I2C1STATbits.ACKSTAT)//收到Ack信号
    {
     I2C1TRN = I2CRegs.Command & 0xff;//发送EEPROM写低8位地址
     I2CRegs.State = I2C_MT_ADDRL_ACK;//下次应该接收EEPROM写低8位地址应答信号
     I2CRegs.Count = 0;//清空发送缓冲计数器
    }
    else//收到NAck信号
    {
     I2cExit();//错误的ACK信号
    }
    break;
   case I2C_MT_ADDRL_ACK://收到EEPROM写高低8位地址应答信号
    if (I2CRegs.TxCount)//写保护只在写入期间不保护,增加了对误写入的安全防护能力!!!
    {
//      WP = 0;//不写保护
    }
   case I2C_MT_DATA_ACK://收到应答信号
    if (!I2C1STATbits.ACKSTAT)//收到Ack信号
    {
     if (I2CRegs.Count < I2CRegs.TxCount)//缓冲区未空
     {
      I2C1TRN = I2CRegs.TxBuffer[I2CRegs.Count ++];//继续发送数据
     }
     else if (I2CRegs.Count == I2CRegs.TxCount)//缓冲区已空
     {
      if (I2CRegs.I2CAddr & 1)//应该开始接收数据
      {
       I2cReStart();//发送重复位命令
      }
      else//只写退出
      {
       I2cStop();//正常发送结束
      }
     }
     else//干扰出错
     {
      I2cExit();//错误
     }
    }
    else//收到NAck信号(可能被写保护)
    {
     I2cExit();//错误的ACK信号
    }
    break;
   case I2C_REP_START://收到ReStart信号
    I2C1TRN = I2CRegs.I2CAddr | I2C_READ;//发送器件读地址(通知从机可以说话)
    I2CRegs.State = I2C_MR_SLA_ACK;//下次应该接收器件写读地址应答信号
    break;
   case I2C_MR_SLA_ACK://收到器件读地址应答信号
    if (!I2C1STATbits.ACKSTAT)//收到Ack信号
    {
     I2C1CONbits.RCEN = 1;//开始接收数据
     I2CRegs.State = I2C_MR_DATA;//下次应该收接收数据
    }
    else//收到NAck信号
    {
     I2cExit();//错误的ACK信号
    }
    break;
   case I2C_MR_DATA://收到接收数据
    if (I2CRegs.Count < I2CRegs.RxCount)
    {
//     I2C1STATbits.I2COV = 0;
     I2CRegs.RxBuffer[I2CRegs.Count ++] = I2C1RCV;
     if (I2CRegs.Count < I2CRegs.RxCount)
     {
       I2C1CONbits.ACKDT = 0;//应答子机
      I2CRegs.State = I2C_MR_DATA_EN;//下次应该收到器件允许继续读信号
     }
     else
     {
      I2C1CONbits.ACKDT = 1;//非应答子机
      I2CRegs.State = I2C_MR_DATA_STOP;//下次应该收到退出信号
     }
      I2C1CONbits.ACKEN = 1;//向从机发送(非)应答信号
    }
    else//正确的状态已分支到I2C_MR_DATA_STOP
    {
     I2cExit();//错误
    }
    break;
   case I2C_MR_DATA_EN://收到器件允许继续读信号
    I2C1CONbits.RCEN = 1;//开始接收数据
    I2CRegs.State = I2C_MR_DATA;//下次应该继续接收数据
    break;
   case I2C_MR_DATA_STOP://收到器件退出信号
    I2cStop();//正常接收结束
    break;
   default://其他不可预料的错误
    I2cExit();//错误
  }
}
else if (I2C1STATbits.P)//收到Stop信号
{
  if (I2CRegs.State == I2C_SUCCEEDED)//成功,回调
  {
   if (I2CRegs.I2CAddr & 1)//读
   {
    I2CBits.ReadFlag = 1;//激活用户读回调函数I2CReadCallBack()
    I2CReadCallBack();
   }
   else//写
   {
    I2CBits.WriteFlag = 1;//激活用户写回调函数I2CWriteCallBack()
    I2CWriteCallBack();
   }
  }
}
else//无法确认的复杂错误
{
  I2cExit();//错误出错退出
}
}

使用特权

评论回复
9
Swallow_0322|  楼主 | 2011-4-11 20:04 | 只看该作者
多谢大叔送裤子一条!

再次谢谢HOT大叔及wzywzy12的指教!在此表示感谢!:handshake

回头认真学习大叔中断方式的IIC程序!

使用特权

评论回复
10
arto| | 2011-4-11 20:04 | 只看该作者
顶一下.........

使用特权

评论回复
11
plc_avr| | 2011-4-12 14:34 | 只看该作者
顶!!!

使用特权

评论回复
12
hotpower| | 2011-4-13 15:10 | 只看该作者
这个俺将会在群课里讲解革命道理的.

使用特权

评论回复
13
Swallow_0322|  楼主 | 2011-4-13 17:21 | 只看该作者
12# hotpower

耐心等待...

使用特权

评论回复
14
hotpower| | 2011-4-26 18:03 | 只看该作者
帮你顶起来,与中断方式比较。

使用特权

评论回复
15
tlb| | 2011-4-27 05:59 | 只看该作者
顶上去

使用特权

评论回复
16
weshiluwei6| | 2011-4-27 19:03 | 只看该作者
顶上去 高手啊 羡慕啊 支持 流口水

使用特权

评论回复
17
tlb| | 2011-4-28 07:24 | 只看该作者
要输就输给理想,要败就败给高手

水王是高人见解呀

使用特权

评论回复
18
Swallow_0322|  楼主 | 2011-4-28 13:39 | 只看该作者
呵呵!楼上各位太抬举我了,多谢顶帖!:handshake

使用特权

评论回复
19
lixupengarm| | 2011-5-23 17:59 | 只看该作者
:D学习!!

使用特权

评论回复
20
电子write_cai| | 2011-8-20 23:30 | 只看该作者

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

121

主题

1393

帖子

4

粉丝