EDN首页   博客首页 用户登陆  |  注册
aaa
发表于 2007/12/26 19:25:22

2

关于投票

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

原文发表在: http://bbs.21ic.com/club/bbs/list.asp?boardid=27&t=2809345

hotpower 发表于 2007-12-26 19:16 PIC 单片机 ←返回版面 按此察看该网友的资料 按此把文章加入收藏夹 按此编辑本帖


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

/*-------------------------------------------------------------------------------------------------
                        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上"声东击西"呢???

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

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

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

菜农 HotPower@126.com   2007.12.25 "鸡蛋节"于大雁塔菜地
--------------------------------------------------------------------------------------------------*/
#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[i] = 0;//发送缓冲区清零
    }
    for (i = 0; i < 256; i ++)
    {
        I2CRegs.RxBuffer[i] = 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();//错误出错退出
    }
}


下附I2CReadBuffers(0, 10)结果图

 

hotpower 发表于 2007-12-26 19:17 PIC 单片机 ←返回版面 按此察看该网友的资料 按此把文章加入收藏夹 按此编辑本帖

2楼: 对应的I2C.H

#include "main.h"

#ifndef __I2C_H
#define __I2C_H

#define I2CBAUD 800000//800KHz  本程序是读写铁电的,故速度很高                

//下面的是I2C的标准状态表,因为PIC的不标准,故凑合着用和补充了加注解的部分~~~
enum enum_I2CState
{
/* Master */
    I2C_START =                    0x08,//启动
    I2C_REP_START=                0x10,//重启动
/* Master Transmitter */
    I2C_MT_SLA_ACK=                0x18,//主发模式下得到从机地址应答信号
    I2C_MT_SLA_NACK=            0x20,

    I2C_MT_DATA_ACK=            0x28,//主发模式下得到从机数据应答信号
    I2C_MT_DATA_NACK=            0x30,



    I2C_MT_ARB_LOST=            0x38,
/* Master Receiver */
    I2C_MR_ARB_LOST=            0x38,
    I2C_MR_SLA_ACK=                0x40,//主收模式下得到从机地址应答信号
    I2C_MR_SLA_NACK=            0x48,
    I2C_MR_DATA_ACK=            0x50,
    I2C_MR_DATA_NACK=            0x58,

/* Slave Transmitter */
    I2C_ST_SLA_ACK=                0xA8,
    I2C_ST_ARB_LOST_SLA_ACK=    0xB0,
    I2C_ST_DATA_ACK=            0xB8,
    I2C_ST_DATA_NACK=            0xC0,
    I2C_ST_LAST_DATA=            0xC8,
/* Slave Receiver */
    I2C_SR_SLA_ACK=                0x60,
    I2C_SR_ARB_LOST_SLA_ACK=    0x68,
    I2C_SR_GCALL_ACK=            0x70,
    I2C_SR_ARB_LOST_GCALL_ACK=  0x78,
    I2C_SR_DATA_ACK=            0x80,
    I2C_SR_DATA_NACK=            0x88,
    I2C_SR_GCALL_DATA_ACK=        0x90,
    I2C_SR_GCALL_DATA_NACK=        0x98,
    I2C_SR_STOP=                0xA0,
/* Misc */
    I2C_NO_INFO=                0xF8,

    I2C_MT_ADDRH_ACK=            0x3a,//主发模式下得到从机高位EEPROM地址应答信号
    I2C_MT_ADDRL_ACK=            0x3b,//主发模式下得到从机高低位EEPROM地址应答信号
    I2C_MR_DATA=                0x5a,//主收模式下接收数据
    I2C_MR_DATA_EN=                0x5b,//主收模式下使能接收数据功能
    I2C_MR_DATA_STOP=            0x5c,//主收模式下接收数据完成

    I2C_FAILED=                    0xcc,//读写操作失败
    I2C_SUCCEEDED=              0x00,//读写操作成功
    I2C_RWBIT=                    0,//
    I2C_READ=                    1,//
    I2C_WRITE=                    0//
};


#define TRIS_SCL1 _TRISG2 //SCL方向控制
#define TRIS_SDA1 _TRISG3 //SDA方向控制
#define ODC_SCL1 _ODG2 //SCL开漏控制
#define ODC_SDA1 _ODG3 //SDA开漏控制

