i2c: set hold time of SDA during transmit to an appropriate value (#273)
* i2c: set hold time of SDA during transmit to 2 for TCS34725 color sensor * i2c: fix issues in i2c_write_blocking_internal * i2c: rename sda_hold_count to sda_tx_hold_count * use assert rather than invalid_params_if for internal consistency checks * i2c: use a more appropriate sda tx hold time at higher baudrates * i2c: reduce 120/1e9 to the smallest possible integer numerator and denominator * Don't use implicit 0 for field LSB Co-authored-by: Luke Wren <wren6991@gmail.com>
This commit is contained in:
		@ -75,6 +75,24 @@ uint i2c_set_baudrate(i2c_inst_t *i2c, uint baudrate) {
 | 
				
			|||||||
    invalid_params_if(I2C, hcnt < 8);
 | 
					    invalid_params_if(I2C, hcnt < 8);
 | 
				
			||||||
    invalid_params_if(I2C, lcnt < 8);
 | 
					    invalid_params_if(I2C, lcnt < 8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Per I2C-bus specification a device in standard or fast mode must
 | 
				
			||||||
 | 
					    // internally provide a hold time of at least 300ns for the SDA signal to
 | 
				
			||||||
 | 
					    // bridge the undefined region of the falling edge of SCL. A smaller hold
 | 
				
			||||||
 | 
					    // time of 120ns is used for fast mode plus.
 | 
				
			||||||
 | 
					    uint sda_tx_hold_count;
 | 
				
			||||||
 | 
					    if (baudrate < 1000000) {
 | 
				
			||||||
 | 
					        // sda_tx_hold_count = freq_in [cycles/s] * 300ns * (1s / 1e9ns)
 | 
				
			||||||
 | 
					        // Reduce 300/1e9 to 3/1e7 to avoid numbers that don't fit in uint.
 | 
				
			||||||
 | 
					        // Add 1 to avoid division truncation.
 | 
				
			||||||
 | 
					        sda_tx_hold_count = ((freq_in * 3) / 10000000) + 1;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        // sda_tx_hold_count = freq_in [cycles/s] * 120ns * (1s / 1e9ns)
 | 
				
			||||||
 | 
					        // Reduce 120/1e9 to 3/25e6 to avoid numbers that don't fit in uint.
 | 
				
			||||||
 | 
					        // Add 1 to avoid division truncation.
 | 
				
			||||||
 | 
					        sda_tx_hold_count = ((freq_in * 3) / 25000000) + 1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    assert(sda_tx_hold_count <= lcnt - 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    i2c->hw->enable = 0;
 | 
					    i2c->hw->enable = 0;
 | 
				
			||||||
    // Always use "fast" mode (<= 400 kHz, works fine for standard mode too)
 | 
					    // Always use "fast" mode (<= 400 kHz, works fine for standard mode too)
 | 
				
			||||||
    hw_write_masked(&i2c->hw->con,
 | 
					    hw_write_masked(&i2c->hw->con,
 | 
				
			||||||
@ -84,6 +102,9 @@ uint i2c_set_baudrate(i2c_inst_t *i2c, uint baudrate) {
 | 
				
			|||||||
    i2c->hw->fs_scl_hcnt = hcnt;
 | 
					    i2c->hw->fs_scl_hcnt = hcnt;
 | 
				
			||||||
    i2c->hw->fs_scl_lcnt = lcnt;
 | 
					    i2c->hw->fs_scl_lcnt = lcnt;
 | 
				
			||||||
    i2c->hw->fs_spklen = lcnt < 16 ? 1 : lcnt / 16;
 | 
					    i2c->hw->fs_spklen = lcnt < 16 ? 1 : lcnt / 16;
 | 
				
			||||||
 | 
					    hw_write_masked(&i2c->hw->sda_hold,
 | 
				
			||||||
 | 
					                    sda_tx_hold_count << I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_LSB,
 | 
				
			||||||
 | 
					                    I2C_IC_SDA_HOLD_IC_SDA_TX_HOLD_BITS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    i2c->hw->enable = 1;
 | 
					    i2c->hw->enable = 1;
 | 
				
			||||||
    return freq_in / period;
 | 
					    return freq_in / period;
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user