# I2C interface for FPGA control

## Introduction
Currently the FPGA has the following registers available:

| Address | Register |
| ------- | -------- |
| 0x00    | Camera register address (High byte) |
| 0x01    | Camera register address (Low byte) |
| 0x02    | Camera register value |
| 0x10    | PWM duty cycle (High byte) |
| 0x11    | PWM duty cycle (Low byte)  | 
| 0x30    | DFU reboot *__(DVT-1 only)__* |
| 0xA0    | Configuration ID code *__(DVT-2 only)__* |
| 0xA1    | SW version number (BCD, high byte) *__(DVT-2 only)__* |
| 0xA2    | SW version number (BCD, low byte) *__(DVT-2 only)__* |

## Register Descriptions

### Camera registers
The 3 registers for camera control (0x00, 0x01, 0x02) can be used to change a single register on both OC0TA1B cameras simultaneously. As soon as FPGA register 0x02 is written, the FPGA will issue I2C write commands for both cameras using the address saved in 0x00-0x01 and new value in 0x02.

For example, if you wish to change camera register 0x01.00 to value 0x00 (camera soft shutdown), write these registers in numerical order:

| Address | Value |
| ------- | -------- |
| 0x00    | 0x01 |
| 0x01    | 0x00 |
| 0x02    | 0x00 |

### PWM registers
The two PWM registers change the duty cycle of the IR LEDs. Writing the second register (low byte, address 0x11) triggers the PWM duty cycle to change. Currently the LEDs are set to a frequency of 10kHz, using a 60MHz clock. The PWM counter maximum is 60,000,000 / 10,000 = 6,000. The default value at startup is 10% or a raw value of 600.

Example to change PWM duty cycle to 28%:
`0.28 * 6000 = 1680 = 0x0690`
| Address | Value |
| ------- | -------- |
| 0x10    | 0x06 |
| 0x11    | 0x90 |

### DFU Reboot
To enter DFU mode and allow for firmware updates, simply write any value to the DFU register 0x30. It doesn't matter what the value is, any write to this register will swap the mode from camera to DFU. Once in DFU mode, use the same register to swap back to camera.

### ID and version
These three registers are read-only. They can be used to identify which configuration is loaded from SPI Flash (either camera or bootloader).

There are currently only 2 IDs that will be seen in register 0xA0:

| Mode | Value in 0xA0 |
| ------- | -------- |
| Camera    | 'A' (0x41) |
| Bootloader    | 'B' (0x42) |

The software version number is saved as binary-coded decimal (BCD) in two bytes. The bytes are intended to be read in sequence from high byte (0xA1) to low byte (0xA2). For example, software version 4.8.2 would be saved as:

| Address | Value |
| ------- | -------- |
| 0xA1    | 0x04 |
| 0xA2    | 0x82 |

## Writing to FPGA registers using the ATSAMG55 microcontroller
I2C commands can be sent from the ATSAMG55 microcontroller to the FPGA. The microcontroller interfaces with the PC using USB HID commands. All FPGA related commands use the 'e' (0x65) command key. Subcommands are determined from the second byte in the command string:

| 2nd Byte | Subcommand |
| ------- | -------- |
| 'R' (0x52)    | Reset FPGA |
| 'B' (0x42)   | Reconfig FPGA (reload bistream from SPI Flash) |
| 'I' (0x49)   | Write I2C register(s) |
| 'i' (0x69)   | Read I2C register(s) |

To perform a reset or reconfig, the full command message is the two bytes, 'e' followed by 'R' or 'B'.

For I2C writes, the command is configured as:
| Byte | Value | Meaning |
| ------- | -------- | --------- |
| 1    | 'e' (0x65) | FPGA commands |
| 2    | 'I' (0x49) | I2C write |
| 3    | \<nnn\>  | number of bytes to write (including the FPGA register address!) |
| 4    | \<AAA\> | I2C register address |
| 5 -> (5+nnn-2) | \<values\> | New values to load in register(s) |

You can write registers sequentially. For every additional data byte, the address written to will auto-increment. For example, writing to a camera register requires 3 FPGA register writes. This can be done in a single microcontroller transaction:
| Byte | Value | Meaning |
| ------- | -------- | --------- |
| 1    | 'e' (0x65) | FPGA commands |
| 2    | 'I' (0x49) | I2C write |
| 3    | 0x04  | 4 bytes to write. FPGA address + 3 camera registers |
| 4    | 0x00 | FPGA register, camera address high byte |
| 5    | 0x01 | Camera address high byte value |
| 6    | 0x00 | Camera address low byte value |
| 7    | 0x00 | Camera register new value |

Using the Python HID library, this would be written as:
```
import hid
beyond=hid.device()
beyond.open(0x35bd,0x0101)

beyond.send_feature_report(bytes([0, ord('e'), ord('I'), 4, 0x00, 0x01, 0x00, 0x00]))
```
Note that in a send_feature_report, the first byte is always zero for the HID report page. In the current microcontroller firmware, only page 0 is implemented.

## Useful camera registers
| Address | Register | Default Value |
| ------- | -------- | --------- |
| 0x3500  | Exposure (hi byte) | 0x00 |
| 0x3501  | Exposure (middle byte) | 0x01 |
| 0x3502  | Exposure (low byte) | 0xAE |
| 0x3508  | Gain (high byte / integer portion) | 0x01 |
| 0x3509  | Gain (low byte / fractional portion) | 0x00 |

The exposure when set to the default `0x00.01.AE` is ~10.46ms. It can be calculated by `(exposure)*24.333us` (the 24.33us comes from the camera's line time setting). 

To set exposure time, each byte of the three (3500, 3501, 3502) needs to be set individually. It's rare that the high byte would be set to anything other than zero, so generally 2 I2C writes (and 2 HID feature report writes) will be needed.

To set 5ms:
New value: `5ms / 24.33us = 205 = 0x00.00.CD`
```
beyond.send_feature_report(bytes([0, ord('e'), ord('I'), 4, 0x00, 0x35, 0x01, 0x00]))
beyond.send_feature_report(bytes([0, ord('e'), ord('I'), 4, 0x00, 0x35, 0x02, 0xCD]))
```