-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Implement deckctrl discovery backend #1533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
5a59f80
Early incomplete implementation of the deckctrl backend
ataffanel 981039a
Fix deck-control enumeration
ataffanel 2f27543
Implement deckctrl GPIO access and add deckctrl dev deck driver
ataffanel 8d25314
Move deckctrl gpio init call to deckctrl backend
ataffanel 63c4697
Document deckctrl backend and protocol
ataffanel d897b34
Move deckctrl_gpio I2C communication back to local variables
ataffanel 8aec7aa
Revert Makefile configuration
ataffanel 4cf733e
Implement documentation changes from review
ataffanel d0478a8
Use constants instead of Bools for deckctrl GPIO API
ataffanel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ataffanel marked this conversation as resolved.
Show resolved
Hide resolved
ataffanel marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,170 @@ | ||
| --- | ||
| title: DeckCtrl protocol specification | ||
| page_id: deckctrl_protocol | ||
| --- | ||
|
|
||
| # DeckCtrl protocol specification | ||
|
|
||
| The DeckCtrl backend implements an I2C-based deck and control discovery mechanism that uses microcontrollers on deck boards to enable dynamic enumeration of multiple decks. | ||
ataffanel marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| The microcontroller implementing the deckctrl protocol is exclusively used for that purpose, deck functionality are implemented on | ||
ataffanel marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| another chip/microcontroller. | ||
|
|
||
| ## Discovery Sequence | ||
|
|
||
| The DeckCtrl backend performs the following sequence during initialization: | ||
|
|
||
| ### 1. Reset Phase (Address 0x41) | ||
|
|
||
| All deck controllers on the bus listen to the reset address. A read operation to this address causes all controllers to reset their I2C configuration and return to default state. | ||
|
|
||
| ```c | ||
| i2cdevReadReg16(I2C1_DEV, 0x41, 0x0000, 2, dummy_buffer); | ||
| vTaskDelay(10); // Wait for controllers to restart | ||
| ``` | ||
| ### 2. Listening Mode (Address 0x42) | ||
| After reset, unconfigured deck controllers must be told to enter listening mode. In this state, they monitor the bus for address assignment. | ||
ataffanel marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ```c | ||
| i2cdevReadReg16(I2C1_DEV, 0x42, 0x0000, 2, dummy_buffer); | ||
| ``` | ||
|
|
||
| ### 3. Enumeration | ||
|
|
||
| For each deck to be discovered: | ||
|
|
||
| #### 3a. Read CPU ID (Address 0x43, Register 0x1900) | ||
|
|
||
| We read the next unconfigured deck with the lowest ID. | ||
| The other decks will observe a bus collision and back-off, they will then be ready to be put back in listening mode in step 2. | ||
|
|
||
| ```c | ||
| uint8_t cpu_id[12]; | ||
| i2cdevReadReg16(I2C1_DEV, 0x43, 0x1900, 12, cpu_id); | ||
| ``` | ||
| *Note: This is the core of the discovery protocol, it uses the native anticollision behavior of | ||
| I2C to be able to detect decks one by one. I2C detects a collition if it tries to let the open-collector data line high | ||
ataffanel marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| while another deck is pulling it low. The deck that wanted it high will detect the line low, assume a collision, and back-off* | ||
| #### 3b. Assign Address (Address 0x43, Register 0x1800) | ||
| Write a unique I2C address to the deck controller. The address range starts at 0x44: | ||
| ```c | ||
| uint8_t deck_address = 0x44 + deck_count; | ||
| i2cdevWriteReg16(I2C1_DEV, 0x43, 0x1800, 1, &deck_address); | ||
| ``` | ||
|
|
||
| After receiving its address, the deck removes itself from the default addresses and begins responding only to its assigned address. | ||
|
|
||
| #### 3c. Read Deck Information (Assigned Address, Register 0x0000) | ||
|
|
||
| Read the deck's identification and capability information from its newly assigned address: | ||
|
|
||
| ```c | ||
| uint8_t deck_info[21]; | ||
| i2cdevReadReg16(I2C1_DEV, deck_address, 0x0000, 21, deck_info); | ||
| ``` | ||
| ### 4. Repeat | ||
| Return to step 2 (listening mode) to discover the next deck. Continue until no more decks respond. | ||
| ## I2C Address Map | ||
| | Address | Purpose | Description | | ||
| |---------|---------|-------------| | ||
| | 0x41 | Reset | Broadcast reset to all deck controllers | | ||
| | 0x42 | Listen | Put unconfigured controllers in listening mode | | ||
| | 0x43 | Default | Read CPU ID and assign unique address | | ||
| | 0x44-0x4F | Assigned | Individual deck addresses (up to 12 decks) | | ||
| The maximum number of decks is configured via `CONFIG_DECK_BACKEND_DECKCTRL_MAX_DECKS`. | ||
| ## Memory Layout | ||
| ### Register Map | ||
| Once configured, the deck-control behaves like an I2C memory with the following memory map: | ||
| | Register | Size | Description | | ||
| |----------|------|---------------------------------------------| | ||
| | 0x0000 | 21 | Deck identification information (Read-only) | | ||
| | 0x0020 | 2016 | ROM partitions | | ||
| | 0x1000 | 4 | GPIO | | ||
| | 0x1800 | 1 | Address assignment (write-only) | | ||
| | 0x1900 | 12 | CPU unique ID (read-only) | | ||
| ### Deck Information Format (Register 0x0000) | ||
| The 21-byte deck information block contains: | ||
| | Offset | Size | Field | Description | | ||
| |--------|------|-------|-------------| | ||
| | 0x00 | 2 | Magic | Magic number 0xBCDC (big-endian) | | ||
| | 0x02 | 1 | Major Version | Firmware major version | | ||
| | 0x03 | 1 | Minor Version | Firmware minor version | | ||
| | 0x04 | 1 | Vendor ID | Deck vendor ID | | ||
| | 0x05 | 1 | Product ID | Deck product ID | | ||
| | 0x06 | 1 | Board Revision | Board revision character | | ||
| | 0x07 | 14 | Product Name | Null-terminated product name string | | ||
| #### Magic Number | ||
| All valid DeckCtrl decks must return the magic number **0xBCDC** in the first two bytes. This validates that the device is a proper DeckCtrl deck. | ||
| Format: Big-endian (0xBC at offset 0, 0xDC at offset 1) | ||
| #### Vendor ID and Product ID | ||
| The VID/PID pair uniquely identifies the deck type and is used to match with the appropriate deck driver in firmware. These should match the `vid` and `pid` fields in the `DeckDriver` structure. | ||
| #### Product Name | ||
| A human-readable name for the deck (up to 14 characters plus null terminator). This is used for logging and debugging. | ||
| ## Backend Context | ||
| Each discovered DeckCtrl deck receives a `DeckCtrlContext` structure that contains backend-specific data: | ||
| ```c | ||
| typedef struct deckCtrlContext_s { | ||
| uint8_t i2cAddress; // Assigned I2C address (0x44+) | ||
| } DeckCtrlContext; | ||
| ``` | ||
|
|
||
| Deck drivers can access this context through the `DeckInfo` structure passed to their `init()` function: | ||
|
|
||
| ```c | ||
| void myDeckInit(DeckInfo *info) { | ||
| DeckCtrlContext *ctx = (DeckCtrlContext *)info->backendContext; | ||
| uint8_t address = ctx->i2cAddress; | ||
|
|
||
| // Use address for I2C communication with the deck | ||
| } | ||
| ``` | ||
| ### ROM Partition formats (address 0x0020) | ||
| The space is split in partitions with the following format: | ||
| | Offset | Size | Field | Description | | ||
| |--------|--------------|---------|--------------------------------------------------| | ||
| | 0x00 | 2 | Length | Partition full size, 0x0000 if no more partition | | ||
| | 0x02 | 4 | Type | Partition type | | ||
| | 0x06 | *Length* - 6 | Data | Partition data | | ||
| At offset *length* the next partition is starting. A length of 0x0000 denote the end of the partition table. | ||
| A length between 1 and 5 included is invalid. | ||
| There are no partition type defined yet. This mechanism is designed to allow for future expansion. | ||
| ### GPIO control | ||
| | Offset | Size | Field | Reset value | Description | | ||
| |---------|------|-----------|-------------|-------------------------------------------| | ||
| | 0x00 | 2 | Direction | 0x00 | GPIO Direction. 0 for input, 1 for output | | ||
| | 0x04 | 2 | Value | * | GPIO Value | | ||
ataffanel marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| /* | ||
| * DeckCtrl GPIO backend for deck initialization and control | ||
| * | ||
| * These functions are intended to be called from deck drivers to | ||
| * power, initialize and control the bootloader state of decks | ||
| * | ||
| * copyright (C) 2025 Bitcraze AB | ||
| */ | ||
|
|
||
| #include <deck.h> | ||
| #include <string.h> | ||
|
|
||
| #include "deckctrl_gpio.h" | ||
| #include "deckctrl.h" | ||
| #include "i2cdev.h" | ||
|
|
||
| #include "FreeRTOS.h" | ||
| #include "semphr.h" | ||
|
|
||
| #define DEBUG_MODULE "DECKCTRL_GPIO" | ||
| #include "debug.h" | ||
|
|
||
|
|
||
| static bool get_i2c_address(DeckInfo* info, uint8_t* address) { | ||
| if (info->backendContext == NULL || strcmp(info->discoveryBackend->name, "deckctrl") != 0) { | ||
| return false; | ||
| } | ||
| *address = ((DeckCtrlContext*)info->backendContext)->i2cAddress; | ||
| return true; | ||
| } | ||
|
|
||
| bool deckctrl_gpio_set_direction(DeckInfo* info, DeckCtrlGPIOPin pin, bool output) { | ||
| if (pin >= DECKCTRL_GPIO_PIN_MAX) { | ||
| return false; | ||
| } | ||
|
|
||
| uint8_t i2c_address; | ||
| if (!get_i2c_address(info, &i2c_address)) { | ||
| return false; | ||
| } | ||
|
|
||
| char buffer[2]; | ||
| // Read current direction register (16-bit) | ||
| if (!i2cdevReadReg16(I2C1_DEV, i2c_address, DECKCTRL_GPIO_DIRECTION_REG, 2, buffer)) { | ||
| return false; | ||
| } | ||
|
|
||
| // Convert bytes to 16-bit value (little endian) | ||
| uint16_t direction_reg = buffer[0] | (buffer[1] << 8); | ||
|
|
||
| // Set or clear the bit for this pin | ||
| if (output) { | ||
| direction_reg |= (1 << pin); | ||
| } else { | ||
| direction_reg &= ~(1 << pin); | ||
| } | ||
|
|
||
| DEBUG_PRINT("Setting GPIO pin %d direction to %s (reg=0x%04x)\n", pin, output ? "output" : "input", direction_reg); | ||
|
|
||
| // Convert back to bytes and write | ||
| buffer[0] = direction_reg & 0xFF; | ||
| buffer[1] = (direction_reg >> 8) & 0xFF; | ||
|
|
||
| bool result = i2cdevWriteReg16(I2C1_DEV, i2c_address, DECKCTRL_GPIO_DIRECTION_REG, 2, buffer); | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| bool deckctrl_gpio_write(DeckInfo* info, DeckCtrlGPIOPin pin, bool value) { | ||
| if (pin >= DECKCTRL_GPIO_PIN_MAX) { | ||
| return false; | ||
| } | ||
|
|
||
| uint8_t i2c_address; | ||
| if (!get_i2c_address(info, &i2c_address)) { | ||
| return false; | ||
| } | ||
|
|
||
| char buffer[2]; | ||
| // Read current value register (16-bit) | ||
| if (!i2cdevReadReg16(I2C1_DEV, i2c_address, DECKCTRL_GPIO_VALUE_REG, 2, buffer)) { | ||
| return false; | ||
| } | ||
|
|
||
| // Convert bytes to 16-bit value (little endian) | ||
| uint16_t value_reg = buffer[0] | (buffer[1] << 8); | ||
|
|
||
| // Set or clear the bit for this pin | ||
| if (value) { | ||
| value_reg |= (1 << pin); | ||
| } else { | ||
| value_reg &= ~(1 << pin); | ||
| } | ||
|
|
||
| // Convert back to bytes and write | ||
| buffer[0] = value_reg & 0xFF; | ||
| buffer[1] = (value_reg >> 8) & 0xFF; | ||
|
|
||
| return i2cdevWriteReg16(I2C1_DEV, i2c_address, DECKCTRL_GPIO_VALUE_REG, 2, buffer); | ||
| } | ||
|
|
||
| bool deckctrl_gpio_read(DeckInfo* info, DeckCtrlGPIOPin pin, bool* value) { | ||
| if (pin >= DECKCTRL_GPIO_PIN_MAX || value == NULL) { | ||
| return false; | ||
| } | ||
|
|
||
| uint8_t i2c_address; | ||
| if (!get_i2c_address(info, &i2c_address)) { | ||
| return false; | ||
| } | ||
|
|
||
| char buffer[2]; | ||
| // Read current value register (16-bit) | ||
| if (!i2cdevReadReg16(I2C1_DEV, i2c_address, DECKCTRL_GPIO_VALUE_REG, 2, buffer)) { | ||
| return false; | ||
| } | ||
|
|
||
| // Convert bytes to 16-bit value (little endian) | ||
| uint16_t value_reg = buffer[0] | (buffer[1] << 8); | ||
|
|
||
| // Extract the bit for this pin | ||
| *value = (value_reg & (1 << pin)) != 0; | ||
|
|
||
| return true; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,10 @@ | ||
| obj-y += deck_backend_onewire.o | ||
| obj-$(CONFIG_DECK_BACKEND_ONEWIRE) += deck_backend_onewire.o | ||
|
|
||
| # Only compile forced backend when DECK_FORCE is set to something other than "none" | ||
| ifneq ($(CONFIG_DECK_FORCE),"none") | ||
| ifneq ($(CONFIG_DECK_FORCE),"") | ||
| obj-y += deck_backend_forced.o | ||
| endif | ||
| endif | ||
|
|
||
| obj-$(CONFIG_DECK_BACKEND_DECKCTRL) += deck_backend_deckctrl.o |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.