/*-----------------------------------------
        写保护管脚定义,需要硬件的支持
------------------------------------------*/
#define TRIS_WP   _TRISD0 //WP方向控制
#define WP _LATD0//写保护

typedef struct tagI2CBITS
{
    union
    {
        unsigned char I2CFlag;
        struct
        {
            unsigned char  BusyFlag:   1;//忙标志
            unsigned char  ReadFlag:   1;//读回调标志
            unsigned char  WriteFlag:  1;//写回调标志
        };
    };
}I2CBITS;

extern volatile I2CBITS I2CBits;

typedef struct tagI2CREGS
{
    unsigned char State;//运行状态编码
    unsigned char I2CAddr;//器件地址0xa1,0xa0
    unsigned int RWAddr;//器件读写地址(自动根据MaxCount判断长短地址)
    unsigned int Count;//运行计数器
    unsigned int TxCount;//发送(写)个数
    unsigned int RxCount;//接收(读)个数
    unsigned int MaxCount;//器件最大容量
    unsigned char TxBuffer[16];//发送缓冲区
    unsigned char RxBuffer[256];//接收缓冲区
}I2CREGS;

extern volatile I2CREGS I2CRegs;


void I2cInit(void);
void I2CReadBuffers(unsigned int, unsigned int);
void I2CReadByte(unsigned int);
void I2CWriteBuffers(unsigned int, unsigned int);
void I2CWriteByte(unsigned int, unsigned char);
void I2CReadCallBack(void);
void I2CWriteCallBack(void);
void I2cStart(void);
void I2cReStart(void);
void I2cStop(void);
void I2CExec(void);

#endif//__I2C_H

老外的i2cEmem.c

/**********************************************************************
* ?2005 Microchip Technology Inc.
*
* FileName:        i2cEmem.c
* Dependencies:    Header (.h) files if applicable, see below
* Processor:       dsPIC33Fxxxx/PIC24Hxxxx
* Compiler:        MPLAB?C30 v3.00 or higher
* Tested On:       dsPIC33FJ256GP710
*
* SOFTWARE LICENSE AGREEMENT:
* Microchip Technology Incorporated ("Microchip") retains all ownership and
* intellectual property rights in the code accompanying this message and in all
* derivatives hereto.  You may use this code, and any derivatives created by
* any person or entity by or on your behalf, exclusively with Microchip's
* proprietary products.  Your acceptance and/or use of this code constitutes
* agreement to the terms and conditions of this notice.
*
* CODE ACCOMPANYING THIS MESSAGE IS SUPPLIED BY MICROCHIP "AS IS".  NO
* WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
* TO, IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE APPLY TO THIS CODE, ITS INTERACTION WITH MICROCHIP'S
* PRODUCTS, COMBINATION WITH ANY OTHER PRODUCTS, OR USE IN ANY APPLICATION.
*
* YOU ACKNOWLEDGE AND AGREE THAT, IN NO EVENT, SHALL MICROCHIP BE LIABLE, WHETHER
* IN CONTRACT, WARRANTY, TORT (INCLUDING NEGLIGENCE OR BREACH OF STATUTORY DUTY),
* STRICT LIABILITY, INDEMNITY, CONTRIBUTION, OR OTHERWISE, FOR ANY INDIRECT, SPECIAL,
* PUNITIVE, EXEMPLARY, INCIDENTAL OR CONSEQUENTIAL LOSS, DAMAGE, FOR COST OR EXPENSE OF
* ANY KIND WHATSOEVER RELATED TO THE CODE, HOWSOEVER CAUSED, EVEN IF MICROCHIP HAS BEEN
* ADVISED OF THE POSSIBILITY OR THE DAMAGES ARE FORESEEABLE.  TO THE FULLEST EXTENT
* ALLOWABLE BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY RELATED TO
* THIS CODE, SHALL NOT EXCEED THE PRICE YOU PAID DIRECTLY TO MICROCHIP SPECIFICALLY TO
* HAVE THIS CODE DEVELOPED.
*
* You agree that you are solely responsible for testing the code and
* determining its suitability.  Microchip has no obligation to modify, test,
* certify, or support the code.
*
* REVISION HISTORY:
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Author            Date      Comments on this revision
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Settu D.          07/09/06       First release of source file
*
**********************************************************************/
#if defined(__dsPIC33F__)
#include "p33Fxxxx.h"
#elif defined(__PIC24H__)
#include "p24Hxxxx.h"
#endif

