Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 14 of 14

Thread: Wire.() works on beta10, but not beta12

  1. #1
    Senior Member Jp3141's Avatar
    Join Date
    Nov 2012
    Posts
    455

    Wire.() works on beta10, but not beta12

    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));}

  2. #2
    Member
    Join Date
    Dec 2012
    Location
    Adelaide, SA
    Posts
    70
    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

  3. #3
    Senior Member Jp3141's Avatar
    Join Date
    Nov 2012
    Posts
    455
    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 by Jp3141; 01-21-2013 at 03:19 AM.

  4. #4
    Member
    Join Date
    Dec 2012
    Location
    Adelaide, SA
    Posts
    70
    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

  5. #5
    Senior Member Jp3141's Avatar
    Join Date
    Nov 2012
    Posts
    455
    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.

  6. #6
    Member
    Join Date
    Dec 2012
    Location
    Adelaide, SA
    Posts
    70
    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

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    15,135
    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.

    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.

    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 by PaulStoffregen; 01-21-2013 at 12:27 PM.

  8. #8
    Senior Member Jp3141's Avatar
    Join Date
    Nov 2012
    Posts
    455
    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.
    Click image for larger version. 

Name:	I2C Capture.PNG 
Views:	121 
Size:	26.1 KB 
ID:	172

    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.

  9. #9
    Member
    Join Date
    Dec 2012
    Location
    Adelaide, SA
    Posts
    70
    I still had mine hooked up, so I took grabbed some scope traces, with 1.5k ext pullups, and with adjacent pin pullups:

    Click image for larger version. 

Name:	10k.jpg 
Views:	130 
Size:	120.9 KB 
ID:	173

    Click image for larger version. 

Name:	adj_pin.jpg 
Views:	118 
Size:	123.7 KB 
ID:	174

    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

  10. #10
    Member
    Join Date
    Dec 2012
    Location
    Adelaide, SA
    Posts
    70
    ... 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

  11. #11
    Senior Member Jp3141's Avatar
    Join Date
    Nov 2012
    Posts
    455
    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.

  12. #12
    Junior Member
    Join Date
    Dec 2012
    Posts
    5
    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.

  13. #13
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    15,135
    Quote Originally Posted by meccup View Post
    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...

  14. #14
    Senior Member Constantin's Avatar
    Join Date
    Nov 2012
    Location
    In the yard with a 17' Dia. Ferris Wheel
    Posts
    1,408
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •