Difference between revisions of "I2C Registers"

From 3dbrew
Jump to navigation Jump to search
(Clarify where each gyroscope is used (although missing old3DSXL and non-XL new3DS))
(22 intermediate revisions by 2 users not shown)
Line 105: Line 105:
 
| 7
 
| 7
 
| Start/busy      (0=Ready, 1=Start/busy)
 
| Start/busy      (0=Ready, 1=Start/busy)
 +
|}
 +
 +
== I2C_CNTEX ==
 +
{| class="wikitable" border="1"
 +
!  BIT
 +
!  DESCRIPTION
 +
|-
 +
| 0-1
 +
| ? Set to 2 normally.
 +
|}
 +
 +
== I2C_SCL ==
 +
{| class="wikitable" border="1"
 +
!  BIT
 +
!  DESCRIPTION
 +
|-
 +
| 0-5
 +
| ?
 +
|-
 +
| 8-12
 +
| ? Set to 5 normally.
 
|}
 
|}
  
Line 173: Line 194:
 
| 0xa6
 
| 0xa6
 
| "i2c::HID"
 
| "i2c::HID"
| Unknown. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].
+
| Debug(?) gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].
 
|-
 
|-
 
| 10
 
| 10
Line 179: Line 200:
 
| 0xd0
 
| 0xd0
 
| "i2c::HID"
 
| "i2c::HID"
| Gyroscope
+
| Gyroscope (old3DS)
 
|-
 
|-
 
| 11
 
| 11
Line 185: Line 206:
 
| 0xd2
 
| 0xd2
 
| "i2c::HID"
 
| "i2c::HID"
| ?
+
| Gyroscope (2DS, new3DSXL, new2DSXL)
 
|-
 
|-
 
| 12
 
| 12
Line 203: Line 224:
 
| 0xa0
 
| 0xa0
 
| "i2c::EEP"
 
| "i2c::EEP"
| eeprom?
+
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])
 
|-
 
|-
 
| 15
 
| 15
Line 230: Line 251:
 
   rw = read-write
 
   rw = read-write
 
   wo = write-only (reading will yield 00, FF, or unpredictable data)
 
   wo = write-only (reading will yield 00, FF, or unpredictable data)
 +
 +
  d* = dynamic register (explaination below this table)
 
   s* = shared register (explaination below this table)
 
   s* = shared register (explaination below this table)
   *v = volatile (survives reboots)
+
   ds = dynamic shared (explaination below this table)
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
!  REGISTER
 
!  REGISTER
Line 249: Line 272:
 
|-
 
|-
 
| 0x02
 
| 0x02
| s
+
| d
| ro
+
| rw
| ?
+
| 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog
 +
  bit0: RTC clock value got reset to defaults
 +
  bit1: Watchdog reset happened
 
|-
 
|-
 
| 0x03
 
| 0x03
| s
+
| ds
 
| rw
 
| rw
| Top screen flicker
+
| Top screen Vcom
 
|-
 
|-
 
| 0x04
 
| 0x04
| s
+
| ds
 
| rw
 
| rw
| Bottom screen flicker
+
| Bottom screen Vcom
 
|-
 
|-
 
| 0x05
 
| 0x05
 
- 0x07
 
- 0x07
| s(3)
+
| s
 
| rw
 