#include "i2cEmem.h"

unsigned int jDone;


/*=============================================================================
I2C Master Interrupt Service Routine
=============================================================================*/
void __attribute__((interrupt, no_auto_psv)) _MI2C1Interrupt(void)
{
        jDone=1;
        IFS1bits.MI2C1IF = 0;        //Clear the DMA0 Interrupt Flag;

}

/*=============================================================================
I2C Slave Interrupt Service Routine
=============================================================================*/
void __attribute__((interrupt, no_auto_psv)) _SI2C1Interrupt(void)
{
    
        IFS1bits.SI2C1IF = 0;        //Clear the DMA0 Interrupt Flag
}



/*=============================================================================
I2C Peripheral Initialisation
=============================================================================*/   
void I2CEMEMinit(I2CEMEM_DRV *i2cMem)
{   
    i2cMem->cmd=0;
    i2cMem->oData=0;

// Configre SCA/SDA pin as open-drain
    ODCGbits.ODCG2=1;
    ODCGbits.ODCG3=1;


    I2C1CONbits.A10M=0;
    I2C1CONbits.SCLREL=1;
    I2C1BRG=300;

    I2C1ADD=0;
    I2C1MSK=0;

    I2C1CONbits.I2CEN=1;
    IEC1bits.MI2C1IE = 1;
      IFS1bits.MI2C1IF = 0;


}

/*=============================================================================
I2C Serial EEPROM, STATE-MACHINE BASED DRIVER
=============================================================================*/                                                             
void I2CEMEMdrv(I2CEMEM_DRV *i2cMem)
{

static int state="0", cntr="0", rtrycntr="0";

    switch(state)
    {
    case 0:
        if( (i2cMem->cmd == I2C_WRITE)  || (i2cMem->cmd == I2C_READ)  )
            state=1;   
          
        break;

    /*==================================*/        
    /* Control/Address Phase            */
    /*==================================*/
    case 1:
        // Start Condition
        I2C1CONbits.SEN=1;        
        state=state+1;
        break;


    case 2:
        // Start Byte with device select id
        if(jDone==1)    {
            jDone=0;
              state=state+1;
              I2C1TRN=(0x00A0)|(((i2cMem->oData->csel)&0x7)<<1);
        }
        break;

    case 3:         
           // Send address byte 1, if ack is received. Else Retry
        if(jDone==1)    {
            jDone=0;
      
            if(I2C1STATbits.ACKSTAT==1) {        // Ack Not received, Retry

                if(rtrycntr < MAX_RETRY)
                    state=18;
                else
                    state=16;                    // Flag error and exit

            } else {

                rtrycntr=0;

                #if ADDRWIDTH==TWO_BYTE
                I2C1TRN=((i2cMem->oData->addr)&0xFF00)>>8;
                   state=state+1;
                #endif
               
                #if ADDRWIDTH==ONE_BYTE
                I2C1TRN=((i2cMem->oData->addr));
                state=state+2;
                #endif

            }
        }
        break;
    
    
    case 4:
        // Send address byte 2, if ack is received. Else Flag error and exit
          if(jDone==1)    {
            jDone=0;

            if(I2C1STATbits.ACKSTAT==1) {        // Ack Not received, Flag error and exit
                state=16;

            } else {

                #if ADDRWIDTH==TWO_BYTE
                I2C1TRN=((i2cMem->oData->addr)&0x00FF);
                #endif
                state=state+1;
            }
        }
        break;
    

    case 5:
        // Read or Write
          if(jDone==1)    {
            jDone=0;

            if(I2C1STATbits.ACKSTAT==1) {        // Ack Not received, Flag error and exit
                state=16;

            } else {

                if(i2cMem->cmd == I2C_WRITE)
                    state=state+1;   

                if(i2cMem->cmd == I2C_READ)
                    state=8;
            }

        }
        break;

    /*==================================*/        
    /* Write Data Phase                    */
    /*==================================*/

    case 6:
        // Send data
        I2C1TRN=*(i2cMem->oData->buff + cntr);
        state=state+1;
        cntr=cntr+1;
        break;   

    case 7:  
        // Look for end of data or no Ack
          if(jDone==1)    {
            jDone=0;
            state=state-1;

            if(I2C1STATbits.ACKSTAT==1) {        // Ack Not received, Flag error and exit
                state=16;
            } else {

                if(cntr== i2cMem->oData->n)
                    state=14;                   // Close the Frame
                }
        }
        break;

    /*==================================*/        
    /* Read Data Phase                    */
    /*==================================*/
    case 8:
        // Repeat Start
         I2C1CONbits.RSEN=1;        
        state=state+1;
        break;

    case 9:
        // Re-send control byte with W/R=R
        if(jDone==1)    {
            jDone=0;
              state=state+1;
              I2C1TRN=(0x00A1)|(((i2cMem->oData->csel)&0x7)<<1);
        }
        break;

    case 10:    
        // Check, if control byte went ok
        if(jDone==1)    {
            jDone=0;
              state=state+1;

            if(I2C1STATbits.ACKSTAT==1)         // Ack Not received, Flag error and exit
                state=16;
    
        }
        break;

    case 11:
          // Receive Enable
         I2C1CONbits.RCEN=1;    
        state++;
        break;

    case 12:  
        // Receive data
        if(jDone==1)    {
            jDone=0;
            state=state+1;

            *(i2cMem->oData->buff+cntr)=I2C1RCV;
            cntr++;

            if(cntr== i2cMem->oData->n) {
                I2C1CONbits.ACKDT=1;        // No ACK        
            } else {
                I2C1CONbits.ACKDT=0;        // ACK
            }

            I2C1CONbits.ACKEN=1;    

        }
        break;

    case 13:
        if(jDone==1)    {
            jDone=0;
              if(cntr== i2cMem->oData->n)
                state=state+1;
               else
                state=state-2;
         }          
         break;
         
    /*==================================*/        
    /* Stop Sequence                    */
    /*==================================*/
    case 14:
          I2C1CONbits.PEN=1;    
        state++;
        break;
           
    case 15:
         if(jDone==1)    {
            jDone=0;
            state=0;
            cntr=0;
            i2cMem->cmd=0;
        }
          break;

    /*==================================*/        
    /* Set Error                         */
    /*==================================*/
    case 16:
           I2C1CONbits.PEN=1;    
          state++;
        break;
    
    case 17:
         if(jDone==1)    {
            jDone=0;
            state=0;
            rtrycntr=0;
            cntr=0;
            i2cMem->cmd=0xFFFF;
        }
          break;

    /*==================================*/        
    /* Retry                             */
    /*==================================*/
    case 18:
           I2C1CONbits.PEN=1;    
          state++;
        rtrycntr++;
        break;

    case 19:
         if(jDone==1)    {
            jDone=0;
            state=0;
            cntr=0;
        }
          break;


     }     

}

