Xmega128A TWI interrupts not triggering: Slave Transmitter

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

My setup is an ATxmega128A4U as a TWI slave transmitter, and a IMX6 as the TWI master.

A brief hardware setup description: 

PC0 for SDA
PC1 for SCL
An external pull up on each SDA and SCL (4.7K resistors) 
A scope shows continual address calls of 0x18 from the master (IMX6), no acknowledgement from the slave (ATxmega128A4U).

 

I can't get the Xmega to recognize TWI transmissions at all. The TWIS interrupt never fires and as such the interrupt routine is never even entered, and the TWI slave control registers (such as TWI_SLAVE_APIF) never change state despite seeing the correct start + address sequence being generated from the master IMX6 on the scope.

 

I have global interrupts (and each specific level) enabled, dug into the datasheet without finding anything pertinent to this specific issue, and checked out the forums to see if anyone else ran into this issue without any luck. 

 

Any input would be greatly appreciated!

 

My slave setup: 

void setup_twi_slave(void)
{
	//set up slave transmitter
	TWIC.CTRL             =   TWI_SDAHOLD_50NS_gc; //enables an internal hold time on SDA with respect to the negative edge of SCL (helps prevent timing glitches)
	TWIC.SLAVE.ADDR       =   0x18;				   //set address
	//APIF flag set when STOP condition is detected | enable data interrupt | enable address interrupt | set as slave | enable interrupts | enable smart enable
	TWIC.SLAVE.CTRLA      |=  TWI_SLAVE_PIEN_bm | TWI_SLAVE_DIEN_bm | TWI_SLAVE_APIEN_bm | TWI_SLAVE_ENABLE_bm | TWI_SLAVE_INTLVL_HI_gc | TWI_SLAVE_SMEN_bm;
}

and my interrupt: 

*! TWIC Slave Interrupt vector for port C. */
ISR(TWIC_TWIS_vect)
{
PORTB.OUTSET = PIN3_bm; //To see if the interrupt is entered
	// HANDLE ADDRESS MATCH
	if(TWIC.SLAVE.STATUS & TWI_SLAVE_APIF_bm){ //If a start or stop is set (APIF is the start/stop interrupt flag)
		if(TWIC.SLAVE.STATUS & TWI_SLAVE_AP_bm) { //AP == 1 means a valid address generated the interrupt APIF, AP == 0 means a stop condition generated the interrupt APIF. Address = 0x18 for TWIC (slave transmitter)
			TWIC.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; //Used in Response to Address/Data Interrupt - sends an ack
		}
		else{  //If it is not an address then it is as top condition
			TWIC.SLAVE.CTRLB  = TWI_SLAVE_CMD_COMPTRANS_gc; //Used To Complete a Transaction (STOP)
		}
	}
	else{    //If a start or stop condition didn't trigger interrupt, that means data did!
		if(TWIC.SLAVE.STATUS & TWI_SLAVE_DIF_bm){   //DIF is set when a data byte is successfully received. When DIF is set, slave forces the SCL line low, stretching the TWI clock period. Clearing the interrupt flag (writing a one) will release the SCL line
			//MASTER READ (Slave Write)
			if(TWIC.SLAVE.STATUS & TWI_SLAVE_DIR_bm){ // If DIR == 1, a master read operation is in progress. Else, when 0, a master write operation is in progress.
				if(TWIC.SLAVE.STATUS & TWI_SLAVE_RXACK_bm) { //This flag contains the most recently received acknowledge bit from the master. This is a read-only flag. When 0, the most recent acknowledge bit from the maser was ACK, and when 1, the most recent acknowledge bit was NACK
					TWIC.SLAVE.CTRLB      =   TWI_SLAVE_ACKACT_bm | TWI_SLAVE_CMD_COMPTRANS_gc; //Send ack and end transaction
				}
				else {
					TWIC.SLAVE.DATA  = 0x02; //send data to the master (hard coded as a 0x02 right now)
				}
			}
		}
	}
}

 

