dc550_i2cdriver.c

来自「一款经典的数字电话设计资料」· C语言 代码 · 共 947 行 · 第 1/2 页

C
947
字号
/*****************************************************************************/
/*  CONFIDENTIAL                                                             */
/*  Sigpro Copyright 2003, All rights reserved                               */
/*****************************************************************************/
/*  CLIENT:  Telematrix                                                      */
/*  PROJECT: DC550 Digital Centrex Phone                                     */
/*  FILE:    dc550_i2cdriver.c                                               */
/*****************************************************************************/
/*  This file contains the code for the i2c driver.  Since this code is      */
/*  called extremely often, this driver may eventually be written entirely   */
/*  in assembly.                                                             */
/*****************************************************************************/

#include "dc550_interrupt.h"
// #include "dc550_usartdriver.h"
#include "dc550_audio.h"
#include "dc550_i2cdriver.h"
#include "dc550_local.h"

/******************************************************************************
 *  CONSTANTS / DATA TYPES
 *****************************************************************************/
#define CONTRAST_RANGE_LOW   72
#define CONTRAST_RANGE_HIGH 232
#define CONTRAST_INCREMENT (CONTRAST_RANGE_HIGH-CONTRAST_RANGE_LOW) / 15;

#define VOLUME_RANGE_LOW    0
#define VOLUME_RANGE_HIGH 256
#define VOLUME_INCREMENT (VOLUME_RANGE_HIGH-VOLUME_RANGE_LOW) / 15;

// These devices can "own" the bus - only one at a time!
typedef enum enum_I2CDEVICE {
    DTMF=0, RTC, EEPROM, DIGPOT, IDLE, LASTI2CDEVICE=IDLE }
I2CDEVICE;

// Slave Addresses for each I2C Device
static unsigned char i2c_AddressList[LASTI2CDEVICE] = {
  0x48, // DTMF
  0xA2, // RTC
  0xA0, // EEPROM
  0x00  // DIGPOT (not I2C)
};

struct str_I2CDATA {
    I2CDEVICE    i2cMaster;
};

struct str_RTCDATA {
    unsigned char rtcMsg[10][2];
    unsigned char TOD[5];
    int Index;
    int LastIndex;
    BOOL UpdateClock;
};

/*
030321 Modification:
struct str_DTMFDATA {
    BOOL          Request;
    unsigned char Tone;
};
*/

struct str_DIGPOTDATA {
    BOOL          VolumeRequest;
    unsigned char VolumeSetting;
    unsigned char VolumeLevels[16];
    BOOL          ContrastRequest;
    unsigned char ContrastSetting;
    unsigned char ContrastLevels[16];
};

struct str_EEPROMDATA {
    unsigned char eepromVersion;
    int           beginIndex;
    int           numberOfMessages;
    int           address[EEPROM_ARRAY_SIZE];
    unsigned char value[EEPROM_ARRAY_SIZE];
};

typedef enum enum_DIGPOT_ID { VOLUME_POT = 1, CONTRAST_POT = 2 } DIGPOT_ID;

/******************************************************************************
 *  STATIC/LOCAL VARIABLES
 *****************************************************************************/
/*------------------------  I2C Buss Data ----------------------------------*/
// Keep track of who has the buss and
//   how long it's been since we
//   made our last I2C state change
static struct str_I2CDATA i2cData;

/*---------------------- Real Time Clock Data ------------------------------*/
// Encapsulated real time clock (RTC) data
static struct str_RTCDATA rtcData;

/*--------------------------- DTMF Data ------------------------------------*/
// 030321: static struct str_DTMFDATA dtmfData;

/*-------------------------- EEPROM Data ------------------------------------*/
static struct str_EEPROMDATA eepromData;

/*-------------------------- DigPot Data -----------------------------------*/
static struct str_DIGPOTDATA digpotData;

/*
 * List of lowest priority I2C tasks.
 * These should only execute when everything else is idle.
 */
static enum I2C_IDLE_TASKS { I2C_IDLE_TASK_FIRST=0,
                             RTC_UPDATE=I2C_IDLE_TASK_FIRST,
                             RTC_DISPLAY,
                             I2C_IDLE_TASK_LAST } idleTask;

// This routine is in assembly (dc550_hwinit.s43)
//   since the delay is only 5-10us long
extern void i2c_bitDelay(void);

/******************************************************************************
 *  FUNCTION: int i2c_StartBit(I2CDEVICE dev)
 ******************************************************************************
 *  DESCRIPTION:
 *  This function creates a START condition on the I2C bus.  It also
 *  assigns the I2C bus to the specified device.  No other device will
 *  be able to talk over the bus until it is released by calling the
 *  i2c_StopBit() function.
 *****************************************************************************/
