Mission 07 / 07
Bus Battle
Dozens of devices, two wires, one storm. Take command.
On this page
01The situation
The final trial. Devices fight over two thin wires, addresses collide and the bus is in flames. Bring order and finish the rescue.
02Why this matters
I²C lets one chip talk to dozens of others over just two wires, because every device has its own address.
- Phone sensorsAccelerometer, compass and light sensor share one I²C bus.
- Tiny sensorsTemperature, humidity and pressure chips speak I²C.
- Small screensThe little OLED displays in maker projects run on I²C.
03The idea in one picture
Think of it as: a teacher calling a student's name (the address) before asking a question (the data). Everyone hears the name, only the one called answers, and only one voice speaks at a time.
Go deeper
| SPI (Level 6) | I²C (Level 7) | |
|---|---|---|
| Wires | SCK, MOSI, MISO, CS | SDA, SCL |
| Direction | Both ways at once | One way at a time |
| Picking a device | Chip Select wire | Address sent first |
04Your mission
- OSCCONStart the clock. Any speed works.
- I2CCON0Switch the port on as the I²C master.
- I2CCON1Switch the port on as the I²C slave.
Hint 1
Hint 2
10 = Master, 00 = Slave (both at Fosc/4).Show the solution
- 1Step 1 — OSCCON (0x20)Set it to
0x05(0000 0101). Switch the internal oscillator on at 4 MHz (any speed wins). - 2Step 2 — I2CCON0 (0x2F)Set it to
0x05(0000 0101). Enable the serial port as I²C Master (SSPM = 10). - 3Step 3 — I2CCON1 (0x30)Set it to
0x01(0000 0001). Enable the serial port as I²C Slave (SSPM = 00). - 4Program & ExecutePress P to compile your configuration into machine code in ROM, then press E to run it. When every register matches you get Target Reached.
The same thing as a program (MOVLW loads a value into W, MOVWF copies W into a register):
MOVLW 0x05
MOVWF 0x20 ; OSCCONMOVLW 0x05
MOVWF 0x2F ; I2CCON0MOVLW 0x01
MOVWF 0x30 ; I2CCON1Bit-by-bit breakdown
OSCCON0x20= 0x05
Switch the internal oscillator on at 4 MHz (any speed wins). 0x05 = 0000 0101 = SCS0 = 1, IRCF = 10
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Value | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| Field | — | IRCF | SCS0 | |||||
- IRCF (bits 2:1) =
10→ 4 MHz - SCS0 (bit 0) =
1→ Internal oscillator on - Every remaining bit is
0: those bits are not needed for this level, but they must stay clear for the value to match.
I2CCON00x2F= 0x05
Enable the serial port as I²C Master (SSPM = 10). 0x05 = 0000 0101 = SPEN = 1, SSPM = 10
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Value | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| Field | — | CKE | CKP | SSPM | SPEN | |||
- SSPM (bits 2:1) =
10→ I²C Master, 10-bit, Fosc/4 - SPEN (bit 0) =
1→ Serial port enabled - Every remaining bit is
0: those bits are not needed for this level, but they must stay clear for the value to match.
I2CCON10x30= 0x01
Enable the serial port as I²C Slave (SSPM = 00). 0x01 = 0000 0001 = SPEN = 1, SSPM = 00
| Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Value | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 |
| Field | — | CKE | CKP | SSPM | SPEN | |||
- SSPM (bits 2:1) =
00→ I²C Slave, 10-bit, Fosc/4 - SPEN (bit 0) =
1→ Serial port enabled - Every remaining bit is
0: those bits are not needed for this level, but they must stay clear for the value to match.
Try it: build the values
Flip the bits until each register matches the mission. It's practice only; nothing is saved.
0 of 3 registers match.
OSCCON 0x20
✗ Not yet – 2 bits differHex 0x00 Binary 0000 0000 Target 0x05 (0000 0101)
- IRCF (bits 2:1) = 00 → 1 MHz
- SCS0 (bit 0) = 0 → Internal oscillator not enabled
I2CCON0 0x2F
✗ Not yet – 2 bits differHex 0x00 Binary 0000 0000 Target 0x05 (0000 0101)
- CKE (bit 4) = 0 → Transmit edge: not part of the win value (leave 0)
- CKP (bit 3) = 0 → Clock idle level: not part of the win value (leave 0)
- SSPM (bits 2:1) = 00 → I²C Slave, 10-bit, Fosc/4
- SPEN (bit 0) = 0 → Serial port disabled
I2CCON1 0x30
✗ Not yet – 1 bit differHex 0x00 Binary 0000 0000 Target 0x01 (0000 0001)
- CKE (bit 4) = 0 → Transmit edge: not part of the win value (leave 0)
- CKP (bit 3) = 0 → Clock idle level: not part of the win value (leave 0)
- SSPM (bits 2:1) = 00 → I²C Slave, 10-bit, Fosc/4
- SPEN (bit 0) = 0 → Serial port disabled
05How you know it worked
- 1The master calls an addressThe slave address 0x07 goes out on SDA, bit by bit.Screenshot coming soon
- 2The right slave answersOnly the device with that address listens and takes the transfer.Screenshot coming soon
- 3Data followsThe data byte travels on the same wire into the slave's buffer.TARGET REACHEDScreenshot coming soon
Simulation note
The game picks the address (0x07) and feeds the data itself (a repeating 0x00 → 0xFF sequence); you only set the roles.
Stuck?
- The master never startsSSPM =
00makes it a slave. I2CCON0 needs0x05. - Nobody receivesBoth sides are masters. I2CCON1 must be the slave (
0x01). - Roles look right, but the bus is silentSPEN (bit 0) is off.
- It runs, but no Target ReachedSSPM =
11or01are the Fosc/16 modes. Use Fosc/4.
06Debrief
You just took command of the bus, and saved the ship.
Addressing is the big idea: two wires, many devices, each answering only to its own name. You've now used every peripheral on a real microcontroller.
// the same idea in CI2CCON0 = 0x05; // I2C masterI2CCON1 = 0x01; // I2C slaveOn a real chip the same switches can sit at different bit positions; its datasheet has the map. The idea is identical.