Mirror with some correction
Dependencies: mbed FastIO FastPWM USBDevice
BitBangI2C/BitBangI2C.h@116:7a67265d7c19, 2021-10-01 (annotated)
- Committer:
- arnoz
- Date:
- Fri Oct 01 08:19:46 2021 +0000
- Revision:
- 116:7a67265d7c19
- Parent:
- 87:8d35c74403af
- Correct information regarding your last merge
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
mjr | 86:e30a1f60f783 | 1 | // Bit-bang I2C for KL25Z |
mjr | 86:e30a1f60f783 | 2 | // |
mjr | 86:e30a1f60f783 | 3 | // This implements an I2C interface that can operate on any KL25Z GPIO |
mjr | 86:e30a1f60f783 | 4 | // ports, whether or not they're connected to I2C hardware on the MCU. |
mjr | 86:e30a1f60f783 | 5 | // We simply send and receive bits using direct port manipulation (often |
mjr | 86:e30a1f60f783 | 6 | // called "bit banging") instead of using the MCU I2C hardware. This |
mjr | 86:e30a1f60f783 | 7 | // is more flexible than the mbed I2C class, since that only works with |
mjr | 86:e30a1f60f783 | 8 | // a small number of pins, and there are only two I2C modules in the |
mjr | 86:e30a1f60f783 | 9 | // system. This GPIO version can be to gain additional I2C ports if |
mjr | 86:e30a1f60f783 | 10 | // the hardware I2C modules are committed to other purposes, or all of |
mjr | 86:e30a1f60f783 | 11 | // the I2C-capable pins are being used for other purposes. |
mjr | 86:e30a1f60f783 | 12 | // |
mjr | 86:e30a1f60f783 | 13 | // The tradeoff for the added flexibility is that the hardware I2C is |
mjr | 86:e30a1f60f783 | 14 | // faster. This implementation can take advantage of bus speeds up to |
mjr | 86:e30a1f60f783 | 15 | // about 500kHz, which produces data rates of about 272 kbps. Higher |
mjr | 86:e30a1f60f783 | 16 | // clock speeds are allowed, but the actual bit rate will plateau at |
mjr | 86:e30a1f60f783 | 17 | // this level due to the performance constraints of the CPU (and of |
mjr | 86:e30a1f60f783 | 18 | // this code itself; some additional performance could probably be |
mjr | 86:e30a1f60f783 | 19 | // gained by optimizing it further). The KL25Z I2C hardware can double |
mjr | 86:e30a1f60f783 | 20 | // our speed: it can achieve bus speeds of 1MHz and data rates of about |
mjr | 86:e30a1f60f783 | 21 | // 540kbps. Of course, such high speeds can only be used with compatible |
mjr | 86:e30a1f60f783 | 22 | // devices; many devices are limited to the "standard mode" at 100kHz or |
mjr | 86:e30a1f60f783 | 23 | // "fast mode" at 400kHz, both of which we can fully saturate. However, |
mjr | 86:e30a1f60f783 | 24 | // even at the slower bus speeds, the hardware I2C has another advantage: |
mjr | 86:e30a1f60f783 | 25 | // it's capable of DMA operation. That's vastly superior for large |
mjr | 86:e30a1f60f783 | 26 | // transactions since it lets the CPU do other work in parallel with |
mjr | 86:e30a1f60f783 | 27 | // I2C bit movement. |
mjr | 82:4f6209cb5c33 | 28 | // |
mjr | 86:e30a1f60f783 | 29 | // This class isn't meant to be directly compatible with the mbed I2C |
mjr | 86:e30a1f60f783 | 30 | // class, but we try to adhere to the mbed conventions and method names |
mjr | 86:e30a1f60f783 | 31 | // to make it a mostly drop-in replacement. In particular, we use the |
mjr | 86:e30a1f60f783 | 32 | // mbed library's "2X" device address convention. Most device data sheets |
mjr | 86:e30a1f60f783 | 33 | // list the device I2C address in 7-bit format, so you'll have to shift |
mjr | 86:e30a1f60f783 | 34 | // the nominal address from the data sheet left one bit in each call |
mjr | 86:e30a1f60f783 | 35 | // to a routine here. |
mjr | 86:e30a1f60f783 | 36 | // |
mjr | 87:8d35c74403af | 37 | // Electrically, the I2C bus consists of two lines, SDA (data) and SCL |
mjr | 87:8d35c74403af | 38 | // (clock). Multiple devices can connect to the bus by connecting to |
mjr | 87:8d35c74403af | 39 | // these two lines; the lines are shared among all of the devices. Each |
mjr | 87:8d35c74403af | 40 | // line has a pull-up resistor that pulls it to logic '1' voltage. Each |
mjr | 87:8d35c74403af | 41 | // device connects with an open-collector circuit that can short the line |
mjr | 87:8d35c74403af | 42 | // to ground (logic '0'). This means that any device can assert a 'low' |
mjr | 87:8d35c74403af | 43 | // but no one can actually assert a 'high'; the pull-up makes it so that |
mjr | 87:8d35c74403af | 44 | // a 'high' occurs when no one is asserting a 'low'. On an MCU, we release |
mjr | 87:8d35c74403af | 45 | // a line by putting the GPIO pin in high-Z state, which we can do on the |
mjr | 87:8d35c74403af | 46 | // KL25Z by setting its direction to INPUT mode. So our GPIO write strategy |
mjr | 87:8d35c74403af | 47 | // is like this: |
mjr | 86:e30a1f60f783 | 48 | // |
mjr | 86:e30a1f60f783 | 49 | // - take a pin low (0): |
mjr | 86:e30a1f60f783 | 50 | // pin.input(); |
mjr | 86:e30a1f60f783 | 51 | // pin.write(0); |
mjr | 86:e30a1f60f783 | 52 | // |
mjr | 86:e30a1f60f783 | 53 | // - take a pin high (1): |
mjr | 86:e30a1f60f783 | 54 | // pin.output(); |
mjr | 86:e30a1f60f783 | 55 | // |
mjr | 86:e30a1f60f783 | 56 | // Note that we don't actually have to write the '0' on each pull low, |
mjr | 86:e30a1f60f783 | 57 | // since we just leave the port output register set with '0'. Changing |
mjr | 86:e30a1f60f783 | 58 | // the direction to output is enough to assert the low level, since the |
mjr | 86:e30a1f60f783 | 59 | // hardware asserts the level that was previously stored in the output |
mjr | 86:e30a1f60f783 | 60 | // register whenever the direction is changed from input to output. |
mjr | 87:8d35c74403af | 61 | // |
mjr | 87:8d35c74403af | 62 | // The KL25Z by default provides a built-in pull-up resistor on each GPIO |
mjr | 87:8d35c74403af | 63 | // set to input mode. This can optionally be used as the bus-wide pull-up |
mjr | 87:8d35c74403af | 64 | // for each line. Standard practice is to use external pull-up resistors |
mjr | 87:8d35c74403af | 65 | // rather than MCU pull-ups, but the internal pull-ups are fine for ad hoc |
mjr | 87:8d35c74403af | 66 | // setups where there's only one external device connected to a GPIO pair. |
mjr | 86:e30a1f60f783 | 67 | |
mjr | 82:4f6209cb5c33 | 68 | |
mjr | 82:4f6209cb5c33 | 69 | #ifndef _BITBANGI2C_H_ |
mjr | 82:4f6209cb5c33 | 70 | #define _BITBANGI2C_H_ |
mjr | 82:4f6209cb5c33 | 71 | |
mjr | 82:4f6209cb5c33 | 72 | #include "mbed.h" |
mjr | 86:e30a1f60f783 | 73 | #include "gpio_api.h" |
mjr | 86:e30a1f60f783 | 74 | #include "pinmap.h" |
mjr | 86:e30a1f60f783 | 75 | |
mjr | 86:e30a1f60f783 | 76 | |
mjr | 87:8d35c74403af | 77 | // For testing purposes: a cover class for the mbed library I2C bridging |
mjr | 87:8d35c74403af | 78 | // the minor differences in our interface. This allows switching between |
mjr | 87:8d35c74403af | 79 | // BitBangI2C and the mbed library I2C via a macro of the like. |
mjr | 87:8d35c74403af | 80 | class MbedI2C: public I2C |
mjr | 87:8d35c74403af | 81 | { |
mjr | 87:8d35c74403af | 82 | public: |
mjr | 87:8d35c74403af | 83 | MbedI2C(PinName sda, PinName scl, bool internalPullups) : I2C(sda, scl) { } |
mjr | 87:8d35c74403af | 84 | |
mjr | 87:8d35c74403af | 85 | int write(int addr, const uint8_t *data, size_t len, bool repeated = false) |
mjr | 87:8d35c74403af | 86 | { |
mjr | 87:8d35c74403af | 87 | return I2C::write(addr, (const char *)data, len, repeated); |
mjr | 87:8d35c74403af | 88 | } |
mjr | 87:8d35c74403af | 89 | int read(int addr, uint8_t *data, size_t len, bool repeated = false) |
mjr | 87:8d35c74403af | 90 | { |
mjr | 87:8d35c74403af | 91 | return I2C::read(addr, (char *)data, len, repeated); |
mjr | 87:8d35c74403af | 92 | } |
mjr | 87:8d35c74403af | 93 | |
mjr | 87:8d35c74403af | 94 | void reset() { } |
mjr | 87:8d35c74403af | 95 | }; |
mjr | 87:8d35c74403af | 96 | |
mjr | 87:8d35c74403af | 97 | |
mjr | 86:e30a1f60f783 | 98 | // DigitalInOut replacmement class for I2C use. I2C uses pins a little |
mjr | 86:e30a1f60f783 | 99 | // differently from other use cases. I2C is a bus, where many devices can |
mjr | 86:e30a1f60f783 | 100 | // be attached to each line. To allow this shared access, devices can |
mjr | 86:e30a1f60f783 | 101 | // only drive the line low. No device can drive the line high; instead, |
mjr | 86:e30a1f60f783 | 102 | // the line is *pulled* high, by the attached pull-up resistors, when no |
mjr | 86:e30a1f60f783 | 103 | // one is driving it low. As a result, we can't use the normal DigitalOut |
mjr | 86:e30a1f60f783 | 104 | // write(), since that would try to actively drive the pin high on write(1). |
mjr | 86:e30a1f60f783 | 105 | // Instead, write(1) needs to change the pin to high-impedance (high-Z) |
mjr | 86:e30a1f60f783 | 106 | // state instead of driving it, which on the KL25Z is accomplished by |
mjr | 86:e30a1f60f783 | 107 | // changing the port direction mode to INPUT. So: |
mjr | 86:e30a1f60f783 | 108 | // |
mjr | 86:e30a1f60f783 | 109 | // write(0) = direction->OUTPUT (pin->0) |
mjr | 86:e30a1f60f783 | 110 | // write(1) = direction->INPUT |
mjr | 86:e30a1f60f783 | 111 | // |
mjr | 86:e30a1f60f783 | 112 | class I2CInOut |
mjr | 86:e30a1f60f783 | 113 | { |
mjr | 86:e30a1f60f783 | 114 | public: |
mjr | 87:8d35c74403af | 115 | I2CInOut(PinName pin, bool internalPullup) |
mjr | 86:e30a1f60f783 | 116 | { |
mjr | 86:e30a1f60f783 | 117 | // initialize the pin |
mjr | 86:e30a1f60f783 | 118 | gpio_t g; |
mjr | 86:e30a1f60f783 | 119 | gpio_init(&g, pin); |
mjr | 86:e30a1f60f783 | 120 | |
mjr | 86:e30a1f60f783 | 121 | // get the registers |
mjr | 87:8d35c74403af | 122 | unsigned int portno = (unsigned int)pin >> PORT_SHIFT; |
mjr | 87:8d35c74403af | 123 | uint32_t pinno = (uint32_t)(pin & 0x7C) >> 2; |
mjr | 87:8d35c74403af | 124 | FGPIO_Type *r = (FGPIO_Type *)(FPTA_BASE + portno*0x40); |
mjr | 87:8d35c74403af | 125 | __IO uint32_t *pin_pcr = &(((PORT_Type *)(PORTA_BASE + 0x1000*portno)))->PCR[pinno]; |
mjr | 86:e30a1f60f783 | 126 | |
mjr | 87:8d35c74403af | 127 | // set the desired internal pull-up mode |
mjr | 87:8d35c74403af | 128 | if (internalPullup) |
mjr | 87:8d35c74403af | 129 | *pin_pcr |= 0x02; |
mjr | 87:8d35c74403af | 130 | else |
mjr | 87:8d35c74403af | 131 | *pin_pcr &= ~0x02; |
mjr | 86:e30a1f60f783 | 132 | |
mjr | 86:e30a1f60f783 | 133 | // save the register information we'll need later |
mjr | 86:e30a1f60f783 | 134 | this->mask = g.mask; |
mjr | 86:e30a1f60f783 | 135 | this->PDDR = &r->PDDR; |
mjr | 86:e30a1f60f783 | 136 | this->PDIR = &r->PDIR; |
mjr | 86:e30a1f60f783 | 137 | |
mjr | 86:e30a1f60f783 | 138 | // initially set as input to release the line |
mjr | 86:e30a1f60f783 | 139 | r->PDDR &= ~mask; |
mjr | 86:e30a1f60f783 | 140 | |
mjr | 86:e30a1f60f783 | 141 | // Set the output value to 0. It will always be zero, since |
mjr | 86:e30a1f60f783 | 142 | // this is the only value we ever drive. When we want the port |
mjr | 86:e30a1f60f783 | 143 | // to go high, we release it by changing the direction to input. |
mjr | 86:e30a1f60f783 | 144 | r->PCOR = mask; |
mjr | 86:e30a1f60f783 | 145 | } |
mjr | 86:e30a1f60f783 | 146 | |
mjr | 86:e30a1f60f783 | 147 | // write a 1 (high) or 0 (low) value to the pin |
mjr | 86:e30a1f60f783 | 148 | inline void write(int b) { if (b) hi(); else lo(); } |
mjr | 86:e30a1f60f783 | 149 | |
mjr | 86:e30a1f60f783 | 150 | // Take the line high: set as input to put it in high-Z state so that |
mjr | 86:e30a1f60f783 | 151 | // the pull-up resistor takes over. |
mjr | 86:e30a1f60f783 | 152 | inline void hi() { *PDDR &= ~mask; } |
mjr | 86:e30a1f60f783 | 153 | |
mjr | 86:e30a1f60f783 | 154 | // Take the line low: set as output to assert our '0' on the line and |
mjr | 86:e30a1f60f783 | 155 | // pull it low. Note that we don't have to explicitly write the port |
mjr | 86:e30a1f60f783 | 156 | // output register, since we initialized it with a '0' on our port and |
mjr | 86:e30a1f60f783 | 157 | // never change it. The hardware will assert the level stored in the |
mjr | 86:e30a1f60f783 | 158 | // register each time we change the direction to output, so there's no |
mjr | 86:e30a1f60f783 | 159 | // need to write the port output register again each time. |
mjr | 86:e30a1f60f783 | 160 | inline void lo() { *PDDR |= mask; } |
mjr | 86:e30a1f60f783 | 161 | |
mjr | 86:e30a1f60f783 | 162 | // read the line |
mjr | 86:e30a1f60f783 | 163 | inline int read() |
mjr | 86:e30a1f60f783 | 164 | { |
mjr | 86:e30a1f60f783 | 165 | *PDDR &= ~mask; // set as input |
mjr | 86:e30a1f60f783 | 166 | return *PDIR & mask; // read the port |
mjr | 86:e30a1f60f783 | 167 | } |
mjr | 86:e30a1f60f783 | 168 | |
mjr | 86:e30a1f60f783 | 169 | // direction register |
mjr | 86:e30a1f60f783 | 170 | volatile uint32_t *PDDR; |
mjr | 86:e30a1f60f783 | 171 | |
mjr | 86:e30a1f60f783 | 172 | // input register |
mjr | 86:e30a1f60f783 | 173 | volatile uint32_t *PDIR; |
mjr | 86:e30a1f60f783 | 174 | |
mjr | 86:e30a1f60f783 | 175 | // pin mask |
mjr | 86:e30a1f60f783 | 176 | uint32_t mask; |
mjr | 86:e30a1f60f783 | 177 | }; |
mjr | 86:e30a1f60f783 | 178 | |
mjr | 86:e30a1f60f783 | 179 | |
mjr | 86:e30a1f60f783 | 180 | |
mjr | 86:e30a1f60f783 | 181 | // bit-bang I2C |
mjr | 82:4f6209cb5c33 | 182 | class BitBangI2C |
mjr | 82:4f6209cb5c33 | 183 | { |
mjr | 82:4f6209cb5c33 | 184 | public: |
mjr | 82:4f6209cb5c33 | 185 | // create the interface |
mjr | 87:8d35c74403af | 186 | BitBangI2C(PinName sda, PinName scl, bool internalPullups); |
mjr | 82:4f6209cb5c33 | 187 | |
mjr | 82:4f6209cb5c33 | 188 | // set the bus frequency in Hz |
mjr | 86:e30a1f60f783 | 189 | void frequency(uint32_t freq); |
mjr | 82:4f6209cb5c33 | 190 | |
mjr | 82:4f6209cb5c33 | 191 | // set START condition on the bus |
mjr | 82:4f6209cb5c33 | 192 | void start(); |
mjr | 82:4f6209cb5c33 | 193 | |
mjr | 82:4f6209cb5c33 | 194 | // set STOP condition on the bus |
mjr | 82:4f6209cb5c33 | 195 | void stop(); |
mjr | 82:4f6209cb5c33 | 196 | |
mjr | 82:4f6209cb5c33 | 197 | // Write a series of bytes. Returns 0 on success, non-zero on failure. |
mjr | 82:4f6209cb5c33 | 198 | // Important: 'addr' is 2X the nominal address - shift left by one bit. |
mjr | 82:4f6209cb5c33 | 199 | int write(uint8_t addr, const uint8_t *data, size_t len, bool repeated = false); |
mjr | 82:4f6209cb5c33 | 200 | |
mjr | 82:4f6209cb5c33 | 201 | // write a byte; returns true if ACK was received |
mjr | 82:4f6209cb5c33 | 202 | int write(uint8_t data); |
mjr | 82:4f6209cb5c33 | 203 | |
mjr | 82:4f6209cb5c33 | 204 | // Read a series of bytes. Returns 0 on success, non-zero on failure. |
mjr | 82:4f6209cb5c33 | 205 | // Important: 'addr' is 2X the nominal address - shift left by one bit. |
mjr | 82:4f6209cb5c33 | 206 | int read(uint8_t addr, uint8_t *data, size_t len, bool repeated = false); |
mjr | 82:4f6209cb5c33 | 207 | |
mjr | 82:4f6209cb5c33 | 208 | // read a byte, optionally sending an ACK on receipt |
mjr | 82:4f6209cb5c33 | 209 | int read(bool ack); |
mjr | 82:4f6209cb5c33 | 210 | |
mjr | 82:4f6209cb5c33 | 211 | // wait for ACK; returns true if ACK was received |
mjr | 82:4f6209cb5c33 | 212 | bool wait(uint32_t timeout_us); |
mjr | 82:4f6209cb5c33 | 213 | |
mjr | 82:4f6209cb5c33 | 214 | // reset the bus |
mjr | 82:4f6209cb5c33 | 215 | void reset(); |
mjr | 82:4f6209cb5c33 | 216 | |
mjr | 82:4f6209cb5c33 | 217 | protected: |
mjr | 82:4f6209cb5c33 | 218 | // read/write a bit |
mjr | 82:4f6209cb5c33 | 219 | int readBit(); |
mjr | 86:e30a1f60f783 | 220 | |
mjr | 86:e30a1f60f783 | 221 | // write a bit |
mjr | 86:e30a1f60f783 | 222 | inline void writeBit(int bit) |
mjr | 86:e30a1f60f783 | 223 | { |
mjr | 86:e30a1f60f783 | 224 | // put the bit on the SDA line |
mjr | 86:e30a1f60f783 | 225 | sdaPin.write(bit); |
mjr | 86:e30a1f60f783 | 226 | hiResWait(tSuDat); |
mjr | 86:e30a1f60f783 | 227 | |
mjr | 86:e30a1f60f783 | 228 | // clock it |
mjr | 86:e30a1f60f783 | 229 | sclPin.hi(); |
mjr | 87:8d35c74403af | 230 | hiResWait(tHigh); |
mjr | 86:e30a1f60f783 | 231 | |
mjr | 86:e30a1f60f783 | 232 | // drop the clock |
mjr | 86:e30a1f60f783 | 233 | sclPin.lo(); |
mjr | 86:e30a1f60f783 | 234 | hiResWait(tLow); |
mjr | 86:e30a1f60f783 | 235 | } |
mjr | 82:4f6209cb5c33 | 236 | |
mjr | 82:4f6209cb5c33 | 237 | // set SCL/SDA lines to high (1) or low(0) |
mjr | 86:e30a1f60f783 | 238 | inline void scl(int level) { sclPin.write(level); } |
mjr | 86:e30a1f60f783 | 239 | inline void sda(int level) { sdaPin.write(level); } |
mjr | 86:e30a1f60f783 | 240 | |
mjr | 86:e30a1f60f783 | 241 | inline void sclHi() { sclPin.hi(); } |
mjr | 86:e30a1f60f783 | 242 | inline void sclLo() { sclPin.lo(); } |
mjr | 86:e30a1f60f783 | 243 | inline void sdaHi() { sdaPin.hi(); } |
mjr | 86:e30a1f60f783 | 244 | inline void sdaLo() { sdaPin.lo(); } |
mjr | 82:4f6209cb5c33 | 245 | |
mjr | 87:8d35c74403af | 246 | // SDA and SCL pins |
mjr | 87:8d35c74403af | 247 | I2CInOut sdaPin; |
mjr | 86:e30a1f60f783 | 248 | I2CInOut sclPin; |
mjr | 82:4f6209cb5c33 | 249 | |
mjr | 82:4f6209cb5c33 | 250 | // inverse of frequency = clock period in microseconds |
mjr | 82:4f6209cb5c33 | 251 | uint32_t clkPeriod_us; |
mjr | 86:e30a1f60f783 | 252 | |
mjr | 86:e30a1f60f783 | 253 | // High-resolution wait. This provides sub-microsecond wait |
mjr | 86:e30a1f60f783 | 254 | // times, to get minimum times for I2C events. With the ARM |
mjr | 86:e30a1f60f783 | 255 | // compiler, this produces measured wait times as follows: |
mjr | 86:e30a1f60f783 | 256 | // |
mjr | 86:e30a1f60f783 | 257 | // n=0 104ns |
mjr | 86:e30a1f60f783 | 258 | // n=1 167ns |
mjr | 86:e30a1f60f783 | 259 | // n=2 271ns |
mjr | 86:e30a1f60f783 | 260 | // n=3 375ns |
mjr | 86:e30a1f60f783 | 261 | // n=4 479ns |
mjr | 86:e30a1f60f783 | 262 | // |
mjr | 86:e30a1f60f783 | 263 | // For n > 1, the wait time is 167ns + (n-1)*104ns. |
mjr | 86:e30a1f60f783 | 264 | // These times take into account caller overhead to load the |
mjr | 86:e30a1f60f783 | 265 | // wait time from a member variable. Callers getting the wait |
mjr | 86:e30a1f60f783 | 266 | // time from a constant or stack variable will have different |
mjr | 86:e30a1f60f783 | 267 | // results. |
mjr | 86:e30a1f60f783 | 268 | inline void hiResWait(volatile int n) |
mjr | 86:e30a1f60f783 | 269 | { |
mjr | 86:e30a1f60f783 | 270 | while (n != 0) |
mjr | 86:e30a1f60f783 | 271 | --n; |
mjr | 86:e30a1f60f783 | 272 | } |
mjr | 86:e30a1f60f783 | 273 | |
mjr | 86:e30a1f60f783 | 274 | // Figure the hiResWait() time for a given nanosecond time. |
mjr | 86:e30a1f60f783 | 275 | // We use this during setup to precompute the wait times required |
mjr | 86:e30a1f60f783 | 276 | // for various events at a given clock speed. |
mjr | 86:e30a1f60f783 | 277 | int calcHiResWaitTime(int nanoseconds) |
mjr | 86:e30a1f60f783 | 278 | { |
mjr | 86:e30a1f60f783 | 279 | // the shortest wait time is 104ns |
mjr | 86:e30a1f60f783 | 280 | if (nanoseconds <= 104) |
mjr | 86:e30a1f60f783 | 281 | return 0; |
mjr | 86:e30a1f60f783 | 282 | |
mjr | 86:e30a1f60f783 | 283 | // Above that, we work in 104ns increments with a base |
mjr | 86:e30a1f60f783 | 284 | // of 167ns. We round at the halfway point, because we |
mjr | 86:e30a1f60f783 | 285 | // assume there's always a little extra overhead in the |
mjr | 86:e30a1f60f783 | 286 | // caller itself that will pad by at least one instruction |
mjr | 86:e30a1f60f783 | 287 | // of 60ns, which is more than half our interval. |
mjr | 86:e30a1f60f783 | 288 | return (nanoseconds - 167 + 52)/104 + 1; |
mjr | 86:e30a1f60f783 | 289 | } |
mjr | 86:e30a1f60f783 | 290 | |
mjr | 86:e30a1f60f783 | 291 | // Time delays for I2C events. I2C has minimum timing requirements |
mjr | 86:e30a1f60f783 | 292 | // based on the clock speed. Some of these are as short as 50ns. |
mjr | 86:e30a1f60f783 | 293 | // The mbed wait timer has microsecond resolution, which is much |
mjr | 86:e30a1f60f783 | 294 | // too coarse for fast I2C clock speeds, so we implement our own |
mjr | 86:e30a1f60f783 | 295 | // finer-grained wait. |
mjr | 86:e30a1f60f783 | 296 | // |
mjr | 86:e30a1f60f783 | 297 | // These are in hiResWait() units - see above. |
mjr | 86:e30a1f60f783 | 298 | // |
mjr | 86:e30a1f60f783 | 299 | int tLow; // SCL low period |
mjr | 86:e30a1f60f783 | 300 | int tHigh; // SCL high period |
mjr | 86:e30a1f60f783 | 301 | int tHdSta; // hold time for start condition |
mjr | 86:e30a1f60f783 | 302 | int tSuSta; // setup time for repeated start condition |
mjr | 86:e30a1f60f783 | 303 | int tSuSto; // setup time for stop condition |
mjr | 86:e30a1f60f783 | 304 | int tSuDat; // data setup time |
mjr | 86:e30a1f60f783 | 305 | int tAck; // ACK time |
mjr | 87:8d35c74403af | 306 | int tBuf; // bus free time between start and stop conditions |
mjr | 87:8d35c74403af | 307 | |
mjr | 87:8d35c74403af | 308 | // are we in a Stop condition? |
mjr | 87:8d35c74403af | 309 | bool inStop; |
mjr | 82:4f6209cb5c33 | 310 | }; |
mjr | 82:4f6209cb5c33 | 311 | |
mjr | 82:4f6209cb5c33 | 312 | #endif /* _BITBANGI2C_H_ */ |