NOTES ON MOBO V4.3 I2C INTERFACE COMMANDS

Robert Brown, M0RZF, 20120212

The MoBo V4.3 has extensive ancillary facilities that place it in a class ahead of any other DC-SDR system. VSWR measurement/protection, PA bias current control/calibration, and PA temperature monitoring are the headline features.

There are five devices on the I2C bus:-

* Si570 clock generator
* AD5301 8-bit DAC
* AD7991 quad channel 12-bit ADC
* TMP100 temperature sensor
* PCF8574 I/O port

This document is intended for programmers of controllers other than the SDR-Widget, or the original MoBo controller, to add support for their designs. It is assumed other designs have basic support for the Si570. Therefore programming the Si570 is not covered in this document. Controller code for the Si570 alone has been created on several platforms before.

The SDR-Widget supports audio I/O but a large part of the code was ported from the original MoBo controller. The target devices were originally the Atmel AT90USB162 for the MoBo, and AT32UC3A3256 for the widget. The compilers for the two were so similar that in those cases only small code changes were required.

The software has a separate file dedicated to each device. The files contain initialisation functions, and an interface to command each device. Of course the software for the widget is under a GPL licence, so there is no problem with reproducing any of it here. Much of the original code was written by Loftur Jonasson, TF3LJ.

Code snippets presented here are for the GNU Compiler Collection (GCC). I have built the code under a version of AVR32Studio, which looks to be no longer developed. However, the code is actually compiled by GCC, so other IDEs can be used. AVR32Studio can be downloaded for free.

The I2C addresses for each device are:-
AD5301 = 0x0D
AD7991 = 0x28
TMP100 = 0x4E
PCF8574 = 0x3F

The addresses can be changed on the MoBo using DIL switches. This facility was only used during the early stages of MoBo development. All the switches are set to off for the current MoBo software, which renders the switchable address pins logic high. Each device acts as an I2C slave, there are no bus masters except the controller itself.

Typical I2C Command Sequences

During startup of the controller code, a write (with value zero) or a read of each device should be done. Any low level routines should provide evidence of an I2C acknowledge, to confirm the devices are working. Some means of indicating I2C communications failure should be considered. Missing from the sequences below are any start/ack instruction for particular hardware.

Set PA bias to zero
I2C transmit 0x0D,0x00,0x00	
Set PA bias to midrange (0x80)
I2C transmit 0x0D,0x08,0x00	
Set PA bias to full-scale
I2C transmit 0x0D,0x0F,0xF0	
Subroutine: uint8_t ad5301(0x0D, uint8_t value); Return value indicates success/failure	

Read temperature sensor
I2C transmit 0x4E,0x00,0x4E	
Receive temperature 0 celsius
I2C receive 0x00,0x00	
Receive temperature 128 celsius
I2C receive 0x7F,0xF0	
Receive temperature 25 celsius
I2C receive 0x19,0x00	
Subroutine: uint8_t tmp100_read(0x4E); Return value indicates success/failure	

Key to transmit (first check temperature within limits)
I2C transmit
0x0D,0x0H,0xL0 (HL is bias value)
0x3F,0b0??????? (bit 8 is low. The other 7 bits are tracked in a global pcf8574_mobo_data_out variable)	

Return to receive
I2C transmit
0x0D,0x00,0x00 (PA bias off)
0x3F,0b1??????? (bit 8 is high. The other 7 bits are tracked in global pcf8574_mobo_data_out variable)	

Set filter bank 0 (normally top band)
I2C transmit 0x3F,0b?????000 (Logic OR 3 LSBs = 0 with pcf8574_mobo_data_out)	
Subroutine: uint8_t pcf8574_out_byte(uint8_t i2c_address, uint8_t data);	

Set filter bank 7 (normally 10m)
I2C transmit 0x3F,0b?????111 (Logic OR 3 LSBs = 1 with pcf8574_mobo_data_out)	

Read analog values
I2C transmit 0x28	
I2C receive 0x0H,0xLL (where HLL is 12 bit value representing PA current)	
I2C receive 0x1H,0xLL (where HLL is 12 bit value representing forward power)	
I2C receive 0x2H,0xLL (where HLL is 12 bit value representing reverse power)	
I2C receive 0x3H,0xLL (where HLL is 12 bit value representing system supply voltage)	
Subroutine: uint8_t ad7991_poll(uint8_t i2c_address);	

The analog values are read back every 100ms on transmit but every 10s on receive. This greatly reduces noise generated by the bus.