#define I2C_POTCS_MSK 0x10
int i2c_StartBit(I2CDEVICE dev)
{
    // NOTE: do we need to make sure some time has passed
    //       since our last use of the bus???
    i2c_bitDelay();

    // Set Start Condition:
    // Make sure CLOCK is high
    I2C_POUT |= I2C_CLOCK_MSK;
    i2c_bitDelay();

    // The digital pot is actually a SPI device
    if (dev == DIGPOT) {
        // CLOCK LOW
        I2C_POUT &= ~I2C_CLOCK_MSK;
        i2c_bitDelay();

        // CS LOW
        I2C_POUT &= ~I2C_POTCS_MSK;
    }

    else {

        // DATA LOW
        I2C_POUT &= ~(I2C_DATA_MSK);
        I2C_PDIR |= I2C_DATA_MSK;
        i2c_bitDelay();

        // CLOCK LOW
        I2C_POUT &= ~(I2C_CLOCK_MSK);
    }

    return 1;
}


/******************************************************************************
 *  STATIC FUNCTION: int ClockByteOut(dev, val)
 ******************************************************************************
 *  DESCRIPTION:
 *  This is a helper routine used to clock a data byte out over the
 *  I2C bus.  Eight bits will be clocked out and the ACK bit value
 *  will be returned.
 *
 *  BUS STATUS
 *    ON ENTRY:  CLOCK  - LOW
 *               DATA   - DON'T CARE
 *
 *    ON EXIT:   CLOCK  - LOW
 *               DATA   - DON'T CARE
 *  
 *****************************************************************************/
static int ClockByteOut(I2CDEVICE dev, unsigned char val)
{
    int i, ack;

    // Make sure the data line is an output
    I2C_PDIR |= I2C_DATA_MSK;

    // Clock out the control byte (slave addr + r/w bit)
    for (i=0; i<8; i++) {

        // Place the next bit on the data line
        I2C_POUT = ((val & 0x80) ? (I2C_POUT | I2C_DATA_MSK) :
                                   (I2C_POUT & ~I2C_DATA_MSK));
        val <<= 1;

        // Raise the clock line
        i2c_bitDelay();
        I2C_POUT |= I2C_CLOCK_MSK;

        // Lower the clock line
        i2c_bitDelay();
        I2C_POUT &= ~I2C_CLOCK_MSK;
    }

    // No ACK for SPI devices
    if (dev == DIGPOT) {
        return 1;
    }

    // Tri-state data line so slave can send the ACK
    I2C_PDIR &= ~I2C_DATA_MSK;
    i2c_bitDelay();

    // Raise the Clock line
    I2C_POUT |= I2C_CLOCK_MSK;
    i2c_bitDelay();

    // Sample the line to get the ACK/NACK
    ack = I2C_PIN & I2C_DATA_MSK;

    // Lower the clock line
    I2C_POUT &= ~I2C_CLOCK_MSK;

    return (ack ? 0 : 1);
}



int i2c_AddressSlave(I2CDEVICE dev, BOOL issueReadOp)
{
    unsigned char devAddr;

    // NOTE: do we need to make sure some time has passed
    //       since our last use of the bus???

    // The digital pot is actually a SPI device
    // No slave addressing is required
    if (dev == DIGPOT)
        return 1;

    // Get the slave address for the specified device
    devAddr = i2c_AddressList[dev];

    // Set R/W bit appropriately
    if (issueReadOp)
        devAddr |= 1;

    if (!ClockByteOut(dev, devAddr)) {
      /*
      switch (dev) {
        case EEPROM:
            dc550_printf("\r\nUnable to Address I2C Slave (EEPROM)!"); break;
        case RTC:
            dc550_printf("\r\nUnable to Address I2C Slave (RTC)!"); break;
        case DTMF:
            dc550_printf("\r\nUnable to Address I2C Slave (DTMF)!"); break;
        }
      */
        return (0);
    }
    else
        return (1);
}


int i2c_WriteByte(I2CDEVICE dev, unsigned char val)
{
    // NOTE: do we need to make sure some time has passed
    //       since our last use of the bus???

    if (!ClockByteOut(dev, val)) {
        /*
        switch (dev) {
        case EEPROM:
            dc550_printf("\r\nFailed during I2C Write (EEPROM)!"); break;
        case RTC:
            dc550_printf("\r\nFailed during I2C Write (RTC)!"); break;
        case DTMF:
            dc550_printf("\r\nFailed during I2C Write (DTMF)!"); break;
        }
        */
        return (0);
    }
    else
        return (1);
}


int i2c_ReadByte(I2CDEVICE dev, unsigned char *val, BOOL lastByte)
{
    int i;
    unsigned char data = '\0';

    // These devices can't be "read"
    if ((dev == DIGPOT) || (dev == DTMF))
        return 0;

    // NOTE: do we need to make sure some time has passed
    //       since our last use of the bus???

    // Make sure the data line is an input
    I2C_PDIR &= ~I2C_DATA_MSK;

    // Clock in the byte, one bit at a time
    for (i=0; i<8; i++) {

        // Bits are received MSB first
        data <<= 1;

        // Raise the clock line so data will stay stable
        i2c_bitDelay();
        I2C_POUT |= I2C_CLOCK_MSK;

        // Sample the line to get the value of the data bit
        if (I2C_PIN & I2C_DATA_MSK)
            data |= 1;

        // Bring clock low so slave can put data on the line
        i2c_bitDelay();
        I2C_POUT &= ~I2C_CLOCK_MSK;

    }

    // If this was the last byte, send a NACK
    if (lastByte)
        I2C_POUT |= I2C_DATA_MSK;

    // If more bytes to be read, send an ACK
    else
        I2C_POUT &= ~I2C_DATA_MSK;

    // Change data line back to an output
    I2C_PDIR |= I2C_DATA_MSK;
    i2c_bitDelay();

    // Raise the Clock line and wait for the slave to get the ACK/NACK
    I2C_POUT |= I2C_CLOCK_MSK;
    i2c_bitDelay();

    // Lower the clock line
    I2C_POUT &= ~I2C_CLOCK_MSK;

    *val = data;
    return 1;
}

