Skip to content
EmbeddedPlayX

Mission 07 / 07

Bus Battle

Dozens of devices, two wires, one storm. Take command.

On this page
  1. The situation
  2. Why this matters
  3. The idea in one picture
  4. Your mission
  5. How you know it worked
  6. Debrief

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

Master calls an address on SDA…MASTERSDASCLSLAVE 0x03SLAVE 0x07SLAVE 0x0ASADDRESS 0x07R/WACKDATAACKSTOP
The master calls address 0x07. Only that slave answers (ACK), then the data byte follows on the same wire.

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)
WiresSCK, MOSI, MISO, CSSDA, SCL
DirectionBoth ways at onceOne way at a time
Picking a deviceChip Select wireAddress 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
Same idea as SPI: one master that starts every transfer, one slave that answers, and the serial port on for both.
Hint 2
SPEN is bit 0. SSPM (bits 2:1) picks the role: 10 = Master, 00 = Slave (both at Fosc/4).
Show the solution
  1. 1Step 1 — OSCCON (0x20)Set it to 0x05 (0000 0101). Switch the internal oscillator on at 4 MHz (any speed wins).
  2. 2Step 2 — I2CCON0 (0x2F)Set it to 0x05 (0000 0101). Enable the serial port as I²C Master (SSPM = 10).
  3. 3Step 3 — I2CCON1 (0x30)Set it to 0x01 (0000 0001). Enable the serial port as I²C Slave (SSPM = 00).
  4. 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   ; I2CCON1
Bit-by-bit breakdown

OSCCON0x20= 0x05

Switch the internal oscillator on at 4 MHz (any speed wins). 0x05 = 0000 0101 = SCS0 = 1, IRCF = 10

Bit76543210
Value00000101
Field—IRCFSCS0
  • 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

Bit76543210
Value00000101
Field—CKECKPSSPMSPEN
  • 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

Bit76543210
Value00000001
Field—CKECKPSSPMSPEN
  • 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 differ
7–
6–
5–
4–
3–
2IRCF1
1IRCF0
0SCS0

Hex 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 differ
7–
6–
5–
4CKE
3CKP
2SSPM1
1SSPM0
0SPEN

Hex 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 differ
7–
6–
5–
4CKE
3CKP
2SSPM1
1SSPM0
0SPEN

Hex 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

  1. 1The master calls an addressThe slave address 0x07 goes out on SDA, bit by bit.
    Screenshot coming soon
  2. 2The right slave answersOnly the device with that address listens and takes the transfer.
    Screenshot coming soon
  3. 3Data followsThe data byte travels on the same wire into the slave's buffer.TARGET REACHED
    Screenshot 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 = 00 makes it a slave. I2CCON0 needs 0x05.
  • 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 = 11 or 01 are 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 slave

On a real chip the same switches can sit at different bit positions; its datasheet has the map. The idea is identical.

All systems restoredYou've used every peripheral on the chip. Back to the ship map.