英锐恩单片机论坛,Microchip单片机,模拟器件,接口电路,麦肯单片机,单片机应用交流

 找回密码
 立即注册
搜索
电子烟方案单片机单片机开发深圳单片机开发
单片机方案国产单片机8位单片机电子烟方案开发
查看: 4092|回复: 0
打印 上一主题 下一主题

PIC24F之EEPROM读写中断事件处理函数要点及说明

[复制链接]
跳转到指定楼层
1#
发表于 2008-8-19 18:44:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
/*-------------------------------------------------------------------------------------------------
                        PIC24F之EEPROM读写中断事件处理函数要点及说明
注意: 这是一个通用的I2C/SMBUS通讯中断处理程序
对于EEPROM来讲,从机后面需要跟EEPROM需要读写的地址(I2CRegs.RWAddr)
对于SMBUS来说,从机后面需要跟SMBUS需要的命令(I2CRegs.RWAddr改为I2CRegs.CMD即可)
由于PIC24F的I2C不太标准,I2C1STAT被搞得很倒塌!!!一点都没I2C的"大家闺秀"的样子~~~
不过它的STOP还能激活中断确实比LPCARM/AVR好一点点~~~

为什么I2C收发都用中断呢???
这主要是为了高低速灵活变化的总线通讯所做,主要是SMBUS总线的通信.
菜农在LPCARM/AVR上用此程序模板可谓不怕数据被干扰~~~

如果为I2cExit()也配上钩子函数,那么任何错误都在手掌中~~~

这个PIC程序虽没SMBUS的PEC校验部分,但"异步"还是完美的.
当然也要注意对写保护硬件管脚的控制时机的把握,原则是关保护的时间最短就更好~~~

菜农本来PIC24F菜鸟已“毕业”,但还是“忍痛”发表出来~~~

主要看到人们编写MCU程序太死板~~~特别是I2C程序.网上收发全中断的很少,可以说几乎没有.

随贴附老外倒塌的非中断I2C状态机读写程序i2cEmem.c~~~可以比较经典和非典的差异在何处~~~

菜农近期将整理出LPCARM和AVR的I2C/SMBUS/TWI/USI收发全中断实战例程供大家“游玩”~~~

如果精通DELPHI程序的人一定会为“事件驱动”机制而痴迷~~~为什么不在MCU上"声东击西"呢???

"有事件才处理"---这才是编程的硬道理~~~轮循的“痴迷等待”最终还是“单相思”~~~

本程序附实战结果图.(因为菜农的程序从来不空谈社会主义~~~)

原本是在"鸡蛋节"献给大家,由于"忆苦思甜"没发~~~就算是“臭蛋节”的礼物吧~~~

"鸡蛋节"于大雁塔菜地
--------------------------------------------------------------------------------------------------*/
#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.I2CAddr = 0xa0;//器件地址
    I2CRegs.RWAddr = 0;//EEPROM读写地址
    I2CRegs.TxCount = 0;//发送数据字节个数
    I2CRegs.RxCount = 0;//接收数据字节个数
    for (i = 0; i < 16; i ++)
    {
        I2CRegs.TxBuffer = 0;//发送缓冲区清零
    }
    for (i = 0; i < 256; 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 I2CReadBuffers(unsigned int E2RomAddr, unsigned int ReadSize)
{
    if (ReadSize && (ReadSize <= 256))
    {
        I2CRegs.TxCount = 0;
        I2CRegs.RxCount = ReadSize;
        I2CRegs.RWAddr = E2RomAddr;
        I2CRegs.I2CAddr |= 1;//0xa1
        I2cStart();
    }
}

void I2CReadByte(unsigned int E2RomAddr)
{
    I2CRegs.TxCount = 0;
    I2CRegs.RxCount = 1;
    I2CRegs.RWAddr = E2RomAddr;
    I2CRegs.I2CAddr |= 1;//0xa1
    I2cStart();
}

/*------------------------------------------------------------------
    EEPROM写块函数
-------------------------------------------------------------------*/
void I2CWriteBuffers(unsigned int E2RomAddr, unsigned int WriteSize)
{
    if (WriteSize && (WriteSize <= 16))
    {
        I2CRegs.TxCount = WriteSize;
        I2CRegs.RxCount = 0;
        I2CRegs.RWAddr = E2RomAddr;
        I2CRegs.I2CAddr &= 0xfe;//0xa0
        I2cStart();
    }
}

void I2CWriteByte(unsigned int E2RomAddr, unsigned char cData)
{
    I2CRegs.TxBuffer[0] = cData;
    I2CRegs.TxCount = 1;
    I2CRegs.RxCount = 0;
    I2CRegs.RWAddr = E2RomAddr;
    I2CRegs.I2CAddr &= 0xfe;//0xa0
    I2cStart();
}

/*------------------------------------------------------------------
    用户读回调函数
-------------------------------------------------------------------*/
void I2CReadCallBack(void)
{
    if ((I2CRegs.RWAddr + I2CRegs.RxCount) <= I2CRegs.MaxCount)
    {
//        I2CRegs.RWAddr += I2CRegs.RxCount;
//        I2CReadBuffers(I2CRegs.RWAddr, I2CRegs.RxCount);//继续读
    }
}

/*------------------------------------------------------------------
    用户写回调函数
-------------------------------------------------------------------*/
void I2CWriteCallBack(void)
{
    if ((I2CRegs.RWAddr + I2CRegs.TxCount) <= I2CRegs.MaxCount)
    {
//        I2CRegs.RWAddr += I2CRegs.TxCount;
//        I2CWriteBuffers(I2CRegs.RWAddr, I2CRegs.TxCount);//继续写
    }
}


/*------------------------------------------------------------------
    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.RWAddr >> 8;//发送EEPROM写高8位地址
                        I2CRegs.State = I2C_MT_ADDRH_ACK;//下次应该接收EEPROM写高8位地址应答信号
                    }
                    else//小容量只需一次发送!!!
                    {
                        I2C1TRN = I2CRegs.RWAddr;//发送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.RWAddr & 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()
            }
            else//写
            {
                I2CBits.WriteFlag = 1;//激活用户写回调函数I2CWriteCallBack()
            }
        }
    }
    else//无法确认的复杂错误
    {
        I2cExit();//错误出错退出
    }
}
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|公司首页|Microchip单片机,模拟器件,接口电路,麦肯单片机,单片机应用交流 ( 粤ICP备09008620号 )

GMT+8, 2024-4-26 07:13 , Processed in 0.054551 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表