Problems with I2C on an ATTiny2313

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'll try to get all the information in here that is needed, but if I'm missing something, just let me know.

Chip: ATTiny2313 (DIP20).
Custom board.

The ATTiny2313 is doing a couple of tasks for me. Here's my main loop:

	/* Initialization */
  	// Set Port B pins as all inputs
  	DDRB = 0x00;
  	// Set Port D pins as all outputs
  	DDRD = 0xFF;
  	// Set Port A0 as input for ON switch
  	//DDRA &= ~(1<<0);
  	// Set Port A1 as output for BCDOUT1
  	//DDRA |= (1<<1);
	DDRA = 0x7;

	// Set Port B pins as pullups (top 3)
	PORTB = 0xE0;
  	// Set the D0(PB5) and USCK(PB7) pins as output for SPI
  	DDRB |= (1<<7)|(1<<5);
  	// Set Switch Sel 2 (PB0) pin as output for knob select mux
  	DDRB |= (1<<0);
	// Set Pin D2 as input for interrupts
	DDRD &= ~(1<<PIND2);
  
	// Enable external interrups on PD2
	PCMSK |= (1<<PIND2); 
	MCUCR |= (1<<ISC01) | (1<<ISC00); //rising edge for INT0
	GIMSK |= (1<<INT0);

  	//enable global interrupts
  	sei();

  	//set up timer interrupt.  Default internal clock is 1MHz, 
  	//so I get 1000000 ticks per second (VERIFY THIS)
  	TCCR1B |= (1 << CS10); // Set up timer 

  	Playing = false;
  	Fading = false;
	Active = false;
  	FaderTap = MIN_TAP;

	while(1)
  	{
	    //check the ON switch
		//CheckOnButton();
		Active = true;

		TimerISR();  //check the timer value, see if I should increment the timers...
		//read timer knobs
		ReadSwitches();
		//update 7 segment
		UpdateLEDs();
		//set attenuator tap outputs
		SetAttenuator();
	}
	return 1;

Now for an explanation. of my functions.
CheckOnButton is commented out because I haven't wired up an on button yet, but the gist is that it simply reads the on pin to see if my board should be running (pin = 1) or has been disabled (pin = 0).
TimerISR() checks the current value of the timer, I'm keeping track of (and displaying via 7-segment LEDs) minute, ten-seconds, and seconds, so this TimerISR checks the values of the timer and updates the globals I use to set my 7-seg display.
ReadSwitches() reads the inputs from three separate BCD switches to get the value that should be printed out on the 7-seg display. It uses a mux to pick which switch to read, then reads the 4 values off the BCD switch, moves on to the next switch, etc... for all three switches.
UpdateLEDs() updates the 7-seg display with the new values in my global's user_seconds, user_tenSeconds, and user_minutes. It works in basically the same way as ReadSwitches. It picks a 7-seg digit by manipulating the select lines on a mux, sets the value, throws the latch pin on the 7-seg driver, and moves on to the next digit.
SetAttenuator() here's where my problems come in. Based on an event happening (my timer running out), I need to control an attenuator chip. The chip I'm using is a Maxim-Dallas DS4420. It uses an I2C interface to set the attenuation value. So, SetAttenuator() should send out the chip's slave address, the memory location of the attenuation register (0xF8) and the value of the attenuator I want.

I'm using the code from this instructable: http://www.instructables.com/id/... to get my I2C system up and running. Here's the code for a Master start:

