//-----------------------------------------------------------------------------
// F04x_SMBus_EEPROM.c
//-----------------------------------------------------------------------------
// Copyright 2006 Silicon Laboratories, Inc.
// http://www.silabs.com
//
// Program Description:
//
// This example demonstrates how the C8051F04x SMBus interface can communicate
// with a 256 byte I2C Serial EEPROM (Microchip 24LC02B).
// - Interrupt-driven SMBus implementation
// - Only master states defined (no slave or arbitration)
// - Timer4 used by SMBus for SCL low timeout detection
// - SCL frequency defined by
constant
// - Pinout:
// P0.0 -> SDA (SMBus)
// P0.1 -> SCL (SMBus)
//
// P1.6 -> LED
//
// all other port pins unused
//
// How To Test:
//
// 1) Download code to a 'F04x device that is connected to a 24LC02B serial
// EEPROM (see the EEPROM datasheet for the pinout information).
// 2) Run the code:
// a) the test will indicate proper communication with the EEPROM by
// turning on the LED at the end the end of the test
// b) the test can also be verified by running to the if statements
// in main and checking the sent and received values by adding
// the variables to the Watch Window
//
// FID: 04X000009
// Target: C8051F04x
// Tool chain: Keil C51 7.50 / Keil EVAL C51
// Command Line: None
//
// Release 1.0
// -Initial Revision (TP)
// -20 APR 2006
//
//-----------------------------------------------------------------------------
// Includes
//-----------------------------------------------------------------------------
Administrator
线条
#include // SFR declarations
//-----------------------------------------------------------------------------
// Global CONSTANTS
//-----------------------------------------------------------------------------
#define SYSCLK 3062500L // System clock frequency in Hz
#define SMB_FREQUENCY 10000L // Target SCL clock rate
// This example supports between 10kHz
// and 100kHz
#define WRITE 0x00 // WRITE direction bit
#define READ 0x01 // READ direction bit
// Device addresses (7 bits, lsb is a don't care)
#define EEPROM_ADDR 0xA0 // Device address for slave target
// Note: This address is specified
// in the Microchip 24LC02B
// datasheet.
#define MY_ADDR 0x02 // Address of this SMBus device
// (dummy value since this device does
// not have any defined slave states)
// SMBus Buffer Size
#define SMB_BUFF_SIZE 0x08 // Defines the maximum number of bytes
// that can be sent or received in a
// single transfer
#define SMB_BUS_ERROR 0x00 // (all modes) BUS ERROR
#define SMB_START 0x08 // (MT & MR) START transmitted
#define SMB_RP_START 0x10 // (MT & MR) repeated START
#define SMB_MTADDACK 0x18 // (MT) Slave address + W transmitted;
// ACK received
#define SMB_MTADDNACK 0x20 // (MT) Slave address + W transmitted;
// NACK received
#define SMB_MTDBACK 0x28 // (MT) data byte transmitted;
// ACK rec'vd
#define SMB_MTDBNACK 0x30 // (MT) data byte transmitted;
// NACK rec'vd
#define SMB_MTARBLOST 0x38 // (MT) arbitration lost
#define SMB_MRADDACK 0x40 // (MR) Slave address + R transmitted;
// ACK received
#define SMB_MRADDNACK 0x48 // (MR) Slave address + R transmitted;
// NACK received
#define SMB_MRDBACK 0x50 // (MR) data byte rec'vd;
Administrator
线条
Administrator
线条
// ACK transmitted
#define SMB_MRDBNACK 0x58 // (MR) data byte rec'vd;
// NACK transmitted
//-----------------------------------------------------------------------------
// Global VARIABLES
//-----------------------------------------------------------------------------
unsigned char* pSMB_DATA_IN; // Global pointer for SMBus data
// All receive data is written here
unsigned char SMB_SINGLEBYTE_OUT; // Global holder for single byte writes.
unsigned char* pSMB_DATA_OUT; // Global pointer for SMBus data.
// All transmit data is read from here
unsigned char SMB_DATA_LEN; // Global holder for number of bytes
// to send or receive in the current
// SMBus transfer.
unsigned char WORD_ADDR; // Global holder for the EEPROM word
// address that will be accessed in
// the next transfer
unsigned char TARGET; // Target SMBus slave address
bit SMB_BUSY = 0; // Software flag to indicate when the
// EEPROM_ByteRead() or
// EEPROM_ByteWrite()
// functions have claimed the SMBus
bit SMB_RW; // Software flag to indicate the
// direction of the current transfer
bit SMB_SENDWORDADDR; // When set, this flag causes the ISR
// to send the 8-bit
// after sending the slave address.
bit SMB_RANDOMREAD; // When set, this flag causes the ISR
// to send a START signal after sending
// the word address.
// For the 24LC02B EEPROM, a random read
// (a read from a particular address in
// memory) starts as a write then
// changes to a read after the repeated
// start is sent. The ISR handles this
// switchover if the
// bit is set.
bit SMB_ACKPOLL; // When set, this flag causes the ISR
// to send a repeated START until the
// slave has acknowledged its address
// 16-bit SFR declarations
sfr16 RCAP4 = 0xCA; // Timer4 reload registers
sfr16 TMR4 = 0xCC; // Timer4 counter registers
sbit LED = P1^6; // LED on P1.6
sbit SDA = P0^0; // SMBus on P0.0
sbit SCL = P0^1; // and P0.1
//-----------------------------------------------------------------------------
// Function PROTOTYPES
//-----------------------------------------------------------------------------
void SYSCLK_Init(void);
void Port_Init(void);
void SMBus_Init(void);
void Timer4_Init(void);
void SMBus_ISR(void);
void Timer4_ISR(void);
void EEPROM_ByteWrite(unsigned char addr, unsigned char dat);
void EEPROM_WriteArray(unsigned char dest_addr, unsigned char* src_addr,
unsigned char len);
unsigned char EEPROM_ByteRead(unsigned char addr);
void EEPROM_ReadArray(unsigned char* dest_addr, unsigned char src_addr,
unsigned char len);
//-----------------------------------------------------------------------------
// MAIN Routine
//-----------------------------------------------------------------------------
void MAIN (void)
{
char in_buff[8] = {0}; // Incoming data buffer
char out_buff[8] = "ABCDEFG"; // Outgoing data buffer
unsigned char temp_char; // Temporary variable
bit error_flag = 0; // Flag for checking EEPROM contents
unsigned char i; // Temporary counter variable
WDTCN = 0xde; // Disable watchdog timer
WDTCN = 0xad;
SYSCLK_Init (); // Set internal oscillator to a setting
// of 3062500 Hz
// If slave is holding SDA low because of an improper SMBus reset or error
while(!SDA)
{
// Provide clock pulses to allow the slave to advance out
// of its current state. This will allow it to release SDA.
XBR1 = 0x40; // Enable Crossbar
SCL = 0; // Drive the clock low
for(i = 0; i < 255; i++); // Hold the clock low
SCL = 1; // Release the clock
while(!SCL); // Wait for open-drain
// clock output to rise
for(i = 0; i < 10; i++); // Hold the clock high
XBR1 = 0x00; // Disable Crossbar
}
Port_Init (); // Initialize Crossbar and GPIO
// Turn off the LED before the test starts
LED = 0;
SMBus_Init (); // Configure and enable SMBus
Timer4_Init (); // Configure and enable Timer4
EIE1 |= 0x02; // Enable the SMBus interrupt
EA = 1; // Global interrupt enable
SFRPAGE = SMB0_PAGE;
SI = 0;
// Read and write some bytes to the EEPROM and check for proper
// communication
// Write the value 0xAA to location 0x25 in the EEPROM
EEPROM_ByteWrite(0x25, 0xAA);
// Read the value at location 0x25 in the EEPROM
temp_char = EEPROM_ByteRead(0x25);
// Check that the data was read properly
if (temp_char != 0xAA)
{
error_flag = 1;
}
// Write the value 0xBB to location 0x25 in the EEPROM
EEPROM_ByteWrite(0x25, 0xBB);
// Write the value 0xCC to location 0x38 in the EEPROM
EEPROM_ByteWrite(0x38, 0xCC);
// Read the value at location 0x25 in the EEPROM
temp_char = EEPROM_ByteRead(0x25);
// Check that the data was read properly
if (temp_char != 0xBB)
{
error_flag = 1;
}
// Read the value at location 0x38 in the EEPROM
temp_char = EEPROM_ByteRead(0x38);
// Check that the data was read properly
if (temp_char != 0xCC)
{
error_flag = 1;
}
// Store the outgoing data buffer at EEPROM address 0x50
EEPROM_WriteArray(0x50, out_buff, sizeof(out_buff));
// Fill the incoming data buffer with data starting at EEPROM address 0x50
EEPROM_ReadArray(in_buff, 0x50, sizeof(in_buff));
// Check that the data that came from the EEPROM is the same as what was
// sent
for (i = 0; i < sizeof(in_buff); i++)
{
if (in_buff[i] != out_buff[i])
{
error_flag = 1;
}
}
// Indicate communication is good
if (error_flag == 0)
{
// LED = ON indicates that the test passed
LED = 1;
}
while(1);
}
//-----------------------------------------------------------------------------
// Initialization Routines
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// SYSCLK_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// This routine initializes the system clock to use the internal oscillator
// at 3.0625 MHz (24.5 / 8 MHz).
//
void SYSCLK_Init (void)
{
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = CONFIG_PAGE; // Set SFR page
OSCICN = 0x80; // Set internal oscillator to run
// at 1/8 its maximum frequency
CLKSEL = 0x00; // Select the internal osc. as
// the SYSCLK source
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page
// detector
}
//-----------------------------------------------------------------------------
// PORT_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Configure the Crossbar and GPIO ports.
//
// P0.0 digital open-drain SMBus SDA
// P0.1 digital open-drain SMBus SCL
//
// P1.6 digital push-pull LED
//
// all other port pins unused
//
// Note: If the SMBus is moved, the SCL and SDA sbit declarations must also
// be adjusted.
//
void PORT_Init (void)
{
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = CONFIG_PAGE;
P0MDOUT = 0x00; // All P0 pins open-drain output
P1MDOUT |= 0x40; // Make the LED (P1.6) a push-pull
// output
XBR0 = 0x01; // Enable SMBus on the crossbar
XBR2 = 0x40; // Enable crossbar and weak pull-ups
P0 = 0xFF;
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page detector
}
//-----------------------------------------------------------------------------
// SMBus_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// The SMBus peripheral is configured as follows:
// - SMBus enabled
// - Assert Acknowledge low (AA bit = 1b)
// - Free and SCL low timeout detection enabled
//
void SMBus_Init (void)
{
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = SMB0_PAGE;
SMB0CN = 0x07; // Assert Acknowledge low (AA bit = 1b);
// Enable SMBus Free timeout detect;
// Enable SCL low timeout detect
// SMBus clock rate (derived approximation from the Tlow and Thigh equations
// in the SMB0CR register description)
SMB0CR = 257 - (SYSCLK / (2 * SMB_FREQUENCY));
SMB0ADR = MY_ADDR; // Set own slave address.
SMB0CN |= 0x40; // Enable SMBus;
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page detector
}
//-----------------------------------------------------------------------------
// Timer4_Init
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Timer4 configured for use by the SMBus low timeout detect feature as
// follows:
// - Timer4 in auto-reload mode
// - SYSCLK/12 as Timer4 clock source
// - Timer4 reload registers loaded for a 25ms overflow period
// - Timer4 pre-loaded to overflow after 25ms
// - Timer4 enabled
//
void Timer4_Init (void)
{
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = TMR4_PAGE;
TMR4CN = 0x00; // Timer4 external enable off;
// Timer4 in timer mode;
// Timer4 in auto-reload mode
TMR4CF = 0x00; // Timer4 uses SYSCLK/12
// Timer4 output not available
// Timer4 counts up
RCAP4 = -(SYSCLK/12/40); // Timer4 configured to overflow after
TMR4 = RCAP4; // ~25ms (for SMBus low timeout detect)
EIE2 |= 0x04; // Timer4 interrupt enable
TR4 = 1; // Start Timer4
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page detector
}
//-----------------------------------------------------------------------------
// Interrupt Service Routines
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// SMBus Interrupt Service Routine (ISR)
//-----------------------------------------------------------------------------
//
// SMBus ISR state machine
// - Master only implementation - no slave states defined
// - All incoming data is written starting at the global pointer
// - All outgoing data is read from the global pointer
//
void SMBUS_ISR (void) interrupt 7
{
bit FAIL = 0; // Used by the ISR to flag failed
// transfers
static char i; // Used by the ISR to count the
// number of data bytes sent or
// received
static bit SEND_START = 0; // Send a start
// Status code for the SMBus (SMB0STA register)
switch (SMB0STA)
{
// Master Transmitter/Receiver: START condition transmitted.
// Load SMB0DAT with slave device (EEPROM) address.
case SMB_START:
SMB0DAT = TARGET; // Load address of the slave.
SMB0DAT &= 0xFE; // Clear the LSB of the address for the
// R/W bit
SMB0DAT |= SMB_RW; // Load R/W bit
STA = 0; // Manually clear STA bit
i = 0; // reset data byte counter
break;
// Master Transmitter/Receiver: Repeated START condition transmitted.
// For a READ: This state only occurs during an EEPROM read operation
// for the EEPROM's "random read", where a write operation with the
// data address must be done before the read operation.
//
// For a WRITE: N/A
case SMB_RP_START:
SMB0DAT = TARGET; // Load address of the slave.
SMB0DAT |= SMB_RW; // Load R/W bit
STA = 0; // Manually clear START bit.
i = 0; // reset data byte counter
break;
// Master Transmitter: Slave address + WRITE transmitted. ACK received.
// For a READ or WRITE: the word (data) address in the EEPROM must now
// be sent after the slave address
case SMB_MTADDACK:
SMB0DAT = WORD_ADDR; // Send word address
if (SMB_RANDOMREAD)
{
SEND_START = 1; // Send a START after the next ACK cycle
SMB_RW = READ;
}
break;
// Master Tra