| rw
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here
+
| Danger zone - [[MCU_Services#MCU_firmware_versions|MCU unlock sequence]] is written here.
 
|-
 
|-
 
| 0x08
 
| 0x08
Line 308: Line 333:
 
| s
 
| s
 
| ro
 
| ro
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of these are read via [[MCU_Services|mcu::RTC]]: bit4 = BatteryChargeState. bit3 = AdapterState. bit1 = ShellState.
+
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].
 +
  bit01: ShellState
 +
  bit03: AdapterState
 +
  bit04: BatteryChargeState
 +
  bit05: Bottom screen backlight on
 +
  bit06: Top screen backlight on
 +
  bit07: GPU on(?)
 
|-
 
|-
 
| 0x10
 
| 0x10
Line 320: Line 351:
 
| s
 
| s
 
| ro
 
| ro
| ?
+
| Unused and unwritable byte :(
 
|-
 
|-
 
| 0x15
 
| 0x15
 +
- 0x17
 
| s
 
| s
| rw v
+
| rw
| ?
+
| Unused and unreferenced free RAM! Good for userdata.
|-
 
| 0x16
 
| s
 
| rw v
 
| ?
 
|-
 
| 0x17
 
| s
 
| rw v
 
| ?
 
 
|-
 
|-
 
| 0x18
 
| 0x18
Line 342: Line 364:
 
| rw
 
| rw
 
| Interrupt mask for register 0x10 (0=enabled,1=disabled)
 
| Interrupt mask for register 0x10 (0=enabled,1=disabled)
   bit00: Power button press
+
   bit00: Power button press (for 27 "ticks")
   bit01: Power button held (the 3DS turns off regardless after a fixed time)
+
   bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)
   bit02: HOME button press
+
   bit02: HOME button press (for 5 "ticks")
 
   bit03: HOME button release
 
   bit03: HOME button release
 
   bit04: WiFi switch button
 
   bit04: WiFi switch button
 
   bit05: Shell close
 
   bit05: Shell close
 
   bit06: Shell open
 
   bit06: Shell open
   bit07: Fatal hardware condition([[Services#Notifications|?]])
+
   bit07: Fatal hardware condition([[Services#Notifications|?]]) (sent when the MCU gets reset by the Watchdog timer)
 
   bit08: Charger removed
 
   bit08: Charger removed
 
   bit09: Charger plugged in
 
   bit09: Charger plugged in
   bit10: ??? (Power Management related?)
+
   bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)
   bit11: ??? (HID related)
+
   bit11: ??? (accelerometer related)
 
   bit12: HID update
 
   bit12: HID update
   bit13: Battery dead(?)
+
   bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)
   bit14: ??? (Power Management related?)
+
   bit14: Battery stopped charging (independent of charger state)
   bit15: ??? (Power Management related?)
+
  bit15: Battery started charging
 +
Nonmaskable(?) interrupts
 +
  bit16: ???
 +
   bit17: ??? (opposite even for bit16)
 
   bit22: Volume slider position change
 
   bit22: Volume slider position change
   bit24: ??? (???)
+
   bit23: ??? Register 0x0E update
   bit25: ??? (???)
+
  bit24: ??? (the off event for below bit)
 +
   bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)
 
   bit26: ??? (???)
 
   bit26: ??? (???)
 
   bit27: ??? (???)
 
   bit27: ??? (???)
 
   bit28: ??? (???)
 
   bit28: ??? (???)
   bit29: ??? (???)
+
   bit29: ??? backlight on?
   bit30: forced off my mcu sysmodule
+
   bit30: bit set by mcu sysmodule
   bit31: forced off my mcu sysmodule
+
   bit31: bit set by mcu sysmodule
 
|-
 
|-
 
| 0x1C
 
| 0x1C
 +
- 0x1F
 
| s
 
| s
| rw v
+
| rw
| ?
+
| Unused and unreferenced free RAM! Good for userdata.
|-
 
| 0x1D
 
| s
 
| rw v
 
| ?
 
|-
 
| 0x1E
 
| s
 
| rw v
 
| ?
 
|-
 
| 0x1F
 
| s
 
| rw v
 
| ?
 
 
|-
 
|-
 
| 0x20
 
| 0x20
| s
+
| d
 
| wo
 
| wo
 
| System power control:
 
| System power control:
Line 396: Line 408:
 
   bit2: reboot (used by mcu sysmodule and LgyBg)
 
   bit2: reboot (used by mcu sysmodule and LgyBg)
 
   bit3: used by LgyBg to power off, causes hangs in 3DS-mode
 
   bit3: used by LgyBg to power off, causes hangs in 3DS-mode
   bit4: doesn't seem to do anything, but an mcu::RTC command uses this
+
   bit4: an mcu::RTC command uses this, seems to do something with the watchdog
Other bits are unused, and seem to do nothing.
+
Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.  
 
|-
 
|-
 
| 0x21
 
| 0x21
| s
+
| d
| ro(?)
+
| wo
| ?
+
| ??? switches up input bits from <code>0123456--</code> to <code>12-0435-</code> then writes them to REG[0x5D] (<code>0xFFC02</code>)
 
|-
 
|-
 
| 0x22
 
| 0x22
| s
+
| d
 
| wo
 
| wo
 
| Used to set LCD states
 
| Used to set LCD states
Line 415: Line 427:
 
   bit5: top screen backlight on
 
   bit5: top screen backlight on
  
bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen
+
Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen.
 +
The rest of the bits are masked away.
 
|-
 
|-
 
| 0x23
 
| 0x23
| s
+
| ??
| ro(?)
+
| wo
| ?
+
| ??? Seems to be stubbed, just returns the written value from the write handler function.
 
|-
 
|-
 
| 0x24
 
| 0x24
 
| s
 
| s
| ??
+
| rw
 
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.
 
| Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.
 
|-
 
|-
Line 438: Line 451:
 
|-
 
|-
 
| 0x27
 
| 0x27
| s
+
| sd
| ro
+
| rw
 
| Raw volume slider state
 
| Raw volume slider state
 
|-
 
|-
Line 448: Line 461:
 
|-
 
|-
 
| 0x29
 
| 0x29
| ds(0x64)
+
| sd(5)
| ro / rw
+
| ??
| Empty battery pattern holder, repeats all written bytes
+
| Power mode indicator state (read-write)
 +
  1 = forced default blue
 +
  2 = sleep mode animation
 +
  3 = "power off" mode
 +
  4 = disable blue power LED and turn on red power LED
 +
  5 = disable red power LED and turn on blue power LED
 +
  6 = animate blue power LED off and flash red power LED
 +
  anything else = automatic mode
 +
The other 4 bytes (32bits) affect the pattern of the red power LED (write only)
 
|-
 
|-
 
| 0x2A
 
| 0x2A
Line 460: Line 481:
 
| s
 
| s
 
| rw
 
| rw
| Camera LED state, 0, 3, 6-0xF = off, 1 = slowly blinking, 2 = constantly on, 4 = flash once, 5 = delay before changing to 2
+
| Camera LED state, 4bits wide,
 +
  0, 3, 6-0xF = off
 +
  1 = slowly blinking
 +
  2 = constantly on
 +
  3 = "TWL" mode
 +
  4 = flash once
 +
  5 = delay before changing to 2
 
|-
 
|-
 
| 0x2C
 
| 0x2C
Line 479: Line 506:
 
| 0x2F
 
| 0x2F
 
| s
 
| s
| ro(?)
+
| wo?
| ?
+
| ??? The write function for this register is stubbed.
 
|-
 
|-
 
| 0x30
 
| 0x30
- 0x37
+
- 0x36
| s
+
| ds
 
| rw
 
| rw
 
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte & 0xF) + (10 * (byte >> 4)).
 
| RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte & 0xF) + (10 * (byte >> 4)).
Line 494: Line 521:
 
   byte 5: months
 
   byte 5: months
 
   byte 6: years
 
   byte 6: years
  byte 7: leap year counter / "watch error correction" register (unused)
+
|-
 +
| 0x37
 +
| s
 +
| rw
 +
| RTC time byte 7: leap year counter / "watch error correction" register (unused in code)
 
|-
 
|-
 
| 0x38
 
| 0x38
- 0x3A
+
- 0x3C
 
| s
 
| s
 
| rw
 
| rw
Line 503: Line 534:
 
   byte 0: minutes
 
   byte 0: minutes
 
   byte 1: hours
 
   byte 1: hours
   byte 2: week
+
   byte 2: day
 +
  byte 3: month
 +
  byte 4: year
 
|-
 
|-
 
| 0x3B
 
| 0x3B
- 0x3D
 
 
| s
 
| s
 
| rw
 
| rw
| Register 0x3B could be used to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.
+
| Could be used on very old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if some conditions are met.
 
|-
 
|-
 
| 0x3D
 
| 0x3D
 
0x3E
 
0x3E
| s (2)
+
| ds
 
| ro
 
| ro
| RTC tick counter (resets to 0 when the seconds increase)
+
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)
 +
Only reading 0x3D will update the in-RAM value
 
|-
 
|-
 
| 0x3F
 
| 0x3F
 
| s
 
| s
 
| wo
 
| wo
| Register-mapped MCU RESET?
+
| 2 bits
 +
  bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)
 +
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output
 