int i2c_StopBit(I2CDEVICE dev)
{
    // NOTE: do we need to make sure some time has passed
    //       since our last use of the bus???

    // The digital pot is actually a SPI device
    // No slave addressing is required
    if (dev == DIGPOT) {
        i2c_bitDelay();

        // CS HIGH
        I2C_POUT |= I2C_POTCS_MSK;
        i2c_bitDelay();
    }

    // Make sure data line is low before bringing clock line high
    i2c_bitDelay();
    I2C_POUT &= ~I2C_DATA_MSK;
    I2C_PDIR |= I2C_DATA_MSK;
    i2c_bitDelay();

    // Raise Clock
    I2C_POUT |= I2C_CLOCK_MSK;
    i2c_bitDelay();

    // Raise Data Line - Stopped
    I2C_POUT |= I2C_DATA_MSK;
    i2c_bitDelay();

    return 1;
}


/******************************************************************************
                        RTC INTERFACE ROUTINES
 *****************************************************************************/
#define RTC_CONTROL_ADDR     0x00
#define   RTC_CONTROL_STOP   0x84
#define   RTC_CONTROL_RUN    0x04
#define RTC_HUNDREDTHS_ADDR  0x01
#define RTC_SECONDS_ADDR     0x02
#define RTC_MINUTES_ADDR     0x03
#define RTC_HOURS_ADDR       0x04
#define RTC_YEARDATE_ADDR    0x05
#define RTC_DOWMONTH_ADDR    0x06
#define RTC_ALARMCTRL_ADDR   0x08
#define   RTC_ALARMCTRL_OFF  0x03
#define   RTC_ALARMCTRL_INT  0x0B


unsigned char rtc_BIN(unsigned char bcdVal)
{
    return (((bcdVal >> 4) * 10) + ((bcdVal & 0xF) % 10));
}

unsigned char rtc_BCD(unsigned char binVal)
{
    return ((((binVal / 10) << 4 & 0xF0)) + ((binVal % 10) & 0xF));
}

int rtc_WriteByte(int addr, unsigned char val)
{
    i2c_StartBit(RTC);
    if (!i2c_AddressSlave(RTC, FALSE)) {
        i2c_StopBit(RTC);
        return 0;
    }

    i2c_WriteByte(RTC, (addr & 0xFF));
    i2c_WriteByte(RTC, val);
    i2c_StopBit(RTC);
    return 1;
}

int rtc_ReadByte(int addr, unsigned char *val)
{
    i2c_StartBit(RTC);
    if (!i2c_AddressSlave(RTC, FALSE)) {
        i2c_StopBit(RTC);
        return 0;
    }
    i2c_WriteByte(RTC, (addr & 0xFF));

    i2c_StartBit(RTC);
    if (!i2c_AddressSlave(RTC, TRUE)) {
        i2c_StopBit(RTC);
        return 0;
    }
    i2c_ReadByte(RTC, val, TRUE);
    i2c_StopBit(RTC);
    return 1;
}


/******************************************************************************
 *  FUNCTION: BOOL rtc_SettingTimeOfDay(void)
 ******************************************************************************
 *  DESCRIPTION:
 *  Returns TRUE if the last clock update is pending.
 *  Returns FALSE if no clock update is currently in progress.
 *****************************************************************************/
BOOL rtc_SettingTimeOfDay(void)
{
  return (rtcData.UpdateClock);
}


int rtc_SetTimeOfDay( int year,
                      int month,
                      int date,
                      int hour,
                      int minutes,
                      int seconds,
                      int dayOfWeek)
{
    rtcData.rtcMsg[0][0] = RTC_CONTROL_ADDR;
    rtcData.rtcMsg[0][1] = RTC_CONTROL_STOP;
    rtcData.rtcMsg[1][0] = RTC_HUNDREDTHS_ADDR;
    rtcData.rtcMsg[1][1] = 0;

    rtcData.rtcMsg[2][0] = RTC_SECONDS_ADDR;
    rtcData.rtcMsg[2][1] = (unsigned char)(seconds & 0xff);

    rtcData.rtcMsg[3][0] = RTC_MINUTES_ADDR;
    rtcData.rtcMsg[3][1] = (unsigned char)(minutes & 0xff);

    rtcData.rtcMsg[4][0] = RTC_HOURS_ADDR;
    rtcData.rtcMsg[4][1] = (unsigned char)(hour & 0xff);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?