系统分类: 单片机  |  用户分类: PIC C30  |  标签: 无标签  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(1567) | 回复(0)

发表于 2007/12/20 2:53:04

0

关于投票

PIC24菜鸟毕业留念---GPS串口2接收中断服务程序

hotpower 发表于 2007-12-20 02:49 PIC 单片机 ←返回版面 按此察看该网友的资料 按此把文章加入收藏夹 按此编辑本帖

楼主: PIC24菜鸟毕业留念---GPS串口2接收中断服务程序

/*------------------------------------------------------------------------
                   GPS串口2接收中断服务程序(接收GPS数据)
菜农HotPower之PIC24菜鸟毕业留念    2007.12.20   2:40 于菜地留念   
-------------------------------------------------------------------------*/
void __attribute__((interrupt, no_auto_psv)) _U2RXInterrupt( void )
{
static unsigned char cData = 0, cPosition;
unsigned char ch;
    if (IFS1bits.U2RXIF)//确实是GPS串口2接收中断
    {
        if (IEC1bits.U2RXIE)//GPS串口2接收中断已被允许
        {
            ch = U2RXREG;//读取串口GPS数据
            if (ch == '$')//是GPS引导字符
            {
                cData = ch;//记录引导字符
                cPosition = GPSRxWriteCount;//引导字符位置记录
                GPSRxBuffers[GPSRxWriteCount ++] = ch;//存入数据
            }
            else if (cData == '$')//收到引导字符
            {        
                if (ch >= ' ')//可能是有效数据
                {
                    GPSRxBuffers[GPSRxWriteCount ++] = ch;//存入数据
                }
                else if (ch == '*')//GPS校验引导字符
                {
                    GPSRxBuffers[GPSRxWriteCount ++] = ch;//存入数据
                }
                else if ((ch == '\r') && (GPSRxBuffers[GPSRxWriteCount - 3] == '*'))//回车字符校验
                {
                    GPSRxBuffers[GPSRxWriteCount ++] = ch;//存入数据
                }
                else
                {
                    if ((ch == '\n') && (GPSRxBuffers[GPSRxWriteCount - 1] == '\r'))//换行字符校验
                    {//只取GPRMC语句
                        if (GPSGPRMCTest((char *)&GPSRxBuffers[cPosition + 1]))
                        {
                            GPSRxBuffers[GPSRxWriteCount ++] = ch;//存入数据
                            GPSbits.RXFlag = 1;//激活GPS接收成功解码程序(在“零耗时键盘OS”的空闲任务内细加工处理)
                        }
                    }
                    cData = 0;//结束或出错
                }
            }
            else
            {
                cData = 0;//出错    
            }
        }
    }
     IFS1bits.U2RXIF = 0;//清除RX2中断标志
}


