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 + -
显示快捷键?