ATmega1284P USART as MSPI problem

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

Good morning,

Just set the scene... I am using the USART1 module on a ATmega1284P as MSPI to write/read to/from a 24bit DAC.

I am using both data SDO/SDI lines on the mega (actually labelled TXD1/RXD1 in USART terms). The DAC has one data line (SDIO - SPI Data IN/OUT) so I use a switch (controlled by PIND6) to flick from mega SDO to SDI after I write a command byte to the DAC to then receive three bytes from the DAC internal register.

After initializing the USART as MSPIM I can (successfully) command the DAC to reset and calibrate. But I run into problems when I then try to read the gain calibration 3-byte value from the DAC.

I have two problems.

1) The first time I write then read data from the DAC, 0x00 is returned from all three calls to SPI_receive().
2) The second (and as many subsequent times as I wish) the first two receive bytes are valid DAC data but the third SPI_receive() call returns zero... every time!!!

Here is the code... (and please don't choke at the amount of _delay_us(); calls!!!)


unsigned char SPI_transmit(unsigned char byte)
{
	while(!(UCSR1A & (1 << UDRE1)))
		;
	
	UDR1 = byte;

	return byte;
}

unsigned char SPI_receive(void)
{
	while(!(UCSR1A & (1 << UDRE1)))
		;
	
	UDR1 = 0x00;

	while(!(UCSR1A & (1 << RXC1)))
		;
	
	return UDR1;
}

unsigned char dac_command()
{
	//..initialise variables
	//reset the DAC
	//calibrate the DAC

	//read calibration 24bit register value from DAC 	
	//*1st attempt...
	dac_comms(WRITE);			//write to DAC "PORTD &= ~(1 << PIND6);"
	_delay_us(10);
	
	byte = CMD_RD_REG_FCR;	//sends command to read calibrated gain
	SPI_transmit(byte);
	_delay_us(80);				//waits for transmission of 8bits (128 KBAUD)			
	
	dac_comms(READ);			//flick switch to read from DAC
	_delay_us(10);

	byte_MSB = SPI_receive();	//receive b23..b16 *but returns 0x00!!
	byte_MID = SPI_receive();	//receive b15..b8  *but returns 0x00!!
	byte_LSB = SPI_receive();	//receive b7..b0   *but returns 0x00!!
	_delay_us(200);				//waits for transmission of 24bits
	

	dac_comms(WRITE);			//flick switch back to write to DAC
	_delay_us(10);
	



	//*1st attempt did not work so have to do it again!?!?!?!?!
	//*2nd attempt..
	byte = CMD_RD_REG_FCR;		//sends command to read calibrated gain
	SPI_transmit(byte);
	_delay_us(80);			//waits for transmission of 8bits			
	
	dac_comms(READ);			//flick switch to read from DAC
	_delay_us(10);

	byte_MSB = SPI_receive();	//receive b23..b16 *returns a valid value from DAC
	byte_MID = SPI_receive();	//receive b15..b8  *returns a valid value from DAC
	byte_LSB = SPI_receive();	//receive b7..b0   *returns 0x00!!
	_delay_us(200);			//waits for transmission of 24bits
}

I have used an oscilloscope to monitor all 4 lines (the switch, SCLK, SDI and SDO) and for both the 1st and 2nd attempts all 24bits on the SDI line comming into the AVR has a valid 24bit calibration byte.

I have respected the timing diagram as best I could according to the ATmega1284P and DAC datasheet but I am still at a loss on what is going on! I have searched on AVR freaks and even did a Google search but it seems that not a lot of people use the USART as MSPI. Or they do and they have no problems at all!

As a final note, the PCB has already been manufactured and the boards populated so I cant use the dedicated SPI module. And I tried to implement the DAC read code after the boards were manufactured.. just in case you were wondering!!!

And while I am writing this, I have just realised that I have not tried communicating with the DAC using the dedicated SPI module.

So while I experiment with that, can somebody out there shed some light on this please?

Thanks in advance. (I have attached a oscilloscope capture for interest)

Attachment(s): 

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

Any of those devices with a single SDIO pin only perform input or output at any one time. So you can connect both TXD and RXD pins to the SDIO.

When you want to send, you enable the TXD pin. When you receive, you have the TXD in 3-state. You should not need any external hardware control. If you look at the SDIO protocol, you tend to have at least one bit space to allow for time to put TXD in 3-state.

David.

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

Thankyou David,

I have hard wired TXD and RXD together to DAC SDIO.

So how do you go about putting the TXD into Tri-state mode without disabling the SPI transmitter?

Because when I do this after the transmission of the 8 bit command (and respecting your comment about leaving 1 BAUD cycle to switch)

UCSR1B &= ~(1 << TXEN1);

Nothing works anymore. TXD needs to be set to enable the SPI Clock, right?

Unless I have done the wrong thing?

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

My functions. I have only used them in regular SPI. The buffered transmit makes a massive difference to throughput.

void spi_config(unsigned char master, unsigned char mode, unsigned char speed)
{
    PORTB |= (1<<SS);     // pull-up i.e. disabled
    DDRB |= (1<<SS);
    UBRR0 = 0;
    XCK0_DDR |= (1<<XCK0);
    UCSR0C = (3<<UMSEL00)|mode;
    UCSR0B = (1<<RXEN0)|(1<<TXEN0);
    UBRR0 = speed;
}

unsigned char spi(unsigned char dat)
{
	while ((UCSR0A & (1<<UDRE0) == 0) ;
	UDR0 = dat;
	while ((UCSR0A & (1<<RXC0)) == 0) ;
	return UDR0;
}

You can enable / disable XCK pin via its DDR.

The Xmega can disable the TXD pin by its DDR. It looks as if the humble Mega can't do this. Use a 1k0 resistor between TXD and SDIO.

I will try this for myself later this afternoon.

If this does not work, you will have to resort to bit-banging.

David.

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

Thanks for the resistor tip. I will give a go.

Regards
Joe

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

No, I have not tried it because I realise I do not have any devices to test it on.

Normally it is the SPI chip that you link DI, DO and connect to a single pin on the MCU.
You have a single pin on the SPI chip, and two pins on the MCU.

With the regular SPI peripheral, you can disable SCK or MOSI at will by just using their respective DDR bits.
The USART does not seem to be so willing!

All the same, I reckon a resistor should do a similar job.

David.