|-
 
|-
 
| 0x40
 
| 0x40
 
| s
 
| s
 
| rw
 
| rw
| Pedometer state (?)
+
| Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.
 
|-
 
|-
 
| 0x41
 
| 0x41
Line 535: Line 570:
 
| s
 
| s
 
| rw
 
| rw
| ?
+
| Unused?
 
|-
 
|-
 
| 0x43
 
| 0x43
 
| s
 
| s
 
| rw
 
| rw
| ???, accelometer related
+
| Unused???, accelometer related
 
|-
 
|-
 
| 0x44
 
| 0x44
 
| s
 
| s
 
| rw
 
| rw
| ???, accelometer related
+
| ???, pedoometer related(?)
 
|-
 
|-
 
| 0x45
 
| 0x45
Line 551: Line 586:
 
| s
 
| s
 
| ro
 
| ro
| Gyroscope 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short
+
| Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
!  AXIS
 
!  AXIS
Line 558: Line 593:
 
!  V=0xC0  
 
!  V=0xC0  
 
|-
 
|-
| Y (=roll)
+
| X (left/right)
| held vertically
+
| held up vertically
| vertical right side
+
| rotated left 90° like a steering wheel
| vertical left side
+
| rotated right 90° like a steering wheel
 
|-
 
|-
| Z? (=yaw)
+
| Y (forwards/backwards)
 +
| laid flat on the desk with the screen facing up
 +
| held up vertically
 +
