Getting I2C on ESP8266 to work

Posted on
Page
of 2
Prev
/ 2
  • I gave up on the build-in I2C and used the following code to bit bang the I2C instead in JavaScript (code almost 1 to 1 from I2C article on wikipedia):

    function jsI2C(p_Info){
      function pinHigh(pin){ pin.mode("input"); }
      function pinLow(pin){ pin.mode("opendrain"); }
      
      function I2C_delay(){ wait(1); }
      function read_SCL(){ return p_Info.scl.read(); } // Set SCL as input and return current level of line, 0 or 1
      function read_SDA(){ return p_Info.sda.read(); } // Set SDA as input and return current level of line, 0 or 1
      function set_SCL(){ pinHigh(p_Info.scl); } // Actively drive SCL signal high
      function clear_SCL(){ pinLow(p_Info.scl); } // Actively drive SCL signal low
      function set_SDA(){ pinHigh(p_Info.sda); } // Actively drive SDA signal high
      function clear_SDA(){ pinLow(p_Info.sda); } // Actively drive SDA signal low
      function arbitration_lost(){}
    
      function wait(n){ for (var index = 0; index < n; index++){}  }
      function waitForClockStretch(){ while (!read_SCL()){ wait(1); } }
      
      var started = false; // global data
    
      function i2c_start_cond(){
        if (started) 
        { 
          // if started, do a restart cond
          // set SDA to 1
          set_SDA();
          I2C_delay();
    
          waitForClockStretch();
          
          // Repeated start setup time, minimum 4.7us
          I2C_delay();
        }
    
        if (!read_SDA()) 
        {
          arbitration_lost();
        }
    
        // SCL is high, set SDA from 1 to 0.
        clear_SDA();
        I2C_delay();
        clear_SCL();
        started = true;
      }
    
      function i2c_stop_cond(){
        // set SDA to 0
        set_SCL();
        clear_SDA();
        I2C_delay();
    
        waitForClockStretch();
    
        // Stop bit setup time, minimum 4us
        I2C_delay();
    
        // SCL is high, set SDA from 0 to 1
        set_SDA();
        I2C_delay();
    
        if (!read_SDA()){
          arbitration_lost();
        }
    
        I2C_delay();
        started = false;
    
      }
    
      // Write a bit to I2C bus
      function i2c_write_bit(bit){
        if (bit) 
        {
          set_SDA();
        } 
        else 
        {
          clear_SDA();
        }
    
        // SDA change propagation delay
        I2C_delay();  
    
        // Set SCL high to indicate a new valid SDA value is available
        set_SCL();
    
        // Wait for SDA value to be read by slave, minimum of 4us for standard mode
        I2C_delay();
    
        waitForClockStretch();
    
        // SCL is high, now data is valid
        // If SDA is high, check that nobody else is driving SDA
        if (bit && !read_SDA()){
          arbitration_lost();
        }
    
        // Clear the SCL to low in preparation for next change
        clear_SCL();
      }
    
      // Read a bit from I2C bus
      function i2c_read_bit(){
        var bit;
    
        // Let the slave drive data
        set_SDA();
    
        // Wait for SDA value to be written by slave, minimum of 4us for standard mode
        I2C_delay();
    
        // Set SCL high to indicate a new valid SDA value is available
        set_SCL();
    
        waitForClockStretch();
    
        // Wait for SDA value to be read by slave, minimum of 4us for standard mode
        I2C_delay();
    
        // SCL is high, read out bit
        bit = read_SDA();
    
        // Set SCL low in preparation for next operation
        clear_SCL();
    
        return bit;
    
      }
    
    // Write a byte to I2C bus. Return 0 if ack by the slave.
      function i2c_write_byte(send_start, send_stop, byte){
        var nack = false;
    
        if (send_start){
          i2c_start_cond();
        }
    
        for (var bit = 0; bit < 8; bit++){
          i2c_write_bit((byte & 0x80) !== 0);
          byte <<= 1;
        }
    
        nack = i2c_read_bit();
    
        if (send_stop){
          i2c_stop_cond();
        }
    
        return nack;
    
      }
    
      // Read a byte from I2C bus
      function i2c_read_byte(nack, send_stop){
        var byte = 0;
    
        for (var bit = 0; bit < 8; bit++ ) 
        {
          byte = ( byte << 1 ) | i2c_read_bit();
        }
    
        i2c_write_bit(nack);
    
        if (send_stop){
          i2c_stop_cond();
        }
    
        return byte;
      }
      
      return {
        readRegister: function(address, register, bytes){
          var result = [];
          // write address and write mode
          if (!i2c_write_byte(true, false, (address << 1) | 0)){
            // write the data to write in this case the register and write a stop
            if (!i2c_write_byte(false, true, register)){
              // write the address and read mode
              if (!i2c_write_byte(true, false, (address << 1) | 1)){
                // read the bytes
                while (bytes){
                  bytes--;
                  // if no bytes remaining send nack followed by stop
                  result.push(i2c_read_byte(bytes === 0, bytes === 0));
                }
              }
            }
          }
          return result;
        },
        write: function(address, p_Bytes){
          if (!i2c_write_byte(true, false, (address << 1) | 0)){
            // write the data to write in this case the register and write a stop
            for (var index = 0; index < p_Bytes.length; index++){
              if (i2c_write_byte(false, index == p_Bytes.length - 1, p_Bytes[index])){
                break;
              }
            }
          }
        }
      };
    }
    
    

    This code actually works with the radio module and I managed to tune the FM station and got it to play :-).

    Here is the radio code that uses my bit-banging I2C for anyone who is interested :

    // create the radio interface
    // specify SDA and SCL pins to use, will default to 4 and 5 if not set, neither pin can be 0
    function Radio(p_SDA, p_SCL){
        "use strict";
    
        var i2c = jsI2C({ sda: p_SDA || D4, scl: p_SCL || D5});
    
        var i2c_write_address = 0x10;
        var i2c_read_address =  0x11;
        var out_buffer = [];
    
        // constants : 
        var rda_CHIP_ID =  0x58, // CHIP ID
    
        // Tuning Band //
        rda_87_108MHz =   0b0000000000000000, // us band
        rda_76_91MHz =    0b0000000000000100, // japan band
        rda_76_108MHz =   0b0000000000001000, // world band we use this
        rda_65_76MHz =    0b0000000000001100, // east europe
    
        // Tuning Steps //
        rda_100kHz =      0b0000000000000000,
        rda_200kHz =      0b0000000000000001, // not US band compatible **//
        rda_50kHz =       0b0000000000000010,
        rda_25kHz =       0b0000000000000011, // we use this
        // ** 200kHz spacing works but frequencies are always out by 100kHz from US frequency slots.
        // It's not possible to tune to a US station with 200kHz spacing you must use 100kHz instead.
    
        // De-emphasis //
        rda_50us =        0b0000100000000000, // eu standard
        rda_75us =        0b0000000000000000, // us standard
    
        // REG 0x02
        rda_DHIZ =        0b1000000000000000,
        rda_DMUTE =       0b0100000000000000,
        rda_MONO =        0b0010000000000000,
        rda_BASS =        0b0001000000000000,
        rda_RCLK =        0b0000100000000000,
        rda_RCKL_DIM =    0b0000010000000000,
        rda_SEEKUP =      0b0000001000000000,
        rda_SEEK =        0b0000000100000000,
        rda_SKMODE =      0b0000000010000000, // default is to wrap around
    
        // Timing XTAL = rda_CLK_MODE //
        // we are not changing the defaults so remark
        rda_32_768kHz =   0b0000000000000000,
        rda_12MHz =       0b0000000000010000,
        rda_24MHz =       0b0000000001010000,
        rda_13MHz =       0b0000000000100000,
        rda_26MHz =       0b0000000001100000,
        rda_19_2MHz =     0b0000000000110000,
        rda_38_4MHz =     0b0000000001110000,
        rda_CLK_MODE =    0b0000000001110000, // default is 32.768kHz which is the build in crystal
        rda_RDS_EN =      0b0000000000001000,
        rda_NEW_METHOD =  0b0000000000000100,
        rda_SOFT_RESET =  0b0000000000000010,
        rda_ENABLE =      0b0000000000000001,
        // REG 0x03
        rda_CHAN =        0b1111111111000000,
        rda_DIRECT_MODE = 0b0000000000100000, // only used with test
        rda_TUNE =        0b0000000000010000,
        rda_BAND =        0b0000000000001100,
        rda_SPACE =       0b0000000000000011,
        // REG 0x04
        rda_DE =          0b0000100000000000,
        rda_SOFTMUTE_EN = 0b0000001000000000,
        rda_AFCD =        0b0000000100000000,
        // REG 0x05
        rda_INT_MODE =    0b1000000000000000,
        rda_SEEKTH =      0b0000111100000000,
        rda_VOLUME =      0b0000000000001111,
        // REG 0x06
        rda_OPEN_MODE =   0b0110000000000000,
        // REG 0x07
        rda_BLEND_TH =    0b0111110000000000,
        rda_65_50M_MODE = 0b0000001000000000,
        rda_SEEK_TH_OLD = 0b0000000011111100,
        rda_BLEND_EN =    0b0000000000000010,
        rda_FREQ_MODE =   0b0000000000000001,
        // REG 0x0A
        rda_RDSR =        0b1000000000000000,
        rda_STC =         0b0100000000000000,
        rda_SF =          0b0010000000000000,
        rda_RDSS =        0b0001000000000000,
        rda_BLK_E =       0b0000100000000000,
        rda_ST =          0b0000010000000000,
        rda_READCHAN =    0b0000001111111111,
        // REG 0x0B
        rda_RSSI =        0b1111110000000000,
        rda_FM_TRUE =     0b0000001000000000,
        rda_FM_READY =    0b0000000100000000,
        rda_ABCD_E =      0b0000000000010000,
        rda_BLERA =       0b0000000000001100,
        rda_BLERB =       0b0000000000000011;
    
        // 
        function read_chip(offset) {
            var data = i2c.readRegister(i2c_read_address, offset, 2);
            return ((data[0] << 8) & 0xff00) | (data[1] & 0x00ff);
        }
    
        function write_chip(size) {
            i2c.write(i2c_write_address, out_buffer.slice(0, size));
        }
    
        function tune(p_On){
          var data = ((out_buffer[2]<<8)|out_buffer[3])|rda_TUNE;
          if (!p_On) { data = data^rda_TUNE; }
          out_buffer[2] = (data >> 8) & 0xff;
          out_buffer[3] = data & 0xff;
        }
        
        function readLoopChip(){
          for (var loop = 2; loop < 8; loop++) {
            var data = read_chip(loop);
            out_buffer[(loop*2)-4] = (data >> 8) & 0xff;
            out_buffer[(loop*2)-3] = data & 0xff;
          }
          tune(false); // disable tuning
        }
    
        function init_chip() {
          var data = read_chip(0);
          var found = 0;
          var result;
    
          if ((data&0xff) == rda_CHIP_ID) {
            found = 1;
          }
          data = (data >> 8)&0xff;
          if (data == rda_CHIP_ID) {
            found = 1;
          }
          if (found === 0) {
          }
          else if ((read_chip(13) == 0x5804) && (read_chip(15) == 0x5804)) {
              result = 0; 
          } // not set up
          else {
              result = 1;
          } // already used
          return result;
        }
    
        function isInitialized(){
          var state = init_chip();
          return state !== 0;
        }
    
        // radio on    
        function on(p_Callback){
          // REG 02
          // normal output, enable mute, stereo, no bass boost, clock = 32.768kHz, RDS enabled, new demod method, power on
          var data = rda_DHIZ|(rda_DMUTE&0)|(rda_MONO&0)|(rda_BASS&0)|rda_RDS_EN|rda_NEW_METHOD|rda_ENABLE;
          out_buffer[0] = (data >> 8) & 0xff;
          out_buffer[1] = data & 0xff;
          // REG 03 - no auto tune, 76-108 band, 0.1 spacing
          data = (rda_TUNE&0)|rda_76_108MHz|rda_100kHz;
          out_buffer[2] = (data >> 8) & 0xff;
          out_buffer[3] = data & 0xff;
          // REG 04 - audio 50us, no soft mute, disable AFC
          data = rda_50us|(rda_SOFTMUTE_EN&0)|rda_AFCD;
          out_buffer[4] = (data >> 8) & 0xff;
          out_buffer[5] = data & 0xff;
          // REG 05 - max volume
          data = rda_INT_MODE|0x0480|rda_VOLUME;
          out_buffer[6] = (data >> 8) & 0xff;
          out_buffer[7] = data & 0xff;
          // REG 06 - reserved
          out_buffer[8] = 0;
          out_buffer[9] = 0;
          // REG 07
          var blend_threshold = 0b0011110000000000; // mix L+R with falling signal strength
          data = blend_threshold|rda_65_50M_MODE|0x80|0x40|rda_BLEND_EN|(rda_FREQ_MODE&0);
          out_buffer[10] = (data >> 8) & 0xff;
          out_buffer[11] = data &0xff;
          write_chip(12);
    
          setTimeout(p_Callback, 200);
        }
    
        function off(){
          if (isInitialized()){
            var data = (read_chip(2)|rda_ENABLE)^rda_ENABLE;
            out_buffer[0] = (data >> 8) & 0xff;
            out_buffer[1] = data &0xff;
            write_chip(2);
          }
        }
    
        function forceMono(p_On){
          if (isInitialized()){
            var data = (read_chip(2)|rda_MONO);
            if (!p_On) { data = data^rda_MONO; }
            out_buffer[0] = (data >> 8) & 0xff;
            out_buffer[1] = data & 0xff;
            write_chip(2);
          }
        }
    
        function bassBoost(p_On){
          if (isInitialized()){
            var data = (read_chip(2)|rda_BASS);
            if (!p_On) { data = data^rda_BASS; }
            out_buffer[0] = (data >> 8) & 0xff;
            out_buffer[1] = data & 0xff;
            write_chip(2);
          }
        }
    
        function mute(p_On){
          if (isInitialized()){
            var data = (read_chip(2)|rda_DMUTE);
            if (p_On) { data = data^rda_DMUTE; }
            out_buffer[0] = (data >> 8) & 0xff;
            out_buffer[1] = data & 0xff;
            write_chip(2);
          }
        }
    
        function checkSeek(p_Callback){
          var regA = read_chip(0x0A);
    
          if (!!(regA & rda_STC)){
            p_Callback(!(regA & rda_SF));
          }
          else {
            setTimeout(function(){ checkSeek(p_Callback); }, 500);
          }
        }
    
        function seekUp(p_Callback){
          if (isInitialized()){
            var data = (read_chip(2)|rda_SEEKUP|rda_SEEK);
            out_buffer[0] = (data >> 8) & 0xff;
            out_buffer[1] = data & 0xff;
            tune(true);
    
            write_chip(2);
    
            checkSeek(p_Callback);
          }
        }
    
        function seekDown(p_Callback){
          if (isInitialized()){
            var data = (read_chip(2)|rda_SEEKUP|rda_SEEK)^rda_SEEKUP;
            out_buffer[0] = (data >> 8) & 0xff;
            out_buffer[1] = data & 0xff;
            tune(true);
    
            write_chip(2);
    
            checkSeek(p_Callback);
          }
        }
    
        function deemphasis75(p_On){
          if (isInitialized()){
            readLoopChip();
            var data = (read_chip(4)|rda_DE)^rda_DE;
            data = (p_On) ? (data | rda_75us) : (data | rda_50us);
            out_buffer[4] = (data >> 8) & 0xff;
            out_buffer[5] = data & 0xff;
            write_chip(6);
          }
        }
    
        function volume(p_Val){
          if (isInitialized()){
            readLoopChip();
            var data = (read_chip(5)|rda_VOLUME)^rda_VOLUME;
            if (p_Val > rda_VOLUME) { p_Val = rda_VOLUME; }
            data = data|p_Val;
            out_buffer[6] = (data >> 8) & 0xff;
            out_buffer[7] = data & 0xff;
            write_chip(8);
          }
        }
    
        function setFrequency(p_Mhz){
          if (isInitialized()){
            mute(false);
            var channel = ((((p_Mhz - 76)/0.1) | 0) & 0x3FF) << 6;
            var data = (read_chip(3)|rda_CHAN)^rda_CHAN;
            data = data|channel;
            out_buffer[2] = (data >> 8) & 0xff;
            out_buffer[3] = data & 0xff;
            tune(true);
            write_chip(4);
          }
        }
    
        function getRDS() {
            // check register A
            var data = read_chip(0x0A);
            var rds = [0,0,0,0, false, false];
    
            // block E type
            rds[4] = !!(data & rda_BLK_E);
    
            // check for new RDS data available
            if (rds[4] || data & rda_RDSR) {
              rds[0] = read_chip(0x0C) & 0xffff;
              rds[1] = read_chip(0x0D) & 0xffff;
              rds[2] = read_chip(0x0E) & 0xffff;
              rds[3] = read_chip(0x0F) & 0xffff;
              rds[5] = true;
            }
            return rds;
        }
    
        function getState(){
          var reg2 = read_chip(2);
          var regA = read_chip(0x0A);
          var regB = read_chip(0x0B);
    
          return { 
              isInitialized:  isInitialized(),
              muteOn:         !(reg2 & rda_DMUTE),
              forceMonoOn:    !!(reg2 & rda_MONO),
              bassBoostOn:    !!(reg2 & rda_BASS),
              rdsOn:          !!(reg2 & rda_RDS_EN),
              de:             !read_chip(4) & rda_DE,
              volume:         read_chip(5) & rda_VOLUME,
              stereo:         !!(regA & rda_ST),
              signalStrength: ((regB & rda_RSSI)>>10),
              channel:        ((regA & 0x03ff) + 760) / 10,
              isStation:      !!(regB & rda_FM_TRUE),
              isReady:        !!(regB & rda_FM_READY),
              seekComplete:   !!(regA & rda_STC),
              seekFail:       !!(regA & rda_SF),
              hasRDS:         !!(regA & rda_BLK_E) || !!(regA & rda_RDSR)
          };
        }
    
        return {
            read_chip: read_chip,
            write_chip: write_chip,
            on: on,
            off: off,
            forceMono: forceMono,
            bassBoost: bassBoost,
            mute: mute,
            deemphasis75: deemphasis75,
            volume: volume,
            setFrequency: setFrequency,
            getState: getState,
            getRDS: getRDS,
            seekUp: seekUp,
            seekDown: seekDown
        };
    }
    
    var radio = Radio(D4, D5);
    radio.on(function(){
      radio.setFrequency(104.5);
    });
    
    
  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Getting I2C on ESP8266 to work

Posted by Avatar for virtualcodewarrior @virtualcodewarrior

Actions