Teensy3.2 => Atmega328 (Arduino) via I2C Dropping Transmissions

Status
Not open for further replies.
I've searched the forums for this specific problem and while I've found a few examples that are close, nothing that matches what I'm looking for specifically. I do apologize though in advance if my search was incomplete.

Here is my issue:

I have a Teensy3.2 hooked up to an Atmega328. I created a sketch and used AtmelStudio to create a project from the Arduio Sketch which I then flashed to the chip. This is essentially the same as the code that would run on the chip if uploaded via Arduino, but it bypasses the bootloader so the code executes immediately. That code is shown below (some omissions for brevity):

Code:
/*Begining of Auto generated code by Atmel studio */
#include <Arduino.h>

/*End of auto generated code by Atmel studio */

#include <Wire.h>

//Beginning of Auto generated function prototypes by Atmel Studio
void receiveWireData(int howMany);
//End of Auto generated function prototypes by Atmel Studio

void setup() {
  Wire.begin(0x10);
  Wire.onReceive(receiveWireData);
}

void loop() {
        // process any queued packets from recieveWireData
}

void receiveWireData(int howMany) {
	// should be 7 bytes total - first byte is offset, followed by three 16 bit integers in big endian (network byte order) 

        // first byte is always the address
        uint8_t address = (uint8_t)Wire.read();
	while(Wire.available()) {
                // read rest of transmission
		byte ch = (byte)Wire.read();
	}

        // queue packet to be processed
}


Here is my Teensy code (uploaded using Arduino IDE):

Code:
#include <Wire.h>

void setup() {
  // put your setup code here, to run once:
  Wire.begin();
}

void loop() {
  // put your main code here, to run repeatedly:
  setColor(0, 0xff0, 0x00, 0x00);
  //delay(20);
  setColor(1, 0xff0, 0x00, 0x00);
  //delay(20);
  setColor(2, 0xff0, 0x00, 0x00);
  //delay(20);
  setColor(3, 0xff0, 0x00, 0x00);
  delay(500);

  setColor(0, 0xf00, 0xf00, 0x00);
  //delay(20);
  setColor(1, 0xf00, 0xf00, 0x00);
  //delay(20);
  setColor(2, 0xf00, 0xf00, 0x00);
  //delay(20);
  setColor(3, 0xf00, 0xf00, 0x00);
  delay(500);
}

void setColor(uint8_t channel, uint16_t red, uint16_t green, uint16_t blue) {
  
  Wire.beginTransmission(0x10); // transmit to device #0x10
  Wire.write(channel);        

  Wire.write((byte)(red>>8));              
  Wire.write((byte)(red&0xff)); 
  Wire.write((byte)(green>>8));              
  Wire.write((byte)(green&0xff));              
  Wire.write((byte)(blue>>8));              
  Wire.write((byte)(blue&0xff));              
  Wire.endTransmission();    // stop transmitting
  
}

You'll notice the commented delays between calls to setColor. If I don't specify those delays the transmissions end up getting "dropped". That's the best way I can explain it. I put some serial debugging statements and when I don't have the delay I basically get every other transmission as demonstrated by printing out the raw bytes as they are received. When I add the delay back I see every transmission in the correct order.

Even without the serial debugging statements if I have the output go to leds or some other indicator I see every other packet take effect just as I see every other packet in the serial debugger. I've added pull up resistors and I tried running the Teensy at 24Mhz to slow the clock down, but still no luck.

I've attached images to the post.

Thank you in advance for any assistance.

image1.png

20171016_224611.png

20171016_224553.png
 
Last edited:
I found the answer in another forum. Apparently it's a problem with the Wire library that requires a simple wait between transmissions. I think the problem is exacerbated when the clock speeds are so different such as with the 72Mhz Teensy 3.2 connected to the 16Mhz Arduino. I moved the Teensy down to 16Mhz to match and I still had the problem, but I was able to reduce the delay to 1ms. Obviously not idea as I'm not using the full potential of the chip which is responsible for a few other things besides driving the rgb leds. Anyway, here is the bug for future reference: https://github.com/arduino/Arduino/issues/1477. It looks like it was resolved at one point, but that PR was reverted and the issue has been reopened.
 