Using the MoBo Control (Python) or CfgSDR (windows.exe) programs on a PC, various calibration sequences can be performed.
ADD SEQUENCES

I2C Initialisation and Low-level I/O

Initialise the I2C interface as bus master, and at 400k bits/sec. The four slave devices are used at their default settings - no initialisation bytes need to be sent.

Here are two suggested interfacing functions to mediate data between existing low level I2C access routines, and the device specific routines. Depending on requirements of existing code, this abstraction may not be required.

uint8_t twi_write_out(uint8_t i2c_address, uint8_t *payload, uint8_t size)
{
	uint8_t	status;

	// Wait for I2C port to become free by checking semaphore flag(s)
	CODE GOES HERE

	twi_package_t packet =
	{
		.chip = i2c_address,
		.addr = 0x00,
		.addr_length = 0,
		.buffer = (void*)payload,
		.length = size
	};
	// Returns TWI_SUCCESS (0) on successful write
	status = twi_master_write(MOBO_TWI, &packet);

	// Release I2C port in semaphore flag(s)
	CODE GOES HERE

	return status;
}

uint8_t twi_read_in(uint8_t i2c_address, uint8_t *data_to_return, uint8_t size)
{
	uint8_t status;

	// Wait for I2C port to become free by checking semaphore flag(s)
	CODE GOES HERE

	twi_package_t packet =
	{
		.chip = i2c_address,
		.addr = 0,
		.addr_length = 0,
		.buffer = data_to_return,
		.length = size
	};

	// Returns TWI_SUCCESS (0) on successful read
	status = twi_master_read(MOBO_TWI, &packet);
	// Release I2C port in semaphore flags
	CODE GOES HERE

	return status;
}

AD5301

This 8-bit DAC from Analog Devices controls bias of the RD16HHF1 MOSFET, which is the RF Power Amplifier. It is used at default settings, and written to by one function:

uint8_t ad5301(uint8_t i2c_address, uint8_t value)
{
	uint8_t data_out[2];

	data_out[0] = (value & 0xf0)>>4;		// Move upper 4 bits into lower 4 bits
	data_out[1] = (value & 0x0f)<<4;		// Move lower 4 bits into upper 4 bits

	return (twi_write_out(i2c_address, data_out, sizeof(data_out)));
}

The above function is called by the taskMoBoCtrl.c code at PA_Bias() to do one of three operations:
1. Set zero bias at startup.
2. Switch to Hi/Lo bias on Tx, and zero on Rx.
2. Reduce bias during high VSWR situation.
3. Calibrate bias settings using the PA current reading of the AD7991.

There is nothing ever read back from the AD5301.

AD7991

This tiny SOT23-8 device from Analog Devices is used at default settings, and reads the following voltages sequentially:

1. Current to the RD16HHF1 MOSFET
2. Forward power (from PWR/SWR board)
3. VSWR returned power (from PWR/SWR board)
4. PSU supply voltage to the whole board

Each of the four values are exposed in a global array:
extern uint16_t ad7991_adc[4];
enum (AD7991_PA_CURRENT, AD7991_POWER_OUT, AD7991_POWER_REF, AD7991_PSU_VOLTAGE);

/* Poll the AD7991 4 x ADC chip
 *
 *  This function reads all four A/D inputs and makes the data available in four
 *  global variables, ad7991_adc[4]
 *
 * \retval I2C status.
 */
uint8_t ad7991_poll(uint8_t i2c_address)
{
	uint8_t i, status;
	uint8_t raw_read[8];

	status = twi_read_in(i2c_address, raw_read, sizeof(raw_read));

	// Write left adjusted into global var uint16_t ad7991_adc[4]
	// This is probably not required in alternative hardware
	for (i = 0; i<8; i+=2)
	{
		if ((raw_read[i]>>4) < 4)			// If data not garbled
			ad7991_adc[raw_read[i]>>4] = 0x100*(((raw_read[i] & 0x0f)<<4)
				+((raw_read[i+1] & 0xf0)>>4)) + ((raw_read[i+1] & 0x0f)<<4);
	}

	return status;
}

Being a 12-bit ADC, there are two bytes for each value. The chip defaults to reading 4 values in sequence.

ad7991_poll() is called as a task, every 10s on Rx and every 10mS on Tx. The raw values returned in the ad7991_adc[4] global array are scaled as appropriate, and used by the LCD display, and DG8SAQ commands.

TMP100

The temperature sensor from Texas Instruments measures the temperature of the RD16HHF1 heatsink. It is used at default settings. The system is returned to Rx mode if the temperature exceeds a threshold.

