Shifting data from SAME70 via TWIHS

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

Hi,

 

I have an ATSAME70Q21 (144-pin) MCU on my board, which (among other things) is used to drive 3 TLC5971 LED driver ICs. The TLC5971s are essentially shift registers, they don't conform to a particular standard interface (although they can be considered SPI-like given their operating frequency etc.). I've previously driven these driver ICs from other platforms, bit banging the required sequence without issue. The process and construct of the data is very nicely demonstrated here (this example displays a sine wave pattern across the LEDs, but the header and data structure is the relevant/important part).

 

I'm new to the SAME70 platform, and new to the Atmel Studio/ASF environment altogether. I plan on writing most of the application code at the register level in the near future, but would like to make use of ASF for the time being to confirm various components of the design. As such, I'm trying to make the TWIHS example work in driving these TLC5971 ICs. As I mentioned above, these ICs aren't dependent on a particular interface, so a generic two-wire interface such as TWIHS on the SAME70 should be fine :)

 

The first of the TLC5971 ICs is connected to TWI1 on the SAME70 (TWD1 on pin 105 and TWCK1 on pin 109). All data is sent to the first IC; the first IC then shifts the data to the second and so on. The ASF TWIHS API to which I will refer below is here.

 

I've started with the Atmel Studio master TWIHS example project, and trimmed away the bits and pieces I don't need. This is what I've got so far:

 

#include "asf.h"
#include "stdio_serial.h"
#include "conf_board.h"

/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
extern "C" {
#endif
/**INDENT-ON**/
/// @endcond

/** EEPROM Wait Time */
#define WAIT_TIME   10
/** TWI Bus Clock 400kHz */
#define TWIHS_CLK     400000

#if (SAMV70 || SAMV71 || SAME70 || SAMS70)
/** TWI ID for simulated EEPROM application to use */
#define I2C_CHANNEL_TLC5971         ID_TWIHS1
/** TWI Base for simulated TWI EEPROM application to use */
#define I2C_BASE_TLC5971       TWIHS1
#endif

/**
 *  \brief Configure the Console UART.
 */
static void configure_console(void)
{
	const usart_serial_options_t uart_serial_options = {
		.baudrate = CONF_UART_BAUDRATE,
#ifdef CONF_UART_CHAR_LENGTH
		.charlength = CONF_UART_CHAR_LENGTH,
#endif
		.paritytype = CONF_UART_PARITY,
#ifdef CONF_UART_STOP_BITS
		.stopbits = CONF_UART_STOP_BITS,
#endif
	};

	/* Configure console UART. */
	sysclk_enable_peripheral_clock(CONSOLE_UART_ID);
	stdio_serial_init(CONF_UART, &uart_serial_options);
}

/**
 * \brief Application entry point for TWI EEPROM example.
 *
 * \return Unused (ANSI-C compatibility).
 */
int main(void)
{
	twihs_options_t opt;

	/* Initialize the SAM system */
	sysclk_init();

	/* Initialize the board */
	board_init();

    /* Initialize the console UART */
	configure_console();

	/* Configure systick for 1 ms */
	puts("Configure system tick to get 1ms tick period.\r");
	if (SysTick_Config(sysclk_get_cpu_hz() / 1000)) {
		puts("-E- Systick configuration error\r");
		while (1) {
			/* Capture error */
		}
	}

	/* Enable the peripheral clock for TWI */
	pmc_enable_periph_clk(I2C_CHANNEL_TLC5971);

	/* Configure the options of TWI driver */
	opt.master_clk = sysclk_get_peripheral_hz();
	opt.speed      = TWIHS_CLK;

    uint8_t packets[3][28];
    
    for (int i = 0; i < 3; i++) {
	long header = 0b10010100010 << (32-6-5);
	    
	Byte brightness = 0x33;
	brightness >>= 1;
	header |= brightness;
	header |= (long)brightness << 7;
	header |= (long)brightness << 7*2;

	packets[i][0] = header >> 8*3;
	packets[i][1] = header >> 8*2;
	packets[i][2] = header >> 8;
	packets[i][3] = header;
	    
	for (int j = 0; j < 12; j++) {
            int value = 127;
	    packets[i][j*2 + 4] = value >> 8;
	    packets[i][j*2 + 5] = value;		    
	}
    }

    if (twihs_master_init(I2C_BASE_TLC5971, &opt) != TWIHS_SUCCESS) {
	puts("-E-\tTWI master initialization failed.\r");
	while (1) {
		/* Capture error */
	}
    }
	
    while(1) {
	for (int k = 0; k < 28; k++) {
		twihs_write_byte(I2C_BASE_TLC5971, packets[0][k]);
	}
	for (int l = 0; l < 28; l++) {
		twihs_write_byte(I2C_BASE_TLC5971, packets[1][l]);
	}
	for (int m = 0; m < 28; m++) {
		twihs_write_byte(I2C_BASE_TLC5971, packets[2][m]);
	}
    }
}

/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
}
#endif
/**INDENT-ON**/
/// @endcond

