documentation and inactive mode code

This commit is contained in:
2025-08-24 19:09:53 -07:00
parent 45f5553763
commit 25a5afe219
5 changed files with 215 additions and 30 deletions

View File

@@ -0,0 +1,132 @@
# Firmware Architecture
The firmware operates under **three distinct modes** that define the devices behavior:
```c
#define MODE_INACTIVE 0
#define MODE_MASTER 1
#define MODE_MODULE 2
```
These modes dictate how the system processes input and manages data flow between modules. Together, they establish the rules for communication and arbitration within the modular keyboard system.
## Master Mode
In **Master Mode**, the device behaves like a standard keyboard connected to a host via USB. However, it also takes on additional responsibilities:
* **USB Handling**: Sends keypress reports directly to the host.
* **Neighbor Listening**: Receives keypress messages from connected Module devices and integrates them into the USB report.
* **Device Queries**: Responds to discovery or status requests from devices currently in **Inactive Mode**.
👉 The system can only enter Master Mode if it is physically connected to a USB host.
## Module Mode
In **Module Mode**, the device functions similarly to Master Mode but with one key difference:
* Instead of sending reports to the USB host, the device forwards its keypress data upstream to its **parent device**.
This parent is determined during the discovery process in Inactive Mode. The parent may be either the Master itself or another Module closer to the Master.
## Inactive Mode
**Inactive Mode** is the initial state of every device upon power-up (unless it is directly connected to USB).
Behavior in this mode includes:
* **Ignoring Local Inputs**: The device does not process its own switch presses.
* **Discovery Queries**: The device broadcasts a query to its neighbors to determine its parent.
* **Awaiting Responses**: Active devices (Master or Modules) respond with a **depth value**—a numerical measure of distance (in hops) to the Master.
* The Master has a depth of **0**.
* Each Module reports its depth as `(parent depth + 1)`.
The querying device selects a parent based on the lowest reported depth. This ensures the shortest and most efficient communication path to the Master.
**Example:**
If device A reports depth = 2, and device B reports depth = 4, the querying device sets:
* **Parent = A**
* **Depth = 3**
After selecting a parent, the device transitions from **Inactive Mode → Module Mode**.
Devices already in Master or Module mode should respond to discovery queries with their current depth value.
Ooh\~ lets make your UART DMA explanation sparkle ✨ Ill clean it up, add clarity on why interrupts could be tricky, and make the flow a bit more structured. Heres a polished version:
# UART Queue with DMA
Data transmission in this system is handled via **UART**. However, standard UART operations are **blocking**:
* When sending or receiving data without DMA, the CPU must wait until the operation completes.
* Running UART directly in the main loop would **stall other critical processes**, such as scanning keypresses or handling module communication.
An alternative is using **interrupts**, which allow the CPU to continue running while UART signals completion.
**Problem:** Interrupts can occur at any moment, potentially interrupting time-sensitive operations like key scanning or other UART transactions.
* Frequent or nested interrupts could lead to **race conditions**, data corruption, or missed key events.
## DMA Solution
The system uses **DMA (Direct Memory Access)** to offload UART data handling:
* Each RX port operates via DMA, continuously collecting incoming data **without CPU intervention**.
* Received data is placed into a **queue**.
* During the main loop, the firmware checks the queue:
* If the queue is not empty, it transmits the collected data to the parent/master device.
This system allows UART to behave like a **background subprocess**, leaving the CPU free for:
* Scanning the modules own keys.
* Processing received messages.
* Managing the module-to-master communication efficiently.
## Queue Usage
The **same queue system** also handles the modules own keypresses:
* Key events are pushed into the queue as they are detected.
* The main loop processes the queue, sending keypresses to the master device.
This unified queue system ensures **non-blocking, deterministic communication** between modules and the master, even under high data load.
Ooo~ lets give your UART message section a little sparkle ✨ and make it clearer, more structured, and easier to read. Ill also fix formatting inconsistencies and clarify the purpose of each field. 💙
---
# UART Message Structure
Each UART message is **32 bits (4 bytes)** and structured as follows:
| Byte 0 (MSB) | Byte 1 | Byte 2 | Byte 3 (LSB) |
|---------------------|-----------------|------------|--------------|
| Senders Depth | Message Type | Extra Bits | Key Code |
**Field Descriptions:**
- **Senders Depth:** Indicates the modules distance from the Master device. Useful for routing and arbitration.
- **Message Type:** Determines the purpose of the message (see below).
- **Extra Bits:** Reserved for additional flags or future extensions.
- **Key Code:** Represents the key being pressed (or released).
## Message Types
| Value | Name | Description |
|--------|------------------------|-----------------------------------------------------------------------------|
| 0x0F | Handshake Request | Sent by modules in **Inactive Mode** to discover parent/master devices. |
| 0xFF | Handshake Confirmation | Sent by active modules (or Master) in response to a Handshake Request. |
| 0x01 | Keycode Message | Generic keycode message sent from module to master. |
**Notes:**
- All communication between modules follows this 4-byte structure.
- Depth values help modules select the optimal parent device.
- Reserved **Extra Bits** can later store flags like key release, special functions, or priority indicators.