| held up vertically with screen facing upside-down
 +
|-
 +
| Z (???)
 
| ???
 
| ???
| ???
 
| ???
 
|-
 
| X? (=pitch)
 
| held vertically
 
 
| ???
 
| ???
 
| ???
 
| ???
Line 580: Line 615:
 
|-
 
|-
 
| 0x4C
 
| 0x4C
| s
+
0x4D
| rw
+
| ??
| ?
+
| ??
|-
+
| ??
| 0x4D
 
| s
 
| rw
 
| ?
 
 
|-
 
|-
 
| 0x4E
 
| 0x4E
| s
+
| d
 
| rw
 
| rw
| ?
+
| ??? this = (0xFFE9E & 1) ? 0x10 : 0
 
|-
 
|-
 
| 0x4F
 
| 0x4F
| 0x156
+
| d(6)
 
| ro
 
| ro
| ???
+
|  
 
|-
 
|-
 
| 0x50
 
| 0x50
Line 629: Line 660:
 
| 0x5A
 
| 0x5A
 
| s
 
| s
| rw
+
| ro/rw
| ?
+
| Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.
 
|-
 
|-
 
| 0x5B
 
| 0x5B
 
- 0x5F
 
- 0x5F
 
| s
 
| s
| ro(?)
+
| -
| ? (these seem to be invalid regsiters)
+
| These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.
 
|-
 
|-
 
| 0x60
 
| 0x60
| ds(1)
+
| ds
| ro
+
| rw
first byte is wo
 
 
| Looping queue register
 
| Looping queue register
 
Writing to first byte resets the queue position to the nth element
 
Writing to first byte resets the queue position to the nth element
Line 650: Line 680:
 
| rw
 
| rw
 
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.
 
| Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives power-off, but does not seem to be saved to non-volatile storage (does not survive battery pulls). This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.
+
The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.
 
|-
 
|-
 
| 0x62 - 0x7E
 
| 0x62 - 0x7E
 
| s
 
| s
| invalid (ro)
+
| -
| These registers don't exist at all, thus reading them will yield 0xFF
+
| These registers don't exist, writing is no-op, reading will yield FFs.
 
|-
 
|-
 
| 0x7F
 
| 0x7F
| 19(?)
+
| d(9-0x13)
 
| ro
 
| ro
| Various system state information.
+
| Various system state information (debug pointer table)
   byte 06: battery related? (seems to decrease while charging and increase while discharging)
+
   byte 0x06: battery related? (seems to decrease while charging and increase while discharging)
   byte 09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)
+
   byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|Cfg:GetSystemModel]] for values)
   byte 10: power LED related? 0 is off, 1 is red
+
   byte 0x0A: Red Power LED mode (0 = off, 1 = on)
   byte 13: RGB LED red intensity
+
  byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)
   byte 14: RGB LED green intensity
+
   byte 0x0D: RGB LED red intensity
   byte 15: RGB LED blue intensity
+
   byte 0x0E: RGB LED green intensity
   byte 17: WiFi LED brightness
+
   byte 0x0F: RGB LED blue intensity
   byte 18: raw button states?
+
   byte 0x11: WiFi LED brightness
     bit0: unset while power button is held
+
   byte 0x12: raw button states?
     bit1: unset while home button is held
+
     bit0: unset while Power button is held
     bit2: unset while Wifi slider is held
+
     bit1: unset while HOME button is held
 +
     bit2: unset while WiFi slider is held
 
     bit5: unset while the charging LED is active
 
     bit5: unset while the charging LED is active
 
     bit6: unset while charger is plugged in
 
     bit6: unset while charger is plugged in
   
+
 
    this byte is reset to 0 before an svcBreak takes effect
+
On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.
 
|-
 
|-
 
| 0x80
 
| 0x80
 
- 0xFF
 
- 0xFF
 
| s
 
| s
| invalid (ro)
+
| -
| These registers don't exist at all, thus reading them will yield 0xFF
+
| These registers don't exist, writing is no-op, reading will yield FFs.
 
|}
 
|}
  
Note: the letter "s" in the size field means that the given register is in a "shared register pool", meaning if you read/write with size more than 1 you can read the next `readamount-1` of shared registers. It's possible to corrupt the shared value of a "non-shared" register by writing into a shared register with a size bigger than one. Writing more than 0x100 bytes into a shared register will corrupt all writable registers, including the shared portion of "non-shared" registers.
+
Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address <code>0xFFBA4 + registernumber</code>.
  
Non-shared register: it's a register separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.
+
Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.
 +
 
 +
Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.
 +
 
 +
On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.
  
 
== Device 5 & 6 ==
 
== Device 5 & 6 ==
Line 734: Line 769:
  
 
== Device 10 ==
 