I put some serial debugging statements and when I don't have the delay I basically get every other transmission as demonstrated by printing out the raw bytes as they are received.

I don't see those serial debugging statements in the code you posted, nor any other way to observe whether or not the AVR chip actually received the data. I could probably add some myself, but before I look into this, I'd much rather see code that is *exactly* what you have personally tested. That way I will be working with the exact same code when I try to reproduce the issue.

It may indeed be an unavoidable limitation on the AVR side. But it might be a subtle bug on either side, or both. I'd like to find out. But to be honest, this is the sort of testing that almost always end up as frustrating guesswork without the exact code that you actually used when testing for the problem. Please post that code.

Also, if you would, please program the AVR chip from Arduino. It should do the same thing, right? I do not personally use Atmel Studio, or really Windows much either.
 
I don't see those serial debugging statements in the code you posted, nor any other way to observe whether or not the AVR chip actually received the data.
As I said, I omitted code for brevity, but I placed a Serial.println() with the byte received after every read


Also, if you would, please program the AVR chip from Arduino. It should do the same thing, right?
Exactly, it does the same thing.


I do not personally use Atmel Studio, or really Windows much either.
They are all tools in my eyes. Whether it's Windows, Linux, OSX, heck even CP/M if I had too. :D

Anyway, problem solved. It's an issue with the Arduino i2c library doing clock stretching and there is no way around it unless I modify the library (or put those timeouts in).

Since I'm developing this to control a tlc5947 pwm driver for 6 leds I decided to change the protocol. Rather than take a single led and color to set I send all 6 leds in the same packet. I had to increase the i2c buffer size, but that was a fairly minor change. That reduces the delays I need in order to compensate for the clock stretching, but clock stretching still remains a problem.

I also might just switch to a Teensy 3.6. The only reason why I'm using the Arduino is that I ran out of pins on the 3.2 :D.

Just to be through here is the code I ended up with.

Slave (Arduino)
Code:
/*Begining of Auto generated code by Atmel studio */
#include <Arduino.h>

/*End of auto generated code by Atmel studio */

#include <Wire.h>
#include <Adafruit_TLC5947.h>

typedef struct led_command {
	uint16_t red;
	uint16_t green;
	uint16_t blue;
	uint8_t led;
} led_command_t;

typedef struct node {
	led_command_t led_command;
	struct node *volatile next;
} node_t;


//Beginning of Auto generated function prototypes by Atmel Studio
void receiveWireData(int howMany);
void setColor(uint8_t led, uint16_t red, uint16_t green, uint16_t blue);
//End of Auto generated function prototypes by Atmel Studio

void pushNode(uint8_t led, uint16_t red, uint16_t green, uint16_t blue);
bool popNode();

node_t *volatile led_command_head = NULL;

#define NUM_TLC5974 1
#define data   4
#define clock   5
#define latch   6
#define oe  -1  // set to -1 to not use the enable pin (its optional)

Adafruit_TLC5947 tlc = Adafruit_TLC5947(NUM_TLC5974, clock, data, latch);


void setup() {
  Wire.begin(0x10);
  tlc.begin();
  Wire.onReceive(receiveWireData);
  
  for(int i=0; i<NUM_TLC5974*8;i++) {
	  setColor(i, 0x00, 0x00, 0x00);
  }
}

void loop() {
	while(popNode()) {
		// loop until all changes are dealt with
	}
}

void receiveWireData(int howMany) {
	// should be 24 bytes total - 6 for each led respectively
	uint16_t red = 0, green = 0, blue = 0;
  
	int count = 0;
	
	int led = 0;
	while(Wire.available()) {
		byte ch = (byte)Wire.read();
		byte highByte, lowByte;
		
		switch(count) {
			case 0:
			highByte = ch;
			break;
			
			case 1:
			lowByte = ch;
			red = (highByte<<8)+lowByte;
			break;
			
			case 2:
			highByte = ch;
			break;
			
			case 3:
			lowByte = ch;
			green = (highByte<<8)+lowByte;
			break;
			
			case 4:
			highByte = ch;
			break;
			
			case 5:
			lowByte = ch;
			blue = (highByte<<8)+lowByte;
			break;
		}
		
		count++;
		
		if(count > 5) {
			pushNode(led, red, green, blue);
			red = green = blue = count = 0;
			
			led++;
		}
	}

}