A global containing the last measured value is read from the TMP100 temperature sensor
int16_t	tmp100_data;

/* Read temperature from TMP100 device
 *
 * Returned temperature reading can be converted to degrees C, by using the formula:
 * temp = 128 / 32768 * tmp100().i;
 *
 * \retval I2C status.
 */
uint8_t tmp100_read(uint8_t i2c_address)
{
	uint8_t status;
	twi_package_t packet;

	xSemaphoreTake( mutexI2C, portMAX_DELAY );

	packet.chip = i2c_address;
	packet.addr = 0x00;
	packet.addr_length = 0;
	packet.buffer = &tmp100_data;
	packet.length = 2;

	status = twi_master_read(MOBO_TWI, &packet);

	// Release I2C port
	xSemaphoreGive( mutexI2C );

	//Suggest add code to shift temp100_data>>=4

	return status;
}

The read function is called every 10s on Rx and 10mS on Tx. The temperature sensor IC gives the reading in a 12-bit format, with zero as 0C.

PCF8574AT

An 8-bit I/O port manufactured by NXP. Opinion: I'm not sure why the PCF8474 was chosen over the cheaper/newer/smaller PCA9554?

The MoBo firmware has various compile-time options for the device, including fan control and band switching. The minimal functions for the MoBo itself are:

* A binary code at P0, P1, P2 representing one of 8 bands. Thus switched filter banks are available for each of the HF frequencies. The actual band edges can be programmed using MoBo control, or the CfgSDR program from Fred Krom (PE0FKO). The code is used to control an FST3251 analog switch. Hardware note - the PCF8574 has very weak pull up resistors internally.
* RxTx control line at P7. Low on Tx.
* The 3 remaining pins are used for various compile time options, which are just that, optional.

The I2C port is brought out of the MoBo hardware at several points. An expander board was available at one time, using another PCF8574 to perform low-pass filter switching for a transmitter.

/*! Write all 8 bits to the PCF8574
 *
 * \retval I2C status.
 * Used to get the current state of the output port
 *
uint8_t pcf8574_out_byte(uint8_t i2c_address, uint8_t data)
{
	uint8_t status;
	status = twi_write_out(i2c_address, &data, sizeof(data));
	return status;
}

/*! Read all 8 bits from the PCF8574
 *
 * \retval I2C status.
 * Used to read the input state of the 8-bit port
 *
uint8_t pcf8574_in_byte(uint8_t i2c_address, uint8_t *read_byte)
{
	uint8_t status;
	status = twi_read_in(i2c_address, read_byte, sizeof(*read_byte));
	return status;
}

/*! Set output bits in the PCF8574 built into to the Mobo 4.3
 *
 * \retval I2C status.
 * Used for setting the filter band, fan and PTT signals
 *
 */
uint8_t pcf8574_mobo_set(uint8_t i2c_address, uint8_t byte)
{
	uint8_t	status;
	// pcf_mobo_data_out contains the current output data on the builtin PCF8574 on the Mobo
	pcf8574_mobo_data_out = pcf8574_mobo_data_out | byte;			// Set bits
	status = pcf8574_out_byte(i2c_address, pcf8574_mobo_data_out);		// Write out to Mobo PCF8574
	return status;
}

/*! Clear output bits in the PCF8574 built into to the Mobo 4.3
 *
 * \retval I2C status.
 *
 * Used for clearing the filter band, fan and PTT signals
 *
uint8_t pcf8574_mobo_clear(uint8_t i2c_address, uint8_t byte)
{
	uint8_t	status;
	// pcf_mobo_data_out contains the current output data on the builtin PCF8574 on the Mobo
	pcf8574_mobo_data_out = pcf8574_mobo_data_out & ~byte;			// Clear bits
	status = pcf8574_out_byte(i2c_address, pcf8574_mobo_data_out);		// Write out to Mobo PCF8574
	return status;
}

Datasheets:

→ http://www.analog.com/static/imported-files/data_sheets/AD5301_5311_5321.pdf

→ http://www.analog.com/static/imported-files/data_sheets/AD7991_7995_7999.pdf

→ http://www.ti.com/lit/ds/symlink/tmp100.pdf

→ http://www.nxp.com/documents/data_sheet/PCF8574.pdf

Original MoBo V4.3 code (slightly outdated but still useful)

http://sites.google.com/site/lofturj/mobo4_3_firmware

SDR-Widget code homepage at Google Code:

http://code.google.com/p/sdr-widget/