== Device 10 ==
 +
See the datasheet linked to on the [[Hardware]] page for reference.
 +
 +
== Device 11 ==
 
See the datasheet linked to on the [[Hardware]] page for reference.
 
See the datasheet linked to on the [[Hardware]] page for reference.
  

Revision as of 21:24, 28 December 2019

Registers

Old3DS Name Address Width Used by
Yes I2C1_DATA 0x10161000 1 I2C bus 1 devices
Yes I2C1_CNT 0x10161001 1 I2C bus 1 devices
Yes I2C1_CNTEX 0x10161002 2 I2C bus 1 devices
Yes I2C1_SCL 0x10161004 2 I2C bus 1 devices
Yes I2C2_DATA 0x10144000 1 I2C bus 2 devices
Yes I2C2_CNT 0x10144001 1 I2C bus 2 devices
Yes I2C2_CNTEX 0x10144002 2 I2C bus 2 devices
Yes I2C2_SCL 0x10144004 2 I2C bus 2 devices
Yes I2C3_DATA 0x10148000 1 I2C bus 3 devices
Yes I2C3_CNT 0x10148001 1 I2C bus 3 devices
Yes I2C3_CNTEX 0x10148002 2 I2C bus 3 devices
Yes I2C3_SCL 0x10148004 2 I2C bus 3 devices

I2C_CNT

BIT DESCRIPTION
0 Stop (0=No, 1=Stop/last byte)
1 Start (0=No, 1=Start/first byte)
2 Pause (0=Transfer Data, 1=Pause after Error, used with/after Stop)
4 Ack Flag (0=Error, 1=Okay) (For DataRead: W, for DataWrite: R)
5 Data Direction (0=Write, 1=Read)
6 Interrupt Enable (0=Disable, 1=Enable)
7 Start/busy (0=Ready, 1=Start/busy)

I2C_CNTEX

BIT DESCRIPTION
0-1 ? Set to 2 normally.

I2C_SCL

BIT DESCRIPTION
0-5 ?
8-12 ? Set to 5 normally.

I2C Devices

Device id Device bus id Device Write Address Accessible via I2C service Device description
0 1 0x4a "i2c::MCU" Power management?(same device addr as the DSi power-management)
1 1 0x7a "i2c::CAM" Camera0?(same dev-addr as DSi cam0)
2 1 0x78 "i2c::CAM" Camera1?(same dev-addr as DSi cam1)
3 2 0x4a "i2c::MCU" MCU
4 2 0x78 "i2c::CAM" ?
5 2 0x2c "i2c::LCD" ?
6 2 0x2e "i2c::LCD" ?
7 2 0x40 "i2c::DEB" ?
8 2 0x44 "i2c::DEB" ?
9 3 0xa6 "i2c::HID" Debug(?) gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with 8.0.0-18.
10 3 0xd0 "i2c::HID" Gyroscope (old3DS)
11 3 0xd2 "i2c::HID" Gyroscope (2DS, new3DSXL, new2DSXL)
12 3 0xa4 "i2c::HID" DebugPad
13 3 0x9a "i2c::IR" IR
14 3 0xa0 "i2c::EEP" HWCAL EEPROM (only present on dev units where SHA256 is used for HWCAL verification)
15 2 0xee "i2c::NFC" New3DS-only NFC
16 1 0x40 "i2c::QTM" New3DS-only QTM
17 3 0x54 "i2c::IR" Used by IR-module starting with 8.0.0-18, for New3DS-only HID via "ir:rst". This deviceid doesn't seem to be supported by i2c module on 8.0.0-18(actual support was later added in New3DS i2c module).

Notice: These device addresses are used for writing to the respective device, for reading bit0 must be set (see I2C protocol). Thus, the actual device address is >> 1.

Device 3

 ro = read-only (writing is no-op)
 rw = read-write
 wo = write-only (reading will yield 00, FF, or unpredictable data)
 d* = dynamic register (explaination below this table)
 s* = shared register (explaination below this table)
 ds = dynamic shared (explaination below this table)
REGISTER WIDTH INFO DESCRIPTION
0x00 s ro Version high
0x01 s ro Version low
0x02 d rw 2bit value, writing will mask away/"acknowledge" the event, set to 3 by mcuMainLoop on reset if reset source is Watchdog
 bit0: RTC clock value got reset to defaults
 bit1: Watchdog reset happened
0x03 ds rw Top screen Vcom
0x04 ds rw Bottom screen Vcom
0x05

- 0x07

s rw Danger zone - MCU unlock sequence is written here.
0x08 s ro Raw 3D slider position
0x09 s ro Volume slider state (0x00 - 0x3F)

This is the same value returned by MCUHWC:GetSoundVolume