A more complete version of my code is below (for checks such as turning on global interrupts and each level, clock setup, etc): 

#define F_CPU 32000000UL

#include <avr/io.h>
#include <stdio.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdbool.h>
#include <string.h>
#include <util/twi.h>

static int uart_putchar(uint8_t c, FILE *stream);
int uart_getchar(FILE *stream);

static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar,
uart_getchar, _FDEV_SETUP_RW);	/* Assign I/O stream to UART */

void uart_init();
void clockSetup();
void setup_twi_slave(void);

volatile unsigned char letter = 0x00;
volatile uint8_t count = 0;
volatile uint8_t start = 1;
volatile char unsigned address = 0x00;

uint8_t accaddress = 0x18;  //Define slave transmitter address

int main(void)
{
	sei();
	PMIC.CTRL |= PMIC_HILVLEN_bm;    //enable high level interrupts
	PMIC.CTRL |= PMIC_MEDLVLEN_bm;    //enable medium level interrupts
	PMIC.CTRL |= PMIC_LOLVLEN_bm;    //enable low level interrupts
	letter = 0x00;
	count = 0;
	uint8_t currentaddress = 0x00;

	PORTA_DIR = 0x00;   //Set Port A as Input
	PORTD_DIR = 0x00;   //Set Port D as input
	PORTC_DIR = 0x00;   //Set Port C as Input
	PORTB_DIR = 0xFF;   //Set Port B as output
	PORTD_OUTCLR = PIN3_bm;
	PORTD_DIRCLR = PIN3_bm; //PD3 as RX signal
	uart_init();
	clockSetup();
	setup_twi_slave();
	stdout = &mystdout;

	while(1)
	{

	}
}

/*! TWIC Slave Interrupt vector for port C. */
ISR(TWIC_TWIS_vect)
{
PORTB.OUTSET = PIN3_bm; //To see if the interrupt is entered
	// HANDLE ADDRESS MATCH
	if(TWIC.SLAVE.STATUS & TWI_SLAVE_APIF_bm){ //If a start or stop is set (APIF is the start/stop interrupt flag)
		if(TWIC.SLAVE.STATUS & TWI_SLAVE_AP_bm) { //AP == 1 means a valid address generated the interrupt APIF, AP == 0 means a stop condition generated the interrupt APIF. Address = 0x18 for TWIC (slave transmitter)
			TWIC.SLAVE.CTRLB = TWI_SLAVE_CMD_RESPONSE_gc; //Used in Response to Address/Data Interrupt - sends an ack
		}
		else{  //If it is not an address then it is as top condition
			TWIC.SLAVE.CTRLB  = TWI_SLAVE_CMD_COMPTRANS_gc; //Used To Complete a Transaction (STOP)
		}
	}
	else{    //If a start or stop condition didn't trigger interrupt, that means data did!
		if(TWIC.SLAVE.STATUS & TWI_SLAVE_DIF_bm){   //DIF is set when a data byte is successfully received. When DIF is set, slave forces the SCL line low, stretching the TWI clock period. Clearing the interrupt flag (writing a one) will release the SCL line
			//MASTER READ (Slave Write)
			if(TWIC.SLAVE.STATUS & TWI_SLAVE_DIR_bm){ // If DIR == 1, a master read operation is in progress. Else, when 0, a master write operation is in progress.
				if(TWIC.SLAVE.STATUS & TWI_SLAVE_RXACK_bm) { //This flag contains the most recently received acknowledge bit from the master. This is a read-only flag. When 0, the most recent acknowledge bit from the maser was ACK, and when 1, the most recent acknowledge bit was NACK
					TWIC.SLAVE.CTRLB      =   TWI_SLAVE_ACKACT_bm | TWI_SLAVE_CMD_COMPTRANS_gc; //Send ack and end transaction
				}
				else {
					TWIC.SLAVE.DATA  = 0x02; //send data to the master (hard coded as a 0x02 right now)
				}
			}

		}
	}
}