/*------------------------------------------------------------------------
    GPS串口2初始化程序
-------------------------------------------------------------------------*/
void Uart2Init(void)
{
    TRIS_TX2 = PORTOUTMODE;//设置TX2为输出IO
    TRIS_RX2 = PORTINPUTMODE;//设置RX2为输入IO
    U2MODE = 0;
//    U2MODEbits.STSEL = 0;// 1-stop bit
//    U2MODEbits.PDSEL = 0;// No Parity, 8-data bits
//    U2MODEbits.ABAUD = 0;// Autobaud Disabled
    U2STA = 0;
//    U2STAbits.UTXISEL0 = 0;            // Interrupt after one Tx character is transmitted
//    U2STAbits.UTXISEL1 = 0;                                        
//    U2STAbits.URXISEL  = 3;            // Interrupt after one RX character is received
    U2BRG = BRGVAL2;//BAUD Rate Setting for 19200
    U2MODEbits.WAKE    = 1;//启动位时唤醒使能位
    U2MODEbits.UARTEN   = 1;        // Enable UART
    U2STAbits.UTXEN     = 1;        // Enable UART Tx
/*------------------------------------------------------------------------
    定义GPS串口2中断优先级位1111)
-------------------------------------------------------------------------*/
    IPC7bits.U2RXIP0 = 1;
    IPC7bits.U2RXIP1 = 1;
    IPC7bits.U2RXIP2 = 1;
    IEC1bits.U2RXIE = 1;
    IFS1bits.U2RXIF = 0;//清除RX1中断标志
    IEC1bits.U2TXIE = 1;
    IFS1bits.U2TXIF = 0;//清除TX1中断标志
}

系统分类: 单片机  |  用户分类: PIC C30  |  标签: 无标签  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(1724) | 回复(0)

发表于 2007/12/9 17:26:53

0

关于投票

使用LcdWaitBusy()并通过测试的LCD12232并行显示C30演示程序

/*------------------------------------------------------------
ST7920中文字库液晶显示模块LCD12232并行8位显示C30演示程序
C30菜鸟HotPower@126.com             2007.12.9 于雁塔菜地
-------------------------------------------------------------*/

#include "lcd.h"

unsigned char LcdRowWriteEnable[2] = {0, 0};//显示行刷新允许标志
unsigned char LcdBuffer[2][15];//LCD显示缓冲区(2行15个字符)
unsigned char LcdRow = 0, LcdCol = 0;//虚拟光标

