I2C: 7 Or 8 Bit Addresses? [Fixed! Pull Request Submitted.] Home » Arduino I2c 8 Bit Address » I2C: 7 Or 8 Bit Addresses? [Fixed! Pull Request Submitted.] Maybe your like Arduino I2c Chat Arduino I2c Eeprom Example Code Arduino I2c Io Expander Code Arduino I2c Lcd1602 Example Arduino I2c Lcd 2004 Example I2C: 7 or 8 Bit Addresses? [Fixed! Pull Request Submitted.] Troubleshooting timb January 7, 2014, 7:18am 1 I’m working with a little OLED display here, which has a 7-Bit address of 0x27. Normally, this is what you’d use with Arduino and what’s listed in the Spark Docs. Unfortunately, according to both my Logic 16 and Bus Pirate, it’s trying to write to 0x26. It doesn’t matter if I put 0x27 or 39 into beginTransmission. However, if I use 0x4E (which is the full write address) it works. 0b0100111 in hex => 0x27 0b01001110 in hex => 0x4E 0b01001111 in hex => 0x4F I’ve tried resetting the Core to factory firmware, then flashing my code over (which was the cause of my last mysterious I2C problem) to no avail. Anyone else seeing similar issues? 1 Like I2C not working as expected BDub January 7, 2014, 7:44am 2 In I2C, all writes are even, and reads are odd… the LSB determines if it’s read or write in the address only. Perhaps you should not try to write to 0x27, but rather 0x4E? It might say it’s a 7 bit address, and it is (left justified), but it has to actually send an extra 8th bit to tell your OLED that it’s trying to write to to that address. Technically, 0x4E is 0x27 left justified, plus the write bit. Also to note that I2C transmits MSB first, so that 8th bit that it sends is actually bit0 in the 8bit address field. If that doesn’t make sense, link me to your datasheet and I’ll see if they clarify better. 1 Like timb January 7, 2014, 8:18am 3 Yeah, I know how I2C works. See the code block in my OP? 0x4E is 0x27 with a zero tacked on to the end. 0x27 = 7-Bits and 0x4E = 8-Bits. (Which you covered.) Wire is supposed to accept 7-Bit addresses; if you give it an 8-Bit address it even drops the last bit. So that part is working, because I can write to 0x4E and 0x4F just fine. (If I can write to the read address [0x27>>1=0x4F] then the library must be going from 8 to 7 to 8 bits with the address like the documentation claims.) In Energia on my MSP430, I can use 0x27 to talk to this OLED, so I assume Arduino would be the same. Does that make more sense? BDub January 7, 2014, 8:35am 4 Ya know… 0x27 is technically an 8-bit number, and should be treated as 0x26 during a write. How would the routine know 0x27 should be left justified? … only if you configured it as 7BIT somehow, then it would left justify everything you send it. There are a lot of commented out parts, so I’m guessing I2C is not fully implemented yet: https://github.com/spark/core-firmware/blob/master/src/spark_wiring_i2c.cpp#L212 txAddress just gets set to the value you send… not sure what happens after that. Just use 0x4E and get dumb! 1 Like timb January 7, 2014, 8:49pm 5 BDub: Ya know... 0x27 is technically an 8-bit number, and should be treated as 0x26 during a write. Haha, well, you are technically correct. (Which is the best type of correct!) BDub: How would the routine know 0x27 should be left justified? ... only if you configured it as 7BIT somehow, then it would left justify everything you send it. The routine is supposed to accept 7b addresses: beginTransmission() Begin a transmission to the I2C slave device with the given address. Subsequently, queue bytes for transmission with the write() function and transmit them by calling endTransmission(). Parameters: address: the 7-bit address of the device to transmit to. But how does it know it's supposed to be 7b? I2C has an address range from 0 to 127. 127 in binary => 0b01111111 So the highest address is seven ones. Remember, in 8-Bit Binary the MSB represents decimal 128. So, the Wire library should be dropping the MSB and adding the read or write bit as the new LSB. However, in the Spark Team's implementation, it seems clear that they're just flat out replacing the LSB without doing any sort of shifting. This fully explains 0x27 becoming 0x26 and 0x4F/0x4E both working as write addresses. I'll start tracking down the trouble in the sources. That byte swapping should be happening well after the code you linked @BDub. Thanks for taking a peak though! 1 Like timb January 7, 2014, 10:28pm 6 Here we go! particle-iot/core-common-lib/blob/master/STM32F10x_StdPeriph_Driver/src/stm32f10x_i2c.c /** ****************************************************************************** * @file stm32f10x_i2c.c * @author MCD Application Team * @version V3.6.1 * @date 05-March-2012 * @brief This file provides all the I2C firmware functions. ****************************************************************************** Released into the public domain. This work is free: you can redistribute it and/or modify it under the terms of Creative Commons Zero license v1.0 This work is licensed under the Creative Commons Zero 1.0 United States License. To view a copy of this license, visit http://creativecommons.org/publicdomain/zero/1.0/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. This file has been truncated. show original Specifically this bit right here: Address &= OAR1_ADD0_Reset; If we look up those #define statements, we see this: /* I2C ADD0 mask */ #define OAR1_ADD0_Set ((uint16_t)0x0001) #define OAR1_ADD0_Reset ((uint16_t)0xFFFE) With a little math, we find this: 0x27 & 0xFFFE => 0x26 0x4E & 0xFFFE => 0x4E 0x4F & 0xFFFE => 0x4E Looking elsewhere we see that even this library (the STM32 driver) is expecting a 7-bit address. It looks like the pre-processing that exists in the Arduino Wire library isn’t present here. So my next step is to go check that code and see if I can make the same changes here! timb January 8, 2014, 3:16am 7 Annnd I fixed it. I just need to run some more tests to make sure all cases are valid, once I confirm I’ll post a patch here and submit a pull request for the Spark Team. 2 Likes BDub January 8, 2014, 4:28am 8 I will agree their implementation is not the standard. There really isn’t 7-bit AND 8-bit addressing… just 7-bit, but some vendors include the read/write bit as part of the address so it makes it look like it’s 8-bit. Technically it’s just 7-bit, especially if you are using a .write(); method which implies that the 8th bit is 0, the read/write bit. The arduino twi.c library expects a 7-bit address and left shifts it by one and ORs in the read/write bit. Documentation should be changed from: Parameters: address: the 7-bit address of the device to transmit to. To something like: Parameters: address: the 7-bit address of the device to transmit to. Note: do not include the r/w bit with the address, or the address will be corrupted. once it’s fixed that is… 1 Like timb January 8, 2014, 7:36am 9 So here's my fix for the I2C address issue! As it turns out, all the magic happens within the platform specific I2C libraries on Arduino. There's no pre-processing or anything, they simply expect you to be providing an 7-Bit address. The fix was actually quite simple, really! ../core-common-lib/STM32F10x_StdPeriph_Driver/src/stm32f10x_i2c.c: void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction) { /* Check the parameters */ assert_param(IS_I2C_ALL_PERIPH(I2Cx)); assert_param(IS_I2C_DIRECTION(I2C_Direction)); /* Test on the direction to set/reset the read/write bit */ if (I2C_Direction != I2C_Direction_Transmitter) { /* Shift the address one bit to the left */ Address = Address << 1; /* Set the address bit0 for read */ Address |= OAR1_ADD0_Set; } else { /* Shift the address one bit to the left */ Address = Address << 1; /* Reset the address bit0 for write */ Address &= OAR1_ADD0_Reset; } /* Send the address */ I2Cx->DR = Address; } So, basically what's happening here is this: I2C_Send7bitAddress is called anytime you're setting up to read or write over the I2C interface. It's called surprisingly late in the code, coming after the START bit has already been transmitted. (I say this is surprising, because a lot of microcontrollers would have already put the slave address into read and write registers during beginTransmission, whereas the STM32 has a single address register that's only being set once endTransmission is called and communication has started.) if (I2C_Direction != I2C_Direction_Transmitter) is checking to see if we're supposed to be reading from this address. If so, we'll use Address |= OAR1_ADD0_Set; to perform a bitwise OR operation between it and 0x0001. If we're supposed to be writing to the address, we'll perform a bitwise AND between it and 0xFFFE like this: Address &= OAR1_ADD0_Reset; However, before we can do either #2 or #3, we need to shift the address one bit to the left. That's what the problem was! I'm not sure if this was previously being done somewhere else or what? Anyway, I've added Address = Address << 1; to the top of the if and else statements. Finally, we call I2Cx->DR = Address; to transfer the full 8-Bit address into the data register, which is then transmitted across the I2C bus! I've tested this with ten different I2C devices and everything seems to working fine with my changes. (So long as you enter a valid 7-Bit address between 0 and 127.) I double checked with my Logic 16 and Tektronix MSO2024B and there were no changes to the signal integrity. (As expected, I just like to be thorough!) @zach I'll be making a pull request with my fix shortly. As an aside, this is a really neat little article/guide on the inner-workings of the ATmega328P TwoWire library. If you want to start understanding just how different Arduino is from actual "low level" C programming on a microcontroller, give it a read. 4 Likes Iv4n January 11, 2014, 8:15am 10 @timb Man, thank you so much for this. I was working with another I2C device and spent HOURS… until I gave this little shift a chance. @BDub Thanks a lot for mentioning it on the BMP085 post, otherwise I would have kept killing myself. 2 Likes timb January 11, 2014, 9:48am 11 @Iv4n No worries man! @zachary said they were going to accept my pull request, so the change should be in the master branch soon! I’m not sure when that gets pushed to their compile-server2 branch, but when it does your I2C code will most likely break until you change the I2C address back to 7-Bit (assuming you’re just adding the R/W bit to the address now yourself). So you may want to note that in a comment at the top of your code and use a global variable (such as byte address = 0x4E) instead of manually assigning it on each call. 1 Like abatardi January 21, 2016, 7:43pm 12 Sorry to bump such an old thread, but it doesn’t appear that timb’s fix was ever pulled into firmware. I have been having issues with the 7/8-bit issue on an I2C device, and building a custom firmware with his changes resolved it. Is there some work-around other than firmware modification for this, or can particle support comment as to when this will be pulled in, and if not, why not? mdma January 22, 2016, 11:55pm 13 Can you explain why you think this wasn’t pulled in? I don’t see any open PRs across any of the firmware repos that are related to I2C. abatardi January 23, 2016, 3:44pm 14 timb: void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction) { /* Check the parameters / assert_param(IS_I2C_ALL_PERIPH(I2Cx)); assert_param(IS_I2C_DIRECTION(I2C_Direction)); / Test on the direction to set/reset the read/write bit / if (I2C_Direction != I2C_Direction_Transmitter) { / Shift the address one bit to the left / Address = Address << 1; / Set the address bit0 for read / Address |= OAR1_ADD0_Set; } else { / Shift the address one bit to the left / Address = Address << 1; / Reset the address bit0 for write / Address &= OAR1_ADD0_Reset; } / Send the address */ I2Cx->DR = Address; } This code. Specifically the Address = Address << 1; byte shift.. this isn't present in any of the new firmware, but it fixed the issue presented in this thread... wondering if particle has an alternative solution? "@Iv4n No worries man! @zachary said they were going to accept my pull request, so the change should be in the master branch soon! I'm not sure when that gets pushed to their compile-server2 branch, but when it does your I2C code will most likely break until you change the I2C address back to 7-Bit (assuming you're just adding the R/W bit to the address now yourself)." Related topics Topic Replies Views Activity I2C not working as expected Troubleshooting 30 6762 August 30, 2014 Important: 7-Bit I2C Addresses are Now Working! (01/24/2014) Firmware 8 5668 July 17, 2015 Casting Variables for I2C Troubleshooting electron 40 6301 July 13, 2019 Basic question about I2C on Photon Troubleshooting 3 1382 November 19, 2016 I2C - two slaves - slave 1 traffic kicks slave 2 to repeat Troubleshooting 24 3556 September 2, 2015 Docs Support Blog Projects Store Console IDE Tag » Arduino I2c 8 Bit Address How To Send An 8-bit Address Over I2C - Arduino Forum I2C - 8-bits Slave Address - Arduino Forum Wire.write 8bit Byte - Programming Questions - Arduino Forum I2C Addressing, Registers, And Bits - Arduino Forum Still Trying To Understand I2C Addressing. - Arduino Forum 8bit I2c - Non Standard Slave Device With No R/w Bit - Arduino Forum Wire - Arduino Reference Why Is Addr = 0x8 For RPi/Arduino I2c Code? - Programming Questions Different I2C Addresses On Arduino? - Stack Overflow 7 Bit 8 Bit And 10 Bit I2C Slave Addressing - Total Phase The List | I2C Addresses! | Adafruit Learning System Arduino I2C - ElectronicWings I2C Tutorial - Robot Electronics How To Scan I2C Address In Arduino - YouTube