diff --git a/.github/instructions/ato.instructions.md b/.github/instructions/ato.instructions.md new file mode 100644 index 0000000..84c67d2 --- /dev/null +++ b/.github/instructions/ato.instructions.md @@ -0,0 +1,713 @@ +--- +description: ato is a declarative DSL to design electronics (PCBs) with. +applyTo: *.ato, ato.yaml +--- + +ato is a declarative DSL to design electronics (PCBs) with. +It is part of the atopile project. +Atopile is run by the vscode/cursor/windsurf extension. +The CLI (which is invoked by the extension) actually builds the project. + +# Not available in ato + +- if statements +- while loops +- functions (calls or definitions) +- classes +- objects +- exceptions +- generators + + +# Ato Syntax + +ato sytax is heavily inspired by Python, but fully declarative. +ato thus has no procedural code, and no side effects. + +## Examples of syntax + +```ato +#pragma text +#pragma func("X") +# enable for loop syntax feature: +#pragma experiment("FOR_LOOP) + +# --- Imports --- +# Standard import (newline terminated) +import ModuleName + +# Import with multiple modules (newline terminated) +import Module1, Module2.Submodule + +# Import from a specific file/source (newline terminated) +from "path/to/source.ato" import SpecificModule + +# Multiple imports on one line (semicolon separated) +import AnotherModule; from "another/source.ato" import AnotherSpecific + +# Deprecated import form (newline terminated) +# TODO: remove when unsupported +import DeprecatedModule from "other/source.ato" + +# --- Top-level Definitions and Statements --- + +pass +pass; + +"docstring-like statement" +"docstring-like statement"; + +top_level_var = 123 + +# Compound statement +pass; another_var = 456; "another docstring" + +# Block definitions +component MyComponent: + # Simple statement inside block (newline terminated) + pass + + # Multiple simple statements on one line (semicolon separated) + pass; internal_flag = True + +module AnotherBaseModule: + pin base_pin + base_param = 10 + +interface MyInterface: + pin io + +module DemoModule from AnotherBaseModule: + # --- Declarations --- + pin p1 # Pin declaration with name + pin 1 # Pin declaration with number + pin "GND" # Pin declaration with string + signal my_signal # Signal definition + a_field: AnotherBaseModule # Field declaration with type hint + + # --- Assignments --- + # Newline terminated: + internal_variable = 123 + + # Semicolon separated on one line: + var_a = 1; var_b = "string" + + # Cumulative assignment (+=, -=) - Newline terminated + value = 1 + value += 1; value -= 1 + + # Set assignment (|=, &=) - Newline terminated + flags |= 1; flags &= 2 + + # --- Connections --- + p1 ~ base_pin + mif ~> bridge + mif ~> bridge ~> bridge + mif ~> bridge ~> bridge ~> mif + bridge ~> mif + mif <~ bridge + mif <~ bridge <~ bridge + mif <~ bridge <~ bridge <~ mif + bridge <~ mif + + # Semicolon separated on one line: + p_multi1 ~ my_signal; p_multi2 ~ sig_multi1 + + # --- Retyping --- + instance.x -> AnotherBaseModule + + # --- Instantiation --- + instance = new MyComponent + container = new MyComponent[10] + templated_instance_a = new MyComponent + templated_instance_b = new MyComponent + templated_instance_c = new MyComponent + templated_instance_d = new MyComponent + templated_instance_e = new MyComponent + templated_instance_f = new MyComponent + + # Semicolon separated instantiations (via assignment): + inst_a = new MyComponent; inst_b = new AnotherBaseModule + + # --- Traits --- + trait trait_name + trait trait_name + trait trait_name + trait trait_name + trait trait_name + trait trait_name::constructor + trait trait_name::constructor + + # Semicolon separated on one line: + trait TraitA; trait TraitB::constructor; trait TraitC + + # --- Assertions --- + assert x > 5V + assert x < 10V + assert 5V < x < 10V + assert x >= 5V + assert x <= 10V + assert current within 1A +/- 10mA + assert voltage within 1V +/- 10% + assert resistance is 1kohm to 1.1kohm + + # Semicolon separated on one line: + assert x is 1V; assert another_param is 2V + + # --- Loops --- + for item in container: + item ~ p1 + + # For loop iterating over a slice + for item in container[0:4]: + pass + item.value = 1; pass + + # For loop iterating over a list literal of field references + for ref in [p1, x.1, x.GND]: + pass + + # --- References and Indexing --- + # Reference with array index assignment + array_element = container[3] + + # --- Literals and Expressions --- + # Integer + int_val = 100 + neg_int_val = -50 + hex_val = 0xF1 + bin_val = 0b10 + oct_val = 0o10 + # Float + float_val = 3.14 + # Physical quantities + voltage: V = 5V + resistance: ohm = 10kohm + capacitance: F = 100nF + # Bilateral tolerance + tolerance_val = 1kohm +/- 10% + tolerance_abs = 5V +/- 500mV + tolerance_explicit_unit = 10A +/- 1A + # Bounded quantity (range) + voltage_range = 3V to 3.6V + # Boolean + is_enabled = True + is_active = False + # String + message = "Hello inside module" + + # Arithmetic expressions + sum_val = 1 + 2 + diff_val = 10 - 3ohm + prod_val = 5 * 2mA + div_val = 10V / 2kohm # Results in current + power_val = 2**3 + complex_expr = (5 + 3) * 2 - 1 + flag_check = state | MASK_VALUE + + # Comparisons + assert voltage within voltage_range + assert length <= 5mm + assert height >= 2mm + + + +# --- Multi-line variations --- +pass; nested_var=1; another=2 + +complex_assignment = ( + voltage + resistance + * capacitance +) + + +``` + +## G4 Grammar + +```g4 +parser grammar AtoParser; + +options { + superClass = AtoParserBase; + tokenVocab = AtoLexer; +} + +file_input: (NEWLINE | stmt)* EOF; + +pragma_stmt: PRAGMA; + +stmt: simple_stmts | compound_stmt | pragma_stmt; +simple_stmts: + simple_stmt (SEMI_COLON simple_stmt)* SEMI_COLON? NEWLINE; +simple_stmt: + import_stmt + | dep_import_stmt + | assign_stmt + | cum_assign_stmt + | set_assign_stmt + | connect_stmt + | directed_connect_stmt + | retype_stmt + | pin_declaration + | signaldef_stmt + | assert_stmt + | declaration_stmt + | string_stmt + | pass_stmt + | trait_stmt; + +compound_stmt: blockdef | for_stmt; + +blockdef: blocktype name blockdef_super? COLON block; +// TODO @v0.4 consider () +blockdef_super: FROM type_reference; +// TODO @v0.4 consider removing component (or more explicit code-as-data) +blocktype: (COMPONENT | MODULE | INTERFACE); +block: simple_stmts | NEWLINE INDENT stmt+ DEDENT; + +// TODO: @v0.4 remove the deprecated import form +dep_import_stmt: IMPORT type_reference FROM string; +import_stmt: (FROM string)? IMPORT type_reference ( + COMMA type_reference + )*; + +declaration_stmt: field_reference type_info; +field_reference_or_declaration: + field_reference + | declaration_stmt; +assign_stmt: field_reference_or_declaration '=' assignable; +cum_assign_stmt: + field_reference_or_declaration cum_operator cum_assignable; +// TODO: consider sets cum operator +set_assign_stmt: + field_reference_or_declaration (OR_ASSIGN | AND_ASSIGN) cum_assignable; +cum_operator: ADD_ASSIGN | SUB_ASSIGN; +cum_assignable: literal_physical | arithmetic_expression; + +assignable: + string + | new_stmt + | literal_physical + | arithmetic_expression + | boolean_; + +retype_stmt: field_reference ARROW type_reference; + +directed_connect_stmt + : bridgeable ((SPERM | LSPERM) bridgeable)+; // only one type of SPERM per stmt allowed. both here for better error messages +connect_stmt: mif WIRE mif; +bridgeable: connectable; +mif: connectable; +connectable: field_reference | signaldef_stmt | pindef_stmt; + +signaldef_stmt: SIGNAL name; +pindef_stmt: pin_stmt; +pin_declaration: pin_stmt; +pin_stmt: PIN (name | number_hint_natural | string); + +new_stmt: NEW type_reference ('[' new_count ']')? template?; +new_count: number_hint_natural; + +string_stmt: + string; // the unbound string is a statement used to add doc-strings + +pass_stmt: + PASS; // the unbound string is a statement used to add doc-strings + +list_literal_of_field_references: + '[' (field_reference (COMMA field_reference)* COMMA?)? ']'; + +iterable_references: + field_reference slice? + | list_literal_of_field_references; + +for_stmt: FOR name IN iterable_references COLON block; + +assert_stmt: ASSERT comparison; + +trait_stmt + : TRAIT type_reference (DOUBLE_COLON constructor)? template?; // TODO: move namespacing to type_reference +constructor: name; +template: '<' (template_arg (COMMA template_arg)* COMMA?)? '>'; +template_arg: name ASSIGN literal; + +// Comparison operators -------------------- +comparison: arithmetic_expression compare_op_pair+; + +compare_op_pair: + lt_arithmetic_or + | gt_arithmetic_or + | lt_eq_arithmetic_or + | gt_eq_arithmetic_or + | in_arithmetic_or + | is_arithmetic_or; + +lt_arithmetic_or: LESS_THAN arithmetic_expression; +gt_arithmetic_or: GREATER_THAN arithmetic_expression; +lt_eq_arithmetic_or: LT_EQ arithmetic_expression; +gt_eq_arithmetic_or: GT_EQ arithmetic_expression; +in_arithmetic_or: WITHIN arithmetic_expression; +is_arithmetic_or: IS arithmetic_expression; + +// Arithmetic operators -------------------- + +arithmetic_expression: + arithmetic_expression (OR_OP | AND_OP) sum + | sum; + +sum: sum (PLUS | MINUS) term | term; + +term: term (STAR | DIV) power | power; + +power: functional (POWER functional)?; + +functional: bound | name '(' bound+ ')'; + +bound: atom; + +// Primary elements ---------------- + +slice: + '[' (slice_start? COLON slice_stop? (COLON slice_step?)?)? ']' + // else [::step] wouldn't match + | '[' ( DOUBLE_COLON slice_step?) ']'; +slice_start: number_hint_integer; +slice_stop: number_hint_integer; +slice_step: number_hint_integer; + +atom: field_reference | literal_physical | arithmetic_group; + +arithmetic_group: '(' arithmetic_expression ')'; + +literal_physical: + bound_quantity + | bilateral_quantity + | quantity; + +bound_quantity: quantity TO quantity; +bilateral_quantity: quantity PLUS_OR_MINUS bilateral_tolerance; +quantity: number name?; +bilateral_tolerance: number_signless (PERCENT | name)?; + +key: number_hint_integer; +array_index: '[' key ']'; + +// backwards compatibility for A.1 +pin_reference_end: DOT number_hint_natural; +field_reference_part: name array_index?; +field_reference: + field_reference_part (DOT field_reference_part)* pin_reference_end?; +type_reference: name (DOT name)*; +// TODO better unit +unit: name; +type_info: COLON unit; +name: NAME; + +// Literals +literal: string | boolean_ | number; + +string: STRING; +boolean_: TRUE | FALSE; +number_hint_natural: number_signless; +number_hint_integer: number; +number: (PLUS | MINUS)? number_signless; +number_signless: NUMBER; +``` + +# Most used library modules/interfaces (api of them) + +```ato +interface Electrical: + pass + +interface ElectricPower: + hv = new Electrical + lv = new Electrical + +module Resistor: + resistance: ohm + max_power: W + max_voltage: V + unnamed = new Electrical[2] + +module Capacitor: + capacitance: F + max_voltage: V + unnamed = new Electrical[2] + +interface I2C: + scl = new ElectricLogic + sda = new ElectricLogic + frequency: Hz + address: dimensionless + +interface ElectricLogic: + line = new Electrical + reference = new ElectricPower +``` + +For the rest use the atopile MCP server +- `get_library_interfaces` to list interfaces +- `get_library_modules` to list modules +- `inspect_library_module_or_interface` to inspect the code + +# Ato language features + +## experimental features + +Enable with `#pragma experiment("BRIDGE_CONNECT")` +BRIDGE_CONNECT: enables `p1 ~> resistor ~> p2` syntax +FOR_LOOP: enables `for item in container: pass` syntax +TRAITS: enables `trait trait_name` syntax +MODULE_TEMPLATING: enables `new MyComponent` syntax + +## modules, interfaces, parameters, traits + +A block is either a module, interface or component. +Components are just modules for code-as-data. +Interfaces describe a connectable interface (e.g Electrical, ElectricPower, I2C, etc). +A module is a block that can be instantiated. +Think of it as the ato equivalent of a class. +Parameters are variables for numbers and they work with constraints. +E.g `resistance: ohm` is a parameter. +Constrain with `assert resistance within 10kohm +/- 10%`. +It's very important to use toleranced values for parameters. +If you constrain a resistor.resistance to 10kohm there won't be a single part found because that's a tolerance of 0%. + +Traits mark a module to have some kind of functionality that can be used in other modules. +E.g `trait has_designator_prefix` is the way to mark a module to have a specific designator prefix that will be used in the designator field in the footprint. + +## connecting + +You can only connect interfaces of the same type. +`resistor0.unnamed[0] ~ resistor0.unnamed[0]` is the way to connect two resistors in series. +If a module has the `can_bridge` trait you can use the sperm operator `~>` to bridge the module. +`led.anode ~> resistor ~> power.hv` connects the anode in series with the resistor and then the resistor in series with the high voltage power supply. + +## for loop syntax + +`for item in container: pass` is the way to iterate over a container. + +# Ato CLI + +## How to run + +You run ato commands through the MCP tool. + +## Packages + +Packages can be found on the ato registry. +To install a package you need to run `ato add `. +e.g `ato install atopile/addressable-leds` +And then can be imported with `from "atopile/addressable-leds/sk6805-ec20.ato" import SK6805_EC20_driver`. +And used like this: + +```ato +module MyModule: + led = new SK6805_EC20_driver +``` + +## Footprints & Part picking + +Footprint selection is done through the part choice (`ato create part` auto-generates ato code for the part). +The `pin` keyword is used to build footprint pinmaps so avoid using it outside of `component` blocks. +Preferrably use `Electrical` interface for electrical interfaces. +A lot of times it's actually `ElectricLogic` for things like GPIOs etc or `ElectricPower` for power supplies. + +Passive modules (Resistors, Capacitors) are picked automatically by the constraints on their parameters. +To constrain the package do e.g `package = "0402"`. +To explictly pick a part for a module use `lcsc = ""`. + + +# Creating a package + +Package generation process: + +Review structure of other pacakges. + +1. Create new Directory in 'packages/packages' with naming convention '-' eg 'adi-adau145x' +2. create an ato.yaml file in the new directory with the following content: + +```yaml +requires-atopile: '^0.9.0' + +paths: + src: '.' + layout: ./layouts + +builds: + default: + entry: .ato:_driver + example: + entry: .ato:Example +``` + +3. Create part using tool call 'search_and_install_jlcpcb_part' +4. Import the part into the .ato file +5. Read the datasheet for the device +6. Find common interfaces in the part eg I2C, I2S, SPI, Power + +7. Create interfaces and connect them + +power interfaces: +power* = new ElectricPower +power*.required = True # If critical to the device +assert power\*.voltage within V to V +power*.vcc ~ . +power\_.gnd ~ . + +i2c interfaces: +i2c = new I2C +i2c.scl.line ~ . +i2c.sda.line ~ . + +spi interfaces: +spi = new SPI +spi.sclk.line ~ . +spi.mosi.line ~ . +spi.miso.line ~ . + +8. Add decoupling capacitors + +looking at the datasheet, determine the required decoupling capacitors + +eg: 2x 100nF 0402: + +power_3v3 = new ElectricPower + +# Decoupling power_3v3 + +power_3v3_caps = new Capacitor[2] +for capacitor in power_3v3_caps: +capacitor.capacitance = 100nF +/- 20% +capacitor.package = "0402" +power_3v3.hv ~> capacitor ~> power_3v3.lv + +9. If device has pin configurable i2c addresses + +If format is: +use addressor module: + +- Use `Addressor` where **N = number of address pins**. +- Connect each `address_lines[i].line` to the corresponding pin, and its `.reference` to a local power rail. +- Set `addressor.base` to the lowest possible address and `assert addressor.address is i2c.address`. + +10. Create a README.md + +# + +## Usage + +```ato + + +``` + +## Contributing + +Contributions to this package are welcome via pull requests on the GitHub repository. + +## License + +This atopile package is provided under the [MIT License](https://opensource.org/license/mit/). + +11. Connect high level interfaces directly in example: + +eg: + +i2c = new I2C +power = new ElectricPower +sensor = new Sensor + +i2c ~ sensor.i2c +power ~ sensor.power_3v3 + +# Additional Notes & Gotchas (generic) + +- Multi-rail devices (VDD / VDDIO, AVDD / DVDD, etc.) + + - Model separate `ElectricPower` interfaces for each rail (e.g. `power_core`, `power_io`). + - Mark each `.required = True` if the device cannot function without it, and add voltage assertions per datasheet. + +- Optional interfaces (SPI vs I²C) + + - If the device supports multiple buses, pick one for the initial driver. Leave unused bus pins as `ElectricLogic` lines or expose a second interface module later. + +- Decoupling guidance + + - If the datasheet shows multiple caps, model the **minimum required** set so the build passes; you can refine values/packages later. + +- File / directory layout recap + - `-/` – package root + - `ato.yaml` – build manifest (include `default` **and** `example` targets) + - `.ato` – driver + optional example module + - `parts//` – atomic part + footprint/symbol/step files + +These tips should prevent common "footprint not found", "pin X missing", and build-time path errors when you add new devices. + + +# Vibe coding a project + +If the user gives you high level description of the project, use the following guide: + +# How LLMs can design electronics: + +#1 Rule: USE THE TOOLS. If the tools dont work, dont freak out, you are probably using them wrong. Ask for help if you get stuck. + +Top level design + +1. Research available packages relevant to the user requests using 'find_packages' +2. Inspect promising packages using 'inspect_package' +3. Propose packages to use for project and architucture to user, revise if needed +4. Install needed packages using 'install_package' +5. Import packages into main file +6. Create instances of packages in main module + +## Power + +1. Review for each package the required voltage and current (current may not be provided, use judement if nessesary) +2. Determine the power rails that need to be generated and a suitable tollerance (typically ~3-5% is acceptable) +3. Determine the input power source, typically a battery, USB connector or other power connector (eg XT30) and install relevant package +4. Find suitable regulators: + a) if input voltage > required voltage and current is low, use an LDO package + b) if input voltage > required voltage and current is high, use buck converter + c) if input votlage < required voltage, use a boost converter + d) if input voltage can be both less than or greater than input voltage, use buck boost (eg battery powered device that needs 3v3) +5. If battery powered, add charger package + +Typical power architucture example with LDO: + +- USB input power +- Low current output (eg microcontroller) + +from "atopile/ti-tlv75901/ti-tlv75901.ato" import TLV75901_driver +from "atopile/usb-connectors/usb-connectors.ato" import USBCConn + +module App: + + # Rails + power_5v = new Power + power_3v3 = new Power + + # Components + ldo = new TLV75901_driver + usb_connector = new USBCConn + + # Connections + usb_connector.power ~ power_vbus + power_vbus ~> ldo ~> power_3v3 + +## Communicaions + +1. Review packages required interfaces, typically i2c, spi or ElectricLogics +2. Find suitable pins on the controller, typically a microcontroller or Linux SOC +3. Connect interfaces eg micro.i2c[0] ~ sensor.i2c + +## Development process notes + +- After making changes, be sure to use 'build_project' to update the PCB +- Builds will often generate errors/warnings, these should be reviewed and fixed +- Prioritize pacakges from 'atopile' over other packages + + diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 42fd325..f6af5d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,6 +20,12 @@ on: - 'partitions.csv' - '.github/workflows/build.yml' +env: + APP_NAME: 'Esp Box Emu' + IDF_VERSION: 'v5.5.1' + IDF_COMPONENT_MANAGER: "1" # whether to enable the component manager or not + FLASH_TOTAL_OVERRIDE: '6291456' # 6MB flash app partition for main app + jobs: build: @@ -30,8 +36,10 @@ jobs: matrix: test: - path: '.' + name: 'box-emu' target: esp32s3 - path: 'components/box-emu/example' + name: 'box-emu/example' target: esp32s3 steps: @@ -43,6 +51,17 @@ jobs: - name: Build Examples uses: espressif/esp-idf-ci-action@v1 with: - esp_idf_version: release-v5.4 + esp_idf_version: ${{ env.IDF_VERSION }} target: ${{matrix.test.target}} path: ${{matrix.test.path}} + + - name: Determine Size Delta + uses: esp-cpp/esp-idf-size-delta@v1 + if: ${{ matrix.test.name == 'box-emu' }} + with: + app_name: ${{ env.APP_NAME }} + app_path: ${{ matrix.test.path }} + idf_target: ${{ matrix.test.target }} + idf_version: ${{ env.IDF_VERSION }} + idf_component_manager: ${{ env.IDF_COMPONENT_MANAGER }} + flash_total_override: ${{ env.FLASH_TOTAL_OVERRIDE }} diff --git a/.github/workflows/package_main.yml b/.github/workflows/package_main.yml index 5d204f9..929491b 100644 --- a/.github/workflows/package_main.yml +++ b/.github/workflows/package_main.yml @@ -7,6 +7,13 @@ on: types: [published] workflow_dispatch: +env: + APP_NAME: 'Esp Box Emu' + IDF_TARGET: 'esp32s3' + IDF_VERSION: 'v5.5.1' + IDF_COMPONENT_MANAGER: "1" # whether to enable the component manager or not + FLASH_TOTAL_OVERRIDE: '6291456' # 6MB flash app partition for main app + jobs: build: @@ -25,8 +32,8 @@ jobs: - name: Build Main Code uses: espressif/esp-idf-ci-action@v1 with: - esp_idf_version: release-v5.4 - target: esp32s3 + esp_idf_version: ${{ env.IDF_VERSION }} + target: ${{ env.IDF_TARGET }} path: '.' command: './patches.sh && idf.py build' @@ -53,6 +60,20 @@ jobs: with: files: firmware-binaries.zip + + - name: Determine Size Delta + # only run this on the esp-box build and if the release is published + if: ${{ github.event.release && github.event.action == 'published' }} + uses: esp-cpp/esp-idf-size-delta@v1 + with: + app_name: ${{ env.APP_NAME }} + app_path: "." + idf_target: ${{ env.IDF_TARGET }} + idf_version: ${{ env.IDF_VERSION }} + idf_component_manager: ${{ env.IDF_COMPONENT_MANAGER }} + flash_total_override: ${{ env.FLASH_TOTAL_OVERRIDE }} + post_comment: 'false' + package: name: Package the binaries into an executables for Windows, MacOS, and Linux (Ubuntu) needs: build diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 8cd2e55..858a240 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -25,8 +25,5 @@ jobs: # Do not build the project and do not use cmake to generate compile_commands.json use_cmake: false - # Use the 5.4 release version since it's what we build with - esp_idf_version: release/v5.4 - # (Optional) cppcheck args cppcheck_args: -i$GITHUB_WORKSPACE/components/gbc/gnuboy -i$GITHUB_WORKSPACE/components/nes/nofrendo -i$GITHUB_WORKSPACE/components/msx/fmsx -i$GITHUB_WORKSPACE/components/doom/prboom -i$GITHUB_WORKSPACE/components/sms/smsplus -i$GITHUB_WORKSPACE/components/genesis/gwenesis -i$GITHUB_WORKSPACE/components/gui/generated -i$GITHUB_WORKSPACE/components/menu/generated -i$GITHUB_WORKSPACE/components/jpegdec --check-level=exhaustive --force --enable=all --inline-suppr --inconclusive --platform=mips32 --std=c++17 --suppressions-list=$GITHUB_WORKSPACE/suppressions.txt diff --git a/.gitmodules b/.gitmodules index cdbfddd..69ec130 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "components/espp"] - path = components/espp - url = git@github.com:esp-cpp/espp [submodule "components/jpegdec"] path = components/jpegdec url = https://github.com/esp-cpp/jpegdec diff --git a/CMakeLists.txt b/CMakeLists.txt index 68d0c55..cebbee4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake) # add the component directories that we want to use set(EXTRA_COMPONENT_DIRS - "components/espp/components" + # "components/espp/components" ) # add compile definition ARDUINO_ARCH_ESP32, enabling jpegdec simd support diff --git a/components/box-emu/CMakeLists.txt b/components/box-emu/CMakeLists.txt index 9bece60..4ee9646 100644 --- a/components/box-emu/CMakeLists.txt +++ b/components/box-emu/CMakeLists.txt @@ -10,6 +10,7 @@ idf_component_register( "hal" "usb" "esp_tinyusb" + "lvgl" "codec" "adc" "aw9523" diff --git a/components/box-emu/example/CMakeLists.txt b/components/box-emu/example/CMakeLists.txt index b909df9..7150d0d 100644 --- a/components/box-emu/example/CMakeLists.txt +++ b/components/box-emu/example/CMakeLists.txt @@ -7,7 +7,6 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake) # add the component directories that we want to use set(EXTRA_COMPONENT_DIRS "../../../components/" - "../../../components/espp/components/" ) set( diff --git a/components/box-emu/idf_component.yml b/components/box-emu/idf_component.yml index 47e8a9f..88c818f 100644 --- a/components/box-emu/idf_component.yml +++ b/components/box-emu/idf_component.yml @@ -1,4 +1,16 @@ ## IDF Component Manager Manifest File dependencies: - espressif/esp_tinyusb: "^1.4.2" - idf: "^5.1" + idf: ">=5.5" + espressif/esp_tinyusb: ">=2.0" + lvgl/lvgl: '>=9.2.2' + espp/adc: ">=1.0" + espp/aw9523: ">=1.0" + espp/button: ">=1.0" + espp/drv2605: ">=1.0" + espp/esp-box: ">=1.0" + espp/event_manager: ">=1.0" + espp/max1704x: ">=1.0" + espp/mcp23x17: ">=1.0" + espp/task: ">=1.0" + espp/timer: ">=1.0" + espp/serialization: ">=1.0" diff --git a/components/box-emu/include/box-emu.hpp b/components/box-emu/include/box-emu.hpp index 0bc48db..6661627 100644 --- a/components/box-emu/include/box-emu.hpp +++ b/components/box-emu/include/box-emu.hpp @@ -12,7 +12,9 @@ #include #include -#include +#include + +#include #include "esp-box.hpp" #include "event_manager.hpp" @@ -367,6 +369,7 @@ class BoxEmu : public espp::BaseComponent { // usb std::atomic usb_enabled_{false}; usb_phy_handle_t jtag_phy_; + tinyusb_msc_storage_handle_t msc_storage_handle_{nullptr}; }; // for libfmt printing of the BoxEmu::Version enum diff --git a/components/box-emu/src/box-emu.cpp b/components/box-emu/src/box-emu.cpp index 1fadefe..58daf0b 100644 --- a/components/box-emu/src/box-emu.cpp +++ b/components/box-emu/src/box-emu.cpp @@ -650,30 +650,47 @@ bool BoxEmu::initialize_usb() { usb_del_phy(jtag_phy_); fmt::print("USB MSC initialization\n"); - // register the callback for the storage mount changed event. - tinyusb_msc_sdmmc_config_t config_sdmmc = { - .card = card, - .callback_mount_changed = nullptr, - .callback_premount_changed = nullptr, - .mount_config = { - .format_if_mount_failed = false, - .max_files = 5, - .allocation_unit_size = 2 * 1024, // sector size is 512 bytes, this should be between sector size and (128 * sector size). Larger means higher read/write performance and higher overhead for small files. - .disk_status_check_enable = false, // true if you see issues or are unmounted properly; slows down I/O + esp_vfs_fat_mount_config_t fat_mount_config = { + .format_if_mount_failed = false, + .max_files = 5, + .allocation_unit_size = 2 * 1024, // sector size is 512 bytes, this should be between sector size and (128 * sector size). Larger means higher read/write performance and higher overhead for small files. + .disk_status_check_enable = false, // true if you see issues or are unmounted properly; slows down I/O + }; + + tinyusb_msc_fatfs_config_t config_msc = { + .base_path = (char*)mount_point, + .config = fat_mount_config, + .do_not_format = true, + .format_flags = 0, + }; + + tinyusb_msc_storage_config_t msc_storage_config = { + .medium = { + .card = card, }, + .fat_fs = config_msc, + .mount_point = TINYUSB_MSC_STORAGE_MOUNT_USB, }; - ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); + + ESP_ERROR_CHECK(tinyusb_msc_new_storage_sdmmc(&msc_storage_config, &msc_storage_handle_)); + // register the callback for the storage mount changed event. // ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); // initialize the tinyusb stack fmt::print("USB MSC initialization\n"); - tinyusb_config_t tusb_cfg; - memset(&tusb_cfg, 0, sizeof(tusb_cfg)); - tusb_cfg.device_descriptor = &descriptor_config; - tusb_cfg.string_descriptor = string_desc_arr; - tusb_cfg.string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]); - tusb_cfg.external_phy = false; - tusb_cfg.configuration_descriptor = desc_configuration; + // no device_event_handler for tud_mount and tud_unmount callbacks + tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG(); + tusb_cfg.task = TINYUSB_TASK_CUSTOM(4096 /*size */, 4 /* priority */, + 0 /* affinity: 0 - CPU0, 1 - CPU1 ... */); + tusb_cfg.descriptor.device = &descriptor_config; + tusb_cfg.descriptor.string = string_desc_arr; + tusb_cfg.descriptor.string_count = + sizeof(string_desc_arr) / sizeof(string_desc_arr[0]); + tusb_cfg.descriptor.full_speed_config = desc_configuration; + tusb_cfg.phy.skip_setup = false; // was external-phy = false + tusb_cfg.phy.self_powered = false; + tusb_cfg.phy.vbus_monitor_io = -1; + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); fmt::print("USB MSC initialization DONE\n"); usb_enabled_ = true; @@ -686,8 +703,16 @@ bool BoxEmu::deinitialize_usb() { logger_.warn("USB MSC not initialized"); return false; } + esp_err_t err; logger_.info("USB MSC deinitialization"); - auto err = tinyusb_driver_uninstall(); + // deinit + delete the msc storage handle + err = tinyusb_msc_delete_storage(msc_storage_handle_); + if (err != ESP_OK) { + logger_.error("tinyusb_msc_delete_storage failed: {}", esp_err_to_name(err)); + return false; + } + logger_.info("USB deinitialization"); + err = tinyusb_driver_uninstall(); if (err != ESP_OK) { logger_.error("tinyusb_driver_uninstall failed: {}", esp_err_to_name(err)); return false; diff --git a/components/box-emu/src/make_color.cpp b/components/box-emu/src/make_color.cpp index eec4bec..50425bd 100644 --- a/components/box-emu/src/make_color.cpp +++ b/components/box-emu/src/make_color.cpp @@ -1,6 +1,6 @@ #include "make_color.h" -#include +#include extern "C" uint16_t make_color(uint8_t r, uint8_t g, uint8_t b) { return lv_color_to_u16(lv_color_make(r, g, b)); diff --git a/components/espp b/components/espp deleted file mode 160000 index 7cf2ea4..0000000 --- a/components/espp +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7cf2ea4153bd3b33f11ac06c58b0354f1648443e diff --git a/components/gui/generated/ui.h b/components/gui/generated/ui.h index ef7ae0f..c655772 100644 --- a/components/gui/generated/ui.h +++ b/components/gui/generated/ui.h @@ -10,7 +10,7 @@ extern "C" { #endif - #include "lvgl/lvgl.h" + #include "lvgl.h" #include "ui_helpers.h" #include "ui_events.h" diff --git a/components/jpegdec b/components/jpegdec index 9ce05f4..dfabfca 160000 --- a/components/jpegdec +++ b/components/jpegdec @@ -1 +1 @@ -Subproject commit 9ce05f474f5a712ccb277c27355325ca4fe5371a +Subproject commit dfabfca52d669cb7a982167f92aec9ef5fdbd50c diff --git a/main/idf_component.yml b/main/idf_component.yml new file mode 100644 index 0000000..96f2515 --- /dev/null +++ b/main/idf_component.yml @@ -0,0 +1,5 @@ +## IDF Component Manager Manifest File +dependencies: + ## Required IDF version + idf: '>=5.4' + espp/monitor: '>=1.0'