xMegaE5 SPI won't work if you're running PWM on TCC4

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

Thought I saw something like this last fall, and somebody asked about it late in December, but I've just spent a couple days scratching my head and I have a small test program to demo the problem.

If you're using TCC4 to do PWM on the bottom 4 bits of PORT C (and maybe some other things too), you cannot successfully use the spi peripheral on the top 4 bits of PORT C.

The test rig has an xMega32E5 chip with an NRF24L01+ connected to the SPI pins on port C.

/*
 * NRFModuleTestE5.cpp
 *
 * Created: 7/21/2014 10:44:33 AM
 *  Author: Tom
 */ 

#include 
#define F_CPU 2000000ul
#include 

#define SPIPORT PORTC
#define SPIMO 7
#define SPIMI 6
#define SPISC 5
#define SPISS 6

#define RFcsPORT PORTC
#define RFcsn 4

#define CSHIGH RFcsPORT.OUTSET = (1<<RFcsn)
#define CSLOW RFcsPORT.OUTCLR = (1<<RFcsn)

#define MotorPort PORTC

#define MotorLowLeftBit 3
#define MotorLowRightBit 0

#define MotorHighLeftBit 2
#define MotorHighRightBit 1

#define MotorTimer TCC4
#define MotorLeftSpeed CCD
#define MotorLeftCTRLE (1<<6)
#define MotorRightSpeed CCA
#define MotorRightCTRLE (1<<0)

// NRF24L01+ Gibberish

#define CMD_READ_REG        0x00  // Define read command to register
#define CMD_WRITE_REG       0x20  // Define write command to register

#define RG_CONFIG          0x00  // 'Config' register address
#define RG_EN_AA           0x01  // 'Enable Auto Acknowledgment' register address


#define ModeBits 0b00111100

uint8_t SPIDir = 0 ;

void spiopen(void)
{
	SPIDir = SPIPORT.DIR ;
	SPIPORT.DIRCLR = (1<<SPIMI);
	SPIPORT.DIRSET = (1<<SPIMO) | (1<<SPISC) | (1<<SPISS) ; // Make sure slave select is an output
	SPIC.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_0_gc | SPI_PRESCALER_DIV64_gc;
}



void spiclose(void)
{
	SPIC.CTRL = 0;
	SPIPORT.DIR = SPIDir ;
	//SPIPort.DIRSET = (1<<SPIMI);
}

uint8_t spi( uint8_t val)
{	
	spiopen();
	SPIC.DATA = val ;
	while(!(SPIC.STATUS & (1<<7)));
	spiclose();
	return SPIC.DATA;
}

uint8_t NRFWriteRegister(uint8_t reg, uint8_t value)
{
	uint8_t status;
	spiopen();
	CSLOW ;
		// Select register
		status = spi(CMD_WRITE_REG+reg);

		// Write value to it
		spi(value);
	CSHIGH ;
	spiclose();
	return(status);
}

uint8_t NRFReadRegister(uint8_t reg)
{
	uint8_t reg_val;
	spiopen();
	CSLOW ;
	// Select register to read from..
	spi(CMD_READ_REG+reg);
	// ..then read register value
	reg_val = spi(0);

	CSHIGH ;
	spiclose();
	return(reg_val);
}

void StartPWM(void)
{
	MotorPort.OUTCLR = (1<<MotorHighLeftBit) | (1<<MotorHighRightBit) ; // Turn off the high side
	MotorTimer.CTRLA = 0 ;
	MotorTimer.CTRLB = 3 ; // Single Slope Mode ;
	MotorTimer.CTRLE = (1<<6) | (1<<0) ;
	MotorTimer.CNT = 0 ;
	MotorTimer.PER = 1024 ;
	MotorTimer.CCA = 0 ;
	MotorTimer.CCB = 0 ;
	MotorTimer.CCC = 0 ;
	MotorTimer.CCD = 0 ;
	MotorTimer.CTRLA = 2 ; // Select clock/2 for 15625 Hz Motor Drive ** Comment this line and spi works!
	
}

volatile uint8_t b = 0 ;
volatile uint8_t c = 0 ;