void setup_twi_slave(void)
{
	//set up slave transmitter
	TWIC.CTRL             =   TWI_SDAHOLD_50NS_gc; //enables an internal hold time on SDA with respect to the negative edge of SCL (supposedly helps prevent timing glitches)
	TWIC.SLAVE.ADDR       =   0x18;				   //set address
	//APIF flag set when STOP condition is detected | enable data interrupt | enable address interrupt | set as slave | enable interrupts | enable smart enable
	TWIC.SLAVE.CTRLA      |=  TWI_SLAVE_PIEN_bm | TWI_SLAVE_DIEN_bm | TWI_SLAVE_APIEN_bm | TWI_SLAVE_ENABLE_bm | TWI_SLAVE_INTLVL_HI_gc | TWI_SLAVE_SMEN_bm;
}

void clockSetup() {
	CCP = CCP_IOREG_gc;  // disable register security for four clock cycles so you can update the clock's prescaler value
	//TC_CLKSEL_DIV1_gc; //No prescaler (or, prescaler = 1)
	CLK.PSCTRL = CLK_PSADIV_1_gc; //divide by 1
	CCP = CCP_IOREG_gc;  // disable register security for four clock cycles so you can update the oscillator value
	//ENABLE THE 32MHZ OSCILLATOR
	OSC.CTRL|=OSC_RC32MEN_bm;
	//WAIT FOR OSCILLATOR TO BECOME STABLE
	while(!(OSC.STATUS & OSC_RC32MRDY_bm));
	CCP = CCP_IOREG_gc;  // disable register security for four clock cycles so you can update the clock value
	//SELECT 32MHZ OSC FOR SYSTEM CLOCK.
	//CLK.CTRL = 0x01; equivalent to the line of code below, but more chip dependent
	CLK.CTRL=CLK_SCLKSEL_RC32M_gc;
}

void uart_init()
{
	//-------------------------------------For port C
	PORTC_OUTSET = PIN3_bm; //PC3 as TX
	PORTC_DIRSET = PIN3_bm; //TX pin as output

	PORTC_OUTCLR = PIN2_bm;
	PORTC_DIRCLR = PIN2_bm; //PC2 as RX

	// Baud rate selection

	USARTC0_BAUDCTRLB = 0;
	USARTC0_BAUDCTRLA =	52; 

	//Disable interrupts
	//USARTC0_CTRLA = 0;
	PMIC.CTRL |= PMIC_MEDLVLEN_bm;    //enable med level interrupts
	//USARTC0_CTRLA = USART_RXCINTLVL_MED_gc; //enable USART RX interrupt

	USARTC0_CTRLC = USART_CHSIZE_8BIT_gc; //set char size to 8 bit

	//Enable receive and transmit
	USARTC0_CTRLB = USART_TXEN_bm | USART_CLK2X_bm | USART_RXEN_bm; // And enable high speed mode	

}

static int uart_putchar (uint8_t c, FILE *stream){
	// Wait for the transmit buffer to be empty
	while (  !(USARTC0_STATUS & USART_DREIF_bm) );

	// Put character into the transmit buffer
	USARTC0_DATA = c;

	return 0;
}

int uart_getchar(FILE *stream)
{
	while( !(USARTC0_STATUS & USART_RXCIF_bm) );
	char data = USARTC0_DATA;
	if(data == '\r')
	data = '\n';
	uart_putchar(data, stream);
	return data;
}

 

Last Edited: Wed. Dec 6, 2017 - 10:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Normally, sensors are the slaves and the MCU is the master. Does the sensor really initiate transfers?

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Thanks for the catch, the MCU is the master and the Xmega is emulating a sensor. I mixed up my wording (fixed now), but the issue remains the same