0x0A s ro ? (seems to be power management related?)
0x0B s ro Battery percentage
0x0C s ro ? (changes to 0 for a second when the charger is plugged in then it resets to its previous value)
0x0D s ro System voltage
0x0E s ro ?
0x0F s ro Flags: bit7-5 are read via mcu::GPU. The rest of them are read via mcu::RTC.
 bit01: ShellState
 bit03: AdapterState
 bit04: BatteryChargeState
 bit05: Bottom screen backlight on
 bit06: Top screen backlight on
 bit07: GPU on(?)
0x10

- 0x13

s ro Received interrupt bitmask, see register 0x18 for possible values

If no interrupt was received this register is 0

0x14 s ro Unused and unwritable byte :(
0x15

- 0x17

s rw Unused and unreferenced free RAM! Good for userdata.
0x18

- 0x1B

s rw Interrupt mask for register 0x10 (0=enabled,1=disabled)
 bit00: Power button press (for 27 "ticks")
 bit01: Power button held (for 375 "ticks"; the 3DS turns off regardless after a fixed time)
 bit02: HOME button press (for 5 "ticks")
 bit03: HOME button release
 bit04: WiFi switch button
 bit05: Shell close
 bit06: Shell open
 bit07: Fatal hardware condition(?) (sent when the MCU gets reset by the Watchdog timer)
 bit08: Charger removed
 bit09: Charger plugged in
 bit10: RTC alarm (when some conditions are met it's sent when the current day and month and year matches the current RTC time)
 bit11: ??? (accelerometer related)
 bit12: HID update
 bit13: Battery percentage status change (triggered at 10%, 5%, and 0% while discharging)
 bit14: Battery stopped charging (independent of charger state)
 bit15: Battery started charging

Nonmaskable(?) interrupts

 bit16: ???
 bit17: ??? (opposite even for bit16)
 bit22: Volume slider position change
 bit23: ??? Register 0x0E update
 bit24: ??? (the off event for below bit)
 bit25: ??? (triggered when something related to the GPU is turned on, most likely backlight)
 bit26: ??? (???)
 bit27: ??? (???)
 bit28: ??? (???)
 bit29: ??? backlight on?
 bit30: bit set by mcu sysmodule
 bit31: bit set by mcu sysmodule
0x1C

- 0x1F

s rw Unused and unreferenced free RAM! Good for userdata.
0x20 d wo System power control:
 bit0: power off
 bit1: reboot (unused?)
 bit2: reboot (used by mcu sysmodule and LgyBg)
 bit3: used by LgyBg to power off, causes hangs in 3DS-mode
 bit4: an mcu::RTC command uses this, seems to do something with the watchdog

Bit 4 sets a bit at a RAM address which seems to control the watcdog timer state, then this bit is immediately unmasked. This field has a bitmask of 0x0F.

0x21 d wo ??? switches up input bits from 0123456-- to 12-0435- then writes them to REG[0x5D] (0xFFC02)
0x22 d wo Used to set LCD states
 bit0: don't push to LCDs
 bit1: push to LCDs
 bit2: bottom screen backlight off
 bit3: bottom screen backlight on
 bit4: top screen backlight off
 bit5: top screen backlight on

Bits 4 and 5 have no effect on a 2DS because the backlight source is the bottom screen. The rest of the bits are masked away.

0x23 ?? wo ??? Seems to be stubbed, just returns the written value from the write handler function.
0x24 s rw Watchdog timer. This must be set *before* the timer is triggered, otherwise the old value is used. Value zero disables the watchdog.
0x25 s rw ?
0x26 s rw ?
0x27 sd rw Raw volume slider state
0x28 s rw Brightness of the WiFi/Power LED
0x29 sd(5) ?? Power mode indicator state (read-write)
 1 = forced default blue
 2 = sleep mode animation
 3 = "power off" mode
 4 = disable blue power LED and turn on red power LED
 5 = disable red power LED and turn on blue power LED
 6 = animate blue power LED off and flash red power LED
 anything else = automatic mode

The other 4 bytes (32bits) affect the pattern of the red power LED (write only)

0x2A s rw WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide
0x2B s rw Camera LED state, 4bits wide,
 0, 3, 6-0xF = off
 1 = slowly blinking
 2 = constantly on
 3 = "TWL" mode
 4 = flash once
 5 = delay before changing to 2
0x2C s rw 3D LED state, 4 bits wide
0x2D 0x64 wo This is used for controlling the notification LED (see MCURTC:SetInfoLEDPatternHeader as well), when this register is written. It's possible to write data here with size less than 0x64, and only that portion of the pattern data will get overwritten. Reading from this register only returns zeroes, so it's considered write-only. Writing past the size of this register seems to do nothing.
0x2E s ro This returns the notification LED status when read (1 means new cycle started)
0x2F s wo? ??? The write function for this register is stubbed.
0x30

- 0x36

ds rw RTC time (system clock). 7 bytes are read from this. The upper nibble of each byte encodes 10s (BCD), so each byte is post-processed with (byte & 0xF) + (10 * (byte >> 4)).
 byte 0: seconds
 byte 1: minutes
 byte 2: hours
 byte 3: current week (unused)
 byte 4: days
 byte 5: months
 byte 6: years
0x37 s rw RTC time byte 7: leap year counter / "watch error correction" register (unused in code)
0x38

- 0x3C

s rw RTC alarm registers
 byte 0: minutes
 byte 1: hours
 byte 2: day
 byte 3: month
 byte 4: year
0x3B s rw Could be used on very old MCU_FIRM versions to upload MCU firmware if some conditions are met.
0x3D

0x3E

ds ro RTC tick counter / "ITMC" (when resets to 0 the seconds increase)

Only reading 0x3D will update the in-RAM value

0x3F s wo 2 bits
 bit0: turns off P00 and sets it to output mode (seems to kill the entire SoC)
 bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output
0x40 s rw Tilt sensor sampling mode. Bits 0 and 1 control the mode. If bits 0 or 1 are set then the tilt sensor is enabled and sampled.
0x41 s rw Index selector for register 0x44
0x42 s rw Unused?
0x43 s rw Unused???, accelometer related
0x44 s rw ???, pedoometer related(?)
0x45

- 0x4A

s ro Tilt sensor 3D rotation from the 12bit ADC, left shifted 4 to fit in a 16bit signed short, relative to the 3DS bottom screen
AXIS V=0x00 V=0x40 V=0xC0
X (left/right) held up vertically rotated left 90° like a steering wheel rotated right 90° like a steering wheel
Y (forwards/backwards) laid flat on the desk with the screen facing up held up vertically held up vertically with screen facing upside-down
Z (???) ??? ??? ???
0x4B s rw PedometerStepCount (for the current day)
0x4C

0x4D

?? ?? ??
0x4E d rw ??? this = (0xFFE9E & 1) ? 0x10 : 0
0x4F d(6) ro
0x50 s rw ???
0x51 s rw ???
0x52

- 0x57

s rw ?
0x58 s rw Register-mapped ADC register

DSP volume slider 0% volume offset (setting this to 0xFF will esentially mute the DSP as it's the volume slider's maximum raw value)

0x59 s rw Register-mapped ADC register

DSP volume slider 100% volume offset (setting both this and the above to 0 will disable the volume slider with 100% volume, setting this to a lower value than the above will make the volume slider have only 2 states; on and off)

0x5A s ro/rw Invalid, do not use! On newer MCU_FIRM versions this is unused, but on older MCU_FIRM versions this is a read-only counter.
0x5B

- 0x5F

s - These registers are out of bounds (0xFFC00 and up), they don't exist, writing is no-op, reading will yield FFs.
0x60 ds rw Looping queue register

Writing to first byte resets the queue position to the nth element Reading from this register causes the values to shift up by `readsize-1`(needs confirmation) bytes after returning `readsize-1` bytes from the top of the stack (first byte is read-only, so is always zero)

0x61 ds(0x100) rw Writing to this register pushes values on top of register 0x60's stack. Reading from this register doesn't advance the stack.

The first byte is used to store flags for managing FIRM/NS state - bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit4 = "LegacyJumpProhibited". This register survives a power-off, but it resides in RAM, so its contents get lost on battery pulls. This register doesn't seem to actually control MCU behaviour by itself, it just seems to be used for storing arbitrary data.

0x62 - 0x7E s - These registers don't exist, writing is no-op, reading will yield FFs.
0x7F d(9-0x13) ro Various system state information (debug pointer table)
 byte 0x06: battery related? (seems to decrease while charging and increase while discharging)
 byte 0x09: system model (see Cfg:GetSystemModel for values)
 byte 0x0A: Red Power LED mode (0 = off, 1 = on)
 byte 0x0B: Blue Power LED intensity  (0x00 - 0xFF)
 byte 0x0D: RGB LED red intensity
 byte 0x0E: RGB LED green intensity
 byte 0x0F: RGB LED blue intensity
 byte 0x11: WiFi LED brightness
 byte 0x12: raw button states?
   bit0: unset while Power button is held
   bit1: unset while HOME button is held
   bit2: unset while WiFi slider is held
   bit5: unset while the charging LED is active
   bit6: unset while charger is plugged in

On MCU_FIRM major version 1 the size of this is 9, reading past the 9th byte will yield AA instead of FF.

0x80

- 0xFF

s - These registers don't exist, writing is no-op, reading will yield FFs.

Shared register: the letter "s" means that the given register is in a "shared register pool", meaning the resgister is in the register pool in RAM at address 0xFFBA4 + registernumber.

Dynamic register: these registers aren't in the shared pool, they just "pretend" to be there. These registers often don't retain their set value, change rapidly, or control various hardware.

Non-shared (dynamic) register: it's a register whose contents separate from the shared register pool. Messing with these registers will not affect the shared register pool at all.

On old versions of MCU_FIRM none of the invalid registers are masked away by the read handler function, but are still read-only. Newer MCU_FIRM versions return the hardcoded value FF instead.

Device 5 & 6

LCD controllers for main/sub displays, most likely.

Register Width Name Description
0x1 8 ?
0x11 8 ?
0x40 8 CMD_IN/CMD_RESULT1 Write to trigger a command? Seen commands: 0xFF=Reset?, 0x62=IsFinished?. Result is stored in CMD_RESULT1:CMD_RESULT0.
0x41 8 CMD_RESULT0 Read result
0x50 8 ?
0x60 8 ?
0xFE 8 ?

Device 10

See the datasheet linked to on the Hardware page for reference.

Device 11

See the datasheet linked to on the Hardware page for reference.

Device 12

REGISTER WIDTH DESCRIPTION
0x0 21 DebugPad state.

This is the DebugPad device, see here.

Device 13

Raw I2C register address Internal register address Width Description
0x0 0x0 0x40 RHR / THR (data receive/send FIFO)
0x8 0x1 0x1 IER
0x10 0x2 0x1 FCR/IIR
0x18 0x3 0x1 LCR
0x20 0x4 0x1 MCR
0x28 0x5 0x1 LSR
0x30 0x6 0x1 MSR/TCR
0x38 0x7 0x1 SPR/TLR
0x40 0x8 0x1 TXLVL
0x48 0x9 0x1 RXLVL
0x50 0xA 0x1 IODir
0x58 0xB 0x1 IOState
0x60 0xC 0x1 IoIntEna
0x68 0xD 0x1 reserved
0x70 0xE 0x1 IOControl
0x78 0xF 0x1 EFCR

See the datasheet linked to on the Hardware page for reference. From that datasheet, for the structure of the I2C register address u8: "Bit 0 is not used, bits 2:1 select the channel, bits 6:3 select one of the UART internal registers. Bit 7 is not used with the I2C-bus interface, but it is used by the SPI interface to indicate a read or a write operation."

Device 14

Used by Cfg-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.

The Cfg-module code which loads the CCAL(nandro:/sys/{HWCAL0.dat/HWCAL1.dat}) file from NAND will load it from I2C instead, if a certain state flag is non-zero. Likewise for the function which writes CCAL to NAND. HMAC/hash verification after loading is skipped when the CCAL was loaded from I2C.

Device 15

This the New3DS NFC controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service commands.

Since the *Raw commands are used with this, this device has no I2C registers. Instead, raw data is transfered after the I2C device is selected. Hence, WriteDeviceRaw is used for sending commands to the controller, while ReadDeviceRaw is for receiving responses from the controller. Certain commands may return multiple command responses.

Command request / response structure:

Offset Size Description
0x0 0x1 Normally 0x10?
0x1 0x1 Command source / destination.
0x2 0x1 CmdID
0x3 0x1 Payload size.

Following the above header is the payload data(when payload size is non-zero), with the size specified in the header. The command response payload is usually at least 1-byte, where that byte appears to be normally 0x0. For command requests the payload data is the command parameters.

For command requests sent to the NFC tag itself, Cmd[1]=0x0 and CmdID=0x0. The command request payload data here is the actual command request data for the NFC tag, starting with the CmdID u8 at payload+0.

During NFC module startup, a certain command is sent to the controller which eventually(after various cmd-reply headers etc) returns the following the payload after the first byte in the payload:

000000: 44 65 63 20 32 32 20 32 30 31 32 31 34 3a 35 33  Dec 22 201214:53 
000010: 3a 35 30 01 05 0d 46 05 1b 79 20 07 32 30 37 39  :50...F..y .2079
000020: 31 42 35                                         1B5

Or that is: "Dec 22 201214:53:50<binary>20791B5". Therefore, this appears to return the part-number of the NFC controller(other command request(s) / response(s) use this part-number value too).

NFC controller commands

CmdRequest[1] CmdID Payload data for parameters Description
0x2E 0x2F Firmware image for this chunk, size varies. This is used during NFC module startup to upload the firmware image to the NFC controller. This is used repeatedly to upload multiple chunks of the image.