As you can see, I've largely handled the data for each of the 3 TLC5971 ICs individually for the time being. I had initially tried using the twihs_master_write() function, but it included a number of I2C-like parameters which weren't applicable in this case. The twihs_write_byte() function seems more suitable, as it essentially writes the received byte directly to the Transmit Holding Register.

 

I don't appear to be getting any activity on the TLC5971 ICs but the twihs_master_init() function executes without issue; where have I gone wrong in the above?

 

Thanks!

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

I2C or TWI is a well defined protocol including addressing and open-drain or open-collector signals to perform wired-AND functions, it seems wrong for this, why not use SPI?

/Lars

 

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

I make use of SPI extensively elsewhere in the design, and make use of some of the alternate functions on the SPI pins as well. I have the TWIHS interface available, and given the low bandwidth requirement of these drivers it doesn't seem like too bad a fit. I have almost certainly missed something in my code above, or misunderstood how the ASF calls are supposed to be made, as opposed to it not being possible at all. At least I certainly hope that's the case!

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

I2C works with I2C compliant devices, getting it to work with a device which is not is going to be a "challenge". USART in SPI mode maybe?

/Lars

 

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

I understand, but if the API call I'm using (twihs_write_byte()) simply puts bytes in the transmit register, and the byte sequence is compliant with the TLC5971 driver, I don't understand why it shouldn't/couldn't work? The sequencing for the drivers is very simple, there's no start/stop bits required, no ACK/NACK considerations etc.

 

What I'm looking for in this thread, is whether the code I've posted above has any obvious issues from a TWIHS perspective, as I'm not getting any activity on the selected TWIHS channels whatsoever. Whether the TWI interface can successfully run the attached TLC5971 drivers is the next issue, but for now I'm trying to understand why I'm not seeing any TWI activity.

 

If all else fails, I can look to use a different interface entirely, but I've got a fully populated board in front of me which I can't afford not to utilise. I'd be inclined to use the TWI pins in GPIO mode and bit bang them for the time being if the TWI interface won't work, and then look to use SPI/USART in the next revision. If bit banging is the only way I'm going to get this to work on the TWI pins then so be it, but I'd first like to understand why simply writing data to the TWI transmit register isn't working.

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


Well I think I've arrived at the inevitable conclusion. I'm not going to be able to use TWIHS for this purpose, no matter how much I butcher the standard operating procedure. Below is the transfer format of the SAME70 TWIHS:

 

 

Here is a comparable figure from the TLC5971 datasheet:

 

 

It would appear that operation of the TWIHS interface mandates a slave ACK after each byte, which the TLC5971 cannot give. I had hoped to be able to simulate or otherwise trick the SAME70 into shifting the data without this ACK byte, but I'm not sure that's possible. As demonstrated in the TLC5971 figure above, it expects a continuous stream of 224-bit packets, so introducing an artificial ACK byte into the mix certainly wouldn't work.

 

Is there anything else I'm perhaps missing? At this stage, given I've got a fully populated board and a pressing timeline, I'm going to look into using the two TWIHS1 pins (105 and 109) as GPIOs and bit banging the required sequence manually in software. This will by no means by a production solution, but for the time being this will be the only software running on the SAME70 so I'm not concerned with processor utilisation etc.