Wire.() works on beta10, but not beta12

Jp3141

Well-known member
I don't have time to debug this (it's in a running system), but my code compiles OK in each beta release (using a Mac). The DS1631 appears to work correctly on beta 10, but reports a temperature of 0 on beta 12 - same as if the I2C bus wasn't working.

I saw some discussion about pullups -- I don't have any on this.#include <stdio.h>

Code:
#include <Wire.h>

#define FLASH(n) {digitalWrite(LEDpin, HIGH); delay(1); digitalWrite(LEDpin, LOW); delay(n); }

// SCL on pin 19
// SDA on pin 18

#define DS1631_ADDR 0b1001111   // A0, A1, A2 ==> VDD
#define DS1631_StartConvertT   0x51
#define DS1631_StopConvertT    0x22
#define DS1631_ReadTemperature 0xAA
#define DS1631_AccessConfig    0xAC
#define DS1631_SoftwarePOR     0x54

#define RTCget() Teensy3Clock.get()
//#define RTCget() (millis()/1000+RTCoffset)

#define RTCset(t) Teensy3Clock.set(t)
//#define RTCset(t) RTCoffset=(t-millis()/1000) // can't reset millis().

#define LEDpin   13
#define CdSpin 23

long int iRollingCounter = 1L;
int TemperatureData;
float TemperatureC;

