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?