int main(void)
{
	PORTA.DIR = 255 ;
	PORTC.DIR = 255 ;
	PORTC.DIRCLR = (1<<SPIMI) ;
	PORTD.DIR = 255 ;
	PORTA.OUT = 0 ;
	PORTC.OUT = 0 ;
	PORTD.OUT = 0 ;
	
	PORTA.PIN0CTRL = (3<<3) ;
	PORTA.PIN1CTRL = (3<<3) ;
	PORTA.PIN2CTRL = (3<<3) ;
	PORTA.PIN3CTRL = (3<<3) ;
	PORTA.PIN4CTRL = (3<<3) ;
	PORTA.PIN5CTRL = (3<<3) ;
	PORTA.PIN6CTRL = (3<<3) ;
	PORTA.PIN7CTRL = (3<<3) ;
	
	PORTC.PIN0CTRL = (3<<3) ;
	PORTC.PIN1CTRL = (3<<3) ;
	PORTC.PIN2CTRL = (3<<3) ;
	PORTC.PIN3CTRL = (3<<3) ;
	PORTC.PIN4CTRL = (3<<3) ;
	PORTC.PIN5CTRL = (3<<3) ;
	PORTC.PIN6CTRL = (3<<3) ;
	PORTC.PIN7CTRL = (3<<3) ;
	
	PORTD.PIN0CTRL = (3<<3) ;
	PORTD.PIN1CTRL = (3<<3) ;
	PORTD.PIN2CTRL = (3<<3) ;
	PORTD.PIN3CTRL = (3<<3) ;
	PORTD.PIN4CTRL = (3<<3) ;
	PORTD.PIN5CTRL = (3<<3) ;
	PORTD.PIN6CTRL = (3<<3) ;
	PORTD.PIN7CTRL = (3<<3) ;
	
	SPIC.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_0_gc | SPI_PRESCALER_DIV64_gc;
	CSHIGH ;

    StartPWM();

	_delay_ms(200) ;
	
	NRFWriteRegister(RG_CONFIG,ModeBits);
	NRFWriteRegister(RG_EN_AA,3); 


    while(1)
    {
	b = NRFReadRegister(RG_EN_AA) ; 
	c = b ;  // b should contain a 3.
        //TODO:: Please write your application code 
    }
}

Inside the while loop, it should read the register it just set to 3, but it always gets zero. If you go up to StartPWM and comment out the line where it sets MotorTimer.CTRLA = 2, it WILL be able to read a 3 from the NRF register.

I just downloaded the datasheet pdf fresh from Atmel and looked in the errata where I find no mention of this problem.

So let's see, how can I make this work. I'm not using the timer on port D for anything, maybe I can use an interrupt to set the port pins high and low. Hmm, maybe I can use the event system to do that. What do you think? Perhaps I just have bogus code again?

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

I have no idea why that's a .cpp file. I was just playing with cpp yesterday to waste some time.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

I used TCD5 to do the pwm, but used interrupts on CCA and OVF to set the pins on port C. Now I have my output waveform AND my NRF messages.

Adapt
Innovate
Overcome

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

This may explain why I was having some similar issues on an A3U. Have you reported the bug to Atmel?

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

Yup. Case number 00001938

Quote:

Hello Tom,

This is an expected behavior.

On XMEGA E devices, an enabled timer output channel takes precedence on all other peripheral port control.

This is for secure reason regarding PWM High voltage or Motor control.

To release unused output TC output port control use OUTOVDIS register. These bits disable the automatic override of the corresponding output port register (i.e., one-to-one bit relation to pin position).

Quote:

Your case 00001938 has been closed.

Um. Since the timer operates bits 0 through 3, I would NOT expect it to override other functions on bits 4 through 7, but let's see what the OUTOVDS register can do.

Apparently, by default, TCC5's compares are connected to bits 4 and 5. I haven't told TCC5 to do anything. Look in the WEX part of the manual.

It doesn't say a lot about OUTOVDIS, but I'll try setting it to:

MCU.WEXLOCK = 1 ;
WEXC.OUTOVDIS = ~((1<<MotorLowLeftBit)|(1<<MotorLowRightBit))