char CRLF[2]={'\0', '\0'};  // Prepare null-terminated string
byte lineindex=0;
int SetD, SetH, SetM, SetS;
int Set6Y=2012, Set6Month=1, Set6D=1, Set6H=0, Set6M=0, Set6S=0;
char *Month[]={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
char *DOW[]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
int MonthL[]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
int iLeapDay=0;
//elapsedMillis milli_s_timer; //seems to initialize at 0
int DateMode=1;  // 0 ==> D, H:M:S; 1 ==> Year, Month, Date, H:M:S

int iLastTime=0;
long RTCoffset=0L;

char DataLine[82], InputLine[82];

int ADC;
float R_CdS, fLogLight;
#define ADC_MAX (1<<16)
#define R_PULLUP 39.e3  // 39 kohm
#define ADC_Full 100  // Full sunlight reading ~ 60 ohm

// SETUP
void setup(){
    
  // Setup Serial connection
  Serial.begin(9600);   
  pinMode(LEDpin, OUTPUT); digitalWrite(LEDpin, HIGH); 
  pinMode(CdSpin, INPUT); // use external 39k pullup  
  analogReference(DEFAULT);
  analogReadRes(16);
 

  pinMode(0, OUTPUT); // for scope triggering
 // pinMode(14, INPUT_PULLUP); digitalWrite(14, HIGH);
 // pinMode(15, INPUT_PULLUP); digitalWrite(15, HIGH); // Act as pullups for I2C pins

  // Wait for Arduino Serial Monitor to open
  int i = 0;
  while (!Serial && (i++ < 10*6)) { // or ~ 10 seconds
    digitalWrite(LEDpin, HIGH); delay(50); digitalWrite(LEDpin, LOW); delay(50); }

  Serial.println(
  "# --------------------------\n" 
  "# DS1631 Temperature Logger\n"  
  "# Dec 19, 2012\n"  
  "# --------------------------");
  
  Wire.begin();             // join I2C bus

  Wire.beginTransmission(DS1631_ADDR); Wire.write(DS1631_SoftwarePOR); Wire.endTransmission();  
  
  Wire.beginTransmission(DS1631_ADDR);
  Wire.write(DS1631_AccessConfig); 
  Wire.write(0x0D); // One-shot conversion & 12 bits resolution
  Wire.endTransmission();
    
  RTCset(3600*24+1); // Gnuplot likes to plot from day #1
}

// Main Loop
void loop(){
  digitalWriteFast(0, HIGH); // Trigger scope 

  // START conversion to get T°
  Wire.beginTransmission(DS1631_ADDR); Wire.write(DS1631_StartConvertT); Wire.endTransmission();
  delay(900);  //conversion takes ~ 750 ms
  /* do {
    Wire.beginTransmission(DS1631_ADDR); Wire.write(DS1631_AccessConfig); Wire.endTransmission();
    Wire.requestFrom(DS1631_ADDR, 1); //Read 1 byte
  } while (!(Wire.read() & 0x80)); // Loop until conversion completed. Takes ~ 750 ms on DS1631
*/
  //READ T°
  Wire.beginTransmission(DS1631_ADDR); Wire.write(DS1631_ReadTemperature); Wire.endTransmission();
  digitalWrite(0, LOW); 
  Wire.requestFrom(DS1631_ADDR, 2); // Read 2 bytes

 // int8_t xx= Wire.read();  
 // Serial.print(xx);Serial.print(" ");
 // int yy = xx * 256;
 // Serial.print(yy);Serial.print(" ");
  TemperatureData = ((int8_t) Wire.read()) * 256; // Does this work correctly with negative values ? 
 //  xx= Wire.read();
 // TemperatureData |= xx;
 // Serial.print(xx); Serial.print("--");
  TemperatureData |= Wire.read();
  TemperatureC = TemperatureData/256.;
  //sprintf(DataLine, "%8li: %+7.2f deg. C\n", iRollingCounter++, TemperatureData/256.);
  
  // Wait for next 1 s interval before restart
  digitalWrite(LEDpin, HIGH); delay(20); digitalWrite(LEDpin, LOW);
  do {} while (RTCget()==iLastTime);
  iLastTime = RTCget(); // The data printed is 1 s off

  if (iLastTime % (3600*24)==0) {
  //  RTCset(iLastTime+3600*24-5);  // 5 s short of a day
    Set6D++;
    iLeapDay = (isLeapYear(Set6Y) && Set6Month==2) ? 1 : 0;
    if (Set6D > (MonthL[Set6Month-1]+iLeapDay)){
       Set6D=1; Set6Month++;
       if (Set6Month==13) {Set6Month=1; Set6Y++;}
    }
  }

    // Read CdS photoresistor and convert to log2(lightlevel w.r.t. full sunlight)
    ADC=analogRead(CdSpin); 
    R_CdS = R_PULLUP*ADC/(1+ADC_MAX-ADC); // 1+ so no div. 0
    #define R_CdS_Full (R_PULLUP*ADC_Full/(1+ADC_MAX-ADC_Full))
    fLogLight = -log10(R_CdS/R_CdS_Full)/log10(2); // 0 ==> Full sunlight, log base 2

  
  //temperatureC = (TemperatureADC*TemperatureScale)/nLoops + TemperatureOffset;
  if (DateMode==0) sprintf(DataLine, "%4i %2d:%02d:%02d DS1631 # %9ld = %+7.2f deg.C; Illumination = %+6.2f%s\n", \
    iLastTime/(24*3600), (iLastTime/3600) % 24, (iLastTime/60) % 60, iLastTime % 60, iRollingCounter++, TemperatureC, fLogLight, CRLF);
  else sprintf(DataLine, "%4i %2d %2d %3s %3s %2i %2d:%02d:%02d DS1631 # %9ld = %+7.2f deg.C; Illumination = %+6.2f%s\n", \
    Set6Y, Set6Month, Set6D, DOW[dayofweek(Set6Y, Set6Month, Set6D)], Month[Set6Month-1], Set6D, (iLastTime/3600) % 24, (iLastTime/60) % 60, iLastTime % 60, iRollingCounter++, TemperatureC, fLogLight, CRLF);
  Serial.write(DataLine);
 // D(iLastTime)
  
  while (Serial.available() && (lineindex < sizeof(InputLine))) {
  InputLine[lineindex] = Serial.read();
  if (InputLine[lineindex]=='R') DateMode=1; 
  if (InputLine[lineindex]=='X') _reboot_Teensyduino_(); 
  if (InputLine[lineindex]=='D') DateMode=0; 
  if (InputLine[lineindex]=='r') CRLF[0]='\r'; // [1] is '\0'
  if (InputLine[lineindex]=='n') CRLF[0]='\0'; // append or not a CRLF or just LF
  if (InputLine[lineindex]=='\n') {  // if there is a whole line in the buffer
                                                                // only process this code if there is a line
    InputLine[lineindex] = '\0';   // Mark as end of a string
    if (DateMode==0 && sscanf(InputLine, "%d %d %d %d", &SetD, &SetH, &SetM, &SetS)==4) {  // only accept 4 numbers
      if (SetD < 0) { Serial.println(((String) "# Date set.").concat(CRLF)); SetD = -SetD; }
      RTCset(SetS+60L*(SetM+60L*(SetH+24L*SetD))); } 
    if (DateMode==1 && sscanf(InputLine, "%d %d %d %d %d %d", &Set6Y, &Set6Month, &Set6D, &Set6H, &Set6M, &Set6S)==6) {  // only accept 6 numbers
      if (Set6Y < 0) { Serial.println(((String) "# Calendar date set.").concat(CRLF)); Set6Y = -Set6Y; }
      RTCset(Set6S+60L*(Set6M+60L*(Set6H+24L*Set6D))); } // Is Set6D important ?
    lineindex=0; } else lineindex++;  // Note InputLine[0] is not reset
 } // while
}

int dayofweek(int y, int m, int d) {	/* 0 = Sunday */
	static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
	y -= m < 3;
	return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;}

long int daynumber(int y, int m, int d) {
  m = (m + 9) % 12;
  y = y - m/10;
  return 365*y + y/4 - y/100 + y/400 + (m*306 + 5)/10 + (d - 1);}

int isLeapYear(int year) {
  return ((year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0));}
 
It's curious that it works at all without them, I guess it's something to do with the particular sensor's interface.

You pretty much have to have pullups for it to work reliably, in any case.

Must just have been a lucky combination with the previous pin settings and your sensor that allowed it to work (somehow). I wouldn't rely on it, though...

- Peter
 
Same hardware, worked/not working back an forth with .10 & .12. It'll be next weekend before I can 'scope the signals.

If there truly is a R-pullup issue, I'd try to use another pair of nearby pins just as pullups (configure as open-drain and a HIGH) and connect them together.
 
Last edited:
Well, if you want to use nearby pins you'd probably need to configure them as inputs with pullup enabled, rather than ODE outputs. Still, the internal pullups are quite weak (22k-50k according to the data). I'm not sure that's going to be enough. Depends on length of traces and such as to how clean your signal will be.

It's well worth reading Paul's findings in the other thread(s) about I2C, and his scope waveforms for 10k vs 1k pullup.

- Peter
 
Actually if 'pullups' are used, the same component is used when acting as an open drain pullup, or a passive input pullup. The value is about 33k.

The outputs actually have two drive strengths (about 2 mA and 9 mA). I believe Teensy3 sets these as 'high strength', but even the 'low strength' is too much for the I2C peripherals.
 
Well, that may be. The freescale data is pretty vague at times.

My impression from the data was as follows:

* In input mode, the pullups (and pulldowns) are available.
* In output mode, unless configured for ODE, it's a push-pull driver. Pullups are not used.

p215 says:
"ODE: Open drain output is enabled on the corresponding pin, if the pin is configured as a digital output."
"PE: Internal pullup or pulldown resistor is enabled on the corresponding pin, if the pin is configured as a digital input"

Anyway, I'm quite prepared to be wrong on this, I'm just guessing based on what the datasheet says, and what I've seen.

33k seems likely, and is within specified values (22k-50k).


- Peter
 
Beta10 enabled the pullup resistors. However, testing revealed the chip does NOT properly enable the weak pullups when I2C is in use. It actually drives the pin with the strong driver... the one that's rated for 9 mA. I measured it actually delivering 15 mA into a 200 ohm resistor. It can probably deliver more, and with Beta10, it was... whenever the other I2C chip would try to drive low.

Amazingly, many I2C chips are able to pull the line low, even when it's being driven high so very strongly. But some are not. The AVR chip on Teensy 2.0 is able to pull it down to approx 0.6 volts. Viewing the waveforms on an oscilloscope revealed something clearly was not right.

According to the datasheet, setting the PE and PS bits (pullup enable and pullup select) is suppose to control the pullup resistor, regardless of which peripheral the pin is using. However, it does not work with I2C is using the pins. I'm almost certain this is a bug in the chip. I spent a few hours trying every possible combination and constructing tests with different external pullups to different voltages, and series resistors to measure the current.

Beta12 does not attempt to enable any built-in pullup.

Here are a couple measurements that show the effect of different pullup resistors. This first image is a 10K resistor. The I2C bus is only between a Teensy 3.0 and Teensy 2.0 (with its internal pullups disabled, so the 10K resistors are the only pullup). This is about 4 inches of wire on a breadboard. You can see the slow rise time, but it's still adaquete for 100 kbit/sec.

View attachment 162

This 2nd image is the exact same setup, with the pullup changed to 1K. The rise times are much faster. This is why 1K is recommended for 400 kbit/sec speeds.

View attachment 163

Sadly, I did not measure this with the weak pullup resistor of another pin. Since making these measurements, I took that all apart and rebuilt it to work on IRremote (where I used both boards on the same breadboard to compare IRremote's modulated waveforms on AVR vs ARM), and since completing IRremote, I'm now setting up to work on synthesizing WS2811 waveforms.....

But anyone with a reasonably good oscilloscope could make these measurements. The first thing you'll see the slow rise times of a very weak pullup make you wonder how so many Arduino projects using only the built-in pullups are just barely working!
 
Last edited:
Paul -- yes, I see there is a confusion (or conflict) in the datasheet. However, I believe it could be argued that the .12 operation is more correct.
I2C Capture.PNG

This is because--in general--you don't know if the peripheral is operating from a lower or higher supply voltage than the MCU. Having the MCU provide a (e.g. 3.3 V) pullup to a lower voltage peripheral could cause problems (may be unable to correctly shut it down, latch up, etc). Of course, I would prefer if we had the choice.

In fact many products don't correctly implement the I2C 'open drain' spec -- they have a parasitic diode from the pin to their VDD which could clamp the I2C pin when that part isn't powered.
 
I still had mine hooked up, so I took grabbed some scope traces, with 1.5k ext pullups, and with adjacent pin pullups:

10k.jpg

adj_pin.jpg

Note it does work for me between the two Teensy3s on breadboard, interesting how the clock has been held off (slows to ~75kHz?), not sure if the master or slave is doing this, in this case, but I suppose it's quite likely related to the rise-time taken to pass the threshold.

Still, while the adjacent pin internal pullup trick works in this test, that waveform doesn't look like something I'd be relying on!

- Peter
 
... actually upon thinking about it, it must be the master holding off the clock due to rise time. The slave is only capable of holding the clock low in order to 'stretch' timing, and all the extra time is in the 'high' (if you can call it that :) ) part of the cycle.

- Peter
 
There's hystereis of ~ 200 mV on each input of the MCU and a 50 ns glitch filter on the DS1631, so it's probably OK, but I'll use resistors anyway now that I understand the issue.
 
Hi all,

there were some buzz related to this: http://code.google.com/p/arduino/issues/detail?id=506

whether internal pull-ups should be enabled for i2c pins by default. It is still opened.

But anyway, I tried to do it manually by:

pinMode(18, INPUT_PULLUP);
digitalWrite(18, HIGH);
pinMode(19, INPUT_PULLUP);
digitalWrite(19, HIGH);

and it does not work, I had to include 10K pull-up resistors in order to make i2c work properly.

Paul, seems to me internal pull-ups do not work at all.
 
Paul, seems to me internal pull-ups do not work at all.

That's correct. Believe me, I put several hours into trying everything I could to get those damn internal pullups to work. It's just not possible.

Also, using pinMode after Wire is active will reconfigure the pins to GPIO mode, where they're no longer controlled by the I2C peripheral. This is one way the ARM is very different from AVR...
 
I remember slapping my forehead at least once when I dicovered that I had omitted the external pullups on a 328P communicating via I2C with a MCP3421 on a custom PCB. But it worked and my understanding is that the AVR chips can handle one device fine without external pullups but that you're better off having them. I generally use 2k2 resistors on account of creating nice square waveforms, but many datasheets suggest that 4k7 or even 10k should work fine also. It's all a matter of speed on the I2C bus, where higher communication speeds require faster pull-up responses, i.e. lower resistance resistors.

With Teensy 3.0, I have had no issues communicating with a MCP3421 and 2k2 pullups using 3.3V power.
 
Back
Top