void LcdInit(void)//LCD初始化
{
 LcdIOInit();//LCD管脚初始化
 LcdClearBuffer();//清空LCD显示缓冲区

//    LcdWriteCommand(0x20);//发送功能设定控制命令(4位)
    LcdWriteCommand(0x30);//发送功能设定控制命令(8位)
    LcdWriteCommand(0x02);//发送位址归位命令,设定DDRAM位址计数器为0
    LcdWriteCommand(0x04);//发送进入点命令
    LcdWriteCommand(0x0c);//发送开显示关光标命令
    LcdWriteCommand(0x01);//发送清除显示命令
    LcdWriteCommand(0x80);//发送设定DDRAM地址0x00命令,光标定位到(0,0)

/*----------------------------------------------------------------------
    LcdWriteCommand(0x30);//发送功能设定控制命令(8位)
    LcdWriteCommand(0x30);//发送功能设定控制命令(8位)
    LcdWriteCommand(0x30);//发送功能设定控制命令(8位)
    LcdWriteCommand(0x01);//发送清除显示命令
    LcdWriteCommand(0x06);//发送进入点命令0x06
    LcdWriteCommand(0x0a);//发送开关显示关光标命令
    LcdWriteCommand(0x0c);//发送开显示关光标命令
    LcdWriteCommand(0x02);//发送位址归位命令,设定DDRAM位址计数器为0
 LcdWriteCommand(0x17);//游标或显示移位控制
    LcdWriteCommand(0x80);//发送设定DDRAM地址0x00命令,光标定位到(0,0)
----------------------------------------------------------------------*/
 LCD_BK = 0;//打开背光
}

void LcdIOInit(void)//LCD管脚初始化
{
 LCD_BK = 0;//打开背光
    LCD_E = 0;//不使能
 LCD_RW = 1;//写读数据
 TRIS_E  =  PORTOUTMODE;//定义E为输出IO
 TRIS_RW =  PORTOUTMODE;//定义RW为输出IO
 TRIS_RS =  PORTOUTMODE;//定义RS为输出IO
 TRIS_BK =  PORTOUTMODE;//定义BK为输出IO
 TRIS_DATA = 0;//定义RE0~RE7为输出IO
}

void LcdWriteCommand(unsigned char Command)//写LCD命令字
{
    LCD_RS = 0;//命令
    Nop();
    LCD_RW = 0;//写命令
    Nop();
    LCD_E = 1;//使能      
    Nop();
    LCD_BUS = Command;//发送8位命令
    Nop();    
    LCD_E = 0;//不使能,读出数据 打入命令
    Nop();    
    LCD_RW = 1;//写命令结束
   if (Command == 0x01)//清除显示命令,需要等待时间相对较长
 {
  LcdWaitBusy(1600);
//  _delay_loop(1600);//st7920要求等待1.6mS
 }
   else
 {
  LcdWaitBusy(72);
//   _delay_loop(72);//st7920要求等待72uS
 }
}

void LcdWriteData(unsigned char data)//写LCD显示数据
{
    LCD_RS = 1;//数据
    Nop();
    LCD_RW = 0;//写数据
    Nop();
    LCD_E = 1;//使能      
    Nop();
    LCD_BUS = data;//发送8位数据
    Nop();    
    LCD_E = 0;//不使能,打入数据
    Nop();    
    LCD_RW = 1;//写数据结束
 LcdWaitBusy(72);
// _delay_loop(72);//st7920要求等待72uS
}

void LcdWaitBusy(unsigned int count)//LCD忙标志测试(有时死机,故没用参数可以防止死机) 
{
    TRIS_DATA = 0xff;//定义RE0~RE7为输入IO
 LCD_BUS = 0xff;
    Nop();
    LCD_RS = 0;//命令        
    Nop();
    LCD_RW = 1;//读数据
    Nop();
    LCD_E = 1;//使能      
    while (count --)
 {
  if (!LCD_BF) break;//读取忙碌标志BF(LCD_DATA.7)
  ClrWdt();
 }
    LCD_E = 0;//不使能,读出状态
    Nop();    
    TRIS_DATA = 0;//定义RE0~RE7为输出IO
    Nop();    
}

void LcdClearBuffer(void)
{
unsigned char i, j;
 for (i = 0;i < 2;i ++)//2行字符
 {//2行汉字
  for (j = 0;j < 15; j ++)//15个字符
  {//每行7个半汉字15个字符
   LcdBuffer[i][j] = ' ';//空白字符
  }
  LcdRowWriteEnable[i] = 1;//允许此行刷新汉字显示
 }
 LcdRow = 0;//虚拟光标定位在0行
 LcdCol = 0;//虚拟光标定位在0列
}


void LcdDisplayPos(unsigned char row, unsigned char col)
{
 if ((row < 2) && (col < 15))//容错处理,光标越界
 {
  LcdRow = row;//刷新虚拟光标
  LcdCol = col;//刷新虚拟光标
  LcdRowWriteEnable[row] = 1;//允许此行刷新汉字显示
 }
}