This generates (poop, can't copy/paste from disassembly again):

	MCU.WEXLOCK = 1 ;
 1c8:	81 e0       	ldi	r24, 0x01	; 1
 1ca:	e0 e9       	ldi	r30, 0x90	; 144
 1cc:	f0 e0       	ldi	r31, 0x00	; 0
 1ce:	81 87       	std	Z+9, r24	; 0x09
	WEXC.OUTOVDIS = ~((1<<MotorLowLeftBit) | (1<<MotorLowRightBit)) ;
 1d0:	86 ef       	ldi	r24, 0xF6	; 246
 1d2:	e0 ea       	ldi	r30, 0xA0	; 160
 1d4:	f8 e0       	ldi	r31, 0x08	; 8
 1d6:	87 87       	std	Z+15, r24	; 0x0f
 1d8:	08 95       	ret

And, when it runs... b still reads back 0.

The program now is:

/*
 * xMegaE5Conflict.c
 *
 * Created: 7/21/2014 3:52:46 PM
 *  Author: Tom
 */ 


#include 
#define F_CPU 2000000ul
#include 

#define SPIPORT PORTC
#define SPIMO 7
#define SPIMI 6
#define SPISC 5
#define SPISS 6

#define RFcsPORT PORTC
#define RFcsn 4

#define CSHIGH RFcsPORT.OUTSET = (1<<RFcsn)
#define CSLOW RFcsPORT.OUTCLR = (1<<RFcsn)

#define MotorPort PORTC

#define MotorLowLeftBit 3
#define MotorLowRightBit 0

#define MotorHighLeftBit 2
#define MotorHighRightBit 1

#define MotorTimer TCC4
#define MotorLeftSpeed CCD
#define MotorLeftCTRLE (1<<6)
#define MotorRightSpeed CCA
#define MotorRightCTRLE (1<<0)

// NRF24L01+ Gibberish

#define CMD_READ_REG        0x00  // Define read command to register
#define CMD_WRITE_REG       0x20  // Define write command to register

#define RG_CONFIG          0x00  // 'Config' register address
#define RG_EN_AA           0x01  // 'Enable Auto Acknowledgment' register address


#define ModeBits 0b00111100

uint8_t SPIDir = 0 ;

void spiopen(void)
{
	SPIDir = SPIPORT.DIR ;
	SPIPORT.DIRCLR = (1<<SPIMI);
	SPIPORT.DIRSET = (1<<SPIMO) | (1<<SPISC) | (1<<SPISS) ; // Make sure slave select is an output
	SPIC.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_0_gc | SPI_PRESCALER_DIV64_gc;
}



void spiclose(void)
{
	SPIC.CTRL = 0;
	SPIPORT.DIR = SPIDir ;
	//SPIPort.DIRSET = (1<<SPIMI);
}

uint8_t spi( uint8_t val)
{	
	spiopen();
	SPIC.DATA = val ;
	while(!(SPIC.STATUS & (1<<7)));
	spiclose();
	return SPIC.DATA;
}

uint8_t NRFWriteRegister(uint8_t reg, uint8_t value)
{
	uint8_t status;
	spiopen();
	CSLOW ;
		// Select register
		status = spi(CMD_WRITE_REG+reg);

		// Write value to it
		spi(value);
	CSHIGH ;
	spiclose();
	return(status);
}

uint8_t NRFReadRegister(uint8_t reg)
{
	uint8_t reg_val;
	spiopen();
	CSLOW ;
	// Select register to read from..
	spi(CMD_READ_REG+reg);
	// ..then read register value
	reg_val = spi(0);

	CSHIGH ;
	spiclose();
	return(reg_val);
}

void StartPWM(void)
{
	MotorPort.OUTCLR = (1<<MotorHighLeftBit) | (1<<MotorHighRightBit) ; // Turn off the high side
	MotorTimer.CTRLA = 0 ;
	MotorTimer.CTRLB = 3 ; // Single Slope Mode ;
	MotorTimer.CTRLE = (1<<6) | (1<<0) ;
	MotorTimer.CNT = 0 ;
	MotorTimer.PER = 1024 ;
	MotorTimer.CCA = 0 ;
	MotorTimer.CCB = 0 ;
	MotorTimer.CCC = 0 ;
	MotorTimer.CCD = 0 ;
	MotorTimer.CTRLA = 2 ; // Select clock/2  ** Comment this line and spi works!
	MCU.WEXLOCK = 1 ;
	WEXC.OUTOVDIS = ~((1<<MotorLowLeftBit) | (1<<MotorLowRightBit)) ;
	
}

volatile uint8_t b = 0 ;
volatile uint8_t c = 0 ;

int main(void)
{
	PORTA.DIR = 255 ;
	PORTC.DIR = 255 ;
	PORTC.DIRCLR = (1<<SPIMI) ;
	PORTD.DIR = 255 ;
	PORTA.OUT = 0 ;
	PORTC.OUT = 0 ;
	PORTD.OUT = 0 ;
	
	PORTA.PIN0CTRL = (3<<3) ;
	PORTA.PIN1CTRL = (3<<3) ;
	PORTA.PIN2CTRL = (3<<3) ;
	PORTA.PIN3CTRL = (3<<3) ;
	PORTA.PIN4CTRL = (3<<3) ;
	PORTA.PIN5CTRL = (3<<3) ;
	PORTA.PIN6CTRL = (3<<3) ;
	PORTA.PIN7CTRL = (3<<3) ;
	
	PORTC.PIN0CTRL = (3<<3) ;
	PORTC.PIN1CTRL = (3<<3) ;
	PORTC.PIN2CTRL = (3<<3) ;
	PORTC.PIN3CTRL = (3<<3) ;
	PORTC.PIN4CTRL = (3<<3) ;
	PORTC.PIN5CTRL = (3<<3) ;
	PORTC.PIN6CTRL = (3<<3) ;
	PORTC.PIN7CTRL = (3<<3) ;
	
	PORTD.PIN0CTRL = (3<<3) ;
	PORTD.PIN1CTRL = (3<<3) ;
	PORTD.PIN2CTRL = (3<<3) ;
	PORTD.PIN3CTRL = (3<<3) ;
	PORTD.PIN4CTRL = (3<<3) ;
	PORTD.PIN5CTRL = (3<<3) ;
	PORTD.PIN6CTRL = (3<<3) ;
	PORTD.PIN7CTRL = (3<<3) ;
	
	SPIC.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_0_gc | SPI_PRESCALER_DIV64_gc;
	CSHIGH ;

    StartPWM();

	_delay_ms(200) ;
	
	NRFWriteRegister(RG_CONFIG,ModeBits);
	NRFWriteRegister(RG_EN_AA,3);                // Enable auto ack on pipes 0 and 1


    while(1)
    {
	    b = NRFReadRegister(RG_EN_AA) ; 
		c = b ;  // b should contain a 3.

    }
}

Attachment(s): 

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Oh. The OUTOFDIS register is double change protected. They want to be SURE you don't make it work by accident.

	CCP = 0xD8 ;
	MCU.WEXLOCK = 0 ;
	WEXC.OUTOVDIS = ((1<<MotorLowLeftBit) | (1<<MotorLowRightBit)) ;

(Still doesn't read back a 3.)

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Oh. I forgot I was trying inverting the OUTOVDIS bits.

But now STUDIO had decided I'm done working and won't connect to the target... Rebooting.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

So now the program sets the WEXC.OUTOVDIS like so:

	CCP = 0xD8 ;
	MCU.WEXLOCK = 0 ;
	WEXC.OUTOVDIS = ~((1<<MotorLowLeftBit) | (1<<MotorLowRightBit)) ;

(strangely, copy/paste now works)

But the SPI still doesn't work.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

You could ask Atmel if they can provide a working example using the OUTOVDIS register. They may find that they do actually have a bug...

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

Should I start another case?

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

I'd just email them back, see what response you get. Presumably they have some test code already.

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

Will do. Out today, so it might be delayed.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Thanks, I appreciate your effort.

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

I just experienced this same issue as well last week on a Xmega8E5 and no where in the manual under TC4/5 that I've seen does it mention the entire port being overridden, unless I'm reading it wrong. Oddly enough, I had a nRF24L01 module hanging off mine as well. It states that the port pins will be overridden, but I would assume they mean just the pin that's enabled? Everything would be fine until I started my timers with OC4A and OC4B enabled, then MISO on PC6 would be pulled low. Finally switched to TCD5 for my PWM and everything worked fine. Thanks for running this down with Atmel and posting about it. Was driving me crazy last week!

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

I was going to look quick to see if there are any appnotes or suchlike for this WEX, but atmel.com isn't answering.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

There's a pdf with some information on this WEX gizmo here: http://www.atmel.com/Images/Atme...

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Got another response from Atmel:

Quote:

Hello Tom,

It seems you have configured the SPI first and then enabled the timer module. Could you please try to enable the timer first, set the necessary values in OUTOVDIS register and then enable SPI module?

Also it seems you are enabling and disabling the SPI module for every read and write. Could you please try to enable the SPI module only once and then perform all the read and write?

So, I've spent some time playing with the order of things.

1. Original Order: Initialize SPI. Start TCC4. Set OUTOVDIS. Initialize SPI when needed. -- Works only if I don't actually start TCC4 running.

2. Start Timer. Set OUTOVDIS. Initialize SPI. -- Works only if I don't actually start TCC4 running.

3. Start Timer. Set OUTOVDIS. Initialize SPI. Don't reinitialize SPI. -- Works only if I don't actually start TCC4 running.

4. Set OUTOVDIS. Start Timer. Initialize SPI. -- Works only if I don't actually start TCC4 running.

He also closed my case again. Thank you.

So, as far as I can tell, there's something wrong with TCC4 or the WEX that disrupts SPI module trying to use the top 4 bits of PORTC.

Suggestions, other than don't do this?

"Doc, it hurts when I do this."

The current code:

/*
 * xMegaE5Conflict.c
 *
 * Created: 7/21/2014 3:52:46 PM
 *  Author: Tom
 */ 

/*
 * NRFModuleTestE5.cpp
 *
 * Created: 7/21/2014 10:44:33 AM
 *  Author: Tom
 */ 

#include 
#define F_CPU 2000000ul
#include 

#define SPIPORT PORTC
#define SPIMO 7
#define SPIMI 6
#define SPISC 5
#define SPISS 6

#define RFcsPORT PORTC
#define RFcsn 4

#define CSHIGH RFcsPORT.OUTSET = (1<<RFcsn)
#define CSLOW RFcsPORT.OUTCLR = (1<<RFcsn)

#define MotorPort PORTC

#define MotorLowLeftBit 3
#define MotorLowRightBit 0

#define MotorHighLeftBit 2
#define MotorHighRightBit 1

#define MotorTimer TCC4
#define MotorLeftSpeed CCD
#define MotorLeftCTRLE (1<<6)
#define MotorRightSpeed CCA
#define MotorRightCTRLE (1<<0)

// NRF24L01+ Gibberish

#define CMD_READ_REG        0x00  // Define read command to register
#define CMD_WRITE_REG       0x20  // Define write command to register

#define RG_CONFIG          0x00  // 'Config' register address
#define RG_EN_AA           0x01  // 'Enable Auto Acknowledgment' register address


#define ModeBits 0b00111100

uint8_t SPIDir = 0 ;

void spiopen(void)
{
	SPIDir = SPIPORT.DIR ;
	SPIPORT.DIRCLR = (1<<SPIMI);
	SPIPORT.DIRSET = (1<<SPIMO) | (1<<SPISC) | (1<<SPISS) ; // Make sure slave select is an output
	SPIC.CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_0_gc | SPI_PRESCALER_DIV64_gc;
}



void spiclose(void)
{
	SPIC.CTRL = 0;
	SPIPORT.DIR = SPIDir ;
	//SPIPort.DIRSET = (1<<SPIMI);
}

uint8_t spi( uint8_t val)
{	
	//spiopen();
	SPIC.DATA = val ;
	while(!(SPIC.STATUS & (1<<7)));
	//spiclose();
	return SPIC.DATA;
}

uint8_t NRFWriteRegister(uint8_t reg, uint8_t value)
{
	uint8_t status;
	//spiopen();
	CSLOW ;
		// Select register
		status = spi(CMD_WRITE_REG+reg);

		// Write value to it
		spi(value);
	CSHIGH ;
	//spiclose();
	return(status);
}

uint8_t NRFReadRegister(uint8_t reg)
{
	uint8_t reg_val;
	//spiopen();
	CSLOW ;
	// Select register to read from..
	spi(CMD_READ_REG+reg);
	// ..then read register value
	reg_val = spi(0);

	CSHIGH ;
	//spiclose();
	return(reg_val);
}

void StartPWM(void)
{
	MotorPort.OUTCLR = (1<<MotorHighLeftBit) | (1<<MotorHighRightBit) ; // Turn off the high side
	MotorTimer.CTRLA = 0 ;
	MotorTimer.CTRLB = 3 ; // Single Slope Mode ;
	MotorTimer.CTRLE = (1<<6) | (1<<0) ;
	MotorTimer.CNT = 0 ;
	MotorTimer.PER = 1024 ;
	MotorTimer.CCA = 0 ;
	MotorTimer.CCB = 0 ;
	MotorTimer.CCC = 0 ;
	MotorTimer.CCD = 0 ;
	//MotorTimer.CTRLA = 2 ; // Select clock/2  ** Comment this line and spi works!
	
}

void BlockTCC5(void)
{
	TCC5.CTRLA = 0 ;
	TCC5.CTRLB = 0 ;
	TCC5.CTRLE = 0 ;
}

volatile uint8_t b = 0 ;
volatile uint8_t c = 0 ;

int main(void)
{
	PORTA.DIR = 255 ;
	PORTC.DIR = 255 ;
	PORTC.DIRCLR = (1<<SPIMI) ;
	PORTD.DIR = 255 ;
	PORTA.OUT = 0 ;
	PORTC.OUT = 0 ;
	PORTD.OUT = 0 ;
	
	PORTA.PIN0CTRL = (3<<3) ;
	PORTA.PIN1CTRL = (3<<3) ;
	PORTA.PIN2CTRL = (3<<3) ;
	PORTA.PIN3CTRL = (3<<3) ;
	PORTA.PIN4CTRL = (3<<3) ;
	PORTA.PIN5CTRL = (3<<3) ;
	PORTA.PIN6CTRL = (3<<3) ;
	PORTA.PIN7CTRL = (3<<3) ;
	
	PORTC.PIN0CTRL = (3<<3) ;
	PORTC.PIN1CTRL = (3<<3) ;
	PORTC.PIN2CTRL = (3<<3) ;
	PORTC.PIN3CTRL = (3<<3) ;
	PORTC.PIN4CTRL = (3<<3) ;
	PORTC.PIN5CTRL = (3<<3) ;
	PORTC.PIN6CTRL = (3<<3) ;
	PORTC.PIN7CTRL = (3<<3) ;
	
	PORTD.PIN0CTRL = (3<<3) ;
	PORTD.PIN1CTRL = (3<<3) ;
	PORTD.PIN2CTRL = (3<<3) ;
	PORTD.PIN3CTRL = (3<<3) ;
	PORTD.PIN4CTRL = (3<<3) ;
	PORTD.PIN5CTRL = (3<<3) ;
	PORTD.PIN6CTRL = (3<<3) ;
	PORTD.PIN7CTRL = (3<<3) ;
    BlockTCC5() ; // Just to be sure TCC5 isn't doing something.
	
    StartPWM();
	WEXC.OUTOVDIS =  0xf0 ; // Only allow wex to play with bottom 4 bits of port c
	c = WEXC.OUTOVDIS ;
		
	spiopen();
	CSHIGH ;

	_delay_ms(200) ;
	
	NRFWriteRegister(RG_CONFIG,ModeBits);
	b = NRFReadRegister(RG_CONFIG) ;
	c = ModeBits ;
	NRFWriteRegister(RG_EN_AA,3);                // Enable auto ack on pipes 0 and 1


    while(1)
    {
	    b = NRFReadRegister(RG_EN_AA) ; 
		c = b ;  // b should contain a 3.

    }
}

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Looks like the OUTOVDIS register is broken. Unless Atmel can provide some example code that works I'd say it's a confirmed silicon bug.

I wish people from Atmel would post here so we can discuss things like this.

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

I'm thinking you're right, but most of my "cases" have been bonehead programmer mistakes.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

I don't know why, but Atmel don't seems to want to solve there own problems, back 15 years ago I reported an error on a 8051 part, and all I got back was newer chips so I could try them (as you can guess they didn't work either )
and Atmel have never made an errata on it :(

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

They confirmed but then ignored my bug report about reading the production signature row with EEPROM power saving enabled. For some reason half the unique ID bytes are in the EEPROM "area" and get powered down when you enable EEPROM power saving, but the other half are in some other memory area and are fine.

Maybe we should start our own errata.

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

Freaks HAS an errata thread. Might need a whole section organized by parts effected.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

A wiki would be ideal.

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

Just received from Atmel:

Quote:

It seems there is a silicon bug with the OUTOVDIS register which doesn’t makes the override disable functionality to work properly. We have already reported this to the concerned team.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Amazeballs.

Thanks for your hard work and perseverance Torby.

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

Hi Torby,
I was assigning pins in preparation for doing a board layout with a 32E5, had never used an xMega before, so I was worried when I saw this thread. I just now did a test with 6 different PWMs spread full on port C and 2 more on port D. Got that working and tested all the output matrix settings and the output overrides. Everything worked as it should. Then I built the USART example from Atmel Studio, tested both USARTs and their alternate pinouts, that worked so I added my PWM code to it, overrode the pins needed for USART, and I have 8 PWM spread on two ports and the USART all working together. I have not tested SPI or TWI yet, they will be a little more work. But based on these tests I am hopeful the chip works just like it says on the box. I am going to finish the PC board before writing any more code. Best regards, Bob

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

Well, knock me down with a feather. I have an E5 Xplained board with an SPI display so I built the simplest project I could for that, worked fine, added PWM code, excluded the SPI pins and BANG, the display ceases to function.

My apologies. Bob

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

Arrgh, now I've really started off well here, haven't I? Those of you who don't yet think I'm a complete idiot, read on :)

Realized I'd set all the pin directions properly but hadn't changed the pin overrides from the USART pins to the SPI pins. Fixed that and now PWM on the lower half of port C works and the display on the top nibble works too. Can send the project if you want to verify, but I warn you, I used ASF style and naming for everything. -Bob

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

Frosting, what revision is your chip?

I tested it on an E5 rev B here and it definitely doesn't work. Can you post some code?

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

mojo-chan wrote:

Frosting, what revision is your chip?

I tested it on an E5 rev B here and it definitely doesn't work. Can you post some code?

Mine is a Rev B also. Here is the code I added to the OLED example for the E5-Xplained board. I used Atmel Studio 6.2 with GCC.

// this goes into init.c
ioport_set_pin_dir(PWM0, IOPORT_DIR_OUTPUT);
ioport_set_pin_dir(PWM1, IOPORT_DIR_OUTPUT);
ioport_set_pin_dir(PWM2, IOPORT_DIR_OUTPUT);
ioport_set_pin_dir(PWM3, IOPORT_DIR_OUTPUT);
//ioport_set_pin_dir(PWM4, IOPORT_DIR_OUTPUT);
//ioport_set_pin_dir(PWM5, IOPORT_DIR_OUTPUT);
//ioport_set_pin_dir(PWM6, IOPORT_DIR_OUTPUT);
//ioport_set_pin_dir(PWM7, IOPORT_DIR_OUTPUT);
ioport_set_pin_dir(PWM8, IOPORT_DIR_OUTPUT);
ioport_set_pin_dir(PWM9, IOPORT_DIR_OUTPUT);

// this goes in conf_board.h
#define PWM0        IOPORT_CREATE_PIN(PORTC, 0)
#define PWM1        IOPORT_CREATE_PIN(PORTC, 1)
#define PWM2        IOPORT_CREATE_PIN(PORTC, 2)
#define PWM3        IOPORT_CREATE_PIN(PORTC, 3)
//#define PWM4        IOPORT_CREATE_PIN(PORTC, 4)
//#define PWM5        IOPORT_CREATE_PIN(PORTC, 5)
//#define PWM6        IOPORT_CREATE_PIN(PORTC, 6)
//#define PWM7        IOPORT_CREATE_PIN(PORTC, 7)
#define PWM8        IOPORT_CREATE_PIN(PORTD, 4)
#define PWM9        IOPORT_CREATE_PIN(PORTD, 5)

//this is in SSD1306_example.c after display initial screen before the scrolling loop
    // Test PWM at the same time
    tc45_enable(&TCC4);
    tc45_set_wgm(&TCC4, TC45_WG_SS);        // single slope PWM
    tc45_write_period(&TCC4, 1000);        // the period of the timer
    tc45_write_clock_source(&TCC4, TC45_CLKSEL_DIV1_gc);
    tc45_write_cc(&TCC4, TC45_CCA, 500);   // period + 1 = 100% duty, 0 = 0% duty
    tc45_write_cc(&TCC4, TC45_CCB, 900);
    tc45_write_cc(&TCC4, TC45_CCC, 800);
    tc45_write_cc(&TCC4, TC45_CCD, 700);
    //    tc45_enable_cc_channels(&TCC4,TC45_CCACOMP);
    tc45_enable_cc_channels(&TCC4,(TC45_CCACOMP | TC45_CCBCOMP | TC45_CCCCOMP | TC45_CCDCOMP));
    //    tc45_wex_set_otmx(&WEXC, WEX_OTMX_4);   // wex spreads pwm to 7 pins
    //    tc45_wex_set_otmx(&WEXC, WEX_OTMX_3);   // pwm to 8 pins
    //    tc45_wex_set_otmx(&WEXC, WEX_OTMX_2);   // split with TC5
    tc45_wex_set_otmx(&WEXC, WEX_OTMX_1);   // all 4 TC4 counters on port bottom plus TC5 A and B doubled on high nibble

    tc45_wex_set_output_override(&WEXC, 0xF0); // a "1" disables

    // additional PWM on TCC5 and TCD5
    tc45_enable(&TCD5);
    tc45_set_wgm(&TCD5, TC45_WG_SS);        // single slope PWM
    tc45_write_period(&TCD5, 10000);        // the period of the timer
    tc45_write_clock_source(&TCD5, TC45_CLKSEL_DIV1_gc);
    tc45_write_cc(&TCD5, TC45_CCA, 100);   // period + 1 = 100% duty, 0 = 0% duty
    // Additional PWM on D5
    tc45_write_cc(&TCD5, TC45_CCB, 200);
    // two channels
    tc45_enable_cc_channels(&TCD5, (TC45_CCACOMP | TC45_CCBCOMP));  // documentation is wrong, says TC45_CCA not TC45_CCACOMP

    tc45_enable(&TCC5);
    tc45_set_wgm(&TCC5, TC45_WG_SS);        // single slope PWM
    tc45_write_period(&TCC5, 5000);        // the period of the timer
    tc45_write_clock_source(&TCC5, TC45_CLKSEL_DIV1_gc);
    tc45_write_cc(&TCC5, TC45_CCA, 200);   // period + 1 = 100% duty, 0 = 0% duty
    tc45_write_cc(&TCC5, TC45_CCB, 4800);
    tc45_enable_cc_channels(&TCC5, (TC45_CCACOMP | TC45_CCBCOMP));

P.S. make sure you use the wizard to add T4/T5 support and also add #include if it doesn't automatically. -Bob

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

Um. So how do you tell which revision you have?

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Torby wrote:

Um. So how do you tell which revision you have?

Hi Torby,
Tools->Device Programming->Device Information
Cheers, Bob

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

Oh. I was looking for something printed on the part.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Hello
I think it was me who was mentioned in starting post. My demo code is here:

https://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=142265&start=0&postdays=0&postorder=asc&highlight=

I haven't found any solution yet, maybe have to go back to D-series.

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

Torby,
I am new to Xmega and have been having problems with SPI. May I ask, why are SPIMI and SPISS both defined as 6?

Quote:

#define SPIMO 7 
#define SPIMI 6 
#define SPISC 5 
#define SPISS 6

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

That looks like a typo to me. In an E5, you want SS to be 4.

   #define SPIPort PORTC
   #define SPIMO   7
   #define SPIMI   6
   #define SPISC   5
   #define SPISS   4

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

After this battle, this photo seemed appropriate, but I had misplaced it.

 

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

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

Seems like this battle result is finally shown on the newest E5 datasheet errata rev. B list:

 

"When Waveform generation is enabled on PORT C Timers, Automatic port override of peripherals other than Tc may not work even though the pin is not used as waveform output pin."

 

Workaround: none

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

Thanks Inspector, and thank you Torby for you hard work tracking this down and reporting it.

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

I don't know if anyone is still following this thread, but I have some more information to add.

The automatic port override is definitely broken. The reason "frosting" got it working, is that the problem appears to be on the peripheral input, MISO pin in the case of SPI. The output MOSI & SCK work OK. The demo on the E5 Xplained board with frosting's changes work because the SPI OLED display only uses the SPI in one direction. It is the MISO pin(input) that has the problem.

 

I was hoping that this might be specific to SPI, so I tried using the USART on port C in SPI mode, and remapped to the higher pin numbers. And with the timers in wave generation mode, I was able to send and receive through the USART/SPI. 

So this might be an acceptable workaround. I have had several hundred PCB's made so I'm really hoping this will work.

 

Could anyone please confirm this

 

Colin

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

Hmm. Hadn't thought of that. Wonder when I can try it.

 

Here's what I did:

 

I did my pwm with TCD4, not TCC4 and generated interrupts when the pin should go high or low. The interrupt handlers then set the pin on PORTC high and low accordingly. 

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.

Last Edited: Wed. Jul 29, 2015 - 02:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello Colin,

 

I am struggling with the precise issue you described in your 28Jul15 post.  Can you confirm that you were successful in using the PortC USART in SPI mode to successfully send AND receive SPI communication while TCC4 was in active wave generation mode on the lower four pins of the port?  I anxiously look forward to your reply.

 

J Gordon

J Gordon

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

Haven't tried that. Instead I moved the PWM functions to port D. Time for playing with these things has been severely restricted lately.

 

"We trained hard... but it seemed that every time we were beginning to form up into a team, we would be reorganized. I was to learn later in life that we tend to meet any new situation by reorganizing. And a wonderful method it can be of creating the illusion of progress while producing confusion, inefficiency and demoralization." Petronius Arbiter, approx. 2000 years ago.