void setColor(uint8_t led, uint16_t red, uint16_t green, uint16_t blue) {
	tlc.setLED(led, red, green, blue);
	tlc.write();  
}

void pushNode(uint8_t led, uint16_t red, uint16_t green, uint16_t blue) {
	node_t * new_node;
	new_node = (node_t *) malloc(sizeof(node_t));
	
	new_node->led_command.led = led;
	new_node->led_command.red = red;
	new_node->led_command.green = green;
	new_node->led_command.blue = blue;
	
	new_node->next = led_command_head;
	led_command_head = new_node;
}

bool popNode() {
	if(led_command_head == NULL) {
		return false;
	}
	
	node_t * next_node = led_command_head->next;
	setColor(led_command_head->led_command.led, led_command_head->led_command.red, led_command_head->led_command.green, led_command_head->led_command.blue);
	free(led_command_head);
	led_command_head = next_node;
	
	return true;
}

Master (Teensy)
Code:
//Predefined LED Colors
#define RED { 0xf00, 0x00, 0x00 }
#define GREEN { 0x00, 0xf00, 0x00 }
#define BLUE { 0x00, 0x00, 0xf00}
#define YELLOW {0xf00, 0xf00, 0x00}
#define MAGENTA { 0xf00, 0x00, 0xf00 }
#define CYAN { 0x00, 0xf00, 0xf00 }
#define ORANGE {0xf00, 0x500, 0x00 }

#define MENU_STATE_HELLO 0
#define MENU_STATE_ADSR 1
#define MENU_STATE_FILTER 2
#define MENU_STATE_OSC1 3
#define MENU_STATE_OSC2 4
#define MENU_STATE_LFO 5

#define MAX_STATE 4

const rgb mode_colors[MAX_STATE] = {   
  BLUE, RED, YELLOW, GREEN
};


int menu_state = 0;

const rgb* state_color = &mode_colors[menu_state];

void update_knob_color() {

  state_color = &mode_colors[menu_state];
  
  Wire.beginTransmission(0x10); // transmit to device #0x10

  for(int i=0; i<KNOB_COUNT; i++) {
    Wire.write((byte)(state_color->r>>8));              
    Wire.write((byte)(state_color->r&0xff)); 
    Wire.write((byte)(state_color->g>>8));              
    Wire.write((byte)(state_color->g&0xff));              
    Wire.write((byte)(state_color->b>>8));              
    Wire.write((byte)(state_color->b&0xff));              
  }

  // end transmission and send packet
  Wire.endTransmission();
}
 
Last edited:
Oh, maybe the AVR is clock stretching too long?

Teensy does support talking to devices that clock stretch, but it has a timeout so it doesn't wait forever. I believe it's 4 only milliseconds before it gives up waiting.
 
Sending data from a Teensy to an old and totally outdated ATMega328 is like View attachment 11821

Sure, but I don't see the justification in spending $20USD on something that I can replace with a $3.00USD chip just so I can drive a couple of RGB LEDs. I ran out of pins on my 3.2 and sticking another 3.2 just to drive those leds seemed overkill and will drive the overall cost up in the end. Besides, protocols like TWI and SPI are designed to be clock agnostic. That's why the master supplies the clock rather than both parties synchronizing with a pre-defined bit rate (baud). As long as both parties can handle the signal (transmit and receive) they should be able to talk TWI (or SPI) even with drastically different clock speeds such as with the Teensy 3.2 at 72Mhz vs the Atmega328 at 16mhz (it's possible to run these chips at 20mhz, but the Arduino libraries will most likely give you some problems).
 
Last edited:
Oh, maybe the AVR is clock stretching too long?

Teensy does support talking to devices that clock stretch, but it has a timeout so it doesn't wait forever. I believe it's 4 only milliseconds before it gives up waiting.

I think that's exactly what's going on. In fact that corresponds directly to my observations. The threshold was about 5ms before I started seeing flakiness. I'm staring at this 3.6 :D. I've already modified my circuit to be 3.3v only, so no need to translate voltages (though I have a couple of drivers for that). I'd rather not have a 5v and 3.3v power rail though and just have a single supply voltage. Thanks again!! :D
 
Status
Not open for further replies.
Back
Top