void LcdDisplay(const char * string)//不真的LCD显示
{
 while(*string)//0字符结束
 {
  LcdBuffer[LcdRow][LcdCol ++] = *string ++;//写入显示缓冲区
 }
}

unsigned char LcdDisplayBuffer(void)//显示刷新LCD缓冲区内的数据(一般0.25S~0.5S刷新一次)
{
unsigned char i, j;
unsigned char num = 0;//刷新字符个数
 for (i = 0; i < 2; i ++)
 {//2行汉字
  if (LcdRowWriteEnable[i])
  {//允许此行刷新汉字显示
   LcdWriteCommand(0x80 + (i << 4));//移动真的LCD光标
   for (j = 0; j < 15; j ++)
   {//每行7个半汉字15个字符
    LcdWriteData(LcdBuffer[i][j]);//刷新显示字符
    num ++;//刷新字符个数计数
   }
   LcdRowWriteEnable[i] = 0;//过后不允许此行刷新汉字显示
  }
 }
 return num;//返回实际刷新字符个数(这个"黑奔时有用~~~")
}

系统分类: 单片机  |  用户分类: PIC C30  |  标签: 无标签  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(1022) | 回复(0)

发表于 2007/12/8 10:20:02

0

关于投票

ST7920中文字库液晶显示模块LCD12232并行8位显示C30演示程序

http://bbs.21ic.com/club/bbs/list.asp?boardid=27&t=2786376

hotpower 发表于 2007-12-8 10:13 PIC 单片机 ←返回版面 按此察看该网友的资料 按此把文章加入收藏夹 按此编辑本帖


楼主: ST7920中文字库液晶显示模块LCD12232并行8位显示C30演示程序

/*------------------------------------------------------------
ST7920中文字库液晶显示模块LCD12232并行8位显示C30演示程序
C30菜鸟HotPower@126.com             2007.12.8 于雁塔菜地
-------------------------------------------------------------*/

#include "lcd.h"

unsigned char LcdRowWriteEnable[2] = {0, 0};//显示行刷新允许标志
unsigned char LcdBuffer[2][15];//LCD显示缓冲区(2行15个字符)
unsigned char LcdRow = 0, LcdCol = 0;//虚拟光标

void LcdInit(void)//LCD初始化
{
    LcdIOInit();//LCD管脚初始化
    LcdClearBuffer();//清空LCD显示缓冲区

//    LcdWriteCommand(0x20);//发送功能设定控制命令(4位)
    LcdWriteCommand(0x30);//发送功能设定控制命令(8位)
    LcdWriteCommand(0x02);//发送位址归位命令,设定DDRAM位址计数器为0
    LcdWriteCommand(0x04);//发送进入点命令
    LcdWriteCommand(0x0c);//发送开显示关光标命令
    LcdWriteCommand(0x01);//发送清除显示命令
    LcdWriteCommand(0x80);//发送设定DDRAM地址0x00命令,光标定位到(0,0)

/*----------------------------------------------------------------------
    LcdWriteCommand(0x30);//发送功能设定控制命令(8位)
    LcdWriteCommand(0x30);//发送功能设定控制命令(8位)
    LcdWriteCommand(0x30);//发送功能设定控制命令(8位)
    LcdWriteCommand(0x01);//发送清除显示命令
    LcdWriteCommand(0x06);//发送进入点命令0x06
    LcdWriteCommand(0x0a);//发送开关显示关光标命令
    LcdWriteCommand(0x0c);//发送开显示关光标命令
    LcdWriteCommand(0x02);//发送位址归位命令,设定DDRAM位址计数器为0
    LcdWriteCommand(0x17);//游标或显示移位控制
    LcdWriteCommand(0x80);//发送设定DDRAM地址0x00命令,光标定位到(0,0)
----------------------------------------------------------------------*/
    LCD_BK = 0;//打开背光
}

void LcdIOInit(void)//LCD管脚初始化
{
    LCD_BK = 0;//打开背光
    LCD_E = 0;//不使能
    LCD_RW = 1;//写读数据
    TRIS_E  =  PORTOUTMODE;//定义E为输出IO
    TRIS_RW =  PORTOUTMODE;//定义RW为输出IO
    TRIS_RS =  PORTOUTMODE;//定义RS为输出IO
    TRIS_BK =  PORTOUTMODE;//定义BK为输出IO
    TRIS_DATA = 0;//定义RE0~RE7为输出IO
}

