Difference between revisions of "I2C Registers"

From 3dbrew
Jump to navigation Jump to search
(Update info about battery middle pin and temperature reading)
 
(137 intermediate revisions by 17 users not shown)
Line 1: Line 1:
== Registers ==
+
= Registers =
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
NAME
+
Old3DS
PHYSICAL ADDRESS
+
Name
PROCESS ADDRESS
+
Address
WIDTH
+
Width
 +
!  Used by
 
|-
 
|-
| REG_I2C1DATA
+
| style="background: green" | Yes
 +
| I2C1_DATA
 
| 0x10161000
 
| 0x10161000
| 0x1EC61000
 
 
| 1
 
| 1
 +
| I2C bus 1 devices
 
|-
 
|-
| REG_I2C1CNT
+
| style="background: green" | Yes
 +
| [[#I2C_CNT|I2C1_CNT]]
 
| 0x10161001
 
| 0x10161001
| 0x1EC61001
 
 
| 1
 
| 1
 +
| I2C bus 1 devices
 
|-
 
|-
| REG_I2C1CNTEX
+
| style="background: green" | Yes
 +
| I2C1_CNTEX
 
| 0x10161002
 
| 0x10161002
| 0x1EC61002
 
 
| 2
 
| 2
 +
| I2C bus 1 devices
 
|-
 
|-
| REG_I2C1SCL
+
| style="background: green" | Yes
 +
| I2C1_SCL
 
| 0x10161004
 
| 0x10161004
| 0x1EC61004
 
 
| 2
 
| 2
 +
| I2C bus 1 devices
 
|-
 
|-
| REG_I2C2DATA
+
| style="background: green" | Yes
 +
| I2C2_DATA
 
| 0x10144000
 
| 0x10144000
| 0x1EC44000
 
 
| 1
 
| 1
 +
| I2C bus 2 devices
 
|-
 
|-
| REG_I2C2CNT
+
| style="background: green" | Yes
 +
| [[#I2C_CNT|I2C2_CNT]]
 
| 0x10144001
 
| 0x10144001
| 0x1EC44001
 
 
| 1
 
| 1
 +
| I2C bus 2 devices
 
|-
 
|-
| REG_I2C2CNTEX
+
| style="background: green" | Yes
 +
| I2C2_CNTEX
 
| 0x10144002
 
| 0x10144002
| 0x1EC44002
 
 
| 2
 
| 2
 +
| I2C bus 2 devices
 
|-
 
|-
| REG_I2C2SCL
+
| style="background: green" | Yes
 +
| I2C2_SCL
 
| 0x10144004
 
| 0x10144004
| 0x1EC44004
 
 
| 2
 
| 2
 +
| I2C bus 2 devices
 
|-
 
|-
| REG_I2C3DATA
+
| style="background: green" | Yes
 +
| I2C3_DATA
 
| 0x10148000
 
| 0x10148000
| 0x1EC48000
 
 
| 1
 
| 1
 +
| I2C bus 3 devices
 
|-
 
|-
| REG_I2C3CNT
+
| style="background: green" | Yes
 +
| [[#I2C_CNT|I2C3_CNT]]
 
| 0x10148001
 
| 0x10148001
| 0x1EC48001
 
 
| 1
 
| 1
 +
| I2C bus 3 devices
 
|-
 
|-
| REG_I2C3CNTEX
+
| style="background: green" | Yes
 +
| I2C3_CNTEX
 
| 0x10148002
 
| 0x10148002
| 0x1EC48002
 
 
| 2
 
| 2
 +
| I2C bus 3 devices
 
|-
 
|-
| REG_I2C3SCL
+
| style="background: green" | Yes
 +
| I2C3_SCL
 
| 0x10148004
 
| 0x10148004
| 0x1EC48004
 
 
| 2
 
| 2
 +
| I2C bus 3 devices
 +
|}
 +
 +
== I2C_CNT ==
 +
{| class="wikitable" border="1"
 +
!  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 Devices ==
+
== I2C_CNTEX ==
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
!  Device id
+
!  BIT
 +
!  DESCRIPTION
 +
|-
 +
| 0-1
 +
| ? Set to 2 normally.
 +
|}
 +
 
 +
== I2C_SCL ==
 +
{| class="wikitable" border="1"
 +
!  BIT
 +
!  DESCRIPTION
 +
|-
 +
| 0-5
 +
| ?
 +
|-
 +
| 8-12
 +
| ? Set to 5 normally.
 +
|}
 +
 
 +
= I2C Devices =
 +
{| class="wikitable" border="1"
 +
[[I2C_Registers|Device id]]
 
!  Device bus id
 
!  Device bus id
!  Device address
+
!  Device Write Address
 +
!  Accessible via I2C [[I2C_Services|service]]
 
!  Device description
 
!  Device description
 
|-
 
|-
Line 77: Line 139:
 
| 1
 
| 1
 
| 0x4a
 
| 0x4a
 +
| "i2c::MCU"
 
| Power management?(same device addr as the DSi power-management)
 
| Power management?(same device addr as the DSi power-management)
 
|-
 
|-
Line 82: Line 145:
 
| 1
 
| 1
 
| 0x7a
 
| 0x7a
 +
| "i2c::CAM"
 
| Camera0?(same dev-addr as DSi cam0)
 
| Camera0?(same dev-addr as DSi cam0)
 
|-
 
|-
Line 87: Line 151:
 
| 1
 
| 1
 
| 0x78
 
| 0x78
 +
| "i2c::CAM"
 
| Camera1?(same dev-addr as DSi cam1)
 
| Camera1?(same dev-addr as DSi cam1)
 
|-
 
|-
Line 92: Line 157:
 
| 2
 
| 2
 
| 0x4a
 
| 0x4a
 +
| "i2c::MCU"
 
| MCU
 
| MCU
 
|-
 
|-
Line 97: Line 163:
 
| 2
 
| 2
 
| 0x78
 
| 0x78
 +
| "i2c::CAM"
 
| ?
 
| ?
 
|-
 
|-
Line 102: Line 169:
 
| 2
 
| 2
 
| 0x2c
 
| 0x2c
 +
| "i2c::LCD"
 
| ?
 
| ?
 
|-
 
|-
Line 107: Line 175:
 
| 2
 
| 2
 
| 0x2e
 
| 0x2e
 +
| "i2c::LCD"
 
| ?
 
| ?
 
|-
 
|-
Line 112: Line 181:
 
| 2
 
| 2
 
| 0x40
 
| 0x40
 +
| "i2c::DEB"
 
| ?
 
| ?
 
|-
 
|-
Line 117: Line 187:
 
| 2
 
| 2
 
| 0x44
 
| 0x44
 +
| "i2c::DEB"
 
| ?
 
| ?
 
|-
 
|-
Line 122: Line 193:
 
| 3
 
| 3
 
| 0xa6
 
| 0xa6
| ?
+
| "i2c::HID"
 +
| Gyroscope. The device table in I2C-module had the device address changed from 0xA6 to 0xD6 with [[8.0.0-18]].
 
|-
 
|-
 
| 10
 
| 10
 
| 3
 
| 3
 
| 0xd0
 
| 0xd0
| ?
+
| "i2c::HID"
 +
| Gyroscope (old3DS)
 
|-
 
|-
 
| 11
 
| 11
 
| 3
 
| 3
 
| 0xd2
 
| 0xd2
| ?
+
| "i2c::HID"
 +
| Gyroscope (2DS, new3DSXL, new2DSXL)
 
|-
 
|-
 
| 12
 
| 12
 
| 3
 
| 3
 
| 0xa4
 
| 0xa4
| ?
+
| "i2c::HID"
 +
| DebugPad (slightly modified [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro])
 
|-
 
|-
 
| 13
 
| 13
 
| 3
 
| 3
 
| 0x9a
 
| 0x9a
| ?
+
| "i2c::IR"
 +
| IR
 
|-
 
|-
 
| 14
 
| 14
 
| 3
 
| 3
 
| 0xa0
 
| 0xa0
| eeprom?
+
| "i2c::EEP"
 +
| HWCAL EEPROM ([[Hardware_calibration#Header|only present on dev units where SHA256 is used for HWCAL verification]])
 +
|-
 +
| 15
 +
| 2
 +
| 0xee
 +
| "i2c::NFC"
 +
| New3DS-only [[NFC_Services|NFC]]
 +
|-
 +
| 16
 +
| 1
 +
| 0x40
 +
| "i2c::QTM"
 +
| New3DS-only [[QTM_Services|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 ==
 
== 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)
 +
 +
Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.
 +
 +
This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.
 +
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
 
!  REGISTER
 
!  REGISTER
 
!  WIDTH
 
!  WIDTH
 +
!  INFO
 
!  DESCRIPTION  
 
!  DESCRIPTION  
 +
|-
 +
| 0x00
 +
| s
 +
| ro
 +
| Version high
 +
|-
 +
| 0x01
 +
| s
 +
| ro
 +
| Version low
 +
|-
 +
| 0x02
 +
| d
 +
| rw
 +
| For bit0 and 1 values, 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
 +
  bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)
 +
  bit6: TWL MCU reg: NTR (0) vs TWL mode (1)
 +
  bit7: TWL MCU reg: Uses NAND
 
|-
 
|-
 
| 0x03
 
| 0x03
| 8
+
| ds
| ?
+
| rw
 +
| Top screen Vcom
 
|-
 
|-
 
| 0x04
 
| 0x04
| 8
+
| ds
 +
| rw
 +
| Bottom screen Vcom
 +
|-
 +
| 0x05
 +
- 0x07
 +
| s
 +
| rw
 +
| Danger zone - [[MCU_Services#MCU_firmware_versions|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|MCUHWC:GetSoundVolume]]
 +
|-
 +
| 0x0A
 +
| s
 +
| ro
 +
| Internal battery temperature (in Celsius)
 +
|-
 +
| 0x0B
 +
| s
 +
| ro
 +
| Battery percentage (integer part).
 +
 
 +
Valid values are in range of 0 to 100 inclusive.
 +
|-
 +
| 0x0C
 +
| s
 +
| ro
 +
| Battery percentage (fractional part).
 +
 
 +
Seems to have a resolution of around 0.1% according to tests.
 +
 
 +
To calculate battery charge percentage in full resolution:
 +
MCU[0x0B] + (MCU[0x0C] / 256.0F)
 +
|-
 +
| 0x0D
 +
| s
 +
| ro
 +
| System voltage.
 +
 
 +
This voltage seems to be measured at the load side, so the voltage reading will always be lower than direct probes across the battery due to the various voltage drops in the system by the time the voltage is measured.
 +
 
 +
To calculate system voltage in Volts:
 +
MCU[0x0D] * 5 / 256.0F
 +
|-
 +
| 0x0E
 +
| s
 +
| ro
 
| ?
 
| ?
 +
|-
 +
| 0x0F
 +
| s
 +
| ro
 +
| Flags: bit7-5 are read via [[MCU_Services|mcu::GPU]]. The rest of them are read via [[MCU_Services|mcu::RTC]].
 +
  bit1: ShellState
 +
  bit3: AdapterState
 +
  bit4: BatteryChargeState
 +
  bit5: Bottom screen backlight on
 +
  bit6: Top screen backlight on
 +
  bit7: LCD panel voltage 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
 
| 0x18
| 8
+
- 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([[Services#Notifications|?]]) (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 I2C read/write done [https://github.com/profi200/libn3ds/blob/083c8ffa3f56a49802fa74b6afe45a96820f0439/include/arm11/drivers/mcu_regmap.h#L124]
 +
  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: GPU off
 +
  bit25: GPU on
 +
  bit26: bottom backlight off
 +
  bit27: bottom backlight on
 +
  bit28: top backlight off
 +
  bit29: top 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
 
| 0x20
| 8
+
| d
| Writing u8 value 4 here triggers a hardware system reboot.
+
| wo
 +
| System power control:
 +
  bit0: power off
 +
  bit1: full reboot (unused). Discards things like [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]]
 +
    - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers
 +
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)
 +
    - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)
 +
  bit2: normal reboot. Preserves [[CONFIG9_Registers#CFG9_BOOTENV|CFG9_BOOTENV]], etc.
 +
    - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)
 +
    - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)
 +
    - Resets TWL MCU i2c registers
 +
  bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)
 +
    - Asserts FCRAM_RESET (P3.0 = 0)
 +
  bit4: signal that sleep mode is about to be entered (used by PTM)
 +
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.
 +
 
 +
If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.
 +
|-
 +
| 0x21
 +
| d
 +
| wo
 +
| Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via [[#LGY_GPIOEMU_MASK|Legacy I/O registers]].
 +
  bit0: Signal TWL POWER button click
 +
  bit1: Signal TWL reset
 +
  bit2: Signal TWL power off
 +
  bit3: Signal TWL battery low
 +
  bit4: Signal TWL battery empty
 +
  bit5: Signal TWL volume button click
 
|-
 
|-
 
| 0x22
 
| 0x22
| 8
+
| d
| ?
+
| wo
 +
| Used to turn on or turn off LCD-related boost circuits. Bits 5:2 can be read back so see whether backlight setting is in progress or not, however bits 1:0 get cleared as soon as the request gets acknowledged.
 +
  bit0: LCD panel voltage off
 +
  bit1: LCD panel voltage on
 +
  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
 
| 0x23
| 8
+
| d
 +
| wo
 +
| Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?
 +
|-
 +
| 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
 
| ?
 
| ?
 
|-
 
|-
| 0x24
+
| 0x26
| 8
+
| s
 +
| rw
 
| ?
 
| ?
 +
|-
 +
| 0x27
 +
| sd
 +
| rw
 +
| Raw volume slider state
 
|-
 
|-
 
| 0x28
 
| 0x28
| 8
+
| s
| ?
+
| rw
 +
| Brightness of the WiFi/Power LED
 
|-
 
|-
 
| 0x29
 
| 0x29
| 8
+
| sd(5)
| ?
+
| rw
 +
| 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
| 8
+
| s
| ?
+
| rw
 +
| WiFi LED state, non-0 value turns on the WiFi LED, 4 bits wide
 
|-
 
|-
 
| 0x2B
 
| 0x2B
| 8
+
| 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
 
| 0x2C
| 8
+
| s
| ?
+
| rw
 +
| 3D LED state, 4 bits wide
 
|-
 
|-
 
| 0x2D
 
| 0x2D
 
| 0x64
 
| 0x64
| This is used for [[MCURTC:SetInfoLEDPattern|controlling]] the notification LED(see [[MCURTC:SetInfoLEDPatternHeader]] as well), when this register is written.
+
| wo
 +
| This is used for [[MCURTC:SetInfoLEDPattern|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
 
| 0x2E
| 1
+
| s
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read.
+
| ro
 +
| This [[MCURTC:GetInfoLEDStatus|returns]] the notification LED status when read (1 means new cycle started)
 +
|-
 +
| 0x2F
 +
| s
 +
| wo?
 +
| ??? The write function for this register is stubbed.
 
|-
 
|-
 
| 0x30
 
| 0x30
| 8
+
- 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
 
|-
 
|-
| 0x31
+
| 0x3B
| 8
+
| s
| ?
+
| rw
 +
| Could be used on extremely old MCU_FIRM versions to upload [[MCU_Services#MCU_firmware_versions|MCU firmware]] if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm).
 
|-
 
|-
| 0x32
+
| 0x3D
| 8
+
0x3E
| ?
+
| ds
 +
| ro
 +
| RTC tick counter / "ITMC" (when resets to 0 the seconds increase)
 +
Only reading 0x3D will update the in-RAM value
 
|-
 
|-
| 0x33
+
| 0x3F
| 8
+
| d
| ?
+
| wo
 +
| 2 bits
 +
  bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn't deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!
 +
  bit1: turns on a prohibited bit in an RTC Control register and turns P12 into an output
 
|-
 
|-
| 0x34
+
| 0x40
| 8
+
| 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.
 
|-
 
|-
| 0x35
+
| 0x41
| 8
+
| s
| ?
+
| rw
 +
| Index selector for register 0x44
 
|-
 
|-
| 0x36
+
| 0x42
| 8
+
| s
| ?
+
| rw
 +
| Unused?
 
|-
 
|-
| 0x37
+
| 0x43
| 8
+
| s
| ?
+
| rw
 +
| Unused???, accelometer related
 
|-
 
|-
| 0x38
+
| 0x44
| 8
+
| s
| ?
+
| rw
 +
| ???, pedoometer related(?)
 
|-
 
|-
| 0x39
+
| 0x45
| 8
+
- 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
 +
{| class="wikitable" border="1"
 +
!  AXIS
 +
!  V=0x00
 +
!  V=0x40
 +
!  V=0xC0
 
|-
 
|-
| 0x3A
+
| X (left/right)
| 8
+
| held up vertically
| ?
+
| rotated left 90° like a steering wheel
 +
| rotated right 90° like a steering wheel
 
|-
 
|-
| 0x3B
+
| Y (forwards/backwards)
| 8
+
| laid flat on the desk with the screen facing up
| ?
+
| held up vertically
 +
| held up vertically with screen facing upside-down
 
|-
 
|-
| 0x3C
+
| Z (???)
| 8
+
| ???
| ?
+
| ???
 +
| ???
 +
|}
 
|-
 
|-
| 0x41
+
| 0x4B
| 8
+
| s
| ?
+
| rw
 +
| PedometerStepCount (for the current day)
 
|-
 
|-
| 0x43
+
| 0x4C
| 8
+
0x4D
| ?
+
| ??
 +
| ??
 +
| ??
 
|-
 
|-
 
| 0x4E
 
| 0x4E
| 8
+
| d
| ?
+
| rw
 +
| ??? this = (0xFFE9E & 1) ? 0x10 : 0
 +
|-
 +
| 0x4F
 +
| d(6)
 +
| ro
 +
|
 
|-
 
|-
 
| 0x50
 
| 0x50
| 8
+
| s
| ?
+
| rw
 +
| ???
 
|-
 
|-
 
| 0x51
 
| 0x51
| 8
+
| s
 +
| rw
 +
| ???
 +
|-
 +
| 0x52
 +
- 0x57
 +
| s
 +
| rw
 
| ?
 
| ?
 
|-
 
|-
 
| 0x58
 
| 0x58
| 8
+
| 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
 +
| d
 +
| rw
 +
| Free register bank address (index) select
 +
Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.
 +
 
 +
  byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"
 +
  byte 1: Legacy LCD data
 +
  bytes 2 and 3: Local Friend Code counter
 +
  bytes 4 and 5: UUID clock sequence
 +
  bytes 6 and 7: Unused
 +
  bytes 8 to 175: Playtime data for legacy titles
 +
  bytes 176 to 188: Temporary playtime data in case console doesn't shut down gracefully, updated every 5 minutes
 +
  bytes 188 to 199: Unused
 +
|-
 +
| 0x61
 +
| d(200)
 +
| rw
 +
| Free register bank, data is read from/written to here.
 +
 
 +
Accessing N bytes of this register increments the selected index by N.
 +
|-
 +
| 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 0x00: Console type, see [[Configuration_Memory#MCU_HW_INFO|here]]
 +
  byte 0x01: PMIC vendor code
 +
  byte 0x02: Battery vendor code (determined from battery middle pin)
 +
    0x00: Maxell (middle pin tied to GND)
 +
      CTR-003 CTR-A-BP (old3DS)
 +
      CTR-003 CTR-A-BPMX-C3 (2DS): Wuxi Hitachi Maxell Co.,Ltd.
 +
      CTR-003 CTR-A-BPMX-C5 (Switch Pro Controller): Wuxi Maxell Co., Ltd.
 +
      SPR-003 SPR-A-BPMX-C3 (new3DSXL): Wuxi Hitachi Maxell Co.,Ltd.
 +
  byte 0x03: MGIC version (major?)
 +
  byte 0x04: MGIC version (minor?)
 +
  byte 0x05: RCOMP(?)
 +
  byte 0x06: On-board battery slot NTC reading (more heat causes this value to go *down*, and cooling off will make this value go back up)
 +
  byte 0x09: system model (see [[Cfg:GetSystemModel#System_Model_Values|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 <code>0xFFBA4 + registernumber</code>.
 +
 
 +
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 ==
 +
These are the chip-on-glass display controllers, also known as I2CLCD.
 +
 
 +
=== Shared registers ===
 +
These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).
 +
 
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x01
 +
| Display enable
 +
| 0x11
 +
| Values:
 +
 
 +
  - 0x00 - screen off, slow burn-in
 +
  - 0x01 - screen off, fast burn-in
 +
  - 0x10 - screen on, color input used
 +
  - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)
 +
|-
 +
| 0x40
 +
| Read address
 +
|
 +
| Write to this register to set the read address.
 +
 
 +
Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.
 +
|-
 +
| 0x54
 +
| Checksum? trigger
 +
| 0x01
 +
| When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.
 +
|-
 +
| 0x55
 +
| ???
 +
| 0x03 (all) /
 +
0x07 (2DS)
 +
| Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).
 +
 
 +
This register is sometimes seen with a value of 0x02 at initialization time on the top screen.
 +
|-
 +
| 0x56
 +
| Checksum?
 +
|
 +
| Unknown. Read-writable with no effect (old3DS) / read-only (all).
 +
 
 +
A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it's oneshot/bugged.
 +
|-
 +
| 0x60
 +
| ???
 +
| 0x01
 +
| Unknown. 0x00 is written here during init. Seems to have no effect.
 +
|-
 +
| 0x61
 +
| Register checksum
 +
|
 +
| Some - but not all - register values are combined using an unknown algorithm into this register. 
 +
It's unknown which registers influence this value, as some registers which influence this value are read-only.
 +
|-
 +
| 0x62
 +
| ???
 +
| 0x01
 +
| Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.
 +
|-
 +
| 0xFE
 +
| ???
 +
|
 +
| Unknown, does nothing. 0xAA is written here during init.
 +
|-
 +
| 0xFF
 +
| Controller ID
 +
|
 +
| Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter. The fields are encoded this way, most likely for the register checksum feature.
 +
 
 +
Manufacturers:
 +
  - 0x0 - SHARP (LTPS(?) TN), old I2CLCD, found in old3DS (non-XL) only
 +
  - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles
 +
  - 0xC - SHARP (LTPS(?) TN), new I2CLCD
 +
  - 0xE - SHARP (TFT), found in 2DS only
 +
 
 +
Known IDs:
 +
  - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL
 +
  - 0xC3 - older old3DSXL
 +
  - 0xE1 - 2DS
 +
    - LQ050B1LW10B
 +
      - LQ = normal TFT
 +
      - 050 = panel 5 inches diagonal
 +
      - B = "other" display format
 +
      - 1 = transmissive (backlight-compatible)
 +
      - L = LVDS
 +
      - W = *unknown coating type*
 +
      - 10 = model number
 +
      - B = *unknown suffix*
 +
  - 0x10 - some select new3DS and new3DSXL with IPS screens
 +
  - 0x01 - old3DS
 +
    - LS035T7LE38P (top screen)
 +
      - LS = TFT (LTPS or SI-TFT ?)
 +
      - 035 = panel 3.5 inches diagonal
 +
      - T = "other 16:9" (even though the panel is 16:10 in physical size, or 32:10 in terms of pixel count)
 +
      - 7 = *unknown backing type*
 +
      - L = LVDS
 +
      - E = *unknown coating type*
 +
      - 38 = model number
 +
      - P = *unknow suffix*
 +
    - LS030Q7DW48P (bottom screen)
 +
      - LS = TFT (LTPS or SI-TFT ?)
 +
      - 030 = panel 3 inches diagonal
 +
      - Q = QVGA (320x240)
 +
      - 7 = *unknown backing type*
 +
      - D = parallel RGB (unspecified, but it's known to be RGB888 for this display)
 +
      - W = *unknown coating type*
 +
      - 48 = model number
 +
      - P = *unknow suffix*
 +
  - 0x00 - no controller, or dead (I2CLCD always ACKs reads, but returns 00 if dead)
 +
|}
 +
 
 +
=== Custom registers for controller 0x00 ===
 +
This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.
 +
 
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x11
 +
| ???
 +
|
 +
| Unknown. Write 0x10 to initialize.
 +
|-
 +
| 0x50
 +
| ???
 +
|
 +
| Unknown. Write 0x01 to initialize.
 +
|}
 +
 
 +
 
 +
=== Custom registers for controller 0x01 ===
 +
 
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x10
 +
| Interface config
 +
| 0xF7
 +
| Regonfigures the input pins and pin behavior of the controller.
 +
 
 +
bit0 - color value invert (D = ~D, or D = 255 - D)
 +
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)
 +
bit2 - ???
 +
bit4 - ???
 +
bit5 - ???
 +
bit6 - ???
 +
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)
 +
|-
 +
| 0x11
 +
| Image config
 +
| 0x7F
 +
| Image filters and pixel clock control.
 +
 
 +
bit0 - Horizontal Flip (scan from right to left)
 +
bit1 - red-blue swap
 +
bit2 - ???
 +
bit3 - ???
 +
bit4 - ???
 +
bit5 - ???
 +
bit6 - ???
 +
|-
 +
| 0x1D
 +
| ???
 +
| 0x0F
 +
| Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.
 +
|-
 +
| 0x50
 +
| ???
 +
| 0x11
 +
| Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).
 +
|-
 +
| 0x53
 +
| ???
 +
| 0x73
 +
| Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.
 +
|}
 +
 
 +
=== Custom registers for controller 0xC3 ===
 +
Basically the same as Controller ID 0xC7.
 +
 
 +
 
 +
=== Custom registers for controller 0xC7 ===
 +
This is the most common non-old3DS display controller. Quite overclockable.
 +
 
 +
Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.
 +
 
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x03
 +
| Factory key 2
 +
|
 +
| Write 0xAA here to unlock a second set of factory controls.
 +
|-
 +
| 0xAF
 +
| Factory key
 +
|
 +
| Write 0xAA here to unlock factory controls.
 +
|}
 +
 
 +
Factory mode registers for unlock register 0x03:
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x10
 +
| Image control?
 +
| 0xD7
 +
| Most bits are unknown.
 +
 
 +
bit0 - color invert
 +
bit1 - slight gamma increase
 +
|-
 +
| 0x11
 +
| Image transform?
 +
| 0x7F
 +
| Mostly unknown.
 +
bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)
 +
bit1 - red-blue swap
 +
bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)
 +
bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)
 +
bit4 - Enable interlaced signal (use bit3 to swap fields)
 +
bit5 - ???
 +
bit6 - ???
 +
|-
 +
| 0x70-0x83
 +
| Color curve red
 +
| rowspan=3 |
 +
| rowspan=3 | These registers are used for fine-tuning the analog driving curve of the screen
 +
 
 +
Positive:
 +
- byte 00 (0xFF) - ???
 +
- byte 01 (0xFF) - ???
 +
- byte 02 (0x3F) - ???
 +
- byte 03 (0x3F) - ???
 +
- byte 04 (0x3F) - ???
 +
- byte 05 (0x3F) - ???
 +
- byte 06 (0x3F) - ???
 +
- byte 07 (0x3F) - ???
 +
- byte 08 (0x3F) - ???
 +
- byte 09 (0x3F) - ???
 +
 +
Negative:
 +
- byte 10 (0xFF) - ???
 +
- byte 11 (0xFF) - ???
 +
- byte 12 (0x3F) - ???
 +
- byte 13 (0x3F) - ???
 +
- byte 14 (0x3F) - ???
 +
- byte 15 (0x3F) - ???
 +
- byte 16 (0x3F) - ???
 +
- byte 17 (0x3F) - ???
 +
- byte 18 (0x3F) - ???
 +
- byte 19 (0x3F) - ???
 +
|-
 +
| 0x84-0x97
 +
| Color curve green
 +
|-
 +
| 0x98-0xAB
 +
| Color curve blue
 +
|}
 +
 
 +
=== Custom registers for controller 0xE1 ===
 +
This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.
 +
 
 +
This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.
 +
 
 +
Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.
 +
 
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x03
 +
| Factory key 2
 +
|
 +
| Write 0xAA here to unlock a 2nd set of factory controls.
 +
|-
 +
| 0xAF
 +
| Factory key
 +
|
 +
| Write 0xAA here to unlock factory controls.
 +
|}
 +
 
 +
Factory mode registers for unlock register 0x03:
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x10
 +
| Image control?
 +
| 0xD7
 +
| Most bits are unknown. This applies to the whole display panel.
 +
 
 +
bit0 - color invert
 +
bit1 - slight gamma increase
 +
|-
 +
| 0x11
 +
| Image transform
 +
| 0x33
 +
|
 +
bit0 - top half horizontal flip
 +
bit1 - top half red-blue swap
 +
bit4 - bottom half horizontal flip
 +
bit5 - bottom half red-blue swap
 +
|-
 +
| 0x70-0x83
 +
| Analog curve top
 +
| rowspan=2 |
 +
| rowspan=2 | Consists of two unknown curve values. Seems to be nonstandard.
 +
 
 +
Pair 1:
 +
byte 00 (0xFF) - ???
 +
byte 01 (0xFF) - ???
 +
byte 02 (0xFF) - ???
 +
byte 03 (0xFF) - ???
 +
byte 04 (0x3F) - ???
 +
byte 05 (0x3F) - ???
 +
byte 06 (0x3F) - ???
 +
byte 07 (0x3F) - ???
 +
byte 08 (0x3F) - ???
 +
byte 09 (0x3F) - ???
 +
 
 +
Part 2:
 +
byte 10 (0xFF) - ???
 +
byte 11 (0xFF) - ???
 +
byte 12 (0xFF) - ???
 +
byte 13 (0xFF) - ???
 +
byte 14 (0x3F) - ???
 +
byte 15 (0x3F) - ???
 +
byte 16 (0x3F) - ???
 +
byte 17 (0x3F) - ???
 +
byte 18 (0x3F) - ???
 +
byte 19 (0x3F) - ???
 +
|-
 +
| 0x84-0x97
 +
| Analog curve bottom
 +
|}
 +
 
 +
=== Custom registers for controller 0x10 ===
 +
JDI IPS controller.
 +
 
 +
Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!
 +
 
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x03
 +
| Factory key 2
 +
|
 +
| Write 0xAA here to unlock advanced IPS curve controls.
 +
|-
 +
| 0xAF
 +
| Factory key
 +
|
 +
| Write 0xAA here to unlock factory controls.
 +
|}
 +
 
 +
Factory mode registers unlocked by register 0xAF:
 +
* 0x41 - 0x4F
 +
* 0x58 - 0x5F
 +
* 0x67 - 0x6F
 +
* 0xD0 - 0xEF
 +
* unknown...
 +
 
 +
Factory mode registers unlocked by register 0x03:
 +
* 0x04 - 0x0F
 +
* unknown...
 +
{| class="wikitable" border="1"
 +
!  Register
 +
!  Name
 +
!  Valid bits
 +
!  Description
 +
|-
 +
| 0x70-0x7F
 +
| Driving curve 1-1
 +
|
 +
|
 +
|-
 +
| 0x80-0x8F
 +
| Driving curve 1-2
 +
|
 +
|
 +
|-
 +
| 0x90-0x9F
 +
| Driving curve 2-1
 +
|
 +
|
 +
|-
 +
| 0xA0-0xAF
 +
| Driving curve 2-2
 +
|
 +
|
 +
|-
 +
| 0xB0-0xBF
 +
| Driving curve 3-1
 +
|
 +
|
 +
|-
 +
| 0xC0-0xCF
 +
| Driving curve 3-2
 +
|
 +
|
 +
|}
 +
 
 +
== 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 ==
 +
{| class="wikitable" border="1"
 +
!  REGISTER
 +
!  WIDTH
 +
!  DESCRIPTION
 +
|-
 +
| 0x0
 +
| 21
 +
| DebugPad state.
 +
|}
 +
 
 +
This is a [https://wiibrew.org/wiki/Wiimote/Extension_Controllers/Classic_Controller_Pro Wii Classic Controller Pro] which was slightly modified to have an encrypted device type of 0xF0 [https://wiibrew.org/wiki/Wiimote/Extension_Controllers#The_New_Way instead of 0xFD].
 +
 
 +
See [[HID_Shared_Memory#Offset_0x238|here]] for the HID shared memory report format.
 +
 
 +
== Device 13 ==
 +
{| class="wikitable" border="1"
 +
!  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
 
| 0x60
| 8
+
| 0xC
| ?
+
| 0x1
 +
| IoIntEna
 +
|-
 +
| 0x68
 +
| 0xD
 +
| 0x1
 +
| reserved
 +
|-
 +
| 0x70
 +
| 0xE
 +
| 0x1
 +
| IOControl
 +
|-
 +
| 0x78
 +
| 0xF
 +
| 0x1
 +
| EFCR
 
|}
 
|}
  
 +
See the [http://www.alldatasheet.net/datasheet-pdf/pdf/347838/NXP/SC16IS750IBS.html 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 ==
  
= I2CCNT =
+
Used by [[Config_Services|Cfg]]-sysmodule via the i2c::EEP service. This is presumably EEPROM going by the service name.
 +
 
 +
The Cfg-module code which loads the [[Flash_Filesystem|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_Services|NFC]] controller "I2C" interface. This device is accessed via the WriteDeviceRaw/ReadDeviceRaw I2C service [[I2C_Services|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:
 
{| class="wikitable" border="1"
 
{| class="wikitable" border="1"
|+ REG_I2CxCNT
+
!  Offset
BIT
+
Size
DESCRIPTION
+
Description
 +
|-
 +
| 0x0
 +
| 0x1
 +
| Normally 0x10?
 
|-
 
|-
| 0
+
| 0x1
| HOLD (0=Last byte of transaction, 1=More bytes coming)
+
| 0x1
 +
| Command source / destination.
 
|-
 
|-
| 2
+
| 0x2
| Error flag/ack?
+
| 0x1
 +
| CmdID
 
|-
 
|-
| 4
+
| 0x3
| Read mode related?
+
| 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  ===
 +
{| class="wikitable" border="1"
 +
!  CmdRequest[1]
 +
!  CmdID
 +
!  Payload data for parameters
 +
!  Description
 
|-
 
|-
| 5
+
| 0x2E
| Read mode?
+
| 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.
 +
|}
 +
 
 +
== Device 17 ==
 +
 
 +
(Stub)
 +
 
 +
Used by New 3DS for ZL, ZR, C stick
 +
 
 +
This device do not use registers. After writing the address, read the next several bytes.
 +
 
 +
{| class="wikitable" border="1"
 +
!  Offset
 +
!  Description
 
|-
 
|-
| 6
+
| 0x0
| IRQ enable?
+
| Fixed 0x80
 
|-
 
|-
| 7
+
| 0x1
| Enable?
+
| Buttons (ZL = 0x4, ZR = 0x2)
 
|}
 
|}

Latest revision as of 14:38, 6 September 2024

Registers[edit]

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[edit]

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[edit]

BIT DESCRIPTION
0-1 ? Set to 2 normally.

I2C_SCL[edit]

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

I2C Devices[edit]

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" 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 (slightly modified Wii Classic Controller Pro)
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[edit]

 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)

Reading or writing multiple bytes from/to single-byte registers increments the register ID along with it. For example reading two bytes from reg 0x00 reads regs 0x00 and 0x01.

This is not the case for multibyte regs (0x29, 0x2D, 0x4F, 0x61 and 0x7F), plus reg 0x60.

REGISTER WIDTH INFO DESCRIPTION
0x00 s ro Version high
0x01 s ro Version low
0x02 d rw For bit0 and 1 values, 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
 bit5: TWL MCU reg: volume mode (0: 8-step, 1: 32-step)
 bit6: TWL MCU reg: NTR (0) vs TWL mode (1)
 bit7: TWL MCU reg: Uses NAND
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 Internal battery temperature (in Celsius)
0x0B s ro Battery percentage (integer part).

Valid values are in range of 0 to 100 inclusive.

0x0C s ro Battery percentage (fractional part).

Seems to have a resolution of around 0.1% according to tests.

To calculate battery charge percentage in full resolution:

MCU[0x0B] + (MCU[0x0C] / 256.0F)
0x0D s ro System voltage.

This voltage seems to be measured at the load side, so the voltage reading will always be lower than direct probes across the battery due to the various voltage drops in the system by the time the voltage is measured.

To calculate system voltage in Volts:

MCU[0x0D] * 5 / 256.0F
0x0E s ro ?
0x0F s ro Flags: bit7-5 are read via mcu::GPU. The rest of them are read via mcu::RTC.
 bit1: ShellState
 bit3: AdapterState
 bit4: BatteryChargeState
 bit5: Bottom screen backlight on
 bit6: Top screen backlight on
 bit7: LCD panel voltage 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 I2C read/write done [1]
 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: GPU off
 bit25: GPU on
 bit26: bottom backlight off
 bit27: bottom backlight on
 bit28: top backlight off
 bit29: top 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: full reboot (unused). Discards things like CFG9_BOOTENV
   - Asserts RESET1 via PMIC command (?) (deasserts nRESET1). This could be the reset that controls some CFG9 registers
   - Asserts RESET2 (P0.1 = 0, PM0.1 = 0 (output)) (deasserts nRESET2)
   - Asserts FCRAM_RESET (P3.0 = 0) (deasserts nFCRAM_RESET)
 bit2: normal reboot. Preserves CFG9_BOOTENV, etc.
   - Asserts RESET2 (P0.1 = 0, PM0.1 = 0)
   - If in NTR emulation mode (see reg 0x02), asserts FCRAM_RESET (P3.0 = 0)
   - Resets TWL MCU i2c registers
 bit3: FCRAM reset (present in by LgyBg. Unused because a system reboot does the same thing & a PDN reg also possibly implements this function)
   - Asserts FCRAM_RESET (P3.0 = 0)
 bit4: signal that sleep mode is about to be entered (used by PTM)

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.

If any of the reset bits is set, the MCU waits for 5ms, then deasserts RESET1 (via PMIC), RESET2 (PM0.1 = 1 (input)) and FCRAM_RESET (P3.0 = 1), and reinitializes some other various registers after a 100ms delay.

0x21 d wo Used in legacy mode to signal events for TWL MCU "emulation" (written to REG[0x5D])? Software then asserts the TWL MCU IRQ pin via Legacy I/O registers.
 bit0: Signal TWL POWER button click
 bit1: Signal TWL reset
 bit2: Signal TWL power off
 bit3: Signal TWL battery low
 bit4: Signal TWL battery empty
 bit5: Signal TWL volume button click
0x22 d wo Used to turn on or turn off LCD-related boost circuits. Bits 5:2 can be read back so see whether backlight setting is in progress or not, however bits 1:0 get cleared as soon as the request gets acknowledged.
 bit0: LCD panel voltage off
 bit1: LCD panel voltage on
 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 d wo Writing 0x72 ('r') resets the MCU, but this is stubbed on retail?
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) rw 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 extremely old MCU_FIRM versions to upload MCU firmware if reg 0xF == 0 and reg 0x10 == 1 (presumably major and minor version fields for mcufw 0.1 which largely predates factory firm).
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 d wo 2 bits
 bit0: Asserts RESET1 (P0.0 = 0, PM0.0 = 0 (output)) but does NOT deassert it (wtf?). This seems to kill the entire SoC: is it because it doesn't deassert it, or does it not deassert it because the SoC hangs anyway? This is the pin that controls some security-critical regs like CFG9_BOOTENV!
 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 d rw Free register bank address (index) select

Selects the index to read from in the free register bank, up to 200. Used in conjunction with reg 0x61.

 byte 0: bit0 = "WirelessDisabled", bit1 = "SoftwareClosed", bit2 = "PowerOffInitiated", bit3 = "LgyNativeResolution", bit4 = "LegacyJumpProhibited"
 byte 1: Legacy LCD data
 bytes 2 and 3: Local Friend Code counter
 bytes 4 and 5: UUID clock sequence
 bytes 6 and 7: Unused
 bytes 8 to 175: Playtime data for legacy titles
 bytes 176 to 188: Temporary playtime data in case console doesn't shut down gracefully, updated every 5 minutes
 bytes 188 to 199: Unused
0x61 d(200) rw Free register bank, data is read from/written to here.

Accessing N bytes of this register increments the selected index by N.

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 0x00: Console type, see here
 byte 0x01: PMIC vendor code
 byte 0x02: Battery vendor code (determined from battery middle pin)
   0x00: Maxell (middle pin tied to GND)
     CTR-003 CTR-A-BP (old3DS)
     CTR-003 CTR-A-BPMX-C3 (2DS): Wuxi Hitachi Maxell Co.,Ltd.
     CTR-003 CTR-A-BPMX-C5 (Switch Pro Controller): Wuxi Maxell Co., Ltd.
     SPR-003 SPR-A-BPMX-C3 (new3DSXL): Wuxi Hitachi Maxell Co.,Ltd.
 byte 0x03: MGIC version (major?)
 byte 0x04: MGIC version (minor?)
 byte 0x05: RCOMP(?)
 byte 0x06: On-board battery slot NTC reading (more heat causes this value to go *down*, and cooling off will make this value go back up)
 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[edit]

These are the chip-on-glass display controllers, also known as I2CLCD.

Shared registers[edit]

These registers are the same across all known I2CLCD controllers (except Controller ID 0x00).

Register Name Valid bits Description
0x01 Display enable 0x11 Values:
 - 0x00 - screen off, slow burn-in
 - 0x01 - screen off, fast burn-in
 - 0x10 - screen on, color input used
 - 0x11 - screen on, color input not used, High-Z (display turns black or white depending on interface config)
0x40 Read address Write to this register to set the read address.

Reading from I2CLCD is non-standard. When you read, it returns pairs of the currently read address, and then the data byte at that address. The read address auto-increments.

0x54 Checksum? trigger 0x01 When transitioning bit0 from 0 to 1, it seems to trigger some sort of checksum calcuation. Broken on controller 0x01, where it's oneshot.
0x55 ??? 0x03 (all) /

0x07 (2DS)

Unknown. When toggling 0x54 bit0 from 0 to 1, this register gets changed to 0x01 (all) / 0x05 (2DS).

This register is sometimes seen with a value of 0x02 at initialization time on the top screen.

0x56 Checksum? Unknown. Read-writable with no effect (old3DS) / read-only (all).

A random value is written here when 0x54 bit0 is changed from 0 to 1. Constantly updates with a seemingly random value, except on Controller ID 0x01, where it's oneshot/bugged.

0x60 ??? 0x01 Unknown. 0x00 is written here during init. Seems to have no effect.
0x61 Register checksum Some - but not all - register values are combined using an unknown algorithm into this register.

It's unknown which registers influence this value, as some registers which influence this value are read-only.

0x62 ??? 0x01 Unknown, does nothing on known controllers. During init, gsp waits for this to become 0x01.
0xFE ??? Unknown, does nothing. 0xAA is written here during init.
0xFF Controller ID Upper 4bits is manufacturer. Lower 4bits is unknown, most likely revision, possibly encoded as a Johnson counter. The fields are encoded this way, most likely for the register checksum feature.

Manufacturers:

 - 0x0 - SHARP (LTPS(?) TN), old I2CLCD, found in old3DS (non-XL) only
 - 0x1 - JDI (LTPS IPS), found in select new3DS and new3DSXL consoles
 - 0xC - SHARP (LTPS(?) TN), new I2CLCD
 - 0xE - SHARP (TFT), found in 2DS only

Known IDs:

 - 0xC7 - new3DS, new3DSXL, new2DSXL, and some select newer old3DSXL
 - 0xC3 - older old3DSXL
 - 0xE1 - 2DS
   - LQ050B1LW10B
     - LQ = normal TFT
     - 050 = panel 5 inches diagonal
     - B = "other" display format
     - 1 = transmissive (backlight-compatible)
     - L = LVDS
     - W = *unknown coating type*
     - 10 = model number
     - B = *unknown suffix*
 - 0x10 - some select new3DS and new3DSXL with IPS screens
 - 0x01 - old3DS
   - LS035T7LE38P (top screen)
     - LS = TFT (LTPS or SI-TFT ?)
     - 035 = panel 3.5 inches diagonal
     - T = "other 16:9" (even though the panel is 16:10 in physical size, or 32:10 in terms of pixel count)
     - 7 = *unknown backing type*
     - L = LVDS
     - E = *unknown coating type*
     - 38 = model number
     - P = *unknow suffix*
   - LS030Q7DW48P (bottom screen)
     - LS = TFT (LTPS or SI-TFT ?)
     - 030 = panel 3 inches diagonal
     - Q = QVGA (320x240)
     - 7 = *unknown backing type*
     - D = parallel RGB (unspecified, but it's known to be RGB888 for this display)
     - W = *unknown coating type*
     - 48 = model number
     - P = *unknow suffix*
 - 0x00 - no controller, or dead (I2CLCD always ACKs reads, but returns 00 if dead)

Custom registers for controller 0x00[edit]

This Controller ID is fully unknown, and the only reason we know about its existance is due to gsp having special handling code for it.

Register Name Valid bits Description
0x11 ??? Unknown. Write 0x10 to initialize.
0x50 ??? Unknown. Write 0x01 to initialize.


Custom registers for controller 0x01[edit]

Register Name Valid bits Description
0x10 Interface config 0xF7 Regonfigures the input pins and pin behavior of the controller.
bit0 - color value invert (D = ~D, or D = 255 - D)
bit1 - color format remap (D7:D2 <-- D5:D0, that is left shift color data by 2)
bit2 - ???
bit4 - ???
bit5 - ???
bit6 - ???
bit7 - DS-style undriven screen (it will be white instead of black, see shared register 0x01)
0x11 Image config 0x7F Image filters and pixel clock control.
bit0 - Horizontal Flip (scan from right to left)
bit1 - red-blue swap
bit2 - ???
bit3 - ???
bit4 - ???
bit5 - ???
bit6 - ???
0x1D ??? 0x0F Unknown, bit0 enables registers 0x12 to 0x19 to control some analog timing controls to the display panel itself.
0x50 ??? 0x11 Unknown. Has no effect on bottom screen. On the top screen, bit4 blanks out the display (LVDS disable?).
0x53 ??? 0x73 Unknown. While other bits seem to have no effect, bit0 kills the controller until a power cycle.

Custom registers for controller 0xC3[edit]

Basically the same as Controller ID 0xC7.


Custom registers for controller 0xC7[edit]

This is the most common non-old3DS display controller. Quite overclockable.

Note: on the 0xC7 controller unlocking the factory controls at register 0x03 glitches out most of the standard controls (like registers 0x50 to 0x56), so use with caution.

Register Name Valid bits Description
0x03 Factory key 2 Write 0xAA here to unlock a second set of factory controls.
0xAF Factory key Write 0xAA here to unlock factory controls.

Factory mode registers for unlock register 0x03:

Register Name Valid bits Description
0x10 Image control? 0xD7 Most bits are unknown.
bit0 - color invert
bit1 - slight gamma increase
0x11 Image transform? 0x7F Mostly unknown.
bit0 - Invert horizontal scan direction (0 = left to right, 1 = right to left)
bit1 - red-blue swap
bit2 - Invert vertical scan direction (0 = top to bottom, 1 = bottom to top)
bit3 - Invert the order of each scanline pair (might be needed if bit2 is toggled)
bit4 - Enable interlaced signal (use bit3 to swap fields)
bit5 - ???
bit6 - ???
0x70-0x83 Color curve red These registers are used for fine-tuning the analog driving curve of the screen

Positive:

- byte 00 (0xFF) - ???
- byte 01 (0xFF) - ???
- byte 02 (0x3F) - ???
- byte 03 (0x3F) - ???
- byte 04 (0x3F) - ???
- byte 05 (0x3F) - ???
- byte 06 (0x3F) - ???
- byte 07 (0x3F) - ???
- byte 08 (0x3F) - ???
- byte 09 (0x3F) - ???

Negative:

- byte 10 (0xFF) - ???
- byte 11 (0xFF) - ???
- byte 12 (0x3F) - ???
- byte 13 (0x3F) - ???
- byte 14 (0x3F) - ???
- byte 15 (0x3F) - ???
- byte 16 (0x3F) - ???
- byte 17 (0x3F) - ???
- byte 18 (0x3F) - ???
- byte 19 (0x3F) - ???
0x84-0x97 Color curve green
0x98-0xAB Color curve blue

Custom registers for controller 0xE1[edit]

This controller is designed to drive a split panel. As such, the factory controls have been slightly altered to accomodate this.

This is the only I2CLCD which responds on both I2CLCD addresses. The dominant screen is the bottom one.

Most registers are similar to controller 0xC7, but there are some differences due to the split shared panel nature.

Register Name Valid bits Description
0x03 Factory key 2 Write 0xAA here to unlock a 2nd set of factory controls.
0xAF Factory key Write 0xAA here to unlock factory controls.

Factory mode registers for unlock register 0x03:

Register Name Valid bits Description
0x10 Image control? 0xD7 Most bits are unknown. This applies to the whole display panel.
bit0 - color invert
bit1 - slight gamma increase
0x11 Image transform 0x33
bit0 - top half horizontal flip
bit1 - top half red-blue swap
bit4 - bottom half horizontal flip
bit5 - bottom half red-blue swap
0x70-0x83 Analog curve top Consists of two unknown curve values. Seems to be nonstandard.

Pair 1:

byte 00 (0xFF) - ???
byte 01 (0xFF) - ???
byte 02 (0xFF) - ???
byte 03 (0xFF) - ???
byte 04 (0x3F) - ???
byte 05 (0x3F) - ???
byte 06 (0x3F) - ???
byte 07 (0x3F) - ???
byte 08 (0x3F) - ???
byte 09 (0x3F) - ???

Part 2:

byte 10 (0xFF) - ???
byte 11 (0xFF) - ???
byte 12 (0xFF) - ???
byte 13 (0xFF) - ???
byte 14 (0x3F) - ???
byte 15 (0x3F) - ???
byte 16 (0x3F) - ???
byte 17 (0x3F) - ???
byte 18 (0x3F) - ???
byte 19 (0x3F) - ???
0x84-0x97 Analog curve bottom

Custom registers for controller 0x10[edit]

JDI IPS controller.

Warning: on the JDI controller, unlocking any of the factory mode registers overshadows some other registers, so don't write to "standard" locations (other than register 0x40) before locking factory mode back!

Register Name Valid bits Description
0x03 Factory key 2 Write 0xAA here to unlock advanced IPS curve controls.
0xAF Factory key Write 0xAA here to unlock factory controls.

Factory mode registers unlocked by register 0xAF:

  • 0x41 - 0x4F
  • 0x58 - 0x5F
  • 0x67 - 0x6F
  • 0xD0 - 0xEF
  • unknown...

Factory mode registers unlocked by register 0x03:

  • 0x04 - 0x0F
  • unknown...
Register Name Valid bits Description
0x70-0x7F Driving curve 1-1
0x80-0x8F Driving curve 1-2
0x90-0x9F Driving curve 2-1
0xA0-0xAF Driving curve 2-2
0xB0-0xBF Driving curve 3-1
0xC0-0xCF Driving curve 3-2

Device 10[edit]

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

Device 11[edit]

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

Device 12[edit]

REGISTER WIDTH DESCRIPTION
0x0 21 DebugPad state.

This is a Wii Classic Controller Pro which was slightly modified to have an encrypted device type of 0xF0 instead of 0xFD.

See here for the HID shared memory report format.

Device 13[edit]

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[edit]

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[edit]

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[edit]

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.

Device 17[edit]

(Stub)

Used by New 3DS for ZL, ZR, C stick

This device do not use registers. After writing the address, read the next several bytes.

Offset Description
0x0 Fixed 0x80
0x1 Buttons (ZL = 0x4, ZR = 0x2)