I have been attempting to configure a TMS320C5535 as an I2C slave device. I am developing on an ezDSP5535 platform. It appears I am not setting up the I2C interrupt properly, but at this point have exhausted any ideas from reading the literature. Basically, if I set up my code with the ICOAR at a slave address, I never hit a breakpoint in my ISR for the I2C. However, if I watch the SDA and SCL lines with a logic analyzer, the DSP pulls down the SDA and SCL lines if my master processor sends the slave address. If the master processor sends to other addresses, the SDA and SCL lines are not pulled down by my code and I can successfully observe other I2C activity by the master processor. In either case, a breakpoint set at the beginning of my I2C interrupt routine is never hit. Can someone please help? Thank you./*****************************************************************************/
/* */
/* FILENAME */
/* main.c */
/* */
/* DESCRIPTION */
/* */
/* Vista Therapeutics, DSP lockin amplifier */
/* */
/*****************************************************************************/
//Note that I2C interrupt service routines are at end of this file due to weird
//compiler error if csl_intc was used as an include when they were in a separate file
#include "stdio.h"
#include "cslr_cpu.h"
#include "csl_intc.h"
#include "usbstk5505.h"
#include "usbstk5505_gpio.h"
#include "usbstk5505_i2c.h"
#include "aic3204.h" //keep for now, may include I2C initialization details
#include "PLL.h"
#include "IOpins.h"
#define One_msec_loop 9000 //1 msec loop time for a simple for loop when PLL at 100 MHz
#define twenty_eight_msec 28
#define twelve_usec 110 //based on 9000 for 1 msec loop time (111 nsec/loop)
#define TrueLowInterval 2 //to reject short logic high glitches, adjust after measuring loop time
#define _40usec 137
inline void SyncADCs(void);
inline void GetADCReadings(void);
interrupt void gpio_isr(void);
interrupt void I2C_ISR(void);
void lockin_I2C_init(void);
void lockin_I2C_test(void);
extern void VECSTART(void);
unsigned Int16 u[12], v[12];
unsigned Int16 NW_drainQ[sizeNW_drain];
unsigned Int16 NW_sourceQ[sizeNW_source];
int cntADC;
static volatile Uint8 current_yn[_48bit];
static volatile Uint8 lockin_drain[_48bit];
/* ------------------------------------------------------------------------ *
* *
* main( ) *
* *
* ------------------------------------------------------------------------ */
void main( void )
{
int i, j, k;
/* Initialize BSL */
USBSTK5505_init( );
/* Initialize PLL */
pll_frequency_setup(100);
j = 0;
k = 0;
//Load them with byte test patterns {1,2,3,4,5,6} and {7,8,9,10,11,12}
for (i=0; i < 6; i++)
{
*(current_yn+i) = i+1;
*(lockin_drain+i) = i+6;
}
lockin_I2C_init();
_enable_interrupts();
lockin_I2C_test();
}
/**************************************************************
I2C defines: configure as a slave transmitter
***************************************************************/
//slave transmitter address
#define LockinSLA 0x60 //lockin slave transmitter address is 0x60 - 0x6E, w/ lower 4 bits read from slot at runtime
//commands
#define ReportSource 0x55 //cmd to lockin to report the source value (actually source*drain/2)
#define ReportDrain 0xAA //cmd to lockin to report the source value (actually drain*drain/2)
#define ResetLockin 0x22 //cmd to lockin instructing it to reset (which clears old lockin queue and recalculates drain)
volatile int icntr;
static volatile Uint8 * TXP;
void lockin_I2C_init(void)
{
/******************************************************************************
I2C setup
Setup is as a slave transmitter
Guidance is from the TMS320C5535 technical reference manual (SPRUH87D)
p 317, sec 9.2.12.2 describes how to configure for slave receiver/transmitter
step by step which is what this section does.
******************************************************************************/
IRQ_disableAll();
/* Set the interrupt vector start address */
IRQ_setVecs((Uint32)(&VECSTART));
IRQ_plug( I2C_EVENT, &I2C_ISR);
IRQ_clearAll(); //clear any pending interrupts
//step 1 says "enable clock from PSC level".
SYS_PCGCR1 &= ~SYSCLKDIS; //system clock active when this bit 0
SYS_PCGCR1 &= ~I2CCG; //I2C peripheral clock active when this bit 0
//step 2 reset the I2C
ICMDR &= ~MDR_IRS;
//step 3
//set slave address, 7-bit
ICMDR &= ~MDR_XA;
ICOAR = 0x60; //0x61; //LockinSLA; //in future, lower 4-bits will be OR-ed with slot ID
//step 4
//ICIMR enables interrupts for specific I2C peripheral occurrences.
//Only 'AAS', the interrupt for DSP recognizing its own slave address is initially
//enabled. This then sets up a sequence of interrupts being enabled as:
// -ICRRDY to detect the command byte that follows the slave address
// -ICXRDY to detect when ICXRDY is ready for new data
// -SCD: the stop bit is detected, which then sets AAS again for next occurrence
ICIMR = ICIMR_AAS;
//step 5
//setup of the I2C prescaler (ICPSC register, p. 335 and p309 clocking diagram)
//This set the prescaled module clock which is described
//as requiring a range of 6.7-13.3 MHz. The clock source is totally unclear at this writing.
//I am following the USBSTK5505_I2C_init() code for the constant used which they state is 20
//for a 12 MHz clock. I don't know where this number comes from, it is not sensible for a 100 MHz
//overall clock, but am using it based on the assumption it must be right since there are other
//I2C devices on the ezDSP and they work, e.g. the AIC3204 codec.
//Step 5 however says the prescaled module clock freq = PLL1 output freq / (IPSC + 1)
//The PLL1 output freq is understood to be 100 MHz. A value of 20 gives 4.76 MHz, which is too slow
//for the required range given. By my calculation, PSC should be 7.33 for 12 MHz
ICPSC = 020;
//step 6
//configure the I2C serial clock freq. Assuming a symmetrical high and low half cycle,
//the operating freq is (step 5 freq)/(ICC + 10) where is ICC represents both ICCL and ICCH
//Again, there is a discrepancy between the USBSTK5505_I2C_init() code and my calculations.
//The code calls out a value of 20 for a SCL of 20 KHz. If step 5 is 12 MHz, this gives SCL
//of 400 KHz. If step 5 is 4.76 MHz, this gives SCL of 158.75 KHz. (p 326)
ICCLKL = 20;
ICCLKH = 20;
//step 7
//Configure the mode register ICMDR (p. 329)
ICMDR = 0;
ICMDR |= RM; //data words continuously rec'd/transmitted until STP bit manually set
ICMDR |= MDR_STT; //monitor bus and transmit/rec'v in response to master commands
//step 8
ICSTR = ICSTR; //clear any pending interrupt flags by writing 1 to them
while ( ICIVR ) //zero ICIVR by reading it
;
ICDXR = current_yn[0]; //keep XSMT=0 from pulling SDA/SCA lines low
//release I2C from reset
ICMDR |= MDR_IRS;
//step 9
//clear any interrupts present
ICSTR = ICSTR; //clear any pending interrupt flags by writing 1 to them
while ( ICIVR ) //zero ICIVR by reading it
;
//step 10
//Detect start condition and own address
ICMDR |= MDR_STT; //this was done in step 7 as well
//enable CPU interrupts
_enable_interrupts();
//IRQ_globalEnable();
icntr = 0;
TXP = &lockin_drain[0];
}
void lockin_I2C_test(void)
{
//verify ability to transmit over I2C as a slave transmitter
icntr = 0;
TXP = &lockin_drain[0];
while (1)
{
SYS_GPIO_DATAOUT0 |= SetPin15;
/*
for (i=0; i < 2000; i++)
for (j=0; j < One_msec_loop; j++)
;
*/
//printf("icntr= %d\n", icntr);
}
}
interrupt void I2C_ISR(void)
{
SYS_GPIO_DATAOUT0 &= ClearPin15;
//determine the source of the interrupt
if ( ICSTR & ICSTR_AAS )
{
//slave address rec'd
icntr = 1;
TXP = ¤t_yn[1];
}
else if ( ICSTR & ICXRDY )
{
//transmit requested data
ICDXR = *(TXP+icntr++);
if ( icntr == 6 )
ICIMR = ICIMR_SCD;
}
else if ( ICSTR & ICSTR_SCD) //stop bit
ICIMR = ICIMR_AAS;
//clear the pending interrupt just processed
ICSTR = ICSTR;
SYS_GPIO_DATAOUT0 |= SetPin15;
_enable_interrupts();
}
/* ------------------------------------------------------------------------ *
* *
* End of main.c *
* *
* ------------------------------------------------------------------------ */
/*****************************************************************************/
/* */
/* FILENAME */
/* usbstk5505_i2c.c */
/* */
/* DESCRIPTION */
/* Header file for i2c communications with codec on TMS320C5505 USB Stick.*/
/* */
/*****************************************************************************/
/*
*
* Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* Neither the name of Texas Instruments Incorporated nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
/*
* Copyright 2009 by Spectrum Digital Incorporated.
* All rights reserved. Property of Spectrum Digital Incorporated.
*/
/*
* I2C implementation
*
*/
#include "usbstk5505_i2c.h"
static Int32 i2c_timeout = 0x7fff;
/* ------------------------------------------------------------------------ *
* *
* _I2C_init( ) *
* *
* Enable and initalize the I2C module *
* The I2C clk is set to run at 20 KHz *
* *
* ------------------------------------------------------------------------ */
Int16 USBSTK5505_I2C_init( )
{
I2C_MDR = 0x0400; // Reset I2C
I2C_PSC = 20; // Config prescaler for 12MHz
I2C_CLKL = 20; // Config clk LOW for 20kHz
I2C_CLKH = 20; // Config clk HIGH for 20kHz
I2C_MDR = 0x0420 ; // Release from reset; Master, Transmitter, 7-bit address
return 0;
}
/* ------------------------------------------------------------------------ *
* *
* _I2C_close( ) *
* *
* ------------------------------------------------------------------------ */
Int16 USBSTK5505_I2C_close( )
{
I2C_MDR = 0; // Reset I2C
return 0;
}
/* ------------------------------------------------------------------------ *
* *
* _I2C_reset( ) *
* *
* ------------------------------------------------------------------------ */
Int16 USBSTK5505_I2C_reset( )
{
USBSTK5505_I2C_close( );
USBSTK5505_I2C_init( );
return 0;
}
/* ------------------------------------------------------------------------ *
* *
* _I2C_write( i2c_addr, data, len ) *
* *
* I2C write in Master mode *
* *
* i2c_addr <- I2C slave address *
* data <- I2C data ptr *
* len <- # of bytes to write *
* *
* ------------------------------------------------------------------------ */
Int16 USBSTK5505_I2C_write( Uint16 i2c_addr, Uint8* data, Uint16 len )
{
Int32 timeout, i;
//I2C_IER = 0x0000;
I2C_CNT = len; // Set length
I2C_SAR = i2c_addr; // Set I2C slave address
I2C_MDR = MDR_STT // Set for Master Write
| MDR_TRX
| MDR_MST
| MDR_IRS
| MDR_FREE;
USBSTK5505_wait(10); // Short delay
for ( i = 0 ; i < len ; i++ )
{
I2C_DXR = data[i]; // Write
timeout = i2c_timeout;
do
{
if ( timeout-- < 0 )
{
USBSTK5505_I2C_reset( );
return -1;
}
} while ( ( I2C_STR & STR_XRDY ) == 0 );// Wait for Tx Ready
}
I2C_MDR |= MDR_STP; // Generate STOP
USBSTK5505_waitusec(100);
return 0;
}
/* ------------------------------------------------------------------------ *
* *
* _I2C_read( i2c_addr, data, len ) *
* *
* I2C read in Master mode *
* *
* i2c_addr <- I2C slave address *
* data <- I2C data ptr *
* len <- # of bytes to write *
* *
* Returns: 0: PASS *
* -1: FAIL Timeout *
* *
* ------------------------------------------------------------------------ */
Int16 USBSTK5505_I2C_read( Uint16 i2c_addr, Uint8* data, Uint16 len )
{
Int32 timeout, i;
I2C_CNT = len; // Set length
I2C_SAR = i2c_addr; // Set I2C slave address
I2C_MDR = MDR_STT // Set for Master Read
| MDR_MST
| MDR_IRS
| MDR_FREE;
USBSTK5505_wait( 10 ); // Short delay
for ( i = 0 ; i < len ; i++ )
{
timeout = i2c_timeout;
//Wait for Rx Ready
do
{
if ( timeout-- < 0 )
{
USBSTK5505_I2C_reset( );
return -1;
}
} while ( ( I2C_STR & STR_RRDY ) == 0 );// Wait for Rx Ready
data[i] = I2C_DRR; // Read
}
I2C_MDR |= MDR_STP; // Generate STOP
USBSTK5505_waitusec(10);
return 0;
}
/* ------------------------------------------------------------------------ *
* *
* End of usbstk5505_i2c.c *
* *
* ------------------------------------------------------------------------ */
I've also attached the source files, but the I2C initialization and interrupt setup code is as follows:
void lockin_I2C_init(void)
{
/******************************************************************************
I2C setup
Setup is as a slave transmitter
Guidance is from the TMS320C5535 technical reference manual (SPRUH87D)
p 317, sec 9.2.12.2 describes how to configure for slave receiver/transmitter
step by step which is what this section does.
******************************************************************************/
IRQ_disableAll();
/* Set the interrupt vector start address */
IRQ_setVecs((Uint32)(&VECSTART));
IRQ_plug( I2C_EVENT, &I2C_ISR);
IRQ_clearAll(); //clear any pending interrupts
//step 1 says "enable clock from PSC level".
SYS_PCGCR1 &= ~SYSCLKDIS; //system clock active when this bit 0
SYS_PCGCR1 &= ~I2CCG; //I2C peripheral clock active when this bit 0
//step 2 reset the I2C
ICMDR &= ~MDR_IRS;
//step 3
//set slave address, 7-bit
ICMDR &= ~MDR_XA;
ICOAR = 0x60; //0x61; //LockinSLA; //in future, lower 4-bits will be OR-ed with slot ID
//step 4
//ICIMR enables interrupts for specific I2C peripheral occurrences.
//Only 'AAS', the interrupt for DSP recognizing its own slave address is initially
//enabled. This then sets up a sequence of interrupts being enabled as:
// -ICRRDY to detect the command byte that follows the slave address
// -ICXRDY to detect when ICXRDY is ready for new data
// -SCD: the stop bit is detected, which then sets AAS again for next occurrence
ICIMR = ICIMR_AAS;
//step 5
//setup of the I2C prescaler (ICPSC register, p. 335 and p309 clocking diagram)
//This set the prescaled module clock which is described
//as requiring a range of 6.7-13.3 MHz. The clock source is totally unclear at this writing.
//I am following the USBSTK5505_I2C_init() code for the constant used which they state is 20
//for a 12 MHz clock. I don't know where this number comes from, it is not sensible for a 100 MHz
//overall clock, but am using it based on the assumption it must be right since there are other
//I2C devices on the ezDSP and they work, e.g. the AIC3204 codec.
//Step 5 however says the prescaled module clock freq = PLL1 output freq / (IPSC + 1)
//The PLL1 output freq is understood to be 100 MHz. A value of 20 gives 4.76 MHz, which is too slow
//for the required range given. By my calculation, PSC should be 7.33 for 12 MHz
ICPSC = 020;
//step 6
//configure the I2C serial clock freq. Assuming a symmetrical high and low half cycle,
//the operating freq is (step 5 freq)/(ICC + 10) where is ICC represents both ICCL and ICCH
//Again, there is a discrepancy between the USBSTK5505_I2C_init() code and my calculations.
//The code calls out a value of 20 for a SCL of 20 KHz. If step 5 is 12 MHz, this gives SCL
//of 400 KHz. If step 5 is 4.76 MHz, this gives SCL of 158.75 KHz. (p 326)
ICCLKL = 20;
ICCLKH = 20;
//step 7
//Configure the mode register ICMDR (p. 329)
ICMDR = 0;
ICMDR |= RM; //data words continuously rec'd/transmitted until STP bit manually set
ICMDR |= MDR_STT; //monitor bus and transmit/rec'v in response to master commands
//step 8
ICSTR = ICSTR; //clear any pending interrupt flags by writing 1 to them
while ( ICIVR ) //zero ICIVR by reading it
;
ICDXR = current_yn[0]; //keep XSMT=0 from pulling SDA/SCA lines low
//release I2C from reset
ICMDR |= MDR_IRS;
//step 9
//clear any interrupts present
ICSTR = ICSTR; //clear any pending interrupt flags by writing 1 to them
while ( ICIVR ) //zero ICIVR by reading it
;
//step 10
//Detect start condition and own address
ICMDR |= MDR_STT; //this was done in step 7 as well
//enable CPU interrupts
_enable_interrupts();
//IRQ_globalEnable();
icntr = 0;
TXP = &lockin_drain[0];
}
And the ISR code is as follows:
interrupt void I2C_ISR(void)
{
SYS_GPIO_DATAOUT0 &= ClearPin15;
//determine the source of the interrupt
if ( ICSTR & ICSTR_AAS )
{
//slave address rec'd
icntr = 1;
TXP = ¤t_yn[1];
}
else if ( ICSTR & ICXRDY )
{
//transmit requested data
ICDXR = *(TXP+icntr++);
if ( icntr == 6 )
ICIMR = ICIMR_SCD;
}
else if ( ICSTR & ICSTR_SCD) //stop bit
ICIMR = ICIMR_AAS;
//clear the pending interrupt just processed
ICSTR = ICSTR;
SYS_GPIO_DATAOUT0 |= SetPin15;
_enable_interrupts();
}