I2C addressing lets devices on the same wire know who is talking. It uses 7-bit or 10-bit addresses to identify each device uniquely.
I2C addressing (7-bit and 10-bit) in Embedded C
/* 7-bit address example */ #define DEVICE_ADDR_7BIT 0x3C /* 10-bit address example */ #define DEVICE_ADDR_10BIT 0x1F3 /* Sending address on I2C bus */ // For 7-bit: address shifted left by 1, last bit is R/W uint8_t address_byte = (DEVICE_ADDR_7BIT << 1) | read_write_bit; // For 10-bit: send special header and two address bytes
7-bit addresses are most common and fit in one byte with a read/write bit.
10-bit addresses require sending extra bits and are less common.
/* 7-bit address example */ #define SENSOR_ADDR 0x50 // To send address with write bit (0) uint8_t addr_write = (SENSOR_ADDR << 1) | 0; // To send address with read bit (1) uint8_t addr_read = (SENSOR_ADDR << 1) | 1;
/* 10-bit address example */ #define DEVICE_10BIT_ADDR 0x305 // First byte: 11110 + 2 MSBs of address + R/W bit uint8_t first_byte = 0xF0 | ((DEVICE_10BIT_ADDR >> 7) & 0x06) | (read_write_bit & 0x01); // Second byte: lower 8 bits of address uint8_t second_byte = DEVICE_10BIT_ADDR & 0xFF;
This program shows how to prepare and print the I2C address bytes for both 7-bit and 10-bit addresses with read and write bits.
#include <stdio.h> #include <stdint.h> #define SENSOR_ADDR_7BIT 0x3C #define DEVICE_ADDR_10BIT 0x1F3 void print_7bit_address(uint8_t addr, uint8_t rw) { uint8_t address_byte = (addr << 1) | (rw & 0x01); printf("7-bit address byte (with R/W=%d): 0x%02X\n", rw, address_byte); } void print_10bit_address(uint16_t addr, uint8_t rw) { uint8_t first_byte = 0xF0 | ((addr >> 7) & 0x06) | (rw & 0x01); uint8_t second_byte = addr & 0xFF; printf("10-bit address first byte (with R/W=%d): 0x%02X\n", rw, first_byte); printf("10-bit address second byte: 0x%02X\n", second_byte); } int main() { print_7bit_address(SENSOR_ADDR_7BIT, 0); // Write print_7bit_address(SENSOR_ADDR_7BIT, 1); // Read print_10bit_address(DEVICE_ADDR_10BIT, 0); // Write print_10bit_address(DEVICE_ADDR_10BIT, 1); // Read return 0; }
Remember that the read/write bit is always the least significant bit in the address byte.
7-bit addresses are shifted left by 1 to make room for the read/write bit.
10-bit addressing is more complex and less common, so check your device datasheet carefully.
I2C uses 7-bit or 10-bit addresses to identify devices on the bus.
7-bit addresses are shifted left by 1 bit and combined with a read/write bit.
10-bit addresses require sending two bytes: a special header and the address split into two parts.
Practice
What is the size of the standard I2C address used to identify devices?
Solution
Step 1: Understand I2C addressing basics
I2C commonly uses 7-bit addresses to identify devices on the bus.Step 2: Recall standard address size
The standard I2C address size is 7 bits, while 10-bit is an extended mode.Final Answer:
7 bits -> Option AQuick Check:
Standard I2C address = 7 bits [OK]
- Confusing 7-bit with 8-bit addresses
- Thinking 10-bit is the default
- Assuming 16-bit addresses exist in I2C
Which of the following is the correct way to prepare a 7-bit I2C address for a write operation in embedded C?
uint8_t address = 0x3C; // 7-bit address
uint8_t write_address = ?;Solution
Step 1: Shift 7-bit address left by 1
The 7-bit address must be shifted left by 1 to make room for the read/write bit.Step 2: Set read/write bit for write operation
For write, the least significant bit is 0, so OR with 0.Final Answer:
write_address = (address << 1) | 0; -> Option DQuick Check:
Shift left 1 + write bit 0 = correct address [OK]
- Shifting right instead of left
- Setting read/write bit incorrectly
- Using wrong bitwise OR values
Given the following code snippet, what is the value of header for a 10-bit I2C address 0x305?
uint16_t addr = 0x305;
uint8_t header = 0xF0 | ((addr >> 7) & 0x06) | 0x00; // write operationSolution
Step 1: Analyze addr = 0x305
Binary 0b1100000101 (bits 9-0: bit9=1, bit8=1, bit7=0). Shift right 7: 0b110 (6).Step 2: Mask and compute header
(6 & 0x06) = 0x06. Then 0xF0 | 0x06 | 0x00 = 0xF6.Final Answer:
0xF6 -> Option AQuick Check:
0xF0 | ((0x305 >> 7) & 0x06) = 0xF6 [OK]
- Not shifting address before masking
- Incorrect mask value
- Confusing read/write bit in header
Identify the error in this code snippet for sending a 10-bit I2C address:
uint16_t addr = 0x3FF;
uint8_t header = 0xF0 | ((addr >> 7) & 0x06) | 0x01; // read operation
uint8_t low_addr = addr & 0xFF;
// send header and low_addr over I2CSolution
Step 1: Check read/write bit in header
For read operation, the last bit is set to 1, which matches the code.Step 2: Verify mask and low address
Mask 0x06 correctly extracts bits 7 and 8; low_addr is the lower 8 bits, no shift needed.Final Answer:
The header calculation is correct; no error -> Option CQuick Check:
Header and low_addr correct for 10-bit read [OK]
- Confusing read/write bit value
- Incorrect mask for bits 7 and 8
- Shifting low address unnecessarily
You need to write a function in embedded C that takes a 10-bit I2C address and returns the two bytes to send: the header byte and the low address byte for a write operation. Which of the following implementations is correct?
void get_i2c_10bit_bytes(uint16_t addr, uint8_t *header, uint8_t *low_addr) {
// ?
}Solution
Step 1: Use pointers to assign output values
The function parameters header and low_addr are pointers, so use * to assign values.Step 2: Calculate header and low_addr correctly
Header is 0xF0 ORed with bits 7 and 8 of addr shifted right 7, and OR 0 for write; low_addr is lower 8 bits.Final Answer:
*header = 0xF0 | ((addr >> 7) & 0x06) | 0x00; *low_addr = addr & 0xFF; -> Option BQuick Check:
Pointer dereference + correct bit ops = correct function [OK]
- Not using * to assign pointer values
- Shifting bits incorrectly
- Setting read/write bit wrong
Ready to prove it?
Pass the test and this pattern moves unknown
