├── .clang-format
├── .cmake-format
├── .github
└── workflows
│ ├── ci.yml
│ └── take.yml
├── .gitignore
├── .naming.style
├── CMakeLists.txt
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── conanfile.py
├── demos
├── CMakeLists.txt
├── conanfile.py
└── main.cpp
├── include
└── libhal
│ ├── accelerometer.hpp
│ ├── adc.hpp
│ ├── angular_velocity_sensor.hpp
│ ├── can.hpp
│ ├── current_sensor.hpp
│ ├── dac.hpp
│ ├── distance_sensor.hpp
│ ├── error.hpp
│ ├── functional.hpp
│ ├── gyroscope.hpp
│ ├── i2c.hpp
│ ├── input_pin.hpp
│ ├── interrupt_pin.hpp
│ ├── magnetometer.hpp
│ ├── motor.hpp
│ ├── output_pin.hpp
│ ├── pwm.hpp
│ ├── rotation_sensor.hpp
│ ├── serial.hpp
│ ├── servo.hpp
│ ├── spi.hpp
│ ├── steady_clock.hpp
│ ├── temperature_sensor.hpp
│ ├── third_party
│ └── inplace_function.hpp
│ ├── timeout.hpp
│ ├── timer.hpp
│ └── units.hpp
├── test_package
├── CMakeLists.txt
├── conanfile.py
└── main.cpp
└── tests
├── accelerometer.test.cpp
├── adc.test.cpp
├── angular_velocity_sensor.test.cpp
├── can.test.cpp
├── conanfile.py
├── current_sensor.test.cpp
├── dac.test.cpp
├── distance_sensor.test.cpp
├── error.test.cpp
├── g_force.test.cpp
├── gyroscope.test.cpp
├── helpers.cpp
├── helpers.hpp
├── i2c.test.cpp
├── input_pin.test.cpp
├── interrupt_pin.test.cpp
├── lengths.test.cpp
├── magnetometer.test.cpp
├── main.test.cpp
├── motor.test.cpp
├── output_pin.test.cpp
├── pwm.test.cpp
├── rotation_sensor.test.cpp
├── serial.test.cpp
├── servo.test.cpp
├── spi.test.cpp
├── steady_clock.test.cpp
├── temperature_sensor.test.cpp
├── timeout.test.cpp
└── timer.test.cpp
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: Mozilla
2 | Language: Cpp
3 | UseTab: Never
4 | AlwaysBreakAfterReturnType: None
5 | AlwaysBreakAfterDefinitionReturnType: None
6 | AllowShortFunctionsOnASingleLine: None
7 | FixNamespaceComments: true
8 | SpacesBeforeTrailingComments: 2
9 | ColumnLimit: 80
10 |
--------------------------------------------------------------------------------
/.cmake-format:
--------------------------------------------------------------------------------
1 | format:
2 | tab_size: 2
3 | line_width: 80
4 | dangle_parens: true
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ✅ CI
2 |
3 | on:
4 | workflow_dispatch:
5 | pull_request:
6 | release:
7 | types:
8 | - published
9 | - deleted
10 | push:
11 | branches:
12 | - main
13 | schedule:
14 | - cron: "0 12 * * 0"
15 |
16 | jobs:
17 | ci:
18 | uses: libhal/ci/.github/workflows/library.yml@4.x.y
19 | secrets: inherit
20 |
--------------------------------------------------------------------------------
/.github/workflows/take.yml:
--------------------------------------------------------------------------------
1 | # .github/workflows/take.yml
2 | name: 📛 assign issue to contributor
3 | on:
4 | issue_comment:
5 |
6 | jobs:
7 | assign:
8 | name: Take an issue
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: take the issue
12 | uses: bdougie/take-action@main
13 | env:
14 | GITHUB_TOKEN: ${{ github.token }}
15 | with:
16 | message: Thanks for taking this issue! Let us know if you have any questions!
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Prerequisites
2 | *.d
3 |
4 | # Compiled Object files
5 | *.slo
6 | *.lo
7 | *.o
8 | *.obj
9 |
10 | # Precompiled Headers
11 | *.gch
12 | *.pch
13 |
14 | # Compiled Dynamic libraries
15 | *.so
16 | *.dylib
17 | *.dll
18 |
19 | # Fortran module files
20 | *.mod
21 | *.smod
22 |
23 | # Compiled Static libraries
24 | *.lai
25 | *.la
26 | *.a
27 | *.lib
28 |
29 | # Executables
30 | *.exe
31 | *.out
32 | *.app
33 |
34 | # IDEs
35 | .vscode/
36 | archives/
37 |
38 | # GDB
39 | */**/.gdb_history
40 |
41 | # Artifacts
42 | build/
43 | docs/api/
44 | docs/doxygen_warn.log
45 | tools/
46 |
47 | # lint artifacts
48 | ncc.stderr
49 |
50 | # OS files
51 | .DS_Store
52 |
53 | # Conan Files
54 | graph_info.json
55 | conaninfo.txt
56 | conan.lock
57 | conanbuildinfo.txt
58 |
59 | # CMake
60 | CMakeUserPresets.json
61 |
--------------------------------------------------------------------------------
/.naming.style:
--------------------------------------------------------------------------------
1 | # All Style settings supported by 'ncc'
2 | StructName: '^[a-z\_]+[a-z0-9_]*$'
3 | UnionName: '^[a-z\_]+[a-z0-9_]*$'
4 | ClassName: '^[a-z\_]+[a-z0-9_]*$'
5 | EnumName: '^[a-z\_]+[a-z0-9_]*$'
6 | EnumConstantName: '^[a-z\_]+[a-z0-9_]*$'
7 | FunctionName: '^[a-z\_]+[a-z0-9_]*[\=\+\/\-\*\>\<\!\|\&\~\^\"\_]*[a-zA-Z0-9_]*$'
8 | ParameterName: '^p_[a-z]+[a-z0-9_]*$'
9 | TypedefName: '^[a-z\_]+[a-z0-9_]*$'
10 | CppMethod: '^[a-z\_]+[a-z0-9_]*[\=\+\/\-\*\>\<\!\|\&\~\^\"\_\(\)]*[a-zA-Z0-9_]*$'
11 | Namespace: '^[a-z\_]*[a-z0-9_]*$'
12 | ConversionFunction: '^.*$'
13 | TemplateTypeParameter: '^.*$'
14 | TemplateNonTypeParameter: '^.*$'
15 | TemplateTemplateParameter: '^.*$'
16 | FunctionTemplate: '^.*$'
17 | ClassTemplate: '^.*$'
18 | ClassTemplatePartialSpecialization: '^.*$'
19 | NamespaceAlias: '^.*$'
20 | UsingDirective: '^.*$'
21 | UsingDeclaration: '^.*$'
22 | TypeAliasName: '^.*$'
23 | ClassAccessSpecifier: '^.*$'
24 | TypeReference: '^.*$'
25 | CxxBaseSpecifier: '^.*$'
26 | TemplateReference: '^.*$'
27 | NamespaceReference: '^.*$'
28 | MemberReference: '^.*$'
29 | LabelReference: '^.*$'
30 | OverloadedDeclarationReference: '^.*$'
31 | VariableReference: '^.*$'
32 | InvalidFile: '^.*$'
33 | NoDeclarationFound: '^.*$'
34 | NotImplemented: '^.*$'
35 | InvalidCode: '^.*$'
36 | UnexposedExpression: '^.*$'
37 | DeclarationReferenceExpression: '^.*$'
38 | MemberReferenceExpression: '^.*$'
39 | CallExpression: '^.*$'
40 | BlockExpression: '^.*$'
41 | IntegerLiteral: '^.*$'
42 | FloatingLiteral: '^.*$'
43 | ImaginaryLiteral: '^.*$'
44 | StringLiteral: '^.*$'
45 | CharacterLiteral: '^.*$'
46 | ParenExpression: '^.*$'
47 | UnaryOperator: '^.*$'
48 | ArraySubscriptExpression: '^.*$'
49 | BinaryOperator: '^.*$'
50 | CompoundAssignmentOperator: '^.*$'
51 | ConditionalOperator: '^.*$'
52 | CstyleCastExpression: '^.*$'
53 | CompoundLiteralExpression: '^.*$'
54 | InitListExpression: '^.*$'
55 | AddrLabelExpression: '^.*$'
56 | StatementExpression: '^.*$'
57 | GenericSelectionExpression: '^.*$'
58 | GnuNullExpression: '^.*$'
59 | CxxStaticCastExpression: '^.*$'
60 | CxxDynamicCastExpression: '^.*$'
61 | CxxReinterpretCastExpression: '^.*$'
62 | CxxConstCastExpression: '^.*$'
63 | CxxFunctionalCastExpression: '^.*$'
64 | CxxTypeidExpression: '^.*$'
65 | CxxBoolLiteralExpression: '^.*$'
66 | CxxNullPointerLiteralExpression: '^.*$'
67 | CxxThisExpression: '^.*$'
68 | CxxThrowExpression: '^.*$'
69 | CxxNewExpression: '^.*$'
70 | CxxDeleteExpression: '^.*$'
71 | CxxUnaryExpression: '^.*$'
72 | PackExpansionExpression: '^.*$'
73 | SizeOfPackExpression: '^.*$'
74 | LambdaExpression: '^.*$'
75 | ObjectBoolLiteralExpression: '^.*$'
76 | ObjectSelfExpression: '^.*$'
77 | UnexposedStatement: '^.*$'
78 | LabelStatement: '^.*$'
79 | CompoundStatement: '^.*$'
80 | CaseStatement: '^.*$'
81 | DefaultStatement: '^.*$'
82 | IfStatement: '^.*$'
83 | SwitchStatement: '^.*$'
84 | WhileStatement: '^.*$'
85 | DoStatement: '^.*$'
86 | ForStatement: '^.*$'
87 | GotoStatement: '^.*$'
88 | IndirectGotoStatement: '^.*$'
89 | ContinueStatement: '^.*$'
90 | BreakStatement: '^.*$'
91 | ReturnStatement: '^.*$'
92 | AsmStatement: '^.*$'
93 | CxxCatchStatement: '^.*$'
94 | CxxTryStatement: '^.*$'
95 | CxxForRangeStatement: '^.*$'
96 | MsAsmStatement: '^.*$'
97 | NullStatement: '^.*$'
98 | DeclarationStatement: '^.*$'
99 | TranslationUnit: '^.*$'
100 | UnexposedAttribute: '^.*$'
101 | CxxFinalAttribute: '^.*$'
102 | CxxOverrideAttribute: '^.*$'
103 | AnnotateAttribute: '^.*$'
104 | AsmLabelAttribute: '^.*$'
105 | PackedAttribute: '^.*$'
106 | PureAttribute: '^.*$'
107 | ConstAttribute: '^.*$'
108 | NoduplicateAttribute: '^.*$'
109 | PreprocessingDirective: '^.*$'
110 | MacroDefinition: '^[A-Z_][A-Z0-9_]*$'
111 | MacroInstantiation: '^[A-Z_][A-Z0-9_]*$'
112 | InclusionDirective: '^.*$'
113 | VariableName:
114 | ScopePrefix:
115 | Global: '^[a-z\_]+[a-z0-9_]*$'
116 | Static: '^[a-z\_]+[a-z0-9_]*$'
117 | ClassMember: '^m_[a-z]+[a-z0-9_]*$'
118 | DataTypePrefix:
119 | String: ''
120 | Integer: ''
121 | Bool: ''
122 | Pointer: ''
123 | # Allow snake_case, and CAP_CASE
124 | # Pattern: '^.*$'
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2023 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | cmake_minimum_required(VERSION 3.15)
16 |
17 | # Because libhal is a header only library, this cmake file only builds and
18 | # executes the unit tests
19 | project(libhal LANGUAGES CXX)
20 |
21 | libhal_unit_test(SOURCES
22 | tests/helpers.cpp
23 | tests/can.test.cpp
24 | tests/pwm.test.cpp
25 | tests/timer.test.cpp
26 | tests/i2c.test.cpp
27 | tests/spi.test.cpp
28 | tests/adc.test.cpp
29 | tests/dac.test.cpp
30 | tests/input_pin.test.cpp
31 | tests/interrupt_pin.test.cpp
32 | tests/output_pin.test.cpp
33 | tests/serial.test.cpp
34 | tests/steady_clock.test.cpp
35 | tests/motor.test.cpp
36 | tests/timeout.test.cpp
37 | tests/error.test.cpp
38 | tests/accelerometer.test.cpp
39 | tests/distance_sensor.test.cpp
40 | tests/gyroscope.test.cpp
41 | tests/magnetometer.test.cpp
42 | tests/rotation_sensor.test.cpp
43 | tests/temperature_sensor.test.cpp
44 | tests/servo.test.cpp
45 | tests/g_force.test.cpp
46 | tests/lengths.test.cpp
47 | tests/angular_velocity_sensor.test.cpp
48 | tests/current_sensor.test.cpp
49 | tests/main.test.cpp
50 |
51 | PACKAGES
52 | boost-leaf
53 | tl-function-ref
54 |
55 | LINK_LIBRARIES
56 | boost::leaf
57 | tl::function-ref)
58 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of
9 | experience, education, socio-economic status, nationality, personal appearance,
10 | race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or reject
41 | comments, commits, code, wiki edits, issues, and other contributions that are
42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any
43 | contributor for other behaviors that they deem inappropriate, threatening,
44 | offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | This Code of Conduct also applies outside the project spaces when the Project
56 | Steward has a reasonable belief that an individual's behavior may have a
57 | negative impact on the project or its community.
58 |
59 | ## Conflict Resolution
60 |
61 | We do not believe that all conflict is bad; healthy debate and disagreement
62 | often yield positive results. However, it is never okay to be disrespectful or
63 | to engage in behavior that violates the project’s code of conduct.
64 |
65 | If you see someone violating the code of conduct, you are encouraged to address
66 | the behavior directly with those involved. Many issues can be resolved quickly
67 | and easily, and this gives people more control over the outcome of their
68 | dispute. If you are unable to resolve the matter for any reason, or if the
69 | behavior is threatening or harassing, report it. We are dedicated to providing
70 | an environment where participants feel welcome and safe.
71 |
72 | Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the
73 | Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to
74 | receive and address reported violations of the code of conduct. They will then
75 | work with a committee consisting of representatives from the Open Source
76 | Programs Office and the Google Open Source Strategy team. If for any reason you
77 | are uncomfortable reaching out to the Project Steward, please email
78 | opensource@google.com.
79 |
80 | We will investigate every complaint, but you may not receive a direct response.
81 | We will use our discretion in determining when and how to follow up on reported
82 | incidents, which may range from not taking action to permanent expulsion from
83 | the project and project-sponsored spaces. We will notify the accused of the
84 | report and provide them an opportunity to discuss it before any action is taken.
85 | The identity of the reporter will be omitted from the details of the report
86 | supplied to the accused. In potentially harmful situations, such as ongoing
87 | harassment or threats to anyone's safety, we may take action without notice.
88 |
89 | ## Attribution
90 |
91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
92 | available at
93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct/
94 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We would love to accept your patches and contributions to this project.
4 |
5 | ## Before you begin
6 |
7 | ### Sign our Contributor License Agreement
8 |
9 | Contributions to this project must be accompanied by a
10 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA).
11 | You (or your employer) retain the copyright to your contribution; this simply
12 | gives us permission to use and redistribute your contributions as part of the
13 | project.
14 |
15 | If you or your current employer have already signed the Google CLA (even if it
16 | was for a different project), you probably don't need to do it again.
17 |
18 | Visit to see your current agreements or to
19 | sign a new one.
20 |
21 | ### Review our Community Guidelines
22 |
23 | This project follows [Google's Open Source Community
24 | Guidelines](https://opensource.google/conduct/).
25 |
26 | ## Contribution process
27 |
28 | ### :raised_hand: Self Assigning to an Issue
29 |
30 | If you find an issue you'd like to work on, simply type and submit a comment
31 | with the phrase `.take` in it to get assigned by our github actions.
32 |
33 | ### :pencil2: Pull Request Guidelines
34 |
35 | 1. Code must finish continuous integration steps before it will be reviewed.
36 | 2. Commit messages should follow these guidelines here
37 | https://cbea.ms/git-commit/.
38 | 3. Pull requests should contain a single commit
39 | 4. Pull requests should be small and implement a single feature where possible.
40 | If it can be broken up into separate parts it most likely should be.
41 | 5. Each PR should have an associated issue with it. Exceptions are made for very
42 | small PRs such as fixing typos, fixing up documentation or removing
43 | unnecessary headers.
44 |
45 | ### Code Reviews
46 |
47 | All submissions, including submissions by project members, require review. We
48 | use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
49 | for this purpose.
50 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # libhal
2 |
3 | [](https://github.com/libhal/libhal/actions/workflows/ci.yml)
4 | [](https://libhal.github.io/libhal/coverage/)
5 | [](https://github.com/libhal/libhal/stargazers)
6 | [](https://github.com/libhal/libhal/network)
7 | [](https://github.com/libhal/libhal/issues)
8 |
9 | The foundation of the libhal ecosystem, containing the core hardware interfaces
10 | used to make the whole ecosystem work.
11 |
12 | ## 📚 Software APIs & Usage
13 |
14 | To learn about the available drivers and APIs see the
15 | [API Reference](https://libhal.github.io/2.2/api/namespacehal/)
16 | documentation page or look at the
17 | [`include/libhal`](https://github.com/libhal/libhal/tree/main/include/libhal)
18 | directory.
19 |
20 | ## 🧰 Setup
21 |
22 | Following the
23 | [🚀 Getting Started](https://libhal.github.io/2.2/getting_started/)
24 | instructions.
25 |
26 | ## 📦 Adding `libhal` to your project
27 |
28 | This section assumes you are using the
29 | [`libhal-starter`](https://github.com/libhal/libhal-starter)
30 | project.
31 |
32 | Add the following to your `requirements()` method to the `ConanFile` class:
33 |
34 | ```python
35 | def requirements(self):
36 | self.requires("libhal/[^2.0.3]", transitive_headers=True)
37 | ```
38 |
39 | The version number can be changed to whatever is appropriate for your
40 | application. If you don't know, using the latest is usually a good choice.
41 |
42 | Normally, this is not done, as usually libhal is into the code base via
43 | implementation libraries such `libhal-lpc40` and `libhal-stm32f1` for
44 | peripheral drivers, or, `libhal-esp8266` and `libhal-mpu` for device drivers.
45 |
46 | ## :busts_in_silhouette: Contributing
47 |
48 | See [`CONTRIBUTING.md`](CONTRIBUTING.md) for details.
49 |
50 | ## License
51 |
52 | Apache 2.0; see [`LICENSE`](LICENSE) for details.
53 |
54 | ## Disclaimer
55 |
56 | This project is not an official Google project. It is not supported by
57 | Google and Google specifically disclaims all warranties as to its quality,
58 | merchantability, or fitness for a particular purpose.
59 |
--------------------------------------------------------------------------------
/conanfile.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | #
3 | # Copyright 2023 Google LLC
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from conan import ConanFile
18 | from conan.tools.cmake import CMake, cmake_layout
19 | from conan.tools.files import copy
20 | from conan.tools.build import check_min_cppstd
21 | import os
22 |
23 |
24 | required_conan_version = ">=2.0.6"
25 |
26 |
27 | class libhal_conan(ConanFile):
28 | name = "libhal"
29 | version = "2.2.0"
30 | license = "Apache-2.0"
31 | url = "https://github.com/conan-io/conan-center-index"
32 | homepage = "https://libhal.github.io/libhal"
33 | description = ("A collection of interfaces and abstractions for embedded "
34 | "peripherals and devices using modern C++")
35 | topics = ("peripherals", "hardware", "abstraction", "devices", "hal")
36 | settings = "compiler", "build_type", "os", "arch"
37 | exports_sources = "include/*", "tests/*", "CMakeLists.txt", "LICENSE"
38 | package_type = "header-library"
39 | generators = "CMakeToolchain", "CMakeDeps"
40 | no_copy_source = True
41 |
42 | @property
43 | def _min_cppstd(self):
44 | return "20"
45 |
46 | @property
47 | def _compilers_minimum_version(self):
48 | return {
49 | "gcc": "11",
50 | "clang": "14",
51 | "apple-clang": "14.0.0"
52 | }
53 |
54 | @property
55 | def _bare_metal(self):
56 | return self.settings.os == "baremetal"
57 |
58 | def validate(self):
59 | if self.settings.get_safe("compiler.cppstd"):
60 | check_min_cppstd(self, self._min_cppstd)
61 |
62 | def build_requirements(self):
63 | self.tool_requires("cmake/3.27.1")
64 | self.tool_requires("libhal-cmake-util/3.0.1")
65 | self.test_requires("boost-ext-ut/1.1.9")
66 |
67 | def requirements(self):
68 | self.requires("tl-function-ref/1.0.0")
69 | self.requires("boost-leaf/1.83.0")
70 |
71 | def layout(self):
72 | cmake_layout(self)
73 |
74 | def build(self):
75 | cmake = CMake(self)
76 | cmake.configure()
77 | cmake.build()
78 |
79 | def package(self):
80 | copy(self, "LICENSE", dst=os.path.join(
81 | self.package_folder, "licenses"), src=self.source_folder)
82 | copy(self, "*.h", dst=os.path.join(self.package_folder, "include"),
83 | src=os.path.join(self.source_folder, "include"))
84 | copy(self, "*.hpp", dst=os.path.join(self.package_folder,
85 | "include"), src=os.path.join(self.source_folder, "include"))
86 |
87 | def package_info(self):
88 | self.cpp_info.bindirs = []
89 | self.cpp_info.frameworkdirs = []
90 | self.cpp_info.libdirs = []
91 | self.cpp_info.resdirs = []
92 |
93 | if self._bare_metal:
94 | self.cpp_info.defines = [
95 | "BOOST_LEAF_EMBEDDED",
96 | # TODO(#694): Remove this or have it be configurable. Users
97 | # should not be forced to operate without thread support
98 | "BOOST_LEAF_NO_THREADS"
99 | ]
100 |
101 | def package_id(self):
102 | self.info.clear()
103 |
--------------------------------------------------------------------------------
/demos/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2023 Google LLC
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | cmake_minimum_required(VERSION 3.20)
16 |
17 | project(demos VERSION 0.0.1 LANGUAGES CXX)
18 |
19 | find_package(libhal REQUIRED CONFIG)
20 |
21 | message(STATUS "Generating Demo for \"${PROJECT_NAME}")
22 | add_executable(${PROJECT_NAME} main.cpp)
23 | target_include_directories(${PROJECT_NAME} PUBLIC .)
24 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
25 | target_link_libraries(${PROJECT_NAME} PRIVATE libhal::libhal)
26 |
--------------------------------------------------------------------------------
/demos/conanfile.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | #
3 | # Copyright 2023 Google LLC
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | from conan import ConanFile
18 | from conan.tools.cmake import CMake, cmake_layout
19 |
20 |
21 | class HalTestDemos(ConanFile):
22 | settings = "os", "compiler", "build_type"
23 | generators = "CMakeToolchain", "CMakeDeps", "VirtualBuildEnv"
24 |
25 | def requirements(self):
26 | self.requires("libhal/2.0.3")
27 |
28 | def layout(self):
29 | cmake_layout(self)
30 |
31 | def build(self):
32 | cmake = CMake(self)
33 | cmake.configure()
34 | cmake.build()
35 |
--------------------------------------------------------------------------------
/demos/main.cpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #include
16 |
17 | int main()
18 | {
19 | return 0;
20 | }
21 |
22 | namespace boost {
23 | void throw_exception(std::exception const& e)
24 | {
25 | hal::halt();
26 | }
27 | } // namespace boost
28 |
--------------------------------------------------------------------------------
/include/libhal/accelerometer.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Acceleration sensing hardware abstraction interface.
23 | */
24 | class accelerometer
25 | {
26 | public:
27 | /**
28 | * @brief Result from reading the accelerometer.
29 | *
30 | */
31 | struct read_t
32 | {
33 | /**
34 | * @brief Acceleration in the X axis, relative to the device's reference
35 | * frame.
36 | *
37 | */
38 | g_force x;
39 | /**
40 | * @brief Acceleration in the Y axis, relative to the device's reference
41 | * frame.
42 | *
43 | */
44 | g_force y;
45 | /**
46 | * @brief Acceleration in the Z axis, relative to the device's reference
47 | * frame.
48 | *
49 | */
50 | g_force z;
51 | };
52 |
53 | /**
54 | * @brief Read the latest acceleration sensed by the device
55 | *
56 | * @return result - acceleration data
57 | */
58 | [[nodiscard]] result read()
59 | {
60 | return driver_read();
61 | }
62 |
63 | virtual ~accelerometer() = default;
64 |
65 | private:
66 | virtual result driver_read() = 0;
67 | };
68 | } // namespace hal
69 |
--------------------------------------------------------------------------------
/include/libhal/adc.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 |
19 | namespace hal {
20 | /**
21 | * @brief Analog to Digital Converter (ADC) hardware abstraction interface.
22 | *
23 | * Use this interface for devices and peripherals that can convert analog
24 | * voltage signals into a digital number.
25 | *
26 | * ADC peripheral only know the proportion of a voltage signal relative to a Vss
27 | * (negative reference) and a Vcc (positive reference) and thus cannot describe
28 | * the voltage directly.
29 | */
30 | class adc
31 | {
32 | public:
33 | /**
34 | * @brief Result from reading the adc.
35 | *
36 | */
37 | struct read_t
38 | {
39 | /**
40 | * @brief Sample value of the adc
41 | *
42 | * Is guaranteed by the implementing driver to be between 0.0f and +1.0f.
43 | * The value representing the voltage measured by the ADC from Vss (negative
44 | * reference) to Vcc (positive reference). For example, if Vss is 0V (gnd)
45 | * and Vcc is 5V and this value is 0.5f, then the voltage measured is 2.5V.
46 | */
47 | float sample;
48 | };
49 |
50 | /**
51 | * @brief Sample the analog to digital converter and return the result
52 | *
53 | * @return result - the sampled adc value
54 | */
55 | [[nodiscard]] result read()
56 | {
57 | return driver_read();
58 | }
59 |
60 | virtual ~adc() = default;
61 |
62 | private:
63 | virtual result driver_read() = 0;
64 | };
65 | } // namespace hal
66 |
--------------------------------------------------------------------------------
/include/libhal/angular_velocity_sensor.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "units.hpp"
18 |
19 | namespace hal {
20 | /**
21 | * @brief angular velocity sensor hardware abstraction interface
22 | *
23 | */
24 | class angular_velocity_sensor
25 | {
26 | public:
27 | /**
28 | * @brief angular velocity reading from the sensor
29 | *
30 | *
31 | */
32 | struct read_t
33 | {
34 | /**
35 | * @brief The angular velocity in rotations per minute. (Underlying type is
36 | * an std::float)
37 | *
38 | */
39 | hal::rpm angular_velocity = 0;
40 | };
41 | /**
42 | * @brief Reads the most up to date angular velocity from the sensor
43 | *
44 | * @return result - angular velocity data
45 | */
46 | [[nodiscard]] result read()
47 | {
48 | return driver_read();
49 | }
50 |
51 | virtual ~angular_velocity_sensor() = default;
52 |
53 | private:
54 | virtual hal::result driver_read() = 0;
55 | };
56 | } // namespace hal
57 |
--------------------------------------------------------------------------------
/include/libhal/can.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 |
20 | #include "error.hpp"
21 | #include "functional.hpp"
22 | #include "units.hpp"
23 |
24 | namespace hal {
25 | /**
26 | * @brief Controller Area Network (CAN bus) hardware abstraction interface.
27 | *
28 | */
29 | class can
30 | {
31 | public:
32 | /**
33 | * @brief Can message ID type trait
34 | *
35 | */
36 | using id_t = uint32_t;
37 |
38 | /**
39 | * @brief Generic settings for a can peripheral
40 | *
41 | * CAN Bit Quanta Timing Diagram of:
42 | *
43 | * | <--- sjw ---> |
44 | * ____ ______ __________ __________
45 | * _/ SYNC \/ PROP \/ PHASE1 \/ PHASE2 \_
46 | * \______/\________/\____________/\____________/
47 | * ^ Sample point
48 | */
49 | struct settings
50 | {
51 | /**
52 | * @brief Bus clock rate in hertz
53 | *
54 | */
55 | hertz baud_rate = 100.0_kHz;
56 |
57 | /**
58 | * @brief Sync Segment (always 1qt)
59 | *
60 | * Initial sync transition, the start of a CAN bit
61 | */
62 | static constexpr std::uint8_t sync_segment = 1;
63 |
64 | /**
65 | * @brief Propagation Delay (1qt ... 8qt)
66 | *
67 | * Propagation time It is used to compensate for signal delays across the
68 | * network.
69 | */
70 | std::uint8_t propagation_delay = 3;
71 |
72 | /**
73 | * @brief Length of Phase Segment 1 (1qt ... 8qt)
74 | *
75 | * Determines the bit rate, phase segment 1 acts as a buffer that can be
76 | * lengthened to resynchronize with the bit stream via the
77 | * synchronization_jump_width. Includes propagation delay
78 | */
79 | std::uint8_t phase_segment1 = 3;
80 |
81 | /**
82 | * @brief Length of Phase Segment 2 (1qt ... 8qt)
83 | *
84 | * Determines the bit rate and is like phase segment 1 and occurs after the
85 | * sampling point. Phase segment 2 can be shortened to resynchronize with
86 | * the bit stream via the synchronization_jump_width.
87 | */
88 | std::uint8_t phase_segment2 = 3;
89 |
90 | /**
91 | * @brief Synchronization jump width (1qt ... 4qt)
92 | *
93 | * This is the maximum time by which the bit sampling period may be
94 | * lengthened or shortened during each cycle to adjust for oscillator
95 | * mismatch between nodes.
96 | *
97 | * This value must be smaller than phase_segment1 and phase_segment2
98 | */
99 | std::uint8_t synchronization_jump_width = 1;
100 | };
101 |
102 | /**
103 | * @brief A CAN message
104 | *
105 | */
106 | struct message_t
107 | {
108 | /**
109 | * @brief ID of the message
110 | *
111 | */
112 | id_t id;
113 | /**
114 | * @brief Message data contents
115 | *
116 | */
117 | std::array payload{};
118 | /**
119 | * @brief The number of valid elements in the payload
120 | *
121 | * Can be between 0 and 8. A length value above 8 should be considered
122 | * invalid and can be discarded.
123 | */
124 | uint8_t length = 0;
125 | /**
126 | * @brief Determines if the message is a remote request frame
127 | *
128 | * If true, then length and payload are ignored.
129 | */
130 | bool is_remote_request = false;
131 | };
132 |
133 | /**
134 | * @brief Receive handler for can messages
135 | *
136 | */
137 | using handler = void(const message_t& p_message);
138 |
139 | /**
140 | * @brief Feedback from sending data over the CAN BUS.
141 | *
142 | * This structure is currently empty as no feedback has been determined for
143 | * now. This structure may be expanded in the future.
144 | */
145 | struct send_t
146 | {};
147 |
148 | /**
149 | * @brief Configure this can bus port to match the settings supplied
150 | *
151 | * @param p_settings - settings to apply to can driver
152 | * @return status - success or failure
153 | * @throws std::errc::invalid_argument if the settings could not be achieved.
154 | */
155 | [[nodiscard]] status configure(const settings& p_settings)
156 | {
157 | return driver_configure(p_settings);
158 | }
159 |
160 | /**
161 | * @brief Transition the CAN device from "bus-off" to "bus-on"
162 | @verbatim embed:rst
163 | ```{warning}
164 | Calling this function when the device is already in "bus-on" will
165 | have no effect. This function is not necessary to call after creating the
166 | CAN driver as the driver should already be "bus-on" on creation.
167 | ```
168 | @endverbatim
169 | *
170 | * Can devices have two counters to determine system health. These two
171 | * counters are the "transmit error counter" and the "receive error counter".
172 | * Transmission errors can occur when the device attempts to communicate on
173 | * the bus and either does not get an acknowledge or sees an unexpected or
174 | * erroneous signal on the bus during its own transmission. When transmission
175 | * errors reach 255 counts, the device will go into the "bus-off" state.
176 | *
177 | * In the "bus-off" state, the CAN peripheral can no longer communicate on the
178 | * bus. Any calls to `send()` will throw the error `std::errc::network_down`.
179 | * If this occurs, this function must be called to re-enable bus
180 | * communication.
181 | *
182 | * @return status - success or failure. In the case this function fails
183 | * repeatedly, it is advised to simply not use the bus anymore as something is
184 | * critical wrong and may not be recoverable.
185 | */
186 | [[nodiscard]] status bus_on()
187 | {
188 | return driver_bus_on();
189 | }
190 |
191 | /**
192 | * @brief Send a can message
193 | *
194 | * @param p_message - the message to be sent
195 | * @return result - success or failure
196 | * @throws std::errc::network_down - if the can device is in the "bus-off"
197 | * state. This can happen if a critical fault in the bus has occurred. A call
198 | * to `bus_on()` will need to be issued to attempt to talk on the bus again.
199 | * See `bus_on()` for more details.
200 | */
201 | [[nodiscard]] result send(const message_t& p_message)
202 | {
203 | return driver_send(p_message);
204 | }
205 |
206 | /**
207 | * @brief Set the message reception handler
208 | *
209 | * All messages received before a message handler is installed are dropped.
210 | *
211 | * @param p_handler - this handler will be called when a message has been
212 | * received.
213 | */
214 | void on_receive(hal::callback p_handler)
215 | {
216 | return driver_on_receive(p_handler);
217 | }
218 |
219 | virtual ~can() = default;
220 |
221 | private:
222 | virtual status driver_configure(const settings& p_settings) = 0;
223 | virtual status driver_bus_on() = 0;
224 | virtual result driver_send(const message_t& p_message) = 0;
225 | virtual void driver_on_receive(hal::callback p_handler) = 0;
226 | };
227 | } // namespace hal
228 |
--------------------------------------------------------------------------------
/include/libhal/current_sensor.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "units.hpp"
18 |
19 | namespace hal {
20 | /**
21 | * @brief current sensor hardware abstraction interface
22 | *
23 | */
24 | class current_sensor
25 | {
26 | public:
27 | /**
28 | * @brief current reading from the sensor
29 | *
30 | *
31 | */
32 | struct read_t
33 | {
34 | hal::ampere current = 0.0f;
35 | };
36 | /**
37 | * @brief Reads the most up to date current from the sensor
38 | *
39 | * @return result - current data
40 | */
41 | [[nodiscard]] result read()
42 | {
43 | return driver_read();
44 | }
45 |
46 | virtual ~current_sensor() = default;
47 |
48 | private:
49 | virtual hal::result driver_read() = 0;
50 | };
51 | } // namespace hal
52 |
--------------------------------------------------------------------------------
/include/libhal/dac.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 |
19 | #include "error.hpp"
20 |
21 | namespace hal {
22 | /**
23 | * @brief Digital to Analog Converter (DAC) hardware abstraction interface.
24 | *
25 | * Use this interface for devices and peripherals that can create arbitrary
26 | * analog voltages between a defined Vss (negative reference) and Vcc (positive
27 | * reference) voltage.
28 | *
29 | */
30 | class dac
31 | {
32 | public:
33 | /**
34 | * @brief Feedback from writing a voltage to the dac.
35 | *
36 | * This structure is currently empty as no feedback has been determined for
37 | * now. This structure may be expanded in the future.
38 | */
39 | struct write_t
40 | {};
41 |
42 | /**
43 | * @brief Set the output voltage of the DAC.
44 | *
45 | * The input value `p_percentage` is a 32-bit floating point value from 0.0f
46 | * to +1.0f.
47 | *
48 | * The floating point value is linearly proportional to the output voltage
49 | * relative to the Vss and Vcc such that if Vss is 0V (gnd) and Vcc is 5V then
50 | * 0.0 is 0V, 0.25 is 1.25V, 0.445 is 2.225V and 1.0 is 5V.
51 | *
52 | * This function clamps the input value between 0.0f and 1.0f and thus values
53 | * passed to driver implementations are guaranteed to be within this range.
54 | * Callers of this function do not need to clamp their values before passing
55 | * them into this function as it would be redundant. The rationale for doing
56 | * this at the interface layer is that it allows callers and driver
57 | * implementors to omit redundant clamping code, reducing code bloat.
58 | *
59 | * @param p_percentage - value from 0.0f to +1.0f representing the proportion
60 | * of the output voltage from the Vss to Vcc.
61 | * @return result - success or failure
62 | */
63 | [[nodiscard]] result write(float p_percentage)
64 | {
65 | auto clamped_percentage = std::clamp(p_percentage, 0.0f, 1.0f);
66 | return driver_write(clamped_percentage);
67 | }
68 |
69 | virtual ~dac() = default;
70 |
71 | private:
72 | virtual result driver_write(float p_percentage) = 0;
73 | };
74 | } // namespace hal
75 |
--------------------------------------------------------------------------------
/include/libhal/distance_sensor.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Linear distance hardware abstraction interface
23 | *
24 | * Examples of distance encoder are:
25 | *
26 | * - Linear Potentiometers
27 | * - LIDAR or TOF (time of flight) sensor
28 | * - Ultrasonic range finder
29 | * - Infrared Distance Sensors
30 | * - Linear Quadrature Encoders
31 | * - Linear Incremental Encoders
32 | * - Linear Absolute Encoders
33 | * - Linear Magnetic Encoders
34 | *
35 | * Distance sensors can be relative or absolute. Relative position means that
36 | * the sensor can only see changes in rotation from where measurement started.
37 | * In other words, at application start, relative encoders will start at 0.
38 | * Absolute encoders know their position at all times. At application start, the
39 | * absolute encoder will be able to determine its exact orientation relative to
40 | * a frame of reference when read.
41 | *
42 | * Examples of relative rotation sensors are:
43 | *
44 | * - Quadrature Encoders
45 | * - Incremental Encoders
46 | *
47 | * Examples of absolute rotation sensors are:
48 | *
49 | * - Potentiometers
50 | * - Absolute Encoders
51 | * - Rotary Magnetic Encoders
52 | * - IMUs
53 | *
54 | * Distance sensors can also be finite or infinite. Finite meaning that the
55 | * angle that can be reported is a fixed amount for the device. Infinite means
56 | * that the encoder can continue rotating and adding more to its angle reading
57 | * forever. Infinite rotation sensors tend to not have a physical stop that
58 | * limits how much they can be rotated.
59 | *
60 | * Examples of finite rotation sensors are:
61 | *
62 | * - Potentiometers
63 | * - Absolute Encoders
64 | * - IMUs
65 | *
66 | * Examples of infinite rotation sensors are:
67 | *
68 | * - Rotary Magnetic Encoders
69 | * - Quadrature Encoders
70 | * - Incremental Encoders
71 | *
72 | * This interface does not provide a means to determine these attributes of a
73 | * rotation sensor as this is an application architecture decision. Drivers that
74 | * implement this interface should document what kind of distance sensor it is
75 | * such that a developer can determine its applicability to their application.
76 | * The context of which sensor ought to be used for an application is solely
77 | * known at architecture definition time and software should not be expected to
78 | * at runtime, if the right type of rotation sensor was passed into the object.
79 | */
80 | class distance_sensor
81 | {
82 | public:
83 | /**
84 | * @brief Result from sampling the distance sensor
85 | *
86 | */
87 | struct read_t
88 | {
89 | /**
90 | * @brief Encoder distance measurement in meters
91 | *
92 | */
93 | meters distance;
94 | };
95 |
96 | /**
97 | * @brief Read the current distance measured by the device
98 | *
99 | * @return result - distance data
100 | */
101 | [[nodiscard]] result read()
102 | {
103 | return driver_read();
104 | }
105 |
106 | virtual ~distance_sensor() = default;
107 |
108 | private:
109 | virtual result driver_read() = 0;
110 | };
111 | } // namespace hal
112 |
--------------------------------------------------------------------------------
/include/libhal/error.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 |
19 | #include
20 |
21 | #define HAL_CHECK BOOST_LEAF_CHECK
22 |
23 | namespace hal {
24 |
25 | template
26 | using match = boost::leaf::match;
27 | template
28 | using result = boost::leaf::result;
29 | using status = result;
30 | using error_handler = void(void);
31 |
32 | inline error_handler* on_error_callback = nullptr;
33 |
34 | /**
35 | * @defgroup Error Error
36 | *
37 | */
38 |
39 | /**
40 | * @ingroup Error
41 | * @brief a readability function for returning successful results;
42 | *
43 | * For functions that return `status`, rather than returning `{}` to default
44 | * initialize the status object as "success", use this function to make it more
45 | * clear to the reader.
46 | *
47 | * An Example:
48 | @verbatim embed:rst
49 | ```{code-block} cpp
50 | hal::status some_function() {
51 | return hal::success();
52 | }
53 | ```
54 | @endverbatim
55 | *
56 | *
57 | * @return status - that is always successful
58 | */
59 | inline status success()
60 | {
61 | // Default initialize the status object using the brace initialization, which
62 | // will set the status to the default "success" state.
63 | status successful_status{};
64 | return successful_status;
65 | }
66 |
67 | template
68 | [[nodiscard]] constexpr auto attempt(TryBlock&& p_try_block, H&&... p_handlers)
69 | {
70 | return boost::leaf::try_handle_some(p_try_block, p_handlers...);
71 | }
72 |
73 | template
74 | [[nodiscard]] constexpr auto attempt_all(TryBlock&& p_try_block,
75 | H&&... p_handlers)
76 | {
77 | return boost::leaf::try_handle_all(p_try_block, p_handlers...);
78 | }
79 |
80 | template
81 | [[nodiscard]] inline auto new_error(Item&&... p_item)
82 | {
83 | if (on_error_callback) {
84 | on_error_callback();
85 | }
86 |
87 | return boost::leaf::new_error(std::forward- (p_item)...);
88 | }
89 |
90 | [[noreturn]] inline void halt()
91 | {
92 | while (true) {
93 | continue;
94 | }
95 | }
96 |
97 | /**
98 | * @brief Error objects, templates, and constants.
99 | *
100 | */
101 | namespace error {
102 | /**
103 | * @brief Used for defining static_asserts that should always fail, but only if
104 | * the static_assert line is hit via `if constexpr` control block. Prefer to NOT
105 | * use this directly but to use `invalid_option` instead
106 | *
107 | * @tparam options ignored by the application but needed to create a non-trivial
108 | * specialization of this class which allows its usage in static_assert.
109 | */
110 | template
111 | struct invalid_option_t : std::false_type
112 | {};
113 | /**
114 | * @brief Helper definition to simplify the usage of invalid_option_t.
115 | *
116 | * @tparam options ignored by the application but needed to create a non-trivial
117 | * specialization of this class which allows its usage in static_assert.
118 | */
119 | template
120 | inline constexpr bool invalid_option = invalid_option_t::value;
121 | } // namespace error
122 | } // namespace hal
123 |
--------------------------------------------------------------------------------
/include/libhal/functional.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 |
19 | #include "third_party/inplace_function.hpp"
20 | #include
21 |
22 | /**
23 | * @defgroup Functional Functional
24 | *
25 | */
26 | namespace hal {
27 | /**
28 | * @ingroup Functional
29 | * @brief Definition of a non-owning callable object
30 | *
31 | * Use this for passing a callable object to a function that the function does
32 | * not need to store in anyway. Best used for timeouts where a function simply
33 | * needs the callable during the runtime of the function and when the function
34 | * is over, the callable is no longer needed.
35 | *
36 | * This function is light weight in comparison to std::function, which is
37 | * allocating, or inplace_function.
38 | *
39 | * @tparam F - function type or call signature
40 | */
41 | template
42 | using function_ref = tl::function_ref;
43 |
44 | /**
45 | * @ingroup Functional
46 | * @brief Definition of a owning callable object
47 | *
48 | * Use this instead of function_ref when a callable object needs to be stored.
49 | *
50 | * @tparam F - function type or call signature
51 | * @tparam Capacity - storage capacity of the function in bytes. If a callable
52 | * object has a size greater than the capacity, then attempting to create an
53 | * inplace function with it will result in a compiler error.
54 | */
55 | template
56 | using inplace_function = stdext::inplace_function;
57 |
58 | /**
59 | * @ingroup Functional
60 | * @brief Definition of a standard libhal owning callback object
61 | *
62 | * This is an inplace_function with its capacity set to two pointers. Callable
63 | * objects must fit within the size of two integers to be able to construct this
64 | * polymorphic callable object.
65 | *
66 | * @tparam F - function type or call signature
67 | */
68 | template
69 | using callback = inplace_function;
70 | } // namespace hal
71 |
--------------------------------------------------------------------------------
/include/libhal/gyroscope.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Angular velocity sensing hardware abstraction interface.
23 | */
24 | class gyroscope
25 | {
26 | public:
27 | /**
28 | * @brief Result from reading the gyroscope.
29 | *
30 | */
31 | struct read_t
32 | {
33 | /**
34 | * @brief Angular velocity in the X axis, relative to the device's reference
35 | * frame
36 | */
37 | rpm x;
38 | /**
39 | * @brief Angular velocity in the Y axis, relative to the device's reference
40 | * frame.
41 | *
42 | */
43 | rpm y;
44 | /**
45 | * @brief Angular velocity in the Z axis, relative to the device's reference
46 | * frame.
47 | *
48 | */
49 | rpm z;
50 | };
51 |
52 | /**
53 | * @brief Read the latest angular velocity sensed by the device
54 | *
55 | * @return result - angular velocity data
56 | */
57 | [[nodiscard]] result read()
58 | {
59 | return driver_read();
60 | }
61 |
62 | virtual ~gyroscope() = default;
63 |
64 | private:
65 | virtual result driver_read() = 0;
66 | };
67 | } // namespace hal
68 |
--------------------------------------------------------------------------------
/include/libhal/i2c.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 |
19 | #include "error.hpp"
20 | #include "functional.hpp"
21 | #include "timeout.hpp"
22 | #include "units.hpp"
23 |
24 | namespace hal {
25 | /**
26 | * @brief Inter-integrated Circuit (I2C) hardware abstract interface.
27 | *
28 | * Also known as Two Wire Interface (TWI) communication protocol. This is a very
29 | * commonly used protocol for communication with sensors and peripheral devices
30 | * because it only requires two connections SDA (data signal) and SCL (clock
31 | * signal). This is possible because the protocol for I2C is addressable.
32 | *
33 | */
34 | class i2c
35 | {
36 | public:
37 | /**
38 | * @brief Generic settings for a standard I2C device
39 | *
40 | */
41 | struct settings
42 | {
43 | /**
44 | * @brief The serial clock rate in hertz.
45 | *
46 | */
47 | hertz clock_rate = 100.0_kHz;
48 | };
49 |
50 | /**
51 | * @brief Feedback from performing a transaction on the i2c bus
52 | *
53 | * This structure is currently empty as no feedback has been determined for
54 | * now. This structure may be expanded in the future.
55 | */
56 | struct transaction_t
57 | {};
58 |
59 | /**
60 | * @brief Configure i2c to match the settings supplied
61 | *
62 | * @param p_settings - settings to apply to i2c driver
63 | * @return status - success or failure
64 | * @throws std::errc::invalid_argument if the settings could not be achieved.
65 | */
66 | [[nodiscard]] status configure(const settings& p_settings)
67 | {
68 | return driver_configure(p_settings);
69 | }
70 |
71 | /**
72 | * @brief perform an i2c transaction with another device on the bus. The type
73 | * of transaction depends on values of input parameters. This function will
74 | * block until the entire transfer is finished.
75 | *
76 | * Performing Write, Read and Write-Then-Read transactions depends on which
77 | * span for data_out and data_in are set to null.
78 | *
79 | * - For write transactions, pass p_data_in as an empty span
80 | * `std::span{}` and pass a buffer to p_data_out.
81 | *
82 | * - For read transactions, pass p_data_out as an empty span
83 | * `std::span{}` and pass a buffer to p_data_in.
84 | *
85 | * - For write-then-read transactions, pass a buffer for both p_data_in
86 | * p_data_out.
87 | *
88 | * - If both p_data_in and p_data_out are empty, simply do nothing and return
89 | * success.
90 | *
91 | * In the event of arbitration loss, this function will wait for the bus to
92 | * become free and try again. Arbitration loss means that during the address
93 | * phase of a transaction 1 or more i2c bus controllers attempted to perform
94 | * an transaction and one of the i2c bus controllers, that isn't this one won
95 | * out.
96 | *
97 | * @param p_address 7-bit address of the device you want to communicate with.
98 | * To perform a transaction with a 10-bit address, this parameter must be the
99 | * address upper byte of the 10-bit address OR'd with 0b1111'0000 (the 10-bit
100 | * address indicator). The lower byte of the address must be contained in the
101 | * first byte of the p_data_out span.
102 | * @param p_data_out data to be written to the addressed device. Set to
103 | * nullptr with length zero in order to skip writing.
104 | * @param p_data_in buffer to store read data from the addressed device. Set
105 | * to nullptr with length 0 in order to skip reading.
106 | * @param p_timeout callable which notifies the i2c driver that it has run out
107 | * of time to perform the transaction and must stop and return control to the
108 | * caller.
109 | * @return result - success or failure
110 | * @throws std::errc::io_error indicates that the i2c lines were put into an
111 | * invalid state during the transaction due to interference, misconfiguration
112 | * of the i2c peripheral or the addressed device or something else.
113 | * @throws std::errc::no_such_device_or_address indicates that no devices on
114 | * the bus acknowledge the address in this transaction, which could mean that
115 | * the device is not connected to the bus, is not powered, not available to
116 | * respond, broken or many other possible outcomes.
117 | * @throws std::errc::timed_out if the transaction exceeded its time allotment
118 | * indicated by p_timeout.
119 | */
120 | [[nodiscard]] result transaction(
121 | hal::byte p_address,
122 | std::span p_data_out,
123 | std::span p_data_in,
124 | hal::function_ref p_timeout)
125 | {
126 | return driver_transaction(p_address, p_data_out, p_data_in, p_timeout);
127 | }
128 |
129 | virtual ~i2c() = default;
130 |
131 | private:
132 | virtual status driver_configure(const settings& p_settings) = 0;
133 | virtual result driver_transaction(
134 | hal::byte p_address,
135 | std::span p_data_out,
136 | std::span p_data_in,
137 | hal::function_ref p_timeout) = 0;
138 | };
139 | } // namespace hal
140 |
--------------------------------------------------------------------------------
/include/libhal/input_pin.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Digital input pin hardware abstraction interface.
23 | *
24 | * Use this to read a pin and determine if the voltage on it is HIGH or LOW.
25 | *
26 | */
27 | class input_pin
28 | {
29 | public:
30 | /// Generic settings for input pins
31 | struct settings
32 | {
33 | /// Pull resistor for an input pin
34 | pin_resistor resistor = pin_resistor::pull_up;
35 | };
36 |
37 | /// Input pin level reading structure
38 | struct level_t
39 | {
40 | /// Measured state of the pin
41 | bool state;
42 | };
43 |
44 | /**
45 | * @brief Configure the input pin to match the settings supplied
46 | *
47 | * @param p_settings - settings to apply to input pin
48 | * @return status - success or failure
49 | * @throws std::errc::invalid_argument if the settings could not be achieved.
50 | */
51 | [[nodiscard]] status configure(const settings& p_settings)
52 | {
53 | return driver_configure(p_settings);
54 | }
55 |
56 | /**
57 | * @brief Read the state of the input pin
58 | *
59 | * @return result - true indicates HIGH voltage level and false
60 | * indicates LOW voltage level
61 | */
62 | [[nodiscard]] result level()
63 | {
64 | return driver_level();
65 | }
66 |
67 | virtual ~input_pin() = default;
68 |
69 | private:
70 | virtual status driver_configure(const settings& p_settings) = 0;
71 | virtual result driver_level() = 0;
72 | };
73 | } // namespace hal
74 |
--------------------------------------------------------------------------------
/include/libhal/interrupt_pin.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "functional.hpp"
19 | #include "units.hpp"
20 |
21 | namespace hal {
22 | /**
23 | * @brief Digital interrupt pin hardware abstraction
24 | *
25 | * Use this to automatically call a function when a pin's state has
26 | * transitioned.
27 | *
28 | * The transition states are:
29 | *
30 | * - falling edge: the pin reads a transitions from HIGH to LOW
31 | * - rising edge: the pin reads a transitions from LOW to HIGH
32 | * - both: the pin reads any state change
33 | */
34 | class interrupt_pin
35 | {
36 | public:
37 | /**
38 | * @brief The condition in which an interrupt it's triggered.
39 | *
40 | */
41 | enum class trigger_edge
42 | {
43 | /**
44 | * @brief Trigger the interrupt when a pin transitions from HIGH voltage to
45 | * LOW voltage.
46 | *
47 | */
48 | falling = 0,
49 | /**
50 | * @brief Trigger the interrupt when a pin transitions from LOW voltage to
51 | * HIGH voltage.
52 | *
53 | */
54 | rising = 1,
55 | /**
56 | * @brief Trigger the interrupt when a pin transitions it state
57 | *
58 | */
59 | both = 2,
60 | };
61 |
62 | /**
63 | * @brief Generic settings for interrupt pins
64 | *
65 | */
66 | struct settings
67 | {
68 | /**
69 | * @brief Pull resistor for an interrupt pin.
70 | *
71 | * In general, it is highly advised to either set the pull resistor to
72 | * something other than "none" or to attach an external pull up resistor to
73 | * the interrupt pin in order to prevent random interrupt from firing.
74 | */
75 | pin_resistor resistor = pin_resistor::pull_up;
76 |
77 | /**
78 | * @brief The trigger condition that will signal the system to run the
79 | * callback.
80 | *
81 | */
82 | trigger_edge trigger = trigger_edge::rising;
83 | };
84 |
85 | /**
86 | * @brief Interrupt pin handler
87 | *
88 | * param p_state - if true state of the pin when the interrupt was triggered
89 | * was HIGH, otherwise LOW
90 | */
91 | using handler = void(bool p_state);
92 |
93 | /**
94 | * @brief Configure the interrupt pin to match the settings supplied
95 | *
96 | * @param p_settings - settings to apply to interrupt pin
97 | * @return status - success or failure
98 | * @throws std::errc::invalid_argument if the settings could not be achieved.
99 | */
100 | [[nodiscard]] status configure(const settings& p_settings)
101 | {
102 | return driver_configure(p_settings);
103 | }
104 |
105 | /**
106 | * @brief Set the callback for when the interrupt occurs
107 | *
108 | * Any state transitions before this function is called are lost.
109 | *
110 | * @param p_callback - function to execute when the trigger condition occurs.
111 | */
112 | void on_trigger(hal::callback p_callback)
113 | {
114 | return driver_on_trigger(p_callback);
115 | }
116 |
117 | virtual ~interrupt_pin() = default;
118 |
119 | private:
120 | virtual status driver_configure(const settings& p_settings) = 0;
121 | virtual void driver_on_trigger(hal::callback p_callback) = 0;
122 | };
123 | } // namespace hal
124 |
--------------------------------------------------------------------------------
/include/libhal/magnetometer.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Magnetic field strength sensing hardware abstraction interface.
23 | *
24 | * Magnetometers are usually used for determining the strength of a magnetic
25 | * field, or calculating compass headings. If the device that the magnetometer
26 | * is mounted on, tends to move, or change its own orientation, then it is
27 | * helpful to use an accelerometer or tilt sensor in order to determine
28 | * appropriate heading for compass calculations.
29 | */
30 | class magnetometer
31 | {
32 | public:
33 | /**
34 | * @brief Result from reading the accelerometer.
35 | *
36 | */
37 | struct read_t
38 | {
39 | /**
40 | * @brief Magnetic field strength in the X axis, relative to the device's
41 | * reference frame.
42 | *
43 | */
44 | gauss x;
45 |
46 | /**
47 | * @brief Magnetic field strength in the Y axis, relative to the device's
48 | * reference frame.
49 | *
50 | */
51 | gauss y;
52 |
53 | /**
54 | * @brief Magnetic field strength in the Z axis, relative to the device's
55 | * reference frame.
56 | *
57 | */
58 | gauss z;
59 | };
60 |
61 | /**
62 | * @brief Read the latest magnetic field strength sensed by the device
63 | *
64 | * @return result - magnetic field strength data
65 | */
66 | [[nodiscard]] result read()
67 | {
68 | return driver_read();
69 | }
70 |
71 | virtual ~magnetometer() = default;
72 |
73 | private:
74 | virtual result driver_read() = 0;
75 | };
76 | } // namespace hal
77 |
--------------------------------------------------------------------------------
/include/libhal/motor.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 |
19 | namespace hal {
20 | /**
21 | * @brief Hardware abstraction for an open loop rotational actuator
22 | *
23 | * The motor interface can represent a variety of things such as:
24 | *
25 | * - A driver for motor controller IC like the DRV8801
26 | * - A driver for a motor with integrated controller & serial interface
27 | * - A unidirectional motor controlled by a single transistor
28 | * - A servo with open loop motor control
29 | *
30 | */
31 | class motor
32 | {
33 | public:
34 | /**
35 | * @brief Feedback from setting the motor power
36 | *
37 | * This structure is currently empty as no feedback has been determined for
38 | * now. This structure may be expanded in the future.
39 | */
40 | struct power_t
41 | {};
42 |
43 | /**
44 | * @brief Apply power to the motor
45 | *
46 | * Power is a percentage and thus cannot be used as a way to gauge how fast
47 | * the motor is moving. In general applying more power means to increase speed
48 | * and/or torque to the motor.
49 | *
50 | * - 0% power would mean that no power is being applied to the motor. In this
51 | * situation an unloaded motor will not move. 0% power does not guarantee
52 | * that the motor will hold its position. These specifics depend greatly on
53 | * the type of motor used and careful selection of motor and motor driver
54 | * are important for applications using this interface.
55 | *
56 | * - 100% power means that the maximum available of power is being applied to
57 | * the motor. As an example, if the max voltage of a DC brushed motor's
58 | * power supply is 12V, then 12V would be supplied to this motor.
59 | *
60 | * - 50% power would mean that half of the available power is being applied to
61 | * the motor. Using the same example, in this case 6V would be applied to
62 | * the motor either as a DC constant voltage or via PWM at 50% duty cycle.
63 | *
64 | * - Negative values will cause the motor to move in the opposite
65 | * direction as positive values. In the event that motor driver can *
66 | * only go in one direction, this function should clamp the power applied to
67 | * 0%.
68 | *
69 | * @param p_power - Percentage of power to apply to the motor from -1.0f to
70 | * +1.0f, -100% to 100%, respectively.
71 | * @return result - success or failure
72 | */
73 | [[nodiscard]] result power(float p_power)
74 | {
75 | auto clamped_power = std::clamp(p_power, -1.0f, +1.0f);
76 | return driver_power(clamped_power);
77 | }
78 |
79 | virtual ~motor() = default;
80 |
81 | private:
82 | virtual result driver_power(float p_power) = 0;
83 | };
84 | } // namespace hal
85 |
--------------------------------------------------------------------------------
/include/libhal/output_pin.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Digital output pin hardware abstraction.
23 | *
24 | * Use this to drive a pin HIGH or LOW in order to send a control signal or turn
25 | * off or on an LED.
26 | *
27 | * Implementations of this interface can be backed by external devices such as
28 | * I/O expanders or other micro-controllers.
29 | *
30 | */
31 | class output_pin
32 | {
33 | public:
34 | /// Generic settings for output pins
35 | struct settings
36 | {
37 | /// Pull resistor for the pin. This generally only helpful when open
38 | /// drain is enabled.
39 | pin_resistor resistor = pin_resistor::none;
40 |
41 | /// Starting level of the output pin. HIGH voltage defined as true and LOW
42 | /// voltage defined as false.
43 | bool open_drain = false;
44 | };
45 |
46 | /**
47 | * @brief Feedback from setting the pin state
48 | *
49 | * This structure is currently empty as no feedback has been determined for
50 | * now. This structure may be expanded in the future.
51 | */
52 | struct set_level_t
53 | {};
54 |
55 | /// Pin level reading structure
56 | struct level_t
57 | {
58 | /// Current state of the pin
59 | bool state;
60 | };
61 |
62 | /**
63 | * @brief Configure the output pin to match the settings supplied
64 | *
65 | * @param p_settings - settings to apply to output pin
66 | * @return status - success or failure
67 | * @throws std::errc::invalid_argument if the settings could not be achieved.
68 | */
69 | [[nodiscard]] status configure(const settings& p_settings)
70 | {
71 | return driver_configure(p_settings);
72 | }
73 |
74 | /**
75 | * @brief Set the state of the pin
76 | *
77 | * @param p_high - if true then the pin state is set to HIGH voltage. If
78 | * false, the pin state is set to LOW voltage.
79 | * @return result - success or failure
80 | */
81 | [[nodiscard]] result level(bool p_high)
82 | {
83 | return driver_level(p_high);
84 | }
85 |
86 | /**
87 | * @brief Read the current state of the output pin
88 | *
89 | * Implementations must read the pin state from hardware and will not simply
90 | * cache the results from the execution of `level(bool)`.
91 | *
92 | * This pin may not equal the state set by `level(bool)` when the pin is
93 | * configured as open-drain.
94 | *
95 | * @return result - return the current level state of the output pin
96 | */
97 | [[nodiscard]] result level()
98 | {
99 | return driver_level();
100 | }
101 |
102 | virtual ~output_pin() = default;
103 |
104 | private:
105 | virtual status driver_configure(const settings& p_settings) = 0;
106 | virtual result driver_level(bool p_high) = 0;
107 | virtual result driver_level() = 0;
108 | };
109 | } // namespace hal
110 |
--------------------------------------------------------------------------------
/include/libhal/pwm.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 |
20 | #include "error.hpp"
21 | #include "units.hpp"
22 |
23 | namespace hal {
24 | /**
25 | * @brief Pulse Width Modulation (PWM) channel hardware abstraction.
26 | *
27 | * This driver controls the waveform generation of a square wave and its
28 | * properties such as frequency and duty cycle.
29 | *
30 | * Frequency, meaning how often the waveform cycles from from low to high.
31 | *
32 | * Duty cycle, what proportion of the wavelength of the pulse is the voltage
33 | * HIGH.
34 | *
35 | * ```
36 | * ____________________ _
37 | * | | |
38 | * _| |_____|
39 | * ^ ^ ^
40 | * |<------ high ------>||
41 | *
42 | * HIGH Duration = 18 segments
43 | * LOW Duration = 5 segments
44 | * Duty Cycle = 20 / (20 + 5) = 80%
45 | *
46 | * If each segment is 1us then the wavelength is 25us
47 | * Thus frequency is (1 / 25us) = 40kHz
48 | * ```
49 | *
50 | * PWM is used for power control like motor control, lighting, transmitting
51 | * signals to servos, sending telemetry and much more.
52 | *
53 | */
54 | class pwm
55 | {
56 | public:
57 | /**
58 | * @brief Feedback setting the pwm duty cycle.
59 | *
60 | * This structure is currently empty as no feedback has been determined for
61 | * now. This structure may be expanded in the future.
62 | */
63 | struct duty_cycle_t
64 | {};
65 |
66 | /**
67 | * @brief Feedback setting the pwm frequency.
68 | *
69 | * This structure is currently empty as no feedback has been determined for
70 | * now. This structure may be expanded in the future.
71 | */
72 | struct frequency_t
73 | {};
74 |
75 | /**
76 | * @brief Set the pwm waveform frequency
77 | *
78 | * This function clamps the input value between 1.0_Hz and 1.0_GHz and thus
79 | * values passed to driver implementations are guaranteed to be within this
80 | * range. Callers of this function do not need to clamp their values before
81 | * passing them into this function as it would be redundant. The rationale for
82 | * doing this at the interface layer is that it allows callers and driver
83 | * implementors to omit redundant clamping code, reducing code bloat.
84 | *
85 | * @param p_frequency - settings to apply to pwm driver
86 | * @return result - success or failure
87 | * @throws std::errc::argument_out_of_domain - if the frequency is beyond what
88 | * the pwm generator is capable of achieving.
89 | */
90 | [[nodiscard]] result frequency(hertz p_frequency)
91 | {
92 | auto clamped_frequency = std::clamp(p_frequency, 1.0_Hz, 1.0_GHz);
93 | return driver_frequency(clamped_frequency);
94 | }
95 |
96 | /**
97 | * @brief Set the pwm waveform duty cycle
98 | *
99 | * The input value `p_duty_cycle` is a 32-bit floating point value from 0.0f
100 | * to 1.0f.
101 | *
102 | * The floating point value is directly proportional to the duty cycle
103 | * percentage, such that 0.0f is 0%, 0.25f is 25%, 0.445f is 44.5% and 1.0f is
104 | * 100%.
105 | *
106 | * This function clamps the input value between 0.0f and 1.0f and thus values
107 | * passed to driver implementations are guaranteed to be within this range.
108 | * Callers of this function do not need to clamp their values before passing
109 | * them into this function as it would be redundant. The rationale for doing
110 | * this at the interface layer is that it allows callers and driver
111 | * implementors to omit redundant clamping code, reducing code bloat.
112 | *
113 | * @param p_duty_cycle - a value from 0.0f to +1.0f representing the duty
114 | * cycle percentage.
115 | * @return result - success or failure
116 | */
117 | [[nodiscard]] result duty_cycle(float p_duty_cycle)
118 | {
119 | auto clamped_duty_cycle = std::clamp(p_duty_cycle, 0.0f, 1.0f);
120 | return driver_duty_cycle(clamped_duty_cycle);
121 | }
122 |
123 | virtual ~pwm() = default;
124 |
125 | private:
126 | virtual result driver_frequency(hertz p_frequency) = 0;
127 | virtual result driver_duty_cycle(float p_duty_cycle) = 0;
128 | };
129 | } // namespace hal
130 |
--------------------------------------------------------------------------------
/include/libhal/rotation_sensor.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Rotation measuring hardware abstraction interface
23 | *
24 | * Examples of rotary encoder are:
25 | *
26 | * - Rotary Potentiometers
27 | * - Quadrature Encoders
28 | * - Incremental Encoders
29 | * - Absolute Encoders
30 | * - Rotary Magnetic Encoders
31 | * - Inertial Measurement Unit or IMU
32 | *
33 | * Rotation sensors can be relative or absolute. Relative position means that
34 | * the sensor can only see changes in rotation from where measurement started.
35 | * In other words, at application start, relative encoders will start at 0.
36 | * Absolute encoders know their position at all times. At application start, the
37 | * absolute encoder will be able to determine its exact orientation relative to
38 | * a frame of reference when read.
39 | *
40 | * Examples of relative rotation sensors are:
41 | *
42 | * - Quadrature Encoders
43 | * - Incremental Encoders
44 | *
45 | * Examples of absolute rotation sensors are:
46 | *
47 | * - Rotary Potentiometers
48 | * - Absolute Encoders
49 | * - Rotary Magnetic Encoders
50 | * - IMUs
51 | *
52 | * Rotation sensors can also be finite or infinite. Finite meaning that the
53 | * angle that can be reported is a fixed amount for the device. Infinite means
54 | * that the encoder can continue rotating and adding more to its angle reading
55 | * forever. Infinite rotation sensors tend to not have a physical stop that
56 | * limits how much they can be rotated.
57 | *
58 | * Examples of finite rotation sensors are:
59 | *
60 | * - Rotary Potentiometers
61 | * - Absolute Encoders
62 | * - IMUs
63 | *
64 | * Examples of infinite rotation sensors are:
65 | *
66 | * - Rotary Magnetic Encoders
67 | * - Quadrature Encoders
68 | * - Incremental Encoders
69 | *
70 | * This interface does not provide a means to determine these attributes of a
71 | * rotation sensor as this is an application architecture decision. Drivers that
72 | * implement this interface should document what kind of rotary sensor it is
73 | * such that a developer can determine its applicability to their application.
74 | * The context of which sensor ought to be used for an application is solely
75 | * known at architecture definition time and software should not be expected to
76 | * at runtime, if the right type of rotation sensor was passed into the object.
77 | */
78 | class rotation_sensor
79 | {
80 | public:
81 | /**
82 | * @brief Result from reading the rotation sensor.
83 | *
84 | */
85 | struct read_t
86 | {
87 | /**
88 | * @brief Rotation angle measurement
89 | *
90 | */
91 | degrees angle;
92 | };
93 |
94 | /**
95 | * @brief Read the current angle measured by the device
96 | *
97 | * @return result - rotation data
98 | */
99 | [[nodiscard]] result read()
100 | {
101 | return driver_read();
102 | }
103 |
104 | virtual ~rotation_sensor() = default;
105 |
106 | private:
107 | virtual result driver_read() = 0;
108 | };
109 | } // namespace hal
110 |
--------------------------------------------------------------------------------
/include/libhal/serial.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #include "error.hpp"
23 | #include "units.hpp"
24 |
25 | namespace hal {
26 | /**
27 | * @brief Hardware abstract interface for the serial communication protocol
28 | *
29 | * Use this interface for hardware that implements a serial protocol like UART,
30 | * RS232, RS485 and others that use a similar communication protocol but may use
31 | * different voltage schemes.
32 | *
33 | * This interface only works 8-bit serial data frames.
34 | *
35 | * Due to the asynchronous and unformatted nature of serial communication
36 | * protocols, all implementations of serial devices must be buffered. Buffered,
37 | * in this case, is defined as automatic storage of received bytes without
38 | * direct application intervention.
39 | *
40 | * All implementations MUST allow the user to supply their own buffer of
41 | * arbitrary size up to the limits of what hardware can support. This allows a
42 | * developer the ability to tailored the buffer size to the needs of the
43 | * application.
44 | *
45 | * Examples of buffering schemes are:
46 | *
47 | * - Using DMA to copy data from a serial peripheral to a region of memory
48 | * - Using interrupts when a serial peripheral's queue has filled to a point
49 | *
50 | */
51 | class serial
52 | {
53 | public:
54 | /**
55 | * @brief Generic settings for a standard serial device.
56 | *
57 | */
58 | struct settings
59 | {
60 | /**
61 | * @brief Set of available stop bits options
62 | *
63 | */
64 | enum class stop_bits : uint8_t
65 | {
66 | one = 0,
67 | two,
68 | };
69 |
70 | /**
71 | * @brief Set of parity bit options
72 | *
73 | */
74 | enum class parity : uint8_t
75 | {
76 | /**
77 | * @brief Disable parity bit as part of the frame
78 | *
79 | */
80 | none = 0,
81 | /**
82 | * @brief Enable parity and set 1 (HIGH) when the number of bits is odd
83 | *
84 | */
85 | odd,
86 | /**
87 | * @brief Enable parity and set 1 (HIGH) when the number of bits is even
88 | *
89 | */
90 | even,
91 | /**
92 | * @brief Enable parity bit and always return 1 (HIGH) for ever
93 | * frame
94 | *
95 | */
96 | forced1,
97 | /**
98 | * @brief Enable parity bit and always return 0 (LOW) for ever
99 | * frame
100 | *
101 | */
102 | forced0,
103 | };
104 |
105 | /**
106 | * @brief The operating speed of the baud rate (in units of bits per second)
107 | *
108 | */
109 | hertz baud_rate = 115200.0f;
110 |
111 | /**
112 | * @brief Number of stop bits for each frame
113 | *
114 | */
115 | stop_bits stop = stop_bits::one;
116 |
117 | /**
118 | * @brief Parity bit type for each frame
119 | *
120 | */
121 | parity parity = parity::none;
122 | };
123 | /**
124 | * @brief Return type for serial read operations
125 | *
126 | */
127 | struct read_t
128 | {
129 | /**
130 | * @brief The filled portion of the input buffer from the serial port
131 | *
132 | * The size of this buffer indicates the number of bytes read The address
133 | * points to the start of the buffer passed into the read() function.
134 | */
135 | std::span data;
136 |
137 | /**
138 | * @brief Number of enqueued and available to be read out bytes
139 | *
140 | * This value can be equal to or exceed the value of capacity. In this
141 | * situation, the number of bytes above the capacity are bytes that have
142 | * been dropped. Not all drivers will indicate the number of bytes lost. It
143 | * is up to the driver or application to decide what to do in this
144 | * situation.
145 | */
146 | size_t available;
147 |
148 | /**
149 | * @brief The maximum number of bytes that the serial port can queue up.
150 | *
151 | */
152 | size_t capacity;
153 | };
154 |
155 | /**
156 | * @brief Return type for serial write operations
157 | *
158 | */
159 | struct write_t
160 | {
161 | /**
162 | * @brief The portion of the buffer transmitted
163 | *
164 | */
165 | std::span data;
166 | };
167 |
168 | /**
169 | * @brief Feedback from performing a flush operation
170 | *
171 | * This structure is currently empty as no feedback has been determined for
172 | * now. This structure may be expanded in the future.
173 | */
174 | struct flush_t
175 | {};
176 |
177 | /**
178 | * @brief Configure serial to match the settings supplied
179 | *
180 | * Implementing drivers must verify if the settings can be applied to hardware
181 | * before modifying the hardware. This will ensure that if this operation
182 | * fails, the state of the serial device has not changed.
183 | *
184 | * @param p_settings - settings to apply to serial driver
185 | * @return status - success or failure
186 | * @throws std::errc::invalid_argument if the settings could not be achieved
187 | */
188 | [[nodiscard]] status configure(const settings& p_settings)
189 | {
190 | return driver_configure(p_settings);
191 | }
192 |
193 | /**
194 | * @brief Write data to the transmitter line of the serial port
195 | *
196 | * @param p_data - data to be transmitted over the serial port
197 | * @return result - serial write response
198 | */
199 | [[nodiscard]] result write(std::span p_data)
200 | {
201 | return driver_write(p_data);
202 | }
203 |
204 | /**
205 | * @brief Copy bytes from working buffer into passed buffer
206 | *
207 | * This operation copies the bytes from the serial driver's internal working
208 | * buffer to the buffer supplied.
209 | *
210 | * The buffer will be filled up either to the end of the buffer or until there
211 | * are no more bytes left in the working buffer. The remaining portion of the
212 | * input buffer is returned in `read_t::remaining`.
213 | *
214 | * If a frame error has occurred at any point during serial reception, this
215 | * function will throw a `std::errc::io_error` value. The contents of the
216 | * internal working buffer will stay the same. No information from the
217 | * internal working buffer will be copied into the supplied buffer and no data
218 | * will be removed from the internal working buffer. The frame error status
219 | * will be internally cleared after its occurrence. Subsequent calls of this
220 | * function will read out the contents of the buffer although the data inside
221 | * may be corrupt.
222 | *
223 | * When an error occurs the options available are to flush the buffer and
224 | * attempt reception again or read out the potentially corrupted data and
225 | * parse it as needed. The choice of operation is application/driver specific.
226 | *
227 | * @param p_data - Buffer to read bytes in to
228 | * @return result - serial read response data
229 | * @throws std::errc::io_error - a frame error occurred at some point during
230 | * reception.
231 | */
232 | [[nodiscard]] result read(std::span p_data)
233 | {
234 | return driver_read(p_data);
235 | }
236 | /**
237 | * @brief Flush working buffer
238 | *
239 | * The behavior of flushing the internal working buffer is this:
240 | *
241 | * - Sets the serial port's internal working buffer to an "empty" state.
242 | * - Clear any received data stored in hardware registers.
243 | * - Use the fastest available option to perform these operations, meaning
244 | * that the contents of the internal working buffer will not be zeroed out.
245 | *
246 | * @return result - success or failure
247 | */
248 | [[nodiscard]] result flush()
249 | {
250 | return driver_flush();
251 | }
252 |
253 | virtual ~serial() = default;
254 |
255 | private:
256 | virtual status driver_configure(const settings& p_settings) = 0;
257 | virtual result driver_write(std::span p_data) = 0;
258 | virtual result driver_read(std::span p_data) = 0;
259 | virtual result driver_flush() = 0;
260 | };
261 | } // namespace hal
262 |
--------------------------------------------------------------------------------
/include/libhal/servo.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Hardware abstraction for a closed loop position controlled rotational
23 | * actuator
24 | *
25 | */
26 | class servo
27 | {
28 | public:
29 | /**
30 | * @brief Feedback from setting the servo position
31 | *
32 | * This structure is currently empty as no feedback has been determined for
33 | * now. This structure may be expanded in the future.
34 | */
35 | struct position_t
36 | {};
37 |
38 | /**
39 | * @brief Error information indicating the range of the servo
40 | *
41 | */
42 | struct range_error
43 | {
44 | /**
45 | * @brief Minimum range of the servo shaft in degrees
46 | *
47 | */
48 | hal::degrees min;
49 | /**
50 | * @brief Maximum range of the servo shaft in degrees
51 | *
52 | */
53 | hal::degrees max;
54 | };
55 |
56 | /**
57 | * @brief Set the position of the servo's output shaft
58 | *
59 | * Position is the rotational position as a angle in degrees that the caller
60 | * wants the shaft to rotate to. The allowed range of positions is defined by
61 | * the servo itself. Many servos have intrinsic limits to their range.
62 | *
63 | * Developers must choose servos that fit the range for their applications.
64 | * Applications must clearly define the range that they require in order to
65 | * perform correctly.
66 | *
67 | * The velocity in which the servo shaft moves is not defined by this function
68 | * but is either intrinsic to the servo or a configuration of the servo.
69 | *
70 | * @param p_position - the position to move the servo shaft in degrees.
71 | * @return result - success or failure
72 | * @throws std::errc::invalid_argument - when position exceeds the range of
73 | * the servo. When this error occurs, the guaranteed behavior is that the
74 | * servo keeps its last set position.
75 | * @throws hal::servo::range_error - when position exceeds the range of
76 | * the servo. Provides details about the min and max range of the servo. When
77 | * this error occurs, the guaranteed behavior is that the servo keeps its last
78 | * set position.
79 | */
80 | [[nodiscard]] result position(hal::degrees p_position)
81 | {
82 | return driver_position(p_position);
83 | }
84 |
85 | virtual ~servo() = default;
86 |
87 | private:
88 | virtual result driver_position(hal::degrees p_position) = 0;
89 | };
90 | } // namespace hal
91 |
--------------------------------------------------------------------------------
/include/libhal/spi.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 | #include
20 |
21 | #include "error.hpp"
22 | #include "units.hpp"
23 |
24 | namespace hal {
25 | /**
26 | * @brief Serial peripheral interface (SPI) communication protocol hardware
27 | * abstract interface
28 | *
29 | */
30 | class spi
31 | {
32 | public:
33 | /**
34 | * @brief Generic settings for a standard SPI device.
35 | *
36 | */
37 | struct settings
38 | {
39 | /**
40 | * @brief Serial clock frequency in hertz
41 | *
42 | */
43 | hertz clock_rate = 100.0_kHz;
44 | /**
45 | * @brief The polarity of the pins when the signal is idle
46 | *
47 | */
48 | bool clock_idles_high = false;
49 | /**
50 | * @brief The phase of the clock signal when communicating
51 | *
52 | */
53 | bool data_valid_on_trailing_edge = false;
54 | };
55 |
56 | /**
57 | * @brief Feedback from performing a transfer on the spi bus
58 | *
59 | * This structure is currently empty as no feedback has been determined for
60 | * now. This structure may be expanded in the future.
61 | */
62 | struct transfer_t
63 | {};
64 |
65 | /// Default filler data placed on the bus in place of actual write data when
66 | /// the write buffer has been exhausted.
67 | static constexpr hal::byte default_filler = hal::byte{ 0xFF };
68 |
69 | /**
70 | * @brief Configure spi to match the settings supplied
71 | *
72 | * @param p_settings - settings to apply to spi
73 | * @return status - success or failure
74 | * @throws std::errc::invalid_argument if the settings could not be achieved.
75 | */
76 | [[nodiscard]] status configure(const settings& p_settings)
77 | {
78 | return driver_configure(p_settings);
79 | }
80 | /**
81 | * @brief Send and receive data between a selected device on the spi bus.
82 | * This function will block until the entire transfer is finished.
83 | *
84 | * @param p_data_out - buffer to write data to the bus. If this is set to
85 | * null/empty then writing is ignored and the p_filler will be written to
86 | * the bus. If the length is less than p_data_in, then p_filler will be
87 | * written to the bus after this buffer has been sent.
88 | * @param p_data_in - buffer to read the data off of the bus. If this is
89 | * null/empty, then the transfer will be write only and the incoming data will
90 | * be ignored. If the length of this buffer is less than p_data_out, once this
91 | * buffer has been filled, the rest of the received bytes on the bus will be
92 | * dropped.
93 | * @param p_filler - filler data placed on the bus in place of actual write
94 | * data when p_data_out has been exhausted.
95 | * @return result - success or failure
96 | */
97 | [[nodiscard]] result transfer(
98 | std::span p_data_out,
99 | std::span p_data_in,
100 | hal::byte p_filler = default_filler)
101 | {
102 | return driver_transfer(p_data_out, p_data_in, p_filler);
103 | }
104 |
105 | virtual ~spi() = default;
106 |
107 | private:
108 | virtual status driver_configure(const settings& p_settings) = 0;
109 | virtual result driver_transfer(
110 | std::span p_data_out,
111 | std::span p_data_in,
112 | hal::byte p_filler) = 0;
113 | };
114 | } // namespace hal
115 |
--------------------------------------------------------------------------------
/include/libhal/steady_clock.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 |
19 | #include "error.hpp"
20 | #include "units.hpp"
21 |
22 | namespace hal {
23 | /**
24 | * @brief Hardware abstraction interface for a steady clock mechanism
25 | *
26 | * Implementations of this interface must follow the same requirements as a
27 | * std::chrono::steady_clock, in that the clock is monotonic & steady. An
28 | * additional requirement is added to ensure that the clock is reliable. Meaning
29 | * calls to the interface functions do not return errors because this clock
30 | * should be infallible. To ensure this, this clock should be driven by the
31 | * platform's peripheral drivers or some other mechanism that is unlikely to go
32 | * offline while the platform is in a normal operating state.
33 | *
34 | * This clock is steady meaning that subsequent calls to get the uptime of this
35 | * clock cannot decrease as physical time moves forward and the time between
36 | * ticks of this clock are constant and defined by the clock's frequency.
37 | *
38 | * This can be used to get the time since the boot up, or to be more accurate,
39 | * the time when the steady clock object is created. This clock is most suitable
40 | * for measuring time intervals.
41 | *
42 | * After creation of this clock, the operating frequency shall not change.
43 | */
44 | class steady_clock
45 | {
46 | public:
47 | /**
48 | * @brief Result from requesting the operating frequency of the steady clock
49 | *
50 | */
51 | struct frequency_t
52 | {
53 | /**
54 | * @brief the frequency of the steady clock.
55 | *
56 | * Guaranteed to be a positive value by the implementing driver.
57 | */
58 | hertz operating_frequency;
59 | };
60 |
61 | /**
62 | * @brief Result from calling uptime
63 | *
64 | */
65 | struct uptime_t
66 | {
67 | /**
68 | * @brief Number of counts that the steady clock has counted since it
69 | * started.
70 | *
71 | */
72 | std::uint64_t ticks;
73 | };
74 |
75 | /**
76 | * @brief Get the operating frequency of the steady clock
77 | *
78 | * @return result - operating frequency of the steady clock.
79 | * Guaranteed to be a positive value by the implementing driver.
80 | */
81 | [[nodiscard]] frequency_t frequency()
82 | {
83 | return driver_frequency();
84 | }
85 |
86 | /**
87 | * @brief Get the current value of the steady clock
88 | *
89 | * @return uptime_t - uptime information
90 | */
91 | [[nodiscard]] uptime_t uptime()
92 | {
93 | return driver_uptime();
94 | }
95 |
96 | virtual ~steady_clock() = default;
97 |
98 | private:
99 | virtual frequency_t driver_frequency() = 0;
100 | virtual uptime_t driver_uptime() = 0;
101 | };
102 | } // namespace hal
103 |
--------------------------------------------------------------------------------
/include/libhal/temperature_sensor.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include "error.hpp"
18 | #include "units.hpp"
19 |
20 | namespace hal {
21 | /**
22 | * @brief Temperature sensing hardware abstraction interface
23 | *
24 | *
25 | */
26 | class temperature_sensor
27 | {
28 | public:
29 | /**
30 | * @brief Result from reading the temperature sensor.
31 | *
32 | */
33 | struct read_t
34 | {
35 | /**
36 | * @brief Measured temperature
37 | *
38 | */
39 | celsius temperature;
40 | };
41 |
42 | /**
43 | * @brief Read the current temperature measured by the device
44 | *
45 | * @return result - temperature data
46 | */
47 | [[nodiscard]] result read()
48 | {
49 | return driver_read();
50 | }
51 |
52 | virtual ~temperature_sensor() = default;
53 |
54 | private:
55 | virtual result driver_read() = 0;
56 | };
57 | } // namespace hal
58 |
--------------------------------------------------------------------------------
/include/libhal/third_party/inplace_function.hpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Boost Software License - Version 1.0 - August 17th, 2003
3 | *
4 | * Permission is hereby granted, free of charge, to any person or organization
5 | * obtaining a copy of the software and accompanying documentation covered by
6 | * this license (the "Software") to use, reproduce, display, distribute,
7 | * execute, and transmit the Software, and to prepare derivative works of the
8 | * Software, and to permit third-parties to whom the Software is furnished to
9 | * do so, all subject to the following:
10 | *
11 | * The copyright notices in the Software and this entire statement, including
12 | * the above license grant, this restriction and the following disclaimer,
13 | * must be included in all copies of the Software, in whole or in part, and
14 | * all derivative works of the Software, unless such copies or derivative
15 | * works are solely in the form of machine-executable object code generated by
16 | * a source language processor.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
21 | * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
22 | * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
23 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 | * DEALINGS IN THE SOFTWARE.
25 | */
26 |
27 | /**
28 | * [libhal] modifications to this file are as follows:
29 | *
30 | * 1. Remove "throw" from empty vtable invoke function and replace it with a
31 | * function that returns uninitialized data, making call to the function
32 | * undefined behavior.
33 | * 2. Remove `nullptr_t` constructors from inplace_function
34 | * 3. Calling a "moved-from" inplace_function will NOT crash but the result
35 | * of the called function will NOT be valid.
36 | * 4. Move SG14_INPLACE_FUNCTION_THROW is no longer needed and thus
37 | * this library no longer needs exceptions.
38 | * 5. After removing the `nullptr_t` constructors, there is a new edge case
39 | * where the vtable_ptr_ is now nullptr and not an empty_vtable, which
40 | * means that access to that vtable_ptr_ results in a null pointer
41 | * dereference. A check must be put in the copy ctor and dtor.
42 | */
43 |
44 | #pragma once
45 |
46 | #include
47 | #include
48 | #include
49 | #include
50 |
51 | namespace stdext {
52 |
53 | namespace inplace_function_detail {
54 |
55 | static constexpr size_t InplaceFunctionDefaultCapacity = 32;
56 |
57 | #ifndef SG14_USE_STD_ALIGNED_STORAGE
58 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61458
59 | // MSVC 32-bit has the same bug.
60 | // libc++ and MSVC 64-bit seem to work fine right now, but why run the risk?
61 | template
62 | union aligned_storage_helper
63 | {
64 | struct double1
65 | {
66 | double a;
67 | };
68 | struct double4
69 | {
70 | double a[4];
71 | };
72 | template
73 | using maybe = std::conditional_t<(Cap >= sizeof(T)), T, char>;
74 | char real_data[Cap];
75 | maybe a;
76 | maybe b;
77 | maybe c;
78 | maybe d;
79 | maybe e;
80 | maybe f;
81 | maybe g;
82 | maybe h;
83 | };
84 |
85 | template)>
86 | struct aligned_storage
87 | {
88 | using type = std::aligned_storage_t;
89 | };
90 |
91 | template)>
92 | using aligned_storage_t = typename aligned_storage::type;
93 | static_assert(sizeof(aligned_storage_t) == sizeof(void*), "A");
94 | static_assert(alignof(aligned_storage_t) == alignof(void*), "B");
95 | #else
96 | using std::aligned_storage;
97 | using std::aligned_storage_t;
98 | static_assert(sizeof(std::aligned_storage_t) == sizeof(void*),
99 | "C");
100 | static_assert(alignof(std::aligned_storage_t) == alignof(void*),
101 | "D");
102 | #endif
103 |
104 | template
105 | struct wrapper
106 | {
107 | using type = T;
108 | };
109 |
110 | template
111 | struct vtable
112 | {
113 | using storage_ptr_t = void*;
114 |
115 | using invoke_ptr_t = R (*)(storage_ptr_t, Args&&...);
116 | using process_ptr_t = void (*)(storage_ptr_t, storage_ptr_t);
117 | using destructor_ptr_t = void (*)(storage_ptr_t);
118 |
119 | invoke_ptr_t invoke_ptr;
120 | process_ptr_t copy_ptr;
121 | process_ptr_t relocate_ptr;
122 | destructor_ptr_t destructor_ptr;
123 |
124 | explicit constexpr vtable() noexcept
125 | : invoke_ptr{ [](storage_ptr_t, Args&&...) -> R {
126 | if constexpr (std::is_same_v) {
127 | return;
128 | } else {
129 | std::array memory;
130 | return *reinterpret_cast(memory.data());
131 | }
132 | } }
133 | , copy_ptr{ [](storage_ptr_t, storage_ptr_t) -> void {} }
134 | , relocate_ptr{ [](storage_ptr_t, storage_ptr_t) -> void {} }
135 | , destructor_ptr{ [](storage_ptr_t) -> void {} }
136 | {
137 | }
138 |
139 | template
140 | explicit constexpr vtable(wrapper) noexcept
141 | : invoke_ptr{ [](storage_ptr_t storage_ptr, Args&&... args) -> R {
142 | return (*static_cast(storage_ptr))(static_cast(args)...);
143 | } }
144 | , copy_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void {
145 | ::new (dst_ptr) C{ (*static_cast(src_ptr)) };
146 | } }
147 | , relocate_ptr{ [](storage_ptr_t dst_ptr, storage_ptr_t src_ptr) -> void {
148 | ::new (dst_ptr) C{ std::move(*static_cast(src_ptr)) };
149 | static_cast(src_ptr)->~C();
150 | } }
151 | , destructor_ptr{ [](storage_ptr_t src_ptr) -> void {
152 | static_cast(src_ptr)->~C();
153 | } }
154 | {
155 | }
156 |
157 | vtable(const vtable&) = delete;
158 | vtable(vtable&&) = delete;
159 |
160 | vtable& operator=(const vtable&) = delete;
161 | vtable& operator=(vtable&&) = delete;
162 |
163 | ~vtable() = default;
164 | };
165 |
166 | template
167 | #if __cplusplus >= 201703L
168 | inline constexpr
169 | #endif
170 | vtable
171 | empty_vtable{};
172 |
173 | template
174 | struct is_valid_inplace_dst : std::true_type
175 | {
176 | static_assert(DstCap >= SrcCap,
177 | "Can't squeeze larger inplace_function into a smaller one");
178 |
179 | static_assert(DstAlign % SrcAlign == 0,
180 | "Incompatible inplace_function alignments");
181 | };
182 |
183 | // C++11 MSVC compatible implementation of std::is_invocable_r.
184 |
185 | template
186 | void accept(R);
187 |
188 | template
189 | struct is_invocable_r_impl : std::false_type
190 | {};
191 |
192 | template
193 | struct is_invocable_r_impl()(std::declval()...),
194 | void()),
195 | void,
196 | F,
197 | Args...> : std::true_type
198 | {};
199 |
200 | template
201 | struct is_invocable_r_impl()(std::declval()...),
202 | void()),
203 | const void,
204 | F,
205 | Args...> : std::true_type
206 | {};
207 |
208 | template
209 | struct is_invocable_r_impl(
210 | std::declval()(std::declval()...))),
211 | R,
212 | F,
213 | Args...> : std::true_type
214 | {};
215 |
216 | template
217 | using is_invocable_r = is_invocable_r_impl;
218 | } // namespace inplace_function_detail
219 |
220 | template)>
225 | class inplace_function; // unspecified
226 |
227 | namespace inplace_function_detail {
228 | template
229 | struct is_inplace_function : std::false_type
230 | {};
231 | template
232 | struct is_inplace_function> : std::true_type
233 | {};
234 | } // namespace inplace_function_detail
235 |
236 | template
237 | class inplace_function
238 | {
239 | using storage_t =
240 | inplace_function_detail::aligned_storage_t;
241 | using vtable_t = inplace_function_detail::vtable;
242 | using vtable_ptr_t = const vtable_t*;
243 |
244 | template
245 | friend class inplace_function;
246 |
247 | public:
248 | using capacity = std::integral_constant;
249 | using alignment = std::integral_constant;
250 |
251 | inplace_function() noexcept
252 | : vtable_ptr_{ std::addressof(
253 | inplace_function_detail::empty_vtable) }
254 | {
255 | }
256 |
257 | template,
259 | class = std::enable_if_t<
260 | !inplace_function_detail::is_inplace_function::value &&
261 | inplace_function_detail::is_invocable_r::value>>
262 | inplace_function(T&& closure)
263 | {
264 | static_assert(
265 | std::is_copy_constructible::value,
266 | "inplace_function cannot be constructed from non-copyable type");
267 |
268 | static_assert(sizeof(C) <= Capacity,
269 | "Function object is too large to be constructed with the "
270 | "type's Capacity");
271 |
272 | static_assert(Alignment % alignof(C) == 0,
273 | "Function object does not fit alignment specifications and "
274 | "thus cannot be constructed");
275 |
276 | static const vtable_t vt{ inplace_function_detail::wrapper{} };
277 | vtable_ptr_ = std::addressof(vt);
278 |
279 | ::new (std::addressof(storage_)) C{ std::forward(closure) };
280 | }
281 |
282 | template
283 | inplace_function(const inplace_function& other)
284 | : inplace_function(other.vtable_ptr_,
285 | other.vtable_ptr_->copy_ptr,
286 | std::addressof(other.storage_))
287 | {
288 | static_assert(
289 | inplace_function_detail::
290 | is_valid_inplace_dst::value,
291 | "conversion not allowed");
292 | }
293 |
294 | template
295 | inplace_function(inplace_function&& other) noexcept
296 | : inplace_function(other.vtable_ptr_,
297 | other.vtable_ptr_->relocate_ptr,
298 | std::addressof(other.storage_))
299 | {
300 | static_assert(
301 | inplace_function_detail::
302 | is_valid_inplace_dst::value,
303 | "conversion not allowed");
304 |
305 | other.vtable_ptr_ =
306 | std::addressof(inplace_function_detail::empty_vtable);
307 | }
308 |
309 | inplace_function(const inplace_function& other)
310 | : vtable_ptr_{ other.vtable_ptr_ }
311 | {
312 | vtable_ptr_->copy_ptr(std::addressof(storage_),
313 | std::addressof(other.storage_));
314 | }
315 |
316 | inplace_function(inplace_function&& other) noexcept
317 | : vtable_ptr_{ std::exchange(
318 | other.vtable_ptr_,
319 | std::addressof(inplace_function_detail::empty_vtable)) }
320 | {
321 | vtable_ptr_->relocate_ptr(std::addressof(storage_),
322 | std::addressof(other.storage_));
323 | }
324 |
325 | inplace_function& operator=(inplace_function other) noexcept
326 | {
327 | if (vtable_ptr_) {
328 | vtable_ptr_->destructor_ptr(std::addressof(storage_));
329 | }
330 |
331 | vtable_ptr_ = std::exchange(
332 | other.vtable_ptr_,
333 | std::addressof(inplace_function_detail::empty_vtable));
334 | vtable_ptr_->relocate_ptr(std::addressof(storage_),
335 | std::addressof(other.storage_));
336 | return *this;
337 | }
338 |
339 | ~inplace_function()
340 | {
341 | if (vtable_ptr_) {
342 | vtable_ptr_->destructor_ptr(std::addressof(storage_));
343 | }
344 | }
345 |
346 | R operator()(Args... args) const
347 | {
348 | return vtable_ptr_->invoke_ptr(std::addressof(storage_),
349 | std::forward(args)...);
350 | }
351 |
352 | void swap(inplace_function& other) noexcept
353 | {
354 | if (this == std::addressof(other))
355 | return;
356 |
357 | storage_t tmp;
358 | vtable_ptr_->relocate_ptr(std::addressof(tmp), std::addressof(storage_));
359 |
360 | other.vtable_ptr_->relocate_ptr(std::addressof(storage_),
361 | std::addressof(other.storage_));
362 |
363 | vtable_ptr_->relocate_ptr(std::addressof(other.storage_),
364 | std::addressof(tmp));
365 |
366 | std::swap(vtable_ptr_, other.vtable_ptr_);
367 | }
368 |
369 | friend void swap(inplace_function& lhs, inplace_function& rhs) noexcept
370 | {
371 | lhs.swap(rhs);
372 | }
373 |
374 | private:
375 | vtable_ptr_t vtable_ptr_;
376 | mutable storage_t storage_;
377 |
378 | inplace_function(vtable_ptr_t vtable_ptr,
379 | typename vtable_t::process_ptr_t process_ptr,
380 | typename vtable_t::storage_ptr_t storage_ptr)
381 | : vtable_ptr_{ vtable_ptr }
382 | {
383 | process_ptr(std::addressof(storage_), storage_ptr);
384 | }
385 | };
386 |
387 | } // namespace stdext
--------------------------------------------------------------------------------
/include/libhal/timeout.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | /**
16 | * @defgroup TimeoutCore Timeout Core
17 | * @file timeout.hpp
18 | * @brief Provides the hal::timeout type and utility functions that use that
19 | * type.
20 | *
21 | */
22 | #pragma once
23 |
24 | #include "error.hpp"
25 | #include "functional.hpp"
26 |
27 | namespace hal {
28 | /**
29 | * @ingroup TimeoutCore
30 | * @brief Represents the state of a coroutine or resumable callable
31 | *
32 | */
33 | enum class work_state
34 | {
35 | // Callable is in progress and has not yet finished performing its work.
36 | in_progress,
37 | // Callable was able to determine that it failed to do what it was tasked to
38 | // do and has terminated.
39 | failed,
40 | // Callable finished the work it needed to perform and has terminated.
41 | finished,
42 | };
43 |
44 | /**
45 | * @ingroup TimeoutCore
46 | * @brief Timeout is a callable object or function that signals to a procedure
47 | * that the procedure has exceeded its time allotment and should return control
48 | * to the calling function.
49 | *
50 | * @throws hal::timeout - when the timeout condition has been met.
51 | * @returns status - sets error flag set when timeout
52 | * condition has been met, otherwise returns success.
53 | */
54 | using timeout_function = status(void);
55 |
56 | template
57 | concept timeout = std::convertible_to>;
58 |
59 | /**
60 | * @ingroup TimeoutCore
61 | * @brief A non-blocking callable that performs work with each call
62 | *
63 | * Each call to a work_function will perform a set of work. The worker will
64 | * return a work_state to indicate its current state. Once the worker reaches a
65 | * terminal state, it MUST perform no additional work and return the terminal
66 | * state. For example, if a work function failed, it must always return failure
67 | * and not interact with hardware or other software from that point on. Same
68 | * will occur for the "finished" state.
69 | *
70 | * This function can be repeatedly tried until it has reached a terminal state
71 | * with the try_until() function.
72 | *
73 | * @returns result - sets error flag set when an error occurs,
74 | * otherwise returns work_state enum.
75 | */
76 | using work_function = result();
77 |
78 | template
79 | concept worker = std::convertible_to>;
80 |
81 | /**
82 | * @ingroup TimeoutCore
83 | * @brief Delay the execution of the application or thread for a duration of
84 | * time.
85 | *
86 | * @tparam Timeout - timeout type
87 | * @param p_timeout - callable timeout object
88 | * @return status - success or failure
89 | */
90 | [[nodiscard]] inline status delay(timeout auto p_timeout)
91 | {
92 | bool waiting = true;
93 |
94 | // This lambda catches a `std::errc::timed_out` handle them by changing
95 | // `waiting` from true to false in order to break the while loop below.
96 | auto timeout_catcher =
97 | [&waiting](hal::match p_errc) -> status {
98 | (void)p_errc;
99 | // Simply change the waiting bool
100 | waiting = false;
101 | // return successful
102 | return {};
103 | };
104 |
105 | HAL_CHECK(hal::attempt(
106 | [&p_timeout]() -> status {
107 | // Continuously call p_callback until it either returns
108 | // `std::errc::timeout_out`
109 | while (true) {
110 | HAL_CHECK(p_timeout());
111 | }
112 | // Unreachable!
113 | return {};
114 | },
115 | timeout_catcher));
116 |
117 | return {};
118 | }
119 |
120 | /**
121 | * @ingroup TimeoutCore
122 | * @brief Create a timeout that will never time out
123 | *
124 | * @return auto - callable that will never return timeout
125 | */
126 | [[nodiscard]] inline auto never_timeout()
127 | {
128 | return []() -> status { return {}; };
129 | }
130 | } // namespace hal
131 |
--------------------------------------------------------------------------------
/include/libhal/timer.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 |
19 | #include "error.hpp"
20 | #include "functional.hpp"
21 | #include "units.hpp"
22 |
23 | namespace hal {
24 | /**
25 | * @brief Timer hardware abstraction interface.
26 | *
27 | * Use this interface for devices and peripherals that have timer like
28 | * capabilities, such that, when a timer's time has expired, an event,
29 | * interrupt, or signal is generated.
30 | *
31 | * Timer drivers tick period must be an integer multiple of 1 nanosecond,
32 | * meaning that the only tick period allowed are 1ns, 2ns, up to the maximum
33 | * holdable in a std::chrono::nanosecond type. sub-nanosecond tick periods are
34 | * not allowed.
35 | *
36 | */
37 | class timer
38 | {
39 | public:
40 | /**
41 | * @brief Error type indicating that the desired time delay is not achievable
42 | * with this timer.
43 | *
44 | * This occurs if the time delay is too large based on the tick period of the
45 | * timer.
46 | */
47 | struct out_of_bounds_error
48 | {
49 | /// The tick period
50 | std::chrono::nanoseconds tick_period;
51 | /// The maximum possible delay allowed
52 | std::chrono::nanoseconds maximum;
53 | };
54 |
55 | /**
56 | * @brief Feedback after checking if the timer is running.
57 | *
58 | */
59 | struct is_running_t
60 | {
61 | /**
62 | * @brief Determines if the timer is currently running
63 | *
64 | * If this value is false, then the timer is not running.
65 | * If this value is true, then the timer is currently running and a callback
66 | * is scheduled to be executed at some point in the future.
67 | *
68 | */
69 | bool is_running;
70 | };
71 |
72 | /**
73 | * @brief Feedback from cancelling a timer
74 | *
75 | * This structure is currently empty as no feedback has been determined for
76 | * now. This structure may be expanded in the future.
77 | */
78 | struct cancel_t
79 | {};
80 |
81 | /**
82 | * @brief Feedback from scheduling a timer
83 | *
84 | * This structure is currently empty as no feedback has been determined for
85 | * now. This structure may be expanded in the future.
86 | */
87 | struct schedule_t
88 | {};
89 |
90 | /**
91 | * @brief Determine if the timer is currently running
92 | *
93 | * @return result - information about the timer
94 | */
95 | [[nodiscard]] result is_running()
96 | {
97 | return driver_is_running();
98 | }
99 |
100 | /**
101 | * @brief Stops a scheduled event from happening.
102 | *
103 | * Does nothing if the timer is not currently running.
104 | *
105 | * Note that there must be sufficient time between the this call finishing and
106 | * the scheduled event's termination. If this call is too close to when the
107 | * schedule event expires, this function may not complete before the hardware
108 | * calls the callback.
109 | *
110 | * @return result - success or failure
111 | */
112 | [[nodiscard]] result cancel()
113 | {
114 | return driver_cancel();
115 | }
116 |
117 | /**
118 | * @brief Schedule an callback be be executed after the delay time
119 | *
120 | * If this is called and the timer has already scheduled an event (in other
121 | * words, `is_running()` returns true), then the previous scheduled event will
122 | * be canceled and the new scheduled event will be started.
123 | *
124 | * If the delay time result in a tick period of 0, then the timer will execute
125 | * after 1 tick period. For example, if the tick period is 1ms and the
126 | * requested time delay is 500us, then the event will be scheduled for 1ms.
127 | *
128 | * If the tick period is 1ms and the requested time is 2.5ms then the event
129 | * will be scheduled after 2 tick periods or in 2ms.
130 | *
131 | * @param p_callback - callback function to be called when the timer expires
132 | * @param p_delay - the amount of time until the timer expires
133 | * @return result - success or failure
134 | * @throws out_of_bounds_error - if p_interval is greater than what can be
135 | * cannot be achieved
136 | */
137 | [[nodiscard]] result schedule(
138 | hal::callback p_callback,
139 | hal::time_duration p_delay)
140 | {
141 | return driver_schedule(p_callback, p_delay);
142 | }
143 |
144 | virtual ~timer() = default;
145 |
146 | private:
147 | virtual result driver_is_running() = 0;
148 | virtual result driver_cancel() = 0;
149 | virtual result driver_schedule(
150 | hal::callback p_callback,
151 | hal::time_duration p_delay) = 0;
152 | };
153 | } // namespace hal
154 |
--------------------------------------------------------------------------------
/include/libhal/units.hpp:
--------------------------------------------------------------------------------
1 | // Copyright 2023 Google LLC
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | #pragma once
16 |
17 | #include
18 | #include
19 |
20 | #include "error.hpp"
21 |
22 | /**
23 | * @brief The foundation of libhal containing, interfaces, utilities and soft
24 | * drivers.
25 | *
26 | */
27 | namespace hal {
28 | /// The standard time durations in libhal std::chrono::nanoseconds
29 | using time_duration = std::chrono::nanoseconds;
30 |
31 | /// Standard type for bytes in libhal. hal::byte has a number of annoyances that
32 | /// results in more verbose code without much benefit and thus hal::byte was
33 | /// created.
34 | using byte = std::uint8_t;
35 |
36 | /// Type for frequency represented in hertz.
37 | using hertz = float;
38 |
39 | /// Type for acceleration represented in the force applied by gravity at sea
40 | /// level.
41 | using g_force = float;
42 |
43 | /// Type for current represented in amps.
44 | using ampere = float;
45 |
46 | /// Type for voltage represented in volts.
47 | using volts = float;
48 |
49 | /// Type for temperature represented in celsius.
50 | using celsius = float;
51 |
52 | /// Type for rotational velocity represented in RPMs.
53 | using rpm = float;
54 |
55 | /// Type for length represented in meters.
56 | using meters = float;
57 |
58 | /// Type for angle represented in degrees.
59 | using degrees = float;
60 |
61 | /// Type for magnetic field represented in gauss.
62 | using gauss = float;
63 |
64 | /**
65 | * @brief Set of possible pin mode resistor settings.
66 | *
67 | * See each enumeration to get more details about when and how these should be
68 | * used.
69 | *
70 | */
71 | enum class pin_resistor
72 | {
73 | /// No pull up. This will cause the pin to float. This may be desirable if the
74 | /// pin has an external resistor attached or if the signal is sensitive to
75 | /// external devices like resistors.
76 | none = 0,
77 | /// Pull the pin down to devices GND. This will ensure that the voltage read
78 | /// by the pin when there is no signal on the pin is LOW (or false).
79 | pull_down,
80 | /// See pull down explanation, but in this case the pin is pulled up to VCC,
81 | /// also called VDD on some systems.
82 | pull_up,
83 | };
84 |
85 | /**
86 | * @brief Namespace containing user defined literals for the hal standard units
87 | *
88 | */
89 | namespace literals {
90 |
91 | // =============================================================================
92 | // Frequency
93 | // =============================================================================
94 |
95 | [[nodiscard]] consteval hertz operator""_Hz(long double p_value) noexcept
96 | {
97 | return static_cast(p_value);
98 | }
99 |
100 | [[nodiscard]] consteval hertz operator""_kHz(long double p_value) noexcept
101 | {
102 | return static_cast(p_value * std::kilo::num);
103 | }
104 |
105 | [[nodiscard]] consteval hertz operator""_MHz(long double p_value) noexcept
106 | {
107 | return static_cast(p_value * std::mega::num);
108 | }
109 |
110 | [[nodiscard]] consteval hertz operator""_GHz(long double p_value) noexcept
111 | {
112 | return static_cast(p_value * std::giga::num);
113 | }
114 |
115 | // =============================================================================
116 | // G force
117 | // =============================================================================
118 |
119 | [[nodiscard]] consteval g_force operator""_g(long double p_value) noexcept
120 | {
121 | return static_cast(p_value);
122 | }
123 |
124 | // =============================================================================
125 | // Ampere
126 | // =============================================================================
127 |
128 | [[nodiscard]] consteval ampere operator""_kA(long double p_value) noexcept
129 | {
130 | return static_cast(p_value * std::kilo::num);
131 | }
132 |
133 | [[nodiscard]] consteval ampere operator""_A(long double p_value) noexcept
134 | {
135 | return static_cast(p_value);
136 | }
137 |
138 | [[nodiscard]] consteval ampere operator""_mA(long double p_value) noexcept
139 | {
140 | return static_cast(p_value / std::milli::den);
141 | }
142 |
143 | [[nodiscard]] consteval ampere operator""_uA(long double p_value) noexcept
144 | {
145 | return static_cast(p_value / std::micro::den);
146 | }
147 |
148 | // =============================================================================
149 | // Voltage
150 | // =============================================================================
151 |
152 | [[nodiscard]] consteval volts operator""_kV(long double p_value) noexcept
153 | {
154 | return static_cast(p_value * std::kilo::num);
155 | }
156 |
157 | [[nodiscard]] consteval volts operator""_V(long double p_value) noexcept
158 | {
159 | return static_cast(p_value);
160 | }
161 |
162 | [[nodiscard]] consteval volts operator""_mV(long double p_value) noexcept
163 | {
164 | return static_cast(p_value / std::milli::den);
165 | }
166 |
167 | [[nodiscard]] consteval volts operator""_uV(long double p_value) noexcept
168 | {
169 | return static_cast(p_value / std::micro::den);
170 | }
171 |
172 | // =============================================================================
173 | // Temperature
174 | // =============================================================================
175 |
176 | [[nodiscard]] consteval celsius operator""_C(long double p_value) noexcept
177 | {
178 | return static_cast(p_value);
179 | }
180 |
181 | [[nodiscard]] consteval celsius operator""_F(long double p_value) noexcept
182 | {
183 | p_value = (p_value - 32.0L) * (5.0L / 9.0L);
184 | return static_cast(p_value);
185 | }
186 |
187 | [[nodiscard]] consteval celsius operator""_K(long double p_value) noexcept
188 | {
189 | return static_cast(p_value - 273.15L);
190 | }
191 |
192 | // =============================================================================
193 | // Rotational Velocity
194 | // =============================================================================
195 |
196 | [[nodiscard]] consteval rpm operator""_rpm(long double p_value) noexcept
197 | {
198 | return static_cast(p_value);
199 | }
200 |
201 | [[nodiscard]] consteval rpm operator""_deg_per_sec(long double p_value) noexcept
202 | {
203 | return static_cast(p_value / 6.0L);
204 | }
205 |
206 | // =============================================================================
207 | // Angle
208 | // =============================================================================
209 |
210 | [[nodiscard]] consteval degrees operator""_deg(long double p_value) noexcept
211 | {
212 | return static_cast(p_value);
213 | }
214 |
215 | // =============================================================================
216 | // Lengths
217 | // =============================================================================
218 |
219 | [[nodiscard]] consteval meters operator""_um(long double p_value) noexcept
220 | {
221 | return static_cast(p_value / std::micro::den);
222 | }
223 |
224 | [[nodiscard]] consteval meters operator""_mm(long double p_value) noexcept
225 | {
226 | return static_cast(p_value / std::milli::den);
227 | }
228 |
229 | [[nodiscard]] consteval meters operator""_m(long double p_value) noexcept
230 | {
231 | return static_cast(p_value);
232 | }
233 |
234 | [[nodiscard]] consteval meters operator""_km(long double p_value) noexcept
235 | {
236 | return static_cast