unsigned char USI_TWI_Master_Start( void )
{
/* Release SCL to ensure that (repeated) Start can be performed */
  PORT_USI |= (1<<PIN_USI_SCL);                     // Release SCL.
  while( !(PORT_USI & (1<<PIN_USI_SCL)) );          // Verify that SCL becomes high.
  _delay_us(T2_TWI);

/* Generate Start Condition */
  PORT_USI &= ~(1<<PIN_USI_SDA);                    // Force SDA LOW.
	_delay_us(T4_TWI);                         
  PORT_USI &= ~(1<<PIN_USI_SCL);                    // Pull SCL LOW.
  PORT_USI |= (1<<PIN_USI_SDA);                     // Release SDA.

#ifdef SIGNAL_VERIFY
  if( !(USISR & (1<<USISIF)) )
  {
    USI_TWI_state.errorState = USI_TWI_MISSING_START_CON;  
    return (FALSE);
  }
#endif

when I try to run this code, I get the USI_TW_MISSING_START_CON error. Can someone point me in the direction I should look to try to find out what's causing my error, or what is a better way to test this I2C interface?

I guess mainly my first question is, do I need to do any fancy setups with PortB and PortD so that I can properly run the I2C interface? I set up some of the Port D pins for inputs/outputs for my LED/Switch interface, and some of the PortB pins also (PB0-4) but I'm using PB5 and PB7 for my SDA and SCL respectively. Am I doing something to PORTB that is messing that up?

To try and diagnose, I reduced my main loop to this:

int main(void)
{
	/* Initialization */
  	// Set Port B pins as all inputs
  	DDRB = 0x00;
  	// Set Port D pins as all outputs
  	DDRD = 0xFF;
  	// Set Port A0 as input for ON switch
  	//DDRA &= ~(1<<0);
  	// Set Port A1 as output for BCDOUT1
  	//DDRA |= (1<<1);
	DDRA = 0x7;

	// Set Port B pins as pullups (top 3)
	PORTB = 0xE0;
  	// Set the D0(PB5) and USCK(PB7) pins as output for SPI
  	DDRB |= (1<<7)|(1<<5);
  	// Set Switch Sel 2 (PB0) pin as output for knob select mux
  	DDRB |= (1<<0);
	// Set Pin D2 as input for interrupts
	DDRD &= ~(1<<PIND2);
  
	// Enable external interrups on PD2
	PCMSK |= (1<<PIND2); 
	MCUCR |= (1<<ISC01) | (1<<ISC00); //rising edge for INT0
	GIMSK |= (1<<INT0);

  	//enable global interrupts
  	sei();

  	//set up timer interrupt.  Default internal clock is 1MHz, 
  	//so I get 1000000 ticks per second (VERIFY THIS)
  	TCCR1B |= (1 << CS10); // Set up timer 

  	Playing = false;
  	Fading = false;
	Active = false;
  	FaderTap = MIN_TAP;

	while(1)
	{USI_TWI_Master_Start
		InitAttenuator();
		_delay_loop_2(6400);
	}
}

where InitAttenuator is just:

void InitAttenuator()
{
	/* SET UP I2C */
	USI_TWI_Master_Initialise();
	FaderTap = MIN_TAP;
	MuteAttenuator();
}
void USI_TWI_Master_Initialise( void )
{
  PORT_USI |= (1<<PIN_USI_SDA);           // Enable pullup on SDA, to set high as released state.
  PORT_USI |= (1<<PIN_USI_SCL);           // Enable pullup on SCL, to set high as released state.
  
  DDR_USI  |= (1<<PIN_USI_SCL);           // Enable SCL as output.
  DDR_USI  |= (1<<PIN_USI_SDA);           // Enable SDA as output.
  
  USIDR    =  0xFF;                       // Preload dataregister with "released level" data.
  USICR    =  (0<<USISIE)|(0<<USIOIE)|                            // Disable Interrupts.
              (1<<USIWM1)|(0<<USIWM0)|                            // Set USI in Two-wire mode.
              (1<<USICS1)|(0<<USICS0)|(1<<USICLK)|                // Software stobe as counter clock source
              (0<<USITC);
  USISR   =   (1<<USISIF)|(1<<USIOIF)|(1<<USIPF)|(1<<USIDC)|      // Clear flags,
              (0x0<<USICNT0);                                     // and reset counter.
}

and MuteAttenuator is:

void MuteAttenuator()
{	// I2C Variables
	unsigned char messageBuf[3];
	unsigned char TWI_targetSlaveAddress;
	unsigned char temp;

	TWI_targetSlaveAddress = FDR_LEFT_1;
	// Write to the I2C Port. Set LEDs according to switches
	messageBuf[0] = (TWI_targetSlaveAddress<<TWI_ADR_BITS) | (FALSE<<TWI_READ_BIT);
	messageBuf[1] = FDRREG; //Send the memory location of the control register in the DS4420
	messageBuf[2] = MUTE;       // The third byte is used for the data.
	//	Must be sure to set 4 LSB's HI so they work as inputs.
	temp = USI_TWI_Start_Read_Write( messageBuf, 3 );
	if (!temp)    // Check Error
	{             // Use TWI status information to detemine cause of failure and take appropriate actions. 
		while(1)
			DisplayError(1, 1, USI_TWI_Get_State_Info());
	}
}

the USI_TWI_Start_Read_Write initially calls the USI_TWI_Master_Start posted above, and that's where my error happens.

Last Edited: Tue. Jan 4, 2011 - 11:38 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you have external pull-ups installed?

What do you see on a 'scope?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, I have a 3.3K on SCL and a 2.2K on SDA (no reason for the differences, it's just what I had lying around). On the scope on SDA, the signal starts out at 0V before I turn on the power. When I power on, SDA goes high (4.2V) for ~100ms, and then stays low (0.5V). I see the exact same thing on SCL

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I can see very few advantages in using the USI as an I2C Master. Likewise, using interrupts is not going to make much difference to single byte transactions.

So I would use the bit-banged Fleury "i2c_master.S" to debug your program.

Once you have this working, you can change to USI and interrupts. After all, you are still going to use a similar API : status = write_register_value(reg, val) and value = read_register(reg)

David.