void LcdWriteCommand(unsigned char Command)//写LCD命令字
{
    LCD_RS = 0;//命令
    Nop();
    LCD_RW = 0;//写命令
    Nop();
    LCD_E = 1;//使能       
    Nop();
    LCD_BUS = Command;//发送8位命令
    Nop();     
    LCD_E = 0;//不使能,读出数据 打入命令
    Nop();     
    LCD_RW = 1;//写命令结束
      if (Command == 0x01)//清除显示命令,需要等待时间相对较长
    {
         _delay_loop(1600);//st7920要求等待1.6mS
    }
      else
    {
         _delay_loop(72);//st7920要求等待72uS
    }
}

void LcdWriteData(unsigned char data)//写LCD显示数据
{
    LCD_RS = 1;//数据
    Nop();
    LCD_RW = 0;//写数据
    Nop();
    LCD_E = 1;//使能       
    Nop();
    LCD_BUS = data;//发送8位数据
    Nop();     
    LCD_E = 0;//不使能,打入数据
    Nop();     
    LCD_RW = 1;//写数据结束
    _delay_loop(72);//st7920要求等待72uS
}

void LcdWaitBusy(void)//LCD忙标志测试(有时死机,故没用)  
{
    TRIS_DATA = 0xff;//定义RE0~RE7为输入IO
    LCD_BUS = 0xff;
    Nop();
    LCD_RS = 0;//命令         
    Nop();
    LCD_RW = 1;//读数据
    Nop();
    LCD_E = 1;//使能       
    Nop();
    while(LCD_BF)//读取忙碌标志BF(LCD_DATA.7)
    {
        Nop();//搞不清有时死机在此处,所以用_delay_loop()代替
    }
    LCD_E = 0;//不使能,读出状态
    Nop();     
    TRIS_DATA = 0;//定义RE0~RE7为输出IO
    Nop();     
}

void LcdClearBuffer(void)
{
unsigned char i, j;
    for (i = 0;i < 2;i ++)//2行字符
    {//2行汉字
        for (j = 0;j < 15; j ++)//15个字符
        {//每行7个半汉字15个字符
            LcdBuffer[i][j] = ' ';//空白字符
        }
        LcdRowWriteEnable[i] = 1;//允许此行刷新汉字显示
    }
    LcdRow = 0;//虚拟光标定位在0行
    LcdCol = 0;//虚拟光标定位在0列
}


void LcdDisplayPos(unsigned char row, unsigned char col)
{
    if ((row < 2) && (col < 15))//容错处理,光标越界
    {
        LcdRow = row;//刷新虚拟光标
        LcdCol = col;//刷新虚拟光标
        LcdRowWriteEnable[row] = 1;//允许此行刷新汉字显示
    }
}

void LcdDisplay(const char * string)//不真的LCD显示
{
    while(*string)//0字符结束
    {
        LcdBuffer[LcdRow][LcdCol ++] = *string ++;//写入显示缓冲区
    }
}

unsigned char LcdDisplayBuffer(void)//显示刷新LCD缓冲区内的数据(一般0.25S~0.5S刷新一次)
{
unsigned char i, j;
unsigned char num = 0;//刷新字符个数
    for (i = 0; i < 2; i ++)
    {//2行汉字
        if (LcdRowWriteEnable[i])
        {//允许此行刷新汉字显示
            LcdWriteCommand(0x80 + (i << 4));//移动真的LCD光标
            for (j = 0; j < 15; j ++)
            {//每行7个半汉字15个字符
                LcdWriteData(LcdBuffer[i][j]);//刷新显示字符
                num ++;//刷新字符个数计数
            }
            LcdRowWriteEnable[i] = 0;//过后不允许此行刷新汉字显示
        }
    }
    return num;//返回实际刷新字符个数
}

void _delay_loop(unsigned long count)//软件延时
{
    while(count --)
    {
        //ClrWdt();//注意大延时要记住喂狗
    }
}


St7920中文字库LCD驱动控制器资料

系统分类: 单片机  |  用户分类: PIC C30  |  标签: 无标签  |  来源: 原创  | 

点击查看原文

发表评论 阅读全文(1512) | 回复(0)

总共 , 当前 /