committing all this stuff so far
This commit is contained in:
parent
51ec3ce996
commit
d7b041913f
351 changed files with 17304 additions and 0 deletions
1
christmas_carol/.gitignore
vendored
Normal file
1
christmas_carol/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/target
|
||||
7
christmas_carol/Cargo.lock
generated
Normal file
7
christmas_carol/Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "christmas_carol"
|
||||
version = "0.1.0"
|
||||
6
christmas_carol/Cargo.toml
Normal file
6
christmas_carol/Cargo.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "christmas_carol"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
32
christmas_carol/src/main.rs
Normal file
32
christmas_carol/src/main.rs
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
fn main() {
|
||||
let days = [
|
||||
"first", "second", "third", "fourth", "fifth", "sixth",
|
||||
"seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth"
|
||||
];
|
||||
|
||||
let gifts = [
|
||||
"A partridge in a pear tree",
|
||||
"Two turtle doves",
|
||||
"Three French hens",
|
||||
"Four calling birds",
|
||||
"Five golden rings",
|
||||
"Six geese a-laying",
|
||||
"Seven swans a-swimming",
|
||||
"Eight maids a-milking",
|
||||
"Nine ladies dancing",
|
||||
"Ten lords a-leaping",
|
||||
"Eleven pipers piping",
|
||||
"Twelve drummers drumming"
|
||||
];
|
||||
|
||||
for day in 0..12 {
|
||||
println!("On the {} day of Christmas my true love sent to me:", days[day]);
|
||||
for gift in (0..=day).rev() {
|
||||
if day > 0 && gift == 0 {
|
||||
print!("And ");
|
||||
}
|
||||
println!("{}", gifts[gift]);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
}
|
||||
1
discovery-master/.github/CODEOWNERS
vendored
Normal file
1
discovery-master/.github/CODEOWNERS
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
* @rust-embedded/resources
|
||||
166
discovery-master/.github/workflows/ci.yml
vendored
Normal file
166
discovery-master/.github/workflows/ci.yml
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push: # Run CI for all branches except GitHub merge queue tmp branches
|
||||
branches-ignore:
|
||||
- "gh-readonly-queue/**"
|
||||
pull_request: # Run CI for PRs on any branch
|
||||
merge_group: # Run CI for the GitHub merge queue
|
||||
|
||||
jobs:
|
||||
# Check build succeeds for each f3discovery chapter containing example code.
|
||||
build-f3discovery-chapter:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
chapter:
|
||||
- 05-led-roulette
|
||||
- 06-hello-world
|
||||
- 07-registers
|
||||
- 08-leds-again
|
||||
- 09-clocks-and-timers
|
||||
- 11-usart
|
||||
- 14-i2c
|
||||
- 15-led-compass
|
||||
- 16-punch-o-meter
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
target: thumbv7em-none-eabihf
|
||||
- name: Build chapter
|
||||
working-directory: f3discovery/src/${{ matrix.chapter }}
|
||||
run: cargo build --target thumbv7em-none-eabihf
|
||||
- name: Build chapter examples
|
||||
working-directory: f3discovery/src/${{ matrix.chapter }}
|
||||
run: cargo build --target thumbv7em-none-eabihf --examples
|
||||
|
||||
# Check build succeeds for f3discovery docs.
|
||||
build-f3discovery-doc:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
target: thumbv7em-none-eabihf
|
||||
- name: Build docs
|
||||
run: cargo doc --target thumbv7em-none-eabihf
|
||||
working-directory: f3discovery
|
||||
|
||||
# Check a build succeeds for each microbit chapter that contains example code.
|
||||
build-microbit-chapter:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
chapter:
|
||||
- 05-led-roulette
|
||||
- 07-uart
|
||||
- 08-i2c
|
||||
- 09-led-compass
|
||||
- 10-punch-o-meter
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
target: thumbv6m-none-eabi
|
||||
- run: rustup target add thumbv7em-none-eabihf
|
||||
- name: Build chapter micro:bit v1
|
||||
working-directory: microbit/src/${{ matrix.chapter }}
|
||||
run: cargo build --features v1 --target thumbv6m-none-eabi
|
||||
- name: Build chapter micro:bit v2
|
||||
working-directory: microbit/src/${{ matrix.chapter }}
|
||||
run: cargo build --features v2 --target thumbv7em-none-eabihf
|
||||
|
||||
# Check build succeeds for microbit docs.
|
||||
build-microbit-doc:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
target: thumbv6m-none-eabi
|
||||
- run: rustup target add thumbv7em-none-eabihf
|
||||
- name: Build docs for micro:bit v1
|
||||
run: cargo doc --features v1 --target thumbv6m-none-eabi
|
||||
working-directory: microbit
|
||||
- name: Build docs for micro:bit v2
|
||||
run: cargo doc --features v2 --target thumbv7em-none-eabihf
|
||||
working-directory: microbit
|
||||
|
||||
# Build the book HTML itself and optionally publish it.
|
||||
build-book:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
target: thumbv7em-none-eabihf
|
||||
- run: rustup target add thumbv6m-none-eabi
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
pip3 install --user python-dateutil linkchecker
|
||||
- name: Put pip binary directory into path
|
||||
run: echo "~/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Cache Cargo installed binaries
|
||||
uses: actions/cache@v1
|
||||
id: cache-cargo
|
||||
with:
|
||||
path: ~/cargo-bin
|
||||
key: cache-cargo
|
||||
- name: Install mdbook
|
||||
if: steps.cache-cargo.outputs.cache-hit != 'true'
|
||||
uses: actions-rs/install@v0.1
|
||||
with:
|
||||
crate: mdbook
|
||||
version: latest
|
||||
- name: Copy mdbook to cache directory
|
||||
if: steps.cache-cargo.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir ~/cargo-bin
|
||||
cp ~/.cargo/bin/mdbook ~/cargo-bin
|
||||
- name: Put new cargo binary directory into path
|
||||
run: echo "~/cargo-bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Build f3discovery book
|
||||
working-directory: f3discovery
|
||||
run: mkdir target && mdbook build
|
||||
- name: Check microbit links
|
||||
working-directory: f3discovery
|
||||
run: linkchecker --ignore-url "print.html" book
|
||||
|
||||
- name: Build microbit book
|
||||
working-directory: microbit
|
||||
run: mkdir target && mdbook build
|
||||
- name: Check microbit links
|
||||
working-directory: microbit
|
||||
run: linkchecker --ignore-url "print.html" book
|
||||
|
||||
- name: Build front page
|
||||
run: mdbook build
|
||||
- name: Check links
|
||||
run: linkchecker book
|
||||
|
||||
- name: Collect books
|
||||
run: |
|
||||
mv f3discovery/book book/f3discovery
|
||||
mv microbit/book book/microbit
|
||||
|
||||
- name: Deploy book
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: book
|
||||
force_orphan: true
|
||||
8
discovery-master/.gitignore
vendored
Normal file
8
discovery-master/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
*.org
|
||||
.#*
|
||||
.gdb_history
|
||||
/template
|
||||
Cargo.lock
|
||||
book/
|
||||
target/
|
||||
/.idea
|
||||
3
discovery-master/.vscode/settings.json
vendored
Normal file
3
discovery-master/.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"rust-analyzer.cargo.features": "all"
|
||||
}
|
||||
37
discovery-master/CODE_OF_CONDUCT.md
Normal file
37
discovery-master/CODE_OF_CONDUCT.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# The Rust Code of Conduct
|
||||
|
||||
## Conduct
|
||||
|
||||
**Contact**: [Resources team][team]
|
||||
|
||||
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
|
||||
* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all.
|
||||
* Please be kind and courteous. There's no need to be mean or rude.
|
||||
* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer.
|
||||
* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works.
|
||||
* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the [Citizen Code of Conduct](http://citizencodeofconduct.org/); if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups.
|
||||
* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Resources team][team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back.
|
||||
* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome.
|
||||
|
||||
## Moderation
|
||||
|
||||
These are the policies for upholding our community's standards of conduct.
|
||||
|
||||
1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.)
|
||||
2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed.
|
||||
3. Moderators will first respond to such remarks with a warning.
|
||||
4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off.
|
||||
5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded.
|
||||
6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology.
|
||||
7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed.
|
||||
8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others.
|
||||
|
||||
In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely.
|
||||
|
||||
And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust.
|
||||
|
||||
The enforcement policies listed above apply to all official embedded WG venues; including official IRC channels (#rust-embedded); GitHub repositories under rust-embedded; and all forums under rust-embedded.org (forum.rust-embedded.org).
|
||||
|
||||
*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).*
|
||||
|
||||
[team]: https://github.com/rust-embedded/wg#the-resources-team
|
||||
201
discovery-master/LICENSE-APACHE
Normal file
201
discovery-master/LICENSE-APACHE
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
395
discovery-master/LICENSE-CC-BY
Normal file
395
discovery-master/LICENSE-CC-BY
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
Attribution 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More_considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution 4.0 International Public License ("Public License"). To the
|
||||
extent this Public License may be interpreted as a contract, You are
|
||||
granted the Licensed Rights in consideration of Your acceptance of
|
||||
these terms and conditions, and the Licensor grants You such rights in
|
||||
consideration of benefits the Licensor receives from making the
|
||||
Licensed Material available under these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
d. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
f. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
g. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
h. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
4. If You Share Adapted Material You produce, the Adapter's
|
||||
License You apply must not prevent recipients of the Adapted
|
||||
Material from complying with this Public License.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
||||
25
discovery-master/LICENSE-MIT
Normal file
25
discovery-master/LICENSE-MIT
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
Copyright (c) 2016 Jorge Aparicio
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
61
discovery-master/README.md
Normal file
61
discovery-master/README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
# `Discovery`
|
||||
|
||||
Discover the world of microcontrollers through [Rust](https://www.rust-lang.org/)!
|
||||
|
||||
There are currently two versions of this book. Both of them provide an
|
||||
introduction to microcontrollers and how to use Rust with them.
|
||||
The first is older and uses an F3 Discovery circuit board, while
|
||||
the second is newer and uses a micro:bit circuit board instead.
|
||||
|
||||
- Read the newer book, using a micro:bit:
|
||||
https://docs.rust-embedded.org/discovery/microbit
|
||||
- Read the older book, using an F3 discovery board:
|
||||
https://docs.rust-embedded.org/discovery/f3discovery
|
||||
- Start working on the examples from this repository
|
||||
- You've got questions?
|
||||
- Have a look at our [discussions section on
|
||||
GitHub](https://github.com/rust-embedded/discovery/discussions)
|
||||
- Maybe it has already been answered
|
||||
- If not, start a new discussion
|
||||
- You've found an issue?
|
||||
- Have a look at our [issues on
|
||||
GitHub](https://github.com/rust-embedded/discovery/issues)
|
||||
- Maybe there is already a workaround
|
||||
- If not, please open a new one - or even better - a [pull
|
||||
request](https://github.com/rust-embedded/discovery/pulls) for solving
|
||||
it
|
||||
- Have fun and enjoy!
|
||||
|
||||
This project is developed and maintained by the [Resources team][team].
|
||||
|
||||
## License
|
||||
|
||||
The documentation is licensed under
|
||||
|
||||
- Creative Commons Attribution 4.0 License ([LICENSE-CC-BY](LICENSE-CC-BY)
|
||||
or https://creativecommons.org/licenses/by/4.0/legalcode)
|
||||
|
||||
And the source code is licensed under either of
|
||||
|
||||
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
|
||||
- MIT License ([LICENSE-MIT](LICENSE-MIT) or
|
||||
https://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
||||
licensed as above, without any additional terms or conditions.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Contribution to this crate is organized under the terms of the [Rust Code of
|
||||
Conduct][CoC], the maintainer of this crate, the [Resources team][team], promises
|
||||
to intervene to uphold that code of conduct.
|
||||
|
||||
[CoC]: CODE_OF_CONDUCT.md
|
||||
[team]: https://github.com/rust-embedded/wg#the-resources-team
|
||||
17
discovery-master/f3discovery/Cargo.toml
Normal file
17
discovery-master/f3discovery/Cargo.toml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"src/05-led-roulette",
|
||||
"src/06-hello-world",
|
||||
"src/07-registers",
|
||||
"src/08-leds-again",
|
||||
"src/09-clocks-and-timers",
|
||||
"src/11-usart",
|
||||
"src/14-i2c",
|
||||
"src/15-led-compass",
|
||||
"src/16-punch-o-meter",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
debug = true
|
||||
lto = true
|
||||
9
discovery-master/f3discovery/book.toml
Normal file
9
discovery-master/f3discovery/book.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[book]
|
||||
title = "Discovery"
|
||||
description = "Discover the world of microcontrollers through Rust"
|
||||
author = "Rust Embedded Resources Team"
|
||||
language = "en"
|
||||
|
||||
[output.html]
|
||||
additional-css = ["custom.css"]
|
||||
git-repository-url = "https://github.com/rust-embedded/discovery/"
|
||||
6
discovery-master/f3discovery/custom.css
Normal file
6
discovery-master/f3discovery/custom.css
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
/* Add this style to the image if it's unreadable
|
||||
when the dark theme is applied */
|
||||
img.white_bg {
|
||||
background-color: white;
|
||||
padding: 1em;
|
||||
}
|
||||
15
discovery-master/f3discovery/src/.cargo/config.toml
Normal file
15
discovery-master/f3discovery/src/.cargo/config.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# default runner starts a GDB sesssion, which requires OpenOCD to be
|
||||
# running, e.g.,
|
||||
## openocd -f interface/stlink.cfg -f target/stm32f3x.cfg
|
||||
# depending on your local GDB, pick one of the following
|
||||
[target.thumbv7em-none-eabihf]
|
||||
runner = "arm-none-eabi-gdb -q -x ../openocd.gdb"
|
||||
# runner = "gdb-multiarch -q -x ../openocd.gdb"
|
||||
# runner = "gdb -q -x ../openocd.gdb"
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
]
|
||||
|
||||
[build]
|
||||
target = "thumbv7em-none-eabihf"
|
||||
|
||||
66
discovery-master/f3discovery/src/01-background/README.md
Normal file
66
discovery-master/f3discovery/src/01-background/README.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# Background
|
||||
|
||||
## What's a microcontroller?
|
||||
|
||||
A microcontroller is a *system* on a chip. Whereas your computer is made up of several discrete
|
||||
components: a processor, RAM sticks, a hard drive, an ethernet port, etc.; a microcontroller
|
||||
has all those components built into a single "chip" or package. This makes it possible to
|
||||
build systems with a minimal part count.
|
||||
|
||||
## What can you do with a microcontroller?
|
||||
|
||||
Lots of things! Microcontrollers are the central part of systems known as *embedded* systems.
|
||||
These systems are everywhere but you don't usually notice them. These systems control the brakes
|
||||
of your car, wash your clothes, print your documents, keep you warm, keep you cool, optimize the
|
||||
fuel consumption of your car, etc.
|
||||
|
||||
The main trait of these systems is that they operate without user intervention even if they expose a
|
||||
user interface as a washing machine does; most of their operation is done on their own.
|
||||
|
||||
The other common trait of these systems is that they *control* a process. And for that these systems
|
||||
usually have one or more sensors and one or more actuators. For example, an HVAC system has several
|
||||
sensors, thermometers, and humidity sensors spread across some areas, and several actuators as well,
|
||||
heating elements and fans connected to ducts.
|
||||
|
||||
## When should I use a microcontroller?
|
||||
|
||||
All these applications I've mentioned, you can probably implement with a Raspberry Pi, a computer
|
||||
that runs Linux. Why should I bother with a microcontroller that operates without an OS? Sounds
|
||||
like it would be harder to develop a program.
|
||||
|
||||
The main reason is cost. A microcontroller is much cheaper than a general-purpose computer. Not only
|
||||
the microcontroller is cheaper; it also requires many fewer external electrical components to operate.
|
||||
This makes Printed Circuit Boards (PCBs) smaller and cheaper to design and manufacture.
|
||||
|
||||
The other big reason is power consumption. A microcontroller consumes orders of magnitude less power
|
||||
than a full-blown processor. If your application will run on batteries that makes a huge difference.
|
||||
|
||||
And last but not least: (hard) *real-time* constraints. Some processes require their controllers to
|
||||
respond to some events within some time interval (e.g. a quadcopter/drone hit by a wind gust). If this
|
||||
*deadline* is not met, the process could end in catastrophic failure (e.g. the drone crashes to the
|
||||
ground). A general-purpose computer running a general-purpose OS has many services running in the
|
||||
background. This makes it hard to guarantee the execution of a program within tight time constraints.
|
||||
|
||||
## When should I *not* use a microcontroller?
|
||||
|
||||
Where heavy computations are involved. To keep their power consumption low, microcontrollers have very
|
||||
limited computational resources available to them. For example, some microcontrollers don't even have
|
||||
hardware support for floating-point operations. On those devices, performing a simple addition of
|
||||
single-precision numbers can take hundreds of CPU cycles.
|
||||
|
||||
## Why use Rust and not C?
|
||||
|
||||
Hopefully, I don't need to convince you here as you are probably familiar with the language
|
||||
differences between Rust and C. One point I do want to bring up is package management. C lacks an
|
||||
official, widely accepted package management solution whereas Rust has Cargo. This makes development
|
||||
*much* easier. And, IMO, easy package management encourages code reuse because libraries can be
|
||||
easily integrated into an application which is also a good thing as libraries get more "battle testing".
|
||||
|
||||
## Why should I not use Rust?
|
||||
|
||||
Or why should I prefer C over Rust?
|
||||
|
||||
The C ecosystem is way more mature. Off the shelf solution for several problems already exist. If you
|
||||
need to control a time-sensitive process, you can grab one of the existing commercial Real-Time Operating
|
||||
Systems (RTOS) out there and solve your problem. There are no commercial, production-grade RTOSes in Rust
|
||||
yet so you would have to either create one yourself or try one of the ones that are in development.
|
||||
123
discovery-master/f3discovery/src/02-requirements/README.md
Normal file
123
discovery-master/f3discovery/src/02-requirements/README.md
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
# Hardware/knowledge requirements
|
||||
|
||||
The primary knowledge requirement to read this book is to know *some* Rust. It's
|
||||
hard for me to quantify *some* but at least I can tell you that you don't need
|
||||
to fully grok generics but you do need to know how to *use* closures. You also
|
||||
need to be familiar with the idioms of the [2018 edition], in particular with
|
||||
the fact that `extern crate` is not necessary in the 2018 edition.
|
||||
|
||||
[2018 edition]: https://rust-lang-nursery.github.io/edition-guide/
|
||||
|
||||
Due to the nature of embedded programming, it will also be extremely helpful to
|
||||
understand how binary and hexadecimal representations of values work, as well
|
||||
as the use of some bitwise operators. For example, it would be useful to
|
||||
understand how the following program produces its output.
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let a = 0x4000_0000 + 0xa2;
|
||||
|
||||
// Use of the bit shift "<<" operation.
|
||||
let b = 1 << 5;
|
||||
|
||||
// {:X} will format values as hexadecimal
|
||||
println!("{:X}: {:X}", a, b);
|
||||
}
|
||||
```
|
||||
|
||||
Also, to follow this material you'll need the following hardware:
|
||||
|
||||
(Some components are optional but recommended)
|
||||
|
||||
- A [STM32F3DISCOVERY] board.
|
||||
|
||||
[STM32F3DISCOVERY]: http://www.st.com/en/evaluation-tools/stm32f3discovery.html
|
||||
|
||||
(You can purchase this board from "big" [electronics][0] [suppliers][1] or from [e-commerce][2]
|
||||
[sites][3])
|
||||
|
||||
[0]: http://www.mouser.com/ProductDetail/STMicroelectronics/STM32F3DISCOVERY
|
||||
[1]: http://www.digikey.com/product-detail/en/stmicroelectronics/STM32F3DISCOVERY/497-13192-ND
|
||||
[2]: https://www.aliexpress.com/wholesale?SearchText=stm32f3discovery
|
||||
[3]: http://www.ebay.com/sch/i.html?_nkw=stm32f3discovery
|
||||
|
||||
<p align="center">
|
||||
<img title="STM32F3DISCOVERY" src="../assets/f3.jpg">
|
||||
</p>
|
||||
|
||||
- OPTIONAL. A **3.3V** USB <-> Serial module. To elaborate: if you have one of
|
||||
the latest revisions of the discovery board (which is usually the case given
|
||||
the first revision was released years ago) then you do *not* need this module
|
||||
because the board includes this functionality on-board. If you have an older
|
||||
revision of the board then you'll need this module for chapters 10 and 11. For
|
||||
completeness, we'll include instructions for using a Serial module. The book
|
||||
will use [this particular model][sparkfun] but you can use any other model as
|
||||
long as it operates at 3.3V. The CH340G module, which you can buy
|
||||
from [e-commerce][4] sites works too and it's probably cheaper for you to get.
|
||||
|
||||
[sparkfun]: https://www.sparkfun.com/products/9873
|
||||
[4]: https://www.aliexpress.com/wholesale?SearchText=CH340G
|
||||
|
||||
<p align="center">
|
||||
<img title="A 3.3v USB <-> Serial module" src="../assets/serial.jpg">
|
||||
</p>
|
||||
|
||||
- OPTIONAL. A HC-05 Bluetooth module (with headers!). A HC-06 would work too.
|
||||
|
||||
(As with other Chinese parts, you pretty much can only find these on [e-commerce][5] [sites][6].
|
||||
(US) Electronics suppliers don't usually stock these for some reason)
|
||||
|
||||
[5]: http://www.ebay.com/sch/i.html?_nkw=hc-05
|
||||
[6]: https://www.aliexpress.com/wholesale?SearchText=hc-05
|
||||
|
||||
<p align="center">
|
||||
<img title="The HC-05 Bluetooth module" src="../assets/bluetooth.jpg">
|
||||
</p>
|
||||
|
||||
- Two mini-B USB cables. One is required to make the STM32F3DISCOVERY board work. The other is only
|
||||
required if you have the Serial <-> USB module. Make sure that the cables both
|
||||
support data transfer as some cables only support charging devices.
|
||||
|
||||
<p align="center">
|
||||
<img title="mini-B USB cable" src="../assets/usb-cable.jpg">
|
||||
</p>
|
||||
|
||||
> **NOTE** These are **not** the USB cables that ship with pretty much every Android phone; those
|
||||
> are *micro* USB cables. Make sure you have the right thing!
|
||||
|
||||
- MOSTLY OPTIONAL. 5 female to female, 4 male to female and 1 Male to Male *jumper* (AKA Dupont)
|
||||
wires. You'll *very likely* need one female to female to get ITM working. The other wires are only
|
||||
needed if you'll be using the USB <-> Serial and Bluetooth modules.
|
||||
|
||||
(You can get these from electronics [suppliers][7] or from [e-commerce][8] [sites][9])
|
||||
|
||||
[7]: https://www.adafruit.com/categories/306
|
||||
[8]: http://www.ebay.com/sch/i.html?_nkw=dupont+wire
|
||||
[9]: https://www.aliexpress.com/wholesale?SearchText=dupont+wire
|
||||
|
||||
<p align="center">
|
||||
<img title="Jumper wires" src="../assets/jumper-wires.jpg">
|
||||
</p>
|
||||
|
||||
> **FAQ**: Wait, why do I need this specific hardware?
|
||||
|
||||
It makes my life and yours much easier.
|
||||
|
||||
The material is much, much more approachable if we don't have to worry about hardware differences.
|
||||
Trust me on this one.
|
||||
|
||||
> **FAQ**: Can I follow this material with a different development board?
|
||||
|
||||
Maybe? It depends mainly on two things: your previous experience with microcontrollers and/or
|
||||
whether there already exists a high level crate, like the [`f3`], for your development board
|
||||
somewhere.
|
||||
|
||||
[`f3`]: https://docs.rs/f3
|
||||
|
||||
With a different development board, this text would lose most if not all its beginner friendliness
|
||||
and "easy to follow"-ness, IMO.
|
||||
|
||||
If you have a different development board and you don't consider yourself a total beginner, you are
|
||||
better off starting with the [quickstart] project template.
|
||||
|
||||
[quickstart]: https://rust-embedded.github.io/cortex-m-quickstart/cortex_m_quickstart/
|
||||
160
discovery-master/f3discovery/src/03-setup/README.md
Normal file
160
discovery-master/f3discovery/src/03-setup/README.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# Setting up a development environment
|
||||
|
||||
Dealing with microcontrollers involves several tools as we'll be dealing with an architecture
|
||||
different than your computer's and we'll have to run and debug programs on a "remote" device.
|
||||
|
||||
## Documentation
|
||||
|
||||
Tooling is not everything though. Without documentation it is pretty much impossible to work with
|
||||
microcontrollers.
|
||||
|
||||
We'll be referring to all these documents throughout this book:
|
||||
|
||||
*HEADS UP* All these links point to PDF files and some of them are hundreds of pages long and
|
||||
several MBs in size.
|
||||
|
||||
- [STM32F3DISCOVERY User Manual][um]
|
||||
- [STM32F303VC Datasheet][ds]
|
||||
- [STM32F303VC Reference Manual][rm]
|
||||
- [LSM303DLHC] \*
|
||||
- [L3GD20] \*
|
||||
|
||||
[L3GD20]: https://www.st.com/content/ccc/resource/technical/document/application_note/2c/d9/a7/f8/43/48/48/64/DM00119036.pdf/files/DM00119036.pdf/jcr:content/translations/en.DM00119036.pdf
|
||||
[LSM303DLHC]: http://www.st.com/resource/en/datasheet/lsm303dlhc.pdf
|
||||
[ds]: http://www.st.com/resource/en/datasheet/stm32f303vc.pdf
|
||||
[rm]: http://www.st.com/resource/en/reference_manual/dm00043574.pdf
|
||||
[um]: http://www.st.com/resource/en/user_manual/dm00063382.pdf
|
||||
|
||||
\* **NOTE**: Newer (from around 2020/09) Discovery boards may have a different e-compass and gyroscope (see the user manual).
|
||||
As such, much in chapters 14-16 will not work as is.
|
||||
Checkout the github issues like [this][gh-issue-274].
|
||||
|
||||
[gh-issue-274]: https://github.com/rust-embedded/discovery/issues/274
|
||||
|
||||
## Tools
|
||||
|
||||
We'll use all the tools listed below. Where a minimum version is not specified, any recent version
|
||||
should work but we have listed the version we have tested.
|
||||
|
||||
- Rust 1.31 or a newer toolchain. Chapter [USART](../11-usart/index.html)
|
||||
requires 1.51 or newer.
|
||||
|
||||
- [`itmdump`] >=0.3.1 (`cargo install itm`). Tested versions: 0.3.1.
|
||||
|
||||
- OpenOCD >=0.8. Tested versions: v0.9.0 and v0.10.0
|
||||
|
||||
- `arm-none-eabi-gdb`. Version 7.12 or newer highly recommended. Tested versions: 7.10, 7.11,
|
||||
7.12 and 8.1
|
||||
|
||||
- [`cargo-binutils`]. Version 0.1.4 or newer.
|
||||
|
||||
[`cargo-binutils`]: https://github.com/rust-embedded/cargo-binutils
|
||||
|
||||
- `minicom` on Linux and macOS. Tested version: 2.7. Readers report that `picocom` also works but
|
||||
we'll use `minicom` in this text.
|
||||
|
||||
- `PuTTY` on Windows.
|
||||
|
||||
[`itmdump`]: https://crates.io/crates/itm
|
||||
|
||||
If your computer has Bluetooth functionality and you have the Bluetooth module, you can additionally
|
||||
install these tools to play with the Bluetooth module. All these are optional:
|
||||
|
||||
- Linux, only if you don't have a Bluetooth manager application like Blueman.
|
||||
- `bluez`
|
||||
- `hcitool`
|
||||
- `rfcomm`
|
||||
- `rfkill`
|
||||
|
||||
macOS / OSX / Windows users only need the default bluetooth manager that ships with their OS.
|
||||
|
||||
Next, follow OS-agnostic installation instructions for a few of the tools:
|
||||
|
||||
### `rustc` & Cargo
|
||||
|
||||
Install rustup by following the instructions at [https://rustup.rs](https://rustup.rs).
|
||||
|
||||
If you already have rustup installed double check that you are on the stable
|
||||
channel and your stable toolchain is up to date. `rustc -V` should return a date
|
||||
newer than the one shown below:
|
||||
|
||||
``` console
|
||||
$ rustc -V
|
||||
rustc 1.31.0 (abe02cefd 2018-12-04)
|
||||
```
|
||||
|
||||
### `itmdump`
|
||||
|
||||
|
||||
``` console
|
||||
cargo install itm
|
||||
```
|
||||
|
||||
Verify the version is >=0.3.1
|
||||
```
|
||||
$ itmdump -V
|
||||
itmdump 0.3.1
|
||||
```
|
||||
|
||||
### `cargo-binutils`
|
||||
|
||||
Install `llvm-tools`
|
||||
|
||||
``` console
|
||||
rustup component add llvm-tools
|
||||
```
|
||||
|
||||
Install `cargo-binutils`
|
||||
```
|
||||
cargo install cargo-binutils
|
||||
```
|
||||
|
||||
#### Verify tools are installed
|
||||
|
||||
Run the following commands at your terminal
|
||||
``` console
|
||||
cargo new test-size
|
||||
```
|
||||
```
|
||||
cd test-size
|
||||
```
|
||||
```
|
||||
cargo run
|
||||
```
|
||||
```
|
||||
cargo size -- --version
|
||||
```
|
||||
|
||||
The results should be something like:
|
||||
```
|
||||
~
|
||||
$ cargo new test-size
|
||||
Created binary (application) `test-size` package
|
||||
|
||||
~
|
||||
$ cd test-size
|
||||
|
||||
~/test-size (main)
|
||||
$ cargo run
|
||||
Compiling test-size v0.1.0 (~/test-size)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.26s
|
||||
Running `target/debug/test-size`
|
||||
Hello, world!
|
||||
|
||||
~/test-size (main)
|
||||
$ cargo size -- --version
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
|
||||
LLVM (http://llvm.org/):
|
||||
LLVM version 11.0.0-rust-1.50.0-stable
|
||||
Optimized build.
|
||||
Default target: x86_64-unknown-linux-gnu
|
||||
Host CPU: znver2
|
||||
```
|
||||
|
||||
### OS specific instructions
|
||||
|
||||
Now follow the instructions specific to the OS you are using:
|
||||
|
||||
- [Linux](linux.md)
|
||||
- [Windows](windows.md)
|
||||
- [macOS](macos.md)
|
||||
169
discovery-master/f3discovery/src/03-setup/linux.md
Normal file
169
discovery-master/f3discovery/src/03-setup/linux.md
Normal file
|
|
@ -0,0 +1,169 @@
|
|||
# Linux
|
||||
|
||||
Here are the installation commands for a few Linux distributions.
|
||||
|
||||
## REQUIRED packages
|
||||
|
||||
### Ubuntu 18.04 or newer / Debian stretch or newer
|
||||
|
||||
> **NOTE** `gdb-multiarch` is the GDB command you'll use to debug your ARM
|
||||
> Cortex-M programs
|
||||
|
||||
<!-- Debian stretch -->
|
||||
<!-- GDB 7.12 -->
|
||||
<!-- OpenOCD 0.9.0 -->
|
||||
|
||||
<!-- Ubuntu 18.04 -->
|
||||
<!-- GDB 8.1 -->
|
||||
<!-- OpenOCD 0.10.0 -->
|
||||
|
||||
``` console
|
||||
sudo apt-get install \
|
||||
gdb-multiarch \
|
||||
minicom \
|
||||
openocd
|
||||
```
|
||||
|
||||
### Ubuntu 14.04 and 16.04
|
||||
|
||||
> **NOTE** `arm-none-eabi-gdb` is the GDB command you'll use to debug your ARM
|
||||
> Cortex-M programs
|
||||
|
||||
<!-- Ubuntu 14.04 -->
|
||||
<!-- GDB 7.6 -->
|
||||
<!-- OpenOCD 0.7.0 -->
|
||||
|
||||
``` console
|
||||
sudo apt-get install \
|
||||
gdb-arm-none-eabi \
|
||||
minicom \
|
||||
openocd
|
||||
```
|
||||
|
||||
### Fedora 23 or newer
|
||||
|
||||
``` console
|
||||
sudo dnf install \
|
||||
minicom \
|
||||
openocd \
|
||||
gdb
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
> **NOTE** `arm-none-eabi-gdb` is the GDB command you'll use to debug your ARM
|
||||
> Cortex-M programs
|
||||
|
||||
``` console
|
||||
sudo pacman -S \
|
||||
arm-none-eabi-gdb \
|
||||
minicom \
|
||||
openocd
|
||||
```
|
||||
|
||||
### Other distros
|
||||
|
||||
> **NOTE** `arm-none-eabi-gdb` is the GDB command you'll use to debug your ARM
|
||||
> Cortex-M programs
|
||||
|
||||
For distros that don't have packages for [ARM's pre-built
|
||||
toolchain](https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads),
|
||||
download the "Linux 64-bit" file and put its `bin` directory on your path.
|
||||
Here's one way to do it:
|
||||
|
||||
``` console
|
||||
mkdir -p ~/local && cd ~/local
|
||||
```
|
||||
``` console
|
||||
tar xjf /path/to/downloaded/file/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux.tar.bz2
|
||||
```
|
||||
|
||||
Then, use your editor of choice to append to your `PATH` in the appropriate
|
||||
shell init file (e.g. `~/.zshrc` or `~/.bashrc`):
|
||||
|
||||
```
|
||||
PATH=$PATH:$HOME/local/gcc-arm-none-eabi-10-2020-q4-major-x86_64-linux/bin
|
||||
```
|
||||
|
||||
## Optional packages
|
||||
|
||||
### Ubuntu / Debian
|
||||
|
||||
``` console
|
||||
sudo apt-get install \
|
||||
bluez \
|
||||
rfkill
|
||||
```
|
||||
|
||||
### Fedora
|
||||
|
||||
``` console
|
||||
sudo dnf install \
|
||||
bluez \
|
||||
rfkill
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
``` console
|
||||
sudo pacman -S \
|
||||
bluez \
|
||||
bluez-utils \
|
||||
rfkill
|
||||
```
|
||||
|
||||
## udev rules
|
||||
|
||||
These rules let you use USB devices like the F3 and the Serial module without root privilege, i.e.
|
||||
`sudo`.
|
||||
|
||||
Create `99-openocd.rules` in `/etc/udev/rules.d` using the `idVendor` and `idProduct`
|
||||
from the `lsusb` output.
|
||||
|
||||
For example, connect the STM32F3DISCOVERY to your computer using a USB cable.
|
||||
Be sure to connect the cable to the "USB ST-LINK" port, the USB port in the
|
||||
center of the edge of the board.
|
||||
|
||||
Execute `lsusb`:
|
||||
``` console
|
||||
lsusb | grep ST-LINK
|
||||
```
|
||||
It should result in something like:
|
||||
```
|
||||
$ lsusb | grep ST-LINK
|
||||
Bus 003 Device 003: ID 0483:374b STMicroelectronics ST-LINK/V2.1
|
||||
```
|
||||
So the `idVendor` is `0483` and `idProduct` is `374b`.
|
||||
|
||||
### Create `/etc/udev/rules.d/99-openocd.rules`:
|
||||
``` console
|
||||
sudo vi /etc/udev/rules.d/99-openocd.rules
|
||||
```
|
||||
With the contents:
|
||||
``` text
|
||||
# STM32F3DISCOVERY - ST-LINK/V2.1
|
||||
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="374b", MODE:="0666"
|
||||
```
|
||||
#### For older devices with OPTIONAL USB <-> FT232 based Serial Module
|
||||
|
||||
Create `/etc/udev/rules.d/99-ftdi.rules`:
|
||||
``` console
|
||||
sudo vi /etc/udev/rules.d/99-openocd.rules
|
||||
```
|
||||
With the contents:
|
||||
``` text
|
||||
# FT232 - USB <-> Serial Converter
|
||||
ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE:="0666"
|
||||
```
|
||||
|
||||
### Reload the udev rules with:
|
||||
|
||||
``` console
|
||||
sudo udevadm control --reload-rules
|
||||
```
|
||||
|
||||
If you had any board plugged to your computer, unplug them and then plug them in again.
|
||||
|
||||
Now, go to the [next section].
|
||||
|
||||
[next section]: verify.md
|
||||
17
discovery-master/f3discovery/src/03-setup/macos.md
Normal file
17
discovery-master/f3discovery/src/03-setup/macos.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# macOS
|
||||
|
||||
All the tools can be installed using [Homebrew]:
|
||||
|
||||
[Homebrew]: http://brew.sh/
|
||||
|
||||
``` console
|
||||
$ # ARM GCC debugger
|
||||
$ brew install arm-none-eabi-gdb
|
||||
|
||||
$ # Minicom and OpenOCD
|
||||
$ brew install minicom openocd
|
||||
```
|
||||
|
||||
That's all! Go to the [next section].
|
||||
|
||||
[next section]: verify.md
|
||||
141
discovery-master/f3discovery/src/03-setup/verify.md
Normal file
141
discovery-master/f3discovery/src/03-setup/verify.md
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
# Verify the installation
|
||||
|
||||
Let's verify that all the tools were installed correctly.
|
||||
|
||||
## Linux only
|
||||
|
||||
### Verify permissions
|
||||
|
||||
Connect the STM32F3DISCOVERY to your computer using an USB cable. Be sure to connect the cable to the "USB ST-LINK"
|
||||
port, the USB port in the center of the edge of the board.
|
||||
|
||||
The STM32F3DISCOVERY should now appear as a USB device (file) in `/dev/bus/usb`. Let's find out how it got
|
||||
enumerated:
|
||||
|
||||
``` console
|
||||
lsusb | grep -i stm
|
||||
```
|
||||
This should result in:
|
||||
``` console
|
||||
$ lsusb | grep -i stm
|
||||
Bus 003 Device 004: ID 0483:374b STMicroelectronics ST-LINK/V2.1
|
||||
$ # ^^^ ^^^
|
||||
```
|
||||
|
||||
In my case, the STM32F3DISCOVERY got connected to the bus #3 and got enumerated as the device #4. This means the
|
||||
file `/dev/bus/usb/003/004` *is* the STM32F3DISCOVERY. Let's check its permissions:
|
||||
``` console
|
||||
$ ls -la /dev/bus/usb/003/004
|
||||
crw-rw-rw-+ 1 root root 189, 259 Feb 28 13:32 /dev/bus/usb/003/00
|
||||
```
|
||||
|
||||
The permissions should be `crw-rw-rw-`. If it's not ... then check your [udev
|
||||
rules] and try re-loading them with:
|
||||
|
||||
[udev rules]: linux.md#udev-rules
|
||||
|
||||
``` console
|
||||
sudo udevadm control --reload-rules
|
||||
```
|
||||
|
||||
#### For older devices with OPTIONAL USB <-> FT232 based Serial Module
|
||||
|
||||
Unplug the STM32F3DISCOVERY and plug the Serial module. Now, figure out what's its associated file:
|
||||
|
||||
``` console
|
||||
$ lsusb | grep -i ft232
|
||||
Bus 003 Device 005: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
|
||||
```
|
||||
|
||||
In my case, it's the `/dev/bus/usb/003/005`. Now, check its permissions:
|
||||
|
||||
``` console
|
||||
$ ls -l /dev/bus/usb/003/005
|
||||
crw-rw-rw- 1 root root 189, 21 Sep 13 00:00 /dev/bus/usb/003/005
|
||||
```
|
||||
|
||||
As before, the permissions should be `crw-rw-rw-`.
|
||||
|
||||
## Verify OpenOCD connection
|
||||
|
||||
Connect the STM32F3DISCOVERY using the USB cable to the USB port in the
|
||||
center of edge of the board, the one that's labeled "USB ST-LINK".
|
||||
|
||||
Two *red* LEDs should turn on right after connecting the USB cable to the board.
|
||||
|
||||
> **IMPORTANT** There is more than one hardware revision of the STM32F3DISCOVERY board. For older
|
||||
> revisions, you'll need to change the "interface" argument to `-f interface/stlink-v2.cfg` (note:
|
||||
> no `-1` at the end). Alternatively, older revisions can use `-f board/stm32f3discovery.cfg`
|
||||
> instead of `-f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg`.
|
||||
|
||||
> **NOTE** OpenOCD v0.11.0 has deprecated `interface/stlink-v2.cfg` in favor of
|
||||
> `interface/stlink.cfg` which supports ST-LINK/V1, ST-LINK/V2, ST-LINK/V2-1, and
|
||||
> ST-LINK/V3.
|
||||
|
||||
### *Nix
|
||||
|
||||
> **FYI:** The `interface` directory is typically located in `/usr/share/openocd/scripts/`,
|
||||
> which is the default location OpenOCD expects these files. If you've installed them
|
||||
> somewhere else use the `-s /path/to/scripts/` option to specify your install directory.
|
||||
|
||||
``` console
|
||||
openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
``` console
|
||||
openocd -f interface/stlink.cfg -f target/stm32f3x.cfg
|
||||
```
|
||||
|
||||
|
||||
### Windows
|
||||
|
||||
Below the references to `C:\OpenOCD` is the directory where OpenOCD is installed.
|
||||
|
||||
``` console
|
||||
openocd -s C:\OpenOCD\share\scripts -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
|
||||
```
|
||||
|
||||
> **NOTE** cygwin users have reported problems with the -s flag. If you run into
|
||||
> that problem you can add `C:\OpenOCD\share\scripts\` directory to the parameters.
|
||||
|
||||
cygwin users:
|
||||
``` console
|
||||
openocd -f C:\OpenOCD\share\scripts\interface\stlink-v2-1.cfg -f C:\OpenOCD\share\scripts\target\stm32f3x.cfg
|
||||
```
|
||||
|
||||
### All
|
||||
|
||||
OpenOCD is a service which forwards debug information from the ITM channel
|
||||
to a file, `itm.txt`, as such it runs forever and does **not** return to the
|
||||
terminal prompt.
|
||||
|
||||
The initial output of OpenOCD is something like:
|
||||
``` console
|
||||
Open On-Chip Debugger 0.10.0
|
||||
Licensed under GNU GPL v2
|
||||
For bug reports, read
|
||||
http://openocd.org/doc/doxygen/bugs.html
|
||||
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
|
||||
adapter speed: 1000 kHz
|
||||
adapter_nsrst_delay: 100
|
||||
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
|
||||
none separate
|
||||
Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Info : clock speed 950 kHz
|
||||
Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B
|
||||
Info : using stlink api v2
|
||||
Info : Target voltage: 2.915608
|
||||
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
|
||||
```
|
||||
|
||||
(If you don't ... then check the [general troubleshooting] instructions.)
|
||||
|
||||
[general troubleshooting]: ../appendix/1-general-troubleshooting/index.html
|
||||
|
||||
Also, one of the red LEDs, the one closest to the USB port, should start oscillating between red
|
||||
light and green light.
|
||||
|
||||
That's it! It works. You can now use `Ctrl-c` to stop OpenOCD or close/kill the terminal.
|
||||
58
discovery-master/f3discovery/src/03-setup/windows.md
Normal file
58
discovery-master/f3discovery/src/03-setup/windows.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Windows
|
||||
|
||||
## `arm-none-eabi-gdb`
|
||||
|
||||
ARM provides `.exe` installers for Windows. Grab one from [here][gcc], and follow the instructions.
|
||||
Just before the installation process finishes tick/select the "Add path to environment variable"
|
||||
option. Then verify that the tools are in your `%PATH%`:
|
||||
|
||||
Verify gcc is installed:
|
||||
``` console
|
||||
arm-none-eabi-gcc -v
|
||||
```
|
||||
The results should be something like:
|
||||
```
|
||||
(..)
|
||||
$ arm-none-eabi-gcc -v
|
||||
gcc version 5.4.1 20160919 (release) (..)
|
||||
```
|
||||
|
||||
[gcc]: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
|
||||
|
||||
## OpenOCD
|
||||
|
||||
There's no official binary release of OpenOCD for Windows but there are unofficial releases
|
||||
available [here][openocd]. Grab the 0.10.x zipfile and extract it somewhere in your drive (I
|
||||
recommend `C:\OpenOCD` but with the drive letter that makes sense to you) then update your `%PATH%`
|
||||
environment variable to include the following path: `C:\OpenOCD\bin` (or the path that you used
|
||||
before).
|
||||
|
||||
[openocd]: https://github.com/xpack-dev-tools/openocd-xpack/releases
|
||||
|
||||
Verify OpenOCD is installed and in your `%PATH%` with:
|
||||
``` console
|
||||
openocd -v
|
||||
```
|
||||
The results should be something like:
|
||||
``` console
|
||||
$ openocd -v
|
||||
Open On-Chip Debugger 0.10.0
|
||||
(..)
|
||||
```
|
||||
|
||||
## PuTTY
|
||||
|
||||
Download the latest `putty.exe` from [this site] and place it somewhere in your `%PATH%`.
|
||||
|
||||
[this site]: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
|
||||
|
||||
## ST-LINK USB driver
|
||||
|
||||
You'll also need to install [this USB driver] or OpenOCD won't work. Follow the installer
|
||||
instructions and make sure you install the right (32-bit or 64-bit) version of the driver.
|
||||
|
||||
[this USB driver]: http://www.st.com/en/embedded-software/stsw-link009.html
|
||||
|
||||
That's all! Go to the [next section].
|
||||
|
||||
[next section]: verify.md
|
||||
111
discovery-master/f3discovery/src/04-meet-your-hardware/README.md
Normal file
111
discovery-master/f3discovery/src/04-meet-your-hardware/README.md
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
# Meet your hardware
|
||||
|
||||
Let's get familiar with the hardware we'll be working with.
|
||||
|
||||
## STM32F3DISCOVERY (the "F3")
|
||||
|
||||
<p align="center">
|
||||
<img title="F3" src="../assets/f3.jpg">
|
||||
</p>
|
||||
|
||||
We'll refer to this board as "F3" throughout this book. Here are some of the
|
||||
many components on the board:
|
||||
|
||||
- A [microcontroller].
|
||||
- A number of LEDs, including the eight aligned in a "compass" formation.
|
||||
- Two buttons.
|
||||
- Two USB ports.
|
||||
- An [accelerometer].
|
||||
- A [magnetometer].
|
||||
- A [gyroscope].
|
||||
|
||||
[microcontroller]: https://en.wikipedia.org/wiki/Microcontroller
|
||||
[accelerometer]: https://en.wikipedia.org/wiki/Accelerometer
|
||||
[magnetometer]: https://en.wikipedia.org/wiki/Magnetometer
|
||||
[gyroscope]: https://en.wikipedia.org/wiki/Gyroscope
|
||||
|
||||
Of these components, the most important is the microcontroller (sometimes
|
||||
shortened to "MCU" for "microcontroller unit"), which is the large black square
|
||||
sitting in the center of your board. The MCU is what runs your code. You might
|
||||
sometimes read about "programming a board", when in reality what we are doing
|
||||
is programming the MCU that is installed on the board.
|
||||
|
||||
## STM32F303VCT6 (the "STM32F3")
|
||||
|
||||
Since the MCU is so important, let's take a closer look at the one sitting on our board.
|
||||
|
||||
Our MCU is surrounded by 100 tiny metal **pins**. These pins are connected to
|
||||
**traces**, the little "roads" that act as the wires connecting components
|
||||
together on the board. The MCU can dynamically alter the electrical properties
|
||||
of the pins. This works similar to a light switch altering how electrical
|
||||
current flows through a circuit. By enabling or disabling electrical current to
|
||||
flow through a specific pin, an LED attached to that pin (via the traces) can
|
||||
be turned on and off.
|
||||
|
||||
Each manufacturer uses a different part numbering scheme, but many will allow
|
||||
you to determine information about a component simply by looking at the part
|
||||
number. Looking at our MCU's part number (`STM32F303VCT6`), the `ST` at the
|
||||
front hints to us that this is a part manufactured by [ST Microelectronics].
|
||||
Searching through [ST's marketing materials] we can also learn the following:
|
||||
|
||||
[ST Microelectronics]: https://st.com/
|
||||
[ST's marketing materials]: https://www.st.com/en/microcontrollers-microprocessors/stm32-mainstream-mcus.html
|
||||
|
||||
- The `M32` represents that this is an Arm®-based 32-bit microcontroller.
|
||||
- The `F3` represents that the MCU is from ST's "STM32F3" series. This is a
|
||||
series of MCUs based on the Cortex®-M4 processor design.
|
||||
- The remainder of the part number goes into more details about things like
|
||||
extra features and RAM size, which at this point we're less concerned about.
|
||||
|
||||
> ### Arm? Cortex-M4?
|
||||
>
|
||||
> If our chip is manufactured by ST, then who is Arm? And if our chip is the
|
||||
> STM32F3, what is the Cortex-M4?
|
||||
>
|
||||
> You might be surprised to hear that while "Arm-based" chips are quite
|
||||
> popular, the company behind the "Arm" trademark ([Arm Holdings][]) doesn't
|
||||
> actually manufacture chips for purchase. Instead, their primary business
|
||||
> model is to just *design* parts of chips. They will then license those designs to
|
||||
> manufacturers, who will in turn implement the designs (perhaps with some of
|
||||
> their own tweaks) in the form of physical hardware that can then be sold.
|
||||
> Arm's strategy here is different from companies like Intel, which both
|
||||
> designs *and* manufactures their chips.
|
||||
>
|
||||
> Arm licenses a bunch of different designs. Their "Cortex-M" family of designs
|
||||
> are mainly used as the core in microcontrollers. For example, the Cortex-M0
|
||||
> is designed for low cost and low power usage. The Cortex-M7 is higher cost,
|
||||
> but with more features and performance. The core of our STM32F3 is based on
|
||||
> the Cortex-M4, which is in the middle: more features and performance than the
|
||||
> Cortex-M0, but less expensive than the Cortex-M7.
|
||||
>
|
||||
> Luckily, you don't need to know too much about different types of processors
|
||||
> or Cortex designs for the sake of this book. However, you are hopefully now a
|
||||
> bit more knowledgeable about the terminology of your device. While you are
|
||||
> working specifically with an STM32F3, you might find yourself reading
|
||||
> documentation and using tools for Cortex-M-based chips, as the STM32F3 is
|
||||
> based on a Cortex-M design.
|
||||
|
||||
[Arm Holdings]: https://www.arm.com/
|
||||
|
||||
## The Serial module
|
||||
|
||||
<p align="center">
|
||||
<img title="Serial module" src="../assets/serial.jpg">
|
||||
</p>
|
||||
|
||||
If you have an older revision of the discovery board, you can use this module to
|
||||
exchange data between the microcontroller in the F3 and your computer. This module
|
||||
will be connected to your computer using an USB cable. I won't say more at this
|
||||
point.
|
||||
|
||||
If you have a newer release of the board then you don't need this module. The
|
||||
ST-LINK will double as a USB<->serial converter connected to the microcontroller USART1 at pins PC4 and PC5.
|
||||
|
||||
## The Bluetooth module
|
||||
|
||||
<p align="center">
|
||||
<img title="The HC-05 Bluetooth module" src="../assets/bluetooth.jpg">
|
||||
</p>
|
||||
|
||||
This module has the exact same purpose as the serial module but it sends the data over Bluetooth
|
||||
instead of over USB.
|
||||
12
discovery-master/f3discovery/src/05-led-roulette/Cargo.toml
Normal file
12
discovery-master/f3discovery/src/05-led-roulette/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
authors = [
|
||||
"Jorge Aparicio <jorge@japaric.io>",
|
||||
"Christopher J. McClellan <chris.mcclellan203@gmail.com>",
|
||||
"Wink Saville <wink@saville.com",
|
||||
]
|
||||
edition = "2018"
|
||||
name = "led-roulette"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
aux5 = { path = "auxiliary" }
|
||||
50
discovery-master/f3discovery/src/05-led-roulette/README.md
Normal file
50
discovery-master/f3discovery/src/05-led-roulette/README.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# LED roulette
|
||||
|
||||
Alright, let's start by building the following application:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://i.imgur.com/0k1r2Lc.gif">
|
||||
</p>
|
||||
|
||||
I'm going to give you a high level API to implement this app but don't worry we'll do low level
|
||||
stuff later on. The main goal of this chapter is to get familiar with the *flashing* and debugging
|
||||
process.
|
||||
|
||||
Throughout this text we'll be using the starter code that's in the [discovery] repository. Make sure
|
||||
you always have the latest version of the master branch because this website tracks that branch.
|
||||
|
||||
The starter code is in the `src` directory of that repository. Inside that directory there are more
|
||||
directories named after each chapter of this book. Most of those directories are starter Cargo
|
||||
projects.
|
||||
|
||||
[discovery]: https://github.com/rust-embedded/discovery
|
||||
|
||||
Now, jump into the `src/05-led-roulette` directory. Check the `src/main.rs` file:
|
||||
|
||||
``` rust
|
||||
{{#include src/main.rs}}
|
||||
```
|
||||
|
||||
Microcontroller programs are different from standard programs in two aspects: `#![no_std]` and
|
||||
`#![no_main]`.
|
||||
|
||||
The `no_std` attribute says that this program won't use the `std` crate, which assumes an underlying
|
||||
OS; the program will instead use the `core` crate, a subset of `std` that can run on bare metal
|
||||
systems (i.e., systems without OS abstractions like files and sockets).
|
||||
|
||||
The `no_main` attribute says that this program won't use the standard `main` interface, which is
|
||||
tailored for command line applications that receive arguments. Instead of the standard `main` we'll
|
||||
use the `entry` attribute from the [`cortex-m-rt`] crate to define a custom entry point. In this
|
||||
program we have named the entry point "main", but any other name could have been used. The entry
|
||||
point function must have the signature `fn() -> !`; this type indicates that the function can't
|
||||
return – this means that the program never terminates.
|
||||
|
||||
[`cortex-m-rt`]: https://crates.io/crates/cortex-m-rt
|
||||
|
||||
If you are a careful observer, you'll also notice there is a `.cargo` directory in the Cargo project
|
||||
as well. This directory contains a Cargo configuration file (`.cargo/config`) that tweaks the
|
||||
linking process to tailor the memory layout of the program to the requirements of the target device.
|
||||
This modified linking process is a requirement of the `cortex-m-rt` crate. You'll also be making
|
||||
further tweaks to `.cargo/config` in future sections to make building and debugging easier.
|
||||
|
||||
Alright, let's start by building this program.
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
authors = [
|
||||
"Jorge Aparicio <jorge@japaric.io>",
|
||||
"Christopher J. McClellan <chris.mcclellan203@gmail.com>",
|
||||
"Wink Saville <wink@saville.com",
|
||||
]
|
||||
edition = "2018"
|
||||
name = "aux5"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
cortex-m-rt = "0.6.14"
|
||||
stm32f3-discovery = "0.7.0"
|
||||
panic-itm = "0.4.2"
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
//! Initialization code
|
||||
|
||||
#![no_std]
|
||||
|
||||
pub use panic_itm; // panic handler
|
||||
|
||||
pub use cortex_m_rt::entry;
|
||||
|
||||
pub use stm32f3_discovery::{leds::Leds, stm32f3xx_hal, switch_hal};
|
||||
pub use switch_hal::{ActiveHigh, OutputSwitch, Switch, ToggleableOutputSwitch};
|
||||
|
||||
use stm32f3xx_hal::prelude::*;
|
||||
pub use stm32f3xx_hal::{
|
||||
delay::Delay,
|
||||
gpio::{gpioe, Output, PushPull},
|
||||
hal::blocking::delay::DelayMs,
|
||||
pac,
|
||||
};
|
||||
|
||||
pub type LedArray = [Switch<gpioe::PEx<Output<PushPull>>, ActiveHigh>; 8];
|
||||
|
||||
pub fn init() -> (Delay, LedArray) {
|
||||
let device_periphs = pac::Peripherals::take().unwrap();
|
||||
let mut reset_and_clock_control = device_periphs.RCC.constrain();
|
||||
|
||||
let core_periphs = cortex_m::Peripherals::take().unwrap();
|
||||
let mut flash = device_periphs.FLASH.constrain();
|
||||
let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr);
|
||||
let delay = Delay::new(core_periphs.SYST, clocks);
|
||||
|
||||
// initialize user leds
|
||||
let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb);
|
||||
let leds = Leds::new(
|
||||
gpioe.pe8,
|
||||
gpioe.pe9,
|
||||
gpioe.pe10,
|
||||
gpioe.pe11,
|
||||
gpioe.pe12,
|
||||
gpioe.pe13,
|
||||
gpioe.pe14,
|
||||
gpioe.pe15,
|
||||
&mut gpioe.moder,
|
||||
&mut gpioe.otyper,
|
||||
);
|
||||
|
||||
(delay, leds.into_array())
|
||||
}
|
||||
126
discovery-master/f3discovery/src/05-led-roulette/build-it.md
Normal file
126
discovery-master/f3discovery/src/05-led-roulette/build-it.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# Build it
|
||||
|
||||
The first step is to build our "binary" crate. Because the microcontroller has a different
|
||||
architecture than your computer we'll have to cross compile. Cross compiling in Rust land is as simple
|
||||
as passing an extra `--target` flag to `rustc`or Cargo. The complicated part is figuring out the
|
||||
argument of that flag: the *name* of the target.
|
||||
|
||||
The microcontroller in the F3 has a Cortex-M4F processor in it. `rustc` knows how to cross compile
|
||||
to the Cortex-M architecture and provides 4 different targets that cover the different processor
|
||||
families within that architecture:
|
||||
|
||||
- `thumbv6m-none-eabi`, for the Cortex-M0 and Cortex-M1 processors
|
||||
- `thumbv7m-none-eabi`, for the Cortex-M3 processor
|
||||
- `thumbv7em-none-eabi`, for the Cortex-M4 and Cortex-M7 processors
|
||||
- `thumbv7em-none-eabihf`, for the Cortex-M4**F** and Cortex-M7**F** processors
|
||||
|
||||
For the F3, we'll use the `thumbv7em-none-eabihf` target. Before cross compiling you have to
|
||||
download a pre-compiled version of the standard library (a reduced version of it actually) for your
|
||||
target. That's done using `rustup`:
|
||||
|
||||
``` console
|
||||
rustup target add thumbv7em-none-eabihf
|
||||
```
|
||||
|
||||
You only need to do the above step once; `rustup` will re-install a new standard library
|
||||
(`rust-std` component) whenever you update your toolchain.
|
||||
|
||||
With the `rust-std` component in place you can now cross compile the program using Cargo.
|
||||
|
||||
> **NOTE** Make sure you are in the `src/05-led-roulette` directory
|
||||
> and run `cargo build` command below to create the executable:
|
||||
``` console
|
||||
cargo build --target thumbv7em-none-eabihf
|
||||
```
|
||||
On your console you should see something like:
|
||||
``` console
|
||||
$ cargo build --target thumbv7em-none-eabihf
|
||||
Compiling typenum v1.12.0
|
||||
Compiling semver-parser v0.7.0
|
||||
Compiling version_check v0.9.2
|
||||
Compiling nb v1.0.0
|
||||
Compiling void v1.0.2
|
||||
Compiling autocfg v1.0.1
|
||||
Compiling cortex-m v0.7.1
|
||||
Compiling proc-macro2 v1.0.24
|
||||
Compiling vcell v0.1.3
|
||||
Compiling unicode-xid v0.2.1
|
||||
Compiling stable_deref_trait v1.2.0
|
||||
Compiling syn v1.0.60
|
||||
Compiling bitfield v0.13.2
|
||||
Compiling cortex-m v0.6.7
|
||||
Compiling cortex-m-rt v0.6.13
|
||||
Compiling r0 v0.2.2
|
||||
Compiling stm32-usbd v0.5.1
|
||||
Compiling stm32f3 v0.12.1
|
||||
Compiling usb-device v0.2.7
|
||||
Compiling cfg-if v1.0.0
|
||||
Compiling paste v1.0.4
|
||||
Compiling stm32f3-discovery v0.6.0
|
||||
Compiling embedded-dma v0.1.2
|
||||
Compiling volatile-register v0.2.0
|
||||
Compiling nb v0.1.3
|
||||
Compiling embedded-hal v0.2.4
|
||||
Compiling semver v0.9.0
|
||||
Compiling generic-array v0.14.4
|
||||
Compiling switch-hal v0.3.2
|
||||
Compiling num-traits v0.2.14
|
||||
Compiling num-integer v0.1.44
|
||||
Compiling rustc_version v0.2.3
|
||||
Compiling bare-metal v0.2.5
|
||||
Compiling cast v0.2.3
|
||||
Compiling quote v1.0.9
|
||||
Compiling generic-array v0.13.2
|
||||
Compiling generic-array v0.12.3
|
||||
Compiling generic-array v0.11.1
|
||||
Compiling panic-itm v0.4.2
|
||||
Compiling lsm303dlhc v0.2.0
|
||||
Compiling as-slice v0.1.4
|
||||
Compiling micromath v1.1.0
|
||||
Compiling accelerometer v0.12.0
|
||||
Compiling chrono v0.4.19
|
||||
Compiling aligned v0.3.4
|
||||
Compiling rtcc v0.2.0
|
||||
Compiling cortex-m-rt-macros v0.1.8
|
||||
Compiling stm32f3xx-hal v0.6.1
|
||||
Compiling aux5 v0.2.0 (~/embedded-discovery/src/05-led-roulette/auxiliary)
|
||||
Compiling led-roulette v0.2.0 (~/embedded-discovery/src/05-led-roulette)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 17.91s
|
||||
```
|
||||
|
||||
> **NOTE** Be sure to compile this crate *without* optimizations. The provided Cargo.toml file and build command above will ensure optimizations are off.
|
||||
|
||||
OK, now we have produced an executable. This executable won't blink any LEDs, it's just a simplified version that we will build upon later in the chapter. As a sanity check, let's verify that the produced executable is actually an ARM binary:
|
||||
|
||||
``` console
|
||||
cargo readobj --target thumbv7em-none-eabihf --bin led-roulette -- --file-header
|
||||
```
|
||||
The `cargo readobj ..` above is equivalent to
|
||||
`readelf -h target/thumbv7em-none-eabihf/debug/led-roulette`
|
||||
and should produce something similar to:
|
||||
``` console
|
||||
$ cargo readobj --target thumbv7em-none-eabihf --bin led-roulette -- --file-header
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
|
||||
ELF Header:
|
||||
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
|
||||
Class: ELF32
|
||||
Data: 2's complement, little endian
|
||||
Version: 1 (current)
|
||||
OS/ABI: UNIX - System V
|
||||
ABI Version: 0
|
||||
Type: EXEC (Executable file)
|
||||
Machine: ARM
|
||||
Version: 0x1
|
||||
Entry point address: 0x8000195
|
||||
Start of program headers: 52 (bytes into file)
|
||||
Start of section headers: 818328 (bytes into file)
|
||||
Flags: 0x5000400
|
||||
Size of this header: 52 (bytes)
|
||||
Size of program headers: 32 (bytes)
|
||||
Number of program headers: 4
|
||||
Size of section headers: 40 (bytes)
|
||||
Number of section headers: 22
|
||||
Section header string table index: 20
|
||||
```
|
||||
|
||||
Next, we'll flash the program into our microcontroller.
|
||||
313
discovery-master/f3discovery/src/05-led-roulette/debug-it.md
Normal file
313
discovery-master/f3discovery/src/05-led-roulette/debug-it.md
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
# Debug it
|
||||
|
||||
We are already inside a debugging session so let's debug our program.
|
||||
|
||||
After the `load` command, our program is stopped at its *entry point*. This is indicated by the
|
||||
"Start address 0x8000XXX" part of GDB's output. The entry point is the part of a program that a
|
||||
processor / CPU will execute first.
|
||||
|
||||
The starter project I've provided to you has some extra code that runs *before* the `main` function.
|
||||
At this time, we are not interested in that "pre-main" part so let's skip right to the beginning of
|
||||
the `main` function. We'll do that using a breakpoint. Issue `break main` at the `(gdb)` prompt:
|
||||
|
||||
> **NOTE** For these GDB commands I generally won't provide a copyable code block
|
||||
> as these are short and it's faster just to type them yourself. In addition most
|
||||
> can be shortened. For instance `b` for `break` or `s` for `step`, see [GDB Quick Reference]
|
||||
> for more info or use Google to find your others. In addition, you can use tab completion
|
||||
> by typing the first few letters than one tab to complete or two tabs to
|
||||
> see all possible commands.
|
||||
>
|
||||
>> Finally, `help xxxx` where xxxx is the command will provide short names and other info:
|
||||
>> ```
|
||||
>> (gdb) help s
|
||||
>> step, s
|
||||
>> Step program until it reaches a different source line.
|
||||
>> Usage: step [N]
|
||||
>> Argument N means step N times (or till program stops for another reason).
|
||||
>> ```
|
||||
|
||||
[GDB Quick Reference]: https://users.ece.utexas.edu/~adnan/gdb-refcard.pdf
|
||||
```
|
||||
(gdb) break main
|
||||
Breakpoint 1 at 0x80001f0: file src/05-led-roulette/src/main.rs, line 7.
|
||||
Note: automatically using hardware breakpoints for read-only addresses.
|
||||
```
|
||||
Next issue a `continue` command:
|
||||
```
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, led_roulette::__cortex_m_rt_main_trampoline () at src/05-led-roulette/src/main.rs:7
|
||||
7 #[entry]
|
||||
```
|
||||
|
||||
Breakpoints can be used to stop the normal flow of a program. The `continue` command will let the
|
||||
program run freely *until* it reaches a breakpoint. In this case, until it reaches `#[entry]`
|
||||
which is a trampoline to the main function and where `break main` sets the breakpoint.
|
||||
|
||||
> **Note** that GDB output says "Breakpoint 1". Remember that our processor can only use six of these
|
||||
> breakpoints so it's a good idea to pay attention to these messages.
|
||||
|
||||
OK. Since we are stopped at `#[entry]` and using the `disassemble /m` we see the code
|
||||
for entry, which is a trampoline to main. That means it sets up the stack and then
|
||||
invokes a subroutine call to the `main` function using an ARM branch and link instruction, `bl`.
|
||||
```
|
||||
(gdb) disassemble /m
|
||||
Dump of assembler code for function main:
|
||||
7 #[entry]
|
||||
0x080001ec <+0>: push {r7, lr}
|
||||
0x080001ee <+2>: mov r7, sp
|
||||
=> 0x080001f0 <+4>: bl 0x80001f6 <_ZN12led_roulette18__cortex_m_rt_main17he61ef18c060014a5E>
|
||||
0x080001f4 <+8>: udf #254 ; 0xfe
|
||||
|
||||
End of assembler dump.
|
||||
```
|
||||
|
||||
Next we need to issue a `step` GDB command which will advance the program statement
|
||||
by statement stepping into functions/procedures. So after this first `step` command we're
|
||||
inside `main` and are positioned at the first executable `rust` statement, line 10, but it is
|
||||
**not** executed:
|
||||
```
|
||||
(gdb) step
|
||||
led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:10
|
||||
10 let x = 42;
|
||||
```
|
||||
|
||||
Next we'll issue a second `step` which executes line 10 and stops at
|
||||
line `11 _y = x;`, again line 11 is **not** executed.
|
||||
|
||||
> **NOTE** We could have pressed enter at the second `(gdb) ` prompt and
|
||||
> it would have reissued the previous statement, `step`, but for clarity
|
||||
> in this tutorial we'll generally retype the command.
|
||||
|
||||
```
|
||||
(gdb) step
|
||||
11 _y = x;
|
||||
```
|
||||
|
||||
As you can see, in this mode, on each `step` command GDB will print the current statement along
|
||||
with its line number. As you'll see later in the TUI mode you'll not see the statement
|
||||
in the command area.
|
||||
|
||||
We are now "on" the `_y = x` statement; that statement hasn't been executed yet. This means that `x`
|
||||
is initialized but `_y` is not. Let's inspect those stack/local variables using the `print`
|
||||
command, `p` for short:
|
||||
|
||||
```
|
||||
(gdb) print x
|
||||
$1 = 42
|
||||
(gdb) p &x
|
||||
$2 = (*mut i32) 0x20009fe0
|
||||
(gdb) p _y
|
||||
$3 = 536870912
|
||||
(gdb) p &_y
|
||||
$4 = (*mut i32) 0x20009fe4
|
||||
```
|
||||
|
||||
As expected, `x` contains the value `42`. `_y`, however, contains the value `536870912` (?). This
|
||||
is because `_y` has not been initialized yet, it contains some garbage value.
|
||||
|
||||
The command `print &x` prints the address of the variable `x`. The interesting bit here is that GDB
|
||||
output shows the type of the reference: `*mut i32`, a mutable pointer to an `i32` value. Another
|
||||
interesting thing is that the addresses of `x` and `_y` are very close to each other: their
|
||||
addresses are just `4` bytes apart.
|
||||
|
||||
Instead of printing the local variables one by one, you can also use the `info locals` command:
|
||||
|
||||
```
|
||||
(gdb) info locals
|
||||
x = 42
|
||||
_y = 536870912
|
||||
```
|
||||
|
||||
OK. With another `step`, we'll be on top of the `loop {}` statement:
|
||||
|
||||
```
|
||||
(gdb) step
|
||||
14 loop {}
|
||||
```
|
||||
|
||||
And `_y` should now be initialized.
|
||||
|
||||
```
|
||||
(gdb) print _y
|
||||
$5 = 42
|
||||
```
|
||||
|
||||
If we use `step` again on top of the `loop {}` statement, we'll get stuck because the program will
|
||||
never pass that statement.
|
||||
|
||||
> **NOTE** If you used the `step` or any other command by mistake and GDB gets stuck, you can get
|
||||
> it unstuck by hitting `Ctrl+C`.
|
||||
|
||||
As introduced above the `disassemble /m` command can be used to disassemble the program around the
|
||||
line you are currently at. You might also want to `set print asm-demangle on`
|
||||
so the names are demangled, this only needs to be done once a debug session. Later
|
||||
this and other commands will be placed in an initialization file which will simplify
|
||||
starting a debug session.
|
||||
|
||||
```
|
||||
(gdb) set print asm-demangle on
|
||||
(gdb) disassemble /m
|
||||
Dump of assembler code for function _ZN12led_roulette18__cortex_m_rt_main17h51e7c3daad2af251E:
|
||||
8 fn main() -> ! {
|
||||
0x080001f6 <+0>: sub sp, #8
|
||||
0x080001f8 <+2>: movs r0, #42 ; 0x2a
|
||||
|
||||
9 let _y;
|
||||
10 let x = 42;
|
||||
0x080001fa <+4>: str r0, [sp, #0]
|
||||
|
||||
11 _y = x;
|
||||
0x080001fc <+6>: str r0, [sp, #4]
|
||||
|
||||
12
|
||||
13 // infinite loop; just so we don't leave this stack frame
|
||||
14 loop {}
|
||||
=> 0x080001fe <+8>: b.n 0x8000200 <led_roulette::__cortex_m_rt_main+10>
|
||||
0x08000200 <+10>: b.n 0x8000200 <led_roulette::__cortex_m_rt_main+10>
|
||||
|
||||
End of assembler dump.
|
||||
```
|
||||
|
||||
See the fat arrow `=>` on the left side? It shows the instruction the processor will execute next.
|
||||
|
||||
Also, as mentioned above if you were to execute the `step` command GDB gets stuck because it
|
||||
is executing a branch instruction to itself and never gets past it. So you need to use
|
||||
`Ctrl+C` to regain control. An alternative is to use the `stepi`(`si`) GDB command, which steps
|
||||
one asm instruction, and GDB will print the address **and** line number of the statement
|
||||
the processor will execute next and it won't get stuck.
|
||||
|
||||
```
|
||||
(gdb) stepi
|
||||
0x08000194 14 loop {}
|
||||
|
||||
(gdb) si
|
||||
0x08000194 14 loop {}
|
||||
```
|
||||
|
||||
One last trick before we move to something more interesting. Enter the following commands into GDB:
|
||||
|
||||
```
|
||||
(gdb) monitor reset halt
|
||||
Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
adapter speed: 950 kHz
|
||||
target halted due to debug-request, current mode: Thread
|
||||
xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, led_roulette::__cortex_m_rt_main_trampoline () at src/05-led-roulette/src/main.rs:7
|
||||
7 #[entry]
|
||||
|
||||
(gdb) disassemble /m
|
||||
Dump of assembler code for function main:
|
||||
7 #[entry]
|
||||
0x080001ec <+0>: push {r7, lr}
|
||||
0x080001ee <+2>: mov r7, sp
|
||||
=> 0x080001f0 <+4>: bl 0x80001f6 <led_roulette::__cortex_m_rt_main>
|
||||
0x080001f4 <+8>: udf #254 ; 0xfe
|
||||
|
||||
End of assembler dump.
|
||||
```
|
||||
|
||||
We are now back at the beginning of `#[entry]`!
|
||||
|
||||
`monitor reset halt` will reset the microcontroller and stop it right at the beginning of the program.
|
||||
The `continue` command will then let the program run freely until it reaches a breakpoint, in
|
||||
this case it is the breakpoint at `#[entry]`.
|
||||
|
||||
This combo is handy when you, by mistake, skipped over a part of the program that you were
|
||||
interested in inspecting. You can easily roll back the state of your program back to its very
|
||||
beginning.
|
||||
|
||||
> **The fine print**: This `reset` command doesn't clear or touch RAM. That memory will retain its
|
||||
> values from the previous run. That shouldn't be a problem though, unless your program behavior
|
||||
> depends of the value of *uninitialized* variables but that's the definition of Undefined Behavior
|
||||
> (UB).
|
||||
|
||||
We are done with this debug session. You can end it with the `quit` command.
|
||||
|
||||
```
|
||||
(gdb) quit
|
||||
A debugging session is active.
|
||||
|
||||
Inferior 1 [Remote target] will be detached.
|
||||
|
||||
Quit anyway? (y or n) y
|
||||
Detaching from program: $PWD/target/thumbv7em-none-eabihf/debug/led-roulette, Remote target
|
||||
Ending remote debugging.
|
||||
```
|
||||
|
||||
For a nicer debugging experience, you can use GDB's Text User Interface (TUI). To enter into that
|
||||
mode enter one of the following commands in the GDB shell:
|
||||
|
||||
```
|
||||
(gdb) layout src
|
||||
(gdb) layout asm
|
||||
(gdb) layout split
|
||||
```
|
||||
|
||||
> **NOTE** Apologies to Windows users, the GDB shipped with the GNU ARM Embedded Toolchain
|
||||
> may not support this TUI mode `:-(`.
|
||||
|
||||
Below is an example of setting up for a `layout split` by executing the follow commands.
|
||||
As you can see we've dropped passing the `--target` parameter:
|
||||
|
||||
``` console
|
||||
$ cargo run
|
||||
(gdb) target remote :3333
|
||||
(gdb) load
|
||||
(gdb) set print asm-demangle on
|
||||
(gdb) set style sources off
|
||||
(gdb) break main
|
||||
(gdb) continue
|
||||
```
|
||||
|
||||
Here is a command line with the above commands as `-ex` parameters to save you some typing,
|
||||
shortly we'll be providing an easier way to execute the initial set of commands:
|
||||
```
|
||||
cargo run -- -q -ex 'target remote :3333' -ex 'load' -ex 'set print asm-demangle on' -ex 'set style sources off' -ex 'b main' -ex 'c' target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
```
|
||||
|
||||
And below is the result:
|
||||
|
||||

|
||||
|
||||
Now we'll scroll the top source window down so we see the entire file and execute `layout split` and then `step`:
|
||||
|
||||

|
||||
|
||||
Then we'll execute a few `info locals` and `step`'s:
|
||||
|
||||
``` console
|
||||
(gdb) info locals
|
||||
(gdb) step
|
||||
(gdb) info locals
|
||||
(gdb) step
|
||||
(gdb) info locals
|
||||
```
|
||||
|
||||

|
||||
|
||||
At any point you can leave the TUI mode using the following command:
|
||||
|
||||
```
|
||||
(gdb) tui disable
|
||||
```
|
||||
|
||||

|
||||
|
||||
> **NOTE** If the default GDB CLI is not to your liking check out [gdb-dashboard]. It uses Python to
|
||||
> turn the default GDB CLI into a dashboard that shows registers, the source view, the assembly view
|
||||
> and other things.
|
||||
|
||||
[gdb-dashboard]: https://github.com/cyrus-and/gdb-dashboard#gdb-dashboard
|
||||
|
||||
Don't close OpenOCD though! We'll use it again and again later on. It's better
|
||||
just to leave it running. If you want to learn more about what GDB can do, check out the section [How to use GDB](../appendix/2-how-to-use-gdb/).
|
||||
|
||||
|
||||
What's next? The high level API I promised.
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use aux5::{Delay, DelayMs, LedArray, OutputSwitch, entry};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (mut delay, mut leds): (Delay, LedArray) = aux5::init();
|
||||
|
||||
let ms = 50_u8;
|
||||
loop {
|
||||
for curr in 0..8 {
|
||||
let next = (curr + 1) % 8;
|
||||
|
||||
leds[next].on().ok();
|
||||
delay.delay_ms(ms);
|
||||
leds[curr].off().ok();
|
||||
delay.delay_ms(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use aux5::{entry, Delay, DelayMs, LedArray, OutputSwitch};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (mut delay, mut leds): (Delay, LedArray) = aux5::init();
|
||||
|
||||
let half_period = 500_u16;
|
||||
|
||||
loop {
|
||||
leds[0].on().ok();
|
||||
delay.delay_ms(half_period);
|
||||
|
||||
leds[0].off().ok();
|
||||
delay.delay_ms(half_period);
|
||||
}
|
||||
}
|
||||
293
discovery-master/f3discovery/src/05-led-roulette/flash-it.md
Normal file
293
discovery-master/f3discovery/src/05-led-roulette/flash-it.md
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
# Flash it
|
||||
|
||||
Flashing is the process of moving our program into the microcontroller's (persistent) memory. Once
|
||||
flashed, the microcontroller will execute the flashed program every time it is powered on.
|
||||
|
||||
In this case, our `led-roulette` program will be the *only* program in the microcontroller memory.
|
||||
By this I mean that there's nothing else running on the microcontroller: no OS, no "daemon",
|
||||
nothing. `led-roulette` has full control over the device.
|
||||
|
||||
Onto the actual flashing. First thing we need to do is launch OpenOCD. We did that in the
|
||||
previous section but this time we'll run the command inside a temporary directory (`/tmp` on \*nix;
|
||||
`%TEMP%` on Windows).
|
||||
|
||||
Make sure the F3 is connected to your computer and run the following commands in a **new terminal**.
|
||||
|
||||
## For *nix & MacOS:
|
||||
``` console
|
||||
cd /tmp
|
||||
openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
|
||||
```
|
||||
|
||||
## For Windows **Note**: substitute `C:` for the actual OpenOCD path:
|
||||
```
|
||||
cd %TEMP%
|
||||
openocd -s C:\share\scripts -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
|
||||
```
|
||||
|
||||
> **NOTE** Older revisions of the board need to pass slightly different arguments to
|
||||
> `openocd`. Review [this section] for the details.
|
||||
|
||||
[this section]: ../03-setup/verify.md#first-openocd-connection
|
||||
|
||||
The program will block; leave that terminal open.
|
||||
|
||||
Now it's a good time to explain what the `openocd` command is actually doing.
|
||||
|
||||
I mentioned that the STM32F3DISCOVERY (aka F3) actually has two microcontrollers. One of them is used as a
|
||||
programmer/debugger. The part of the board that's used as a programmer is called ST-LINK (that's what
|
||||
STMicroelectronics decided to call it). This ST-LINK is connected to the target microcontroller
|
||||
using a Serial Wire Debug (SWD) interface (this interface is an ARM standard so you'll run into it
|
||||
when dealing with other Cortex-M based microcontrollers). This SWD interface can be used to flash
|
||||
and debug a microcontroller. The ST-LINK is connected to the "USB ST-LINK" port and will appear as
|
||||
a USB device when you connect the F3 to your computer.
|
||||
|
||||
<p align="center">
|
||||
<img height=640 title="On-board ST-LINK" src="../assets/st-link.png">
|
||||
</p>
|
||||
|
||||
|
||||
As for OpenOCD, it's software that provides some services like a *GDB server* on top of USB
|
||||
devices that expose a debugging protocol like SWD or JTAG.
|
||||
|
||||
Onto the actual command: those `.cfg` files we are using instruct OpenOCD to look for a ST-LINK USB
|
||||
device (`interface/stlink-v2-1.cfg`) and to expect a STM32F3XX microcontroller
|
||||
(`target/stm32f3x.cfg`) to be connected to the ST-LINK.
|
||||
|
||||
The OpenOCD output looks like this:
|
||||
``` console
|
||||
$ openocd -f interface/stlink-v2-1.cfg -f target/stm32f3x.cfg
|
||||
Open On-Chip Debugger 0.10.0
|
||||
Licensed under GNU GPL v2
|
||||
For bug reports, read
|
||||
http://openocd.org/doc/doxygen/bugs.html
|
||||
Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
|
||||
adapter speed: 1000 kHz
|
||||
adapter_nsrst_delay: 100
|
||||
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
|
||||
none separate
|
||||
Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Info : clock speed 950 kHz
|
||||
Info : STLINK v2 JTAG v37 API v2 SWIM v26 VID 0x0483 PID 0x374B
|
||||
Info : using stlink api v2
|
||||
Info : Target voltage: 2.888183
|
||||
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
|
||||
```
|
||||
|
||||
The "6 breakpoints, 4 watchpoints" part indicates the debugging features the processor has
|
||||
available.
|
||||
|
||||
Leave that `openocd` process running, and in the previous terminal or a new terminal
|
||||
**make sure that you are inside the project's `src/05-led-roulette/` directory**.
|
||||
|
||||
I mentioned that OpenOCD provides a GDB server so let's connect to that right now:
|
||||
|
||||
## Execute GDB
|
||||
|
||||
First, we need to determine what version of `gdb` you have that is capable of debugging ARM binaries.
|
||||
|
||||
This could be any one of the commands below, try each one:
|
||||
``` console
|
||||
arm-none-eabi-gdb -q -ex "target remote :3333" target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
```
|
||||
``` console
|
||||
gdb-multiarch -q -ex "target remote :3333" target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
```
|
||||
``` console
|
||||
gdb -q -ex "target remote :3333" target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
```
|
||||
|
||||
> **NOTE**: If you are getting `target/thumbv7em-none-eabihf/debug/led-roulette: No such file or directory`
|
||||
> error, try adding `../../` to the file path, for example:
|
||||
>
|
||||
> ```shell
|
||||
> $ gdb -q -ex "target remote :3333" ../../target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
> ```
|
||||
>
|
||||
> This is caused by each example project being in a `workspace` that contains the entire book, and workspaces have
|
||||
> a single `target` directory. Check out [Workspaces chapter in Rust Book] for more.
|
||||
|
||||
### **Failing case**
|
||||
|
||||
You can detect a failing case if there is a `warning` or `error` after the `Remote debugging using :3333` line:
|
||||
```
|
||||
$ gdb -q -ex "target remote :3333" target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
Reading symbols from target/thumbv7em-none-eabihf/debug/led-roulette...
|
||||
Remote debugging using :3333
|
||||
warning: Architecture rejected target-supplied description
|
||||
Truncated register 16 in remote 'g' packet
|
||||
(gdb)
|
||||
```
|
||||
### **Successful case**
|
||||
Successful case 1:
|
||||
```
|
||||
$ arm-none-eabi-gdb -q -ex "target remote :3333" target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
Reading symbols from target/thumbv7em-none-eabihf/debug/led-roulette...
|
||||
Remote debugging using :3333
|
||||
cortex_m_rt::Reset () at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:497
|
||||
497 pub unsafe extern "C" fn Reset() -> ! {
|
||||
(gdb)
|
||||
```
|
||||
Successful case 2:
|
||||
```
|
||||
~/embedded-discovery/src/05-led-roulette (master)
|
||||
$ arm-none-eabi-gdb -q -ex "target remote :3333" target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
Reading symbols from target/thumbv7em-none-eabihf/debug/led-roulette...
|
||||
Remote debugging using :3333
|
||||
0x00000000 in ?? ()
|
||||
(gdb)
|
||||
```
|
||||
In both failing and successful cases you should see new output in the **OpenOCD terminal**, something like the following:
|
||||
``` diff
|
||||
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
|
||||
+Info : accepting 'gdb' connection on tcp/3333
|
||||
+Info : device id = 0x10036422
|
||||
+Info : flash size = 256kbytes
|
||||
```
|
||||
|
||||
> **NOTE** If you are getting an error like `undefined debug reason 7 - target needs reset`, you can try running `monitor reset halt` as described [here](https://stackoverflow.com/questions/38994596/reason-7-target-needs-reset-unreliable-debugging-setup).
|
||||
|
||||
By default OpenOCD's GDB server listens on TCP port 3333 (localhost). This command is connecting to
|
||||
that port.
|
||||
|
||||
## Update ../.cargo/config.toml
|
||||
|
||||
Now that you've successfully determined which debugger you need to use
|
||||
we need to change `../.cargo/config.toml` so that the `cargo run` command will succeed.
|
||||
|
||||
> **NOTE** `cargo` is the Rust package manager and you can read about it
|
||||
[here](https://doc.rust-lang.org/cargo/).
|
||||
|
||||
Get back to the terminal prompt and look at `../.cargo/config.toml`:
|
||||
``` console
|
||||
~/embedded-discovery/src/05-led-roulette
|
||||
$ cat ../.cargo/config.toml
|
||||
# default runner starts a GDB sesssion, which requires OpenOCD to be
|
||||
# running, e.g.,
|
||||
## openocd -f interface/stlink.cfg -f target/stm32f3x.cfg
|
||||
# depending on your local GDB, pick one of the following
|
||||
[target.thumbv7em-none-eabihf]
|
||||
runner = "arm-none-eabi-gdb -q -x ../openocd.gdb"
|
||||
# runner = "gdb-multiarch -q -x ../openocd.gdb"
|
||||
# runner = "gdb -q -x ../openocd.gdb"
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
]
|
||||
|
||||
[build]
|
||||
target = "thumbv7em-none-eabihf"
|
||||
|
||||
```
|
||||
Use your favorite editor to edit `../.cargo/config.toml` so that the
|
||||
`runner` line contains the correct name of that debugger:
|
||||
``` console
|
||||
nano ../.cargo/config.toml
|
||||
```
|
||||
For example, if your debugger was `gdb-multiarch` then after
|
||||
editing the `git diff` should be:
|
||||
``` diff
|
||||
$ git diff ../.cargo/config.toml
|
||||
diff --git a/f3discovery/src/.cargo/config.toml b/f3discovery/src/.cargo/config.toml
|
||||
index 2f38f6b..95860a0 100644
|
||||
--- a/f3discovery/src/.cargo/config.toml
|
||||
+++ b/f3discovery/src/.cargo/config.toml
|
||||
@@ -3,8 +3,8 @@
|
||||
## openocd -f interface/stlink.cfg -f target/stm32f3x.cfg
|
||||
# depending on your local GDB, pick one of the following
|
||||
[target.thumbv7em-none-eabihf]
|
||||
-runner = "arm-none-eabi-gdb -q -x ../openocd.gdb"
|
||||
-# runner = "gdb-multiarch -q -x ../openocd.gdb"
|
||||
+# runner = "arm-none-eabi-gdb -q -x ../openocd.gdb"
|
||||
+runner = "gdb-multiarch -q -x ../openocd.gdb"
|
||||
# runner = "gdb -q -x ../openocd.gdb"
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
```
|
||||
|
||||
Now that you have `../.cargo/config.toml` setup let's test it using `cargo run` to
|
||||
start the debug session.
|
||||
|
||||
> **NOTE** The `--target thumbv7em-none-eabihf` defines which architecture
|
||||
> to build and run. In our `../.cargo/config.toml` file we have
|
||||
> `target = "thumbv7em-none-eabihf"` so it is actually not necessary
|
||||
> to specify `--target` we do it here just so you know that parameters on
|
||||
> the command line can be used and they override those in `config.toml` files.
|
||||
|
||||
```
|
||||
cargo run --target thumbv7em-none-eabihf
|
||||
```
|
||||
Results in:
|
||||
```
|
||||
~/embedded-discovery/src/05-led-roulette
|
||||
$ cargo run --target thumbv7em-none-eabihf
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.14s
|
||||
Running `gdb-multiarch -q -x ../openocd.gdb /home/adam/vc/rust-training/discovery/f3discovery/target/thumbv7em-none-eabihf/debug/led-roulette`
|
||||
Reading symbols from /home/adam/vc/rust-training/discovery/f3discovery/target/thumbv7em-none-eabihf/debug/led-roulette...
|
||||
0x08000230 in core::fmt::Arguments::new_v1 (pieces=..., args=...)
|
||||
at /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/fmt/mod.rs:394
|
||||
394 /rustc/d5a82bbd26e1ad8b7401f6a718a9c57c96905483/library/core/src/fmt/mod.rs: No such file or directory.
|
||||
Loading section .vector_table, size 0x194 lma 0x8000000
|
||||
Loading section .text, size 0x1ad8 lma 0x8000194
|
||||
Loading section .rodata, size 0x5a4 lma 0x8001c6c
|
||||
Start address 0x08000194, load size 8720
|
||||
Transfer rate: 12 KB/sec, 2906 bytes/write.
|
||||
Breakpoint 1 at 0x80001e8: file src/05-led-roulette/src/main.rs, line 7.
|
||||
Note: automatically using hardware breakpoints for read-only addresses.
|
||||
Breakpoint 2 at 0x800020a: file src/lib.rs, line 570.
|
||||
Breakpoint 3 at 0x8001c5a: file src/lib.rs, line 560.
|
||||
|
||||
Breakpoint 1, led_roulette::__cortex_m_rt_main_trampoline () at src/05-led-roulette/src/main.rs:7
|
||||
7 #[entry]
|
||||
halted: PC: 0x080001ee
|
||||
led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:10
|
||||
10 let x = 42;
|
||||
```
|
||||
|
||||
Bravo, we will be modifying `../.cargo/config.toml` in future. **But**, since
|
||||
this file is shared with all of the chapters those changes should be made with
|
||||
that in mind. If you want or we need to make changes that only pertain to
|
||||
a particular chapter then create a `.cargo/config.toml` local to that chapter
|
||||
directory.
|
||||
|
||||
## Flash the device
|
||||
|
||||
Assuming you have GDB running, if not start it as suggested in the previous section.
|
||||
|
||||
> **NOTE** The `-x ../openocd.gdb` arguments to `gdb` is already setup
|
||||
> to flash the device, so explicitly flashing the project code to the
|
||||
> device is normally handled with a simple `cargo run`. We'll cover
|
||||
> the openocd configuration script in the next section.
|
||||
|
||||
Now use the `load` command in `gdb` to actually flash the program into the device:
|
||||
```
|
||||
(gdb) load
|
||||
Loading section .vector_table, size 0x194 lma 0x8000000
|
||||
Loading section .text, size 0x20ec lma 0x8000194
|
||||
Loading section .rodata, size 0x514 lma 0x8002280
|
||||
Start address 0x08000194, load size 10132
|
||||
Transfer rate: 17 KB/sec, 3377 bytes/write.
|
||||
```
|
||||
|
||||
You'll also see new output in the OpenOCD terminal, something like:
|
||||
|
||||
``` diff
|
||||
Info : flash size = 256kbytes
|
||||
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
+adapter speed: 950 kHz
|
||||
+target halted due to debug-request, current mode: Thread
|
||||
+xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
|
||||
+Info : Unable to match requested speed 8000 kHz, using 4000 kHz
|
||||
+Info : Unable to match requested speed 8000 kHz, using 4000 kHz
|
||||
+adapter speed: 4000 kHz
|
||||
+target halted due to breakpoint, current mode: Thread
|
||||
+xPSR: 0x61000000 pc: 0x2000003a msp: 0x2000a000
|
||||
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
+Info : Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
+adapter speed: 950 kHz
|
||||
+target halted due to debug-request, current mode: Thread
|
||||
+xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
|
||||
```
|
||||
|
||||
Our program is loaded, let's debug it!
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
# My solution
|
||||
|
||||
What solution did you come up with?
|
||||
|
||||
Here's mine:
|
||||
|
||||
``` rust
|
||||
{{#include examples/my-solution.rs}}
|
||||
```
|
||||
|
||||
One more thing! Check that your solution also works when compiled in "release" mode:
|
||||
|
||||
``` console
|
||||
$ cargo build --target thumbv7em-none-eabihf --release
|
||||
```
|
||||
|
||||
You can test it with this `gdb` command:
|
||||
|
||||
``` console
|
||||
$ # or, you could simply call `cargo run --target thumbv7em-none-eabihf --release`
|
||||
$ arm-none-eabi-gdb target/thumbv7em-none-eabihf/release/led-roulette
|
||||
$ # ~~~~~~~
|
||||
```
|
||||
|
||||
Binary size is something we should always keep an eye on! How big is your solution? You can check
|
||||
that using the `size` command on the release binary:
|
||||
|
||||
``` console
|
||||
$ # equivalent to size target/thumbv7em-none-eabihf/debug/led-roulette
|
||||
$ cargo size --target thumbv7em-none-eabihf --bin led-roulette -- -A
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
|
||||
led-roulette :
|
||||
section size addr
|
||||
.vector_table 404 0x8000000
|
||||
.text 21144 0x8000194
|
||||
.rodata 3144 0x800542c
|
||||
.data 0 0x20000000
|
||||
.bss 4 0x20000000
|
||||
.uninit 0 0x20000004
|
||||
.debug_abbrev 19160 0x0
|
||||
.debug_info 471239 0x0
|
||||
.debug_aranges 18376 0x0
|
||||
.debug_ranges 102536 0x0
|
||||
.debug_str 508618 0x0
|
||||
.debug_pubnames 76975 0x0
|
||||
.debug_pubtypes 112797 0x0
|
||||
.ARM.attributes 58 0x0
|
||||
.debug_frame 55848 0x0
|
||||
.debug_line 282067 0x0
|
||||
.debug_loc 845 0x0
|
||||
.comment 147 0x0
|
||||
Total 1673362
|
||||
|
||||
|
||||
$ cargo size --target thumbv7em-none-eabihf --bin led-roulette --release -- -A
|
||||
Finished release [optimized + debuginfo] target(s) in 0.03s
|
||||
led-roulette :
|
||||
section size addr
|
||||
.vector_table 404 0x8000000
|
||||
.text 5380 0x8000194
|
||||
.rodata 564 0x8001698
|
||||
.data 0 0x20000000
|
||||
.bss 4 0x20000000
|
||||
.uninit 0 0x20000004
|
||||
.debug_loc 9994 0x0
|
||||
.debug_abbrev 1821 0x0
|
||||
.debug_info 74974 0x0
|
||||
.debug_aranges 600 0x0
|
||||
.debug_ranges 6848 0x0
|
||||
.debug_str 52828 0x0
|
||||
.debug_pubnames 20821 0x0
|
||||
.debug_pubtypes 18891 0x0
|
||||
.ARM.attributes 58 0x0
|
||||
.debug_frame 1088 0x0
|
||||
.debug_line 15307 0x0
|
||||
.comment 19 0x0
|
||||
Total 209601
|
||||
```
|
||||
|
||||
> **NOTE** The Cargo project is already configured to build the release binary using LTO.
|
||||
|
||||
Know how to read this output? The `text` section contains the program instructions. It's around 5.25KB
|
||||
in my case. On the other hand, the `data` and `bss` sections contain variables statically allocated
|
||||
in RAM (`static` variables). A `static` variable is being used in `aux5::init`; that's why it shows 4
|
||||
bytes of `bss`.
|
||||
|
||||
One final thing! We have been running our programs from within GDB but our programs doesn't depend on
|
||||
GDB at all. You can confirm this be closing both GDB and OpenOCD and then resetting the board by
|
||||
pressing the black button on the board. The LED roulette application will run without intervention
|
||||
of GDB.
|
||||
15
discovery-master/f3discovery/src/05-led-roulette/src/main.rs
Normal file
15
discovery-master/f3discovery/src/05-led-roulette/src/main.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use aux5::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let _y;
|
||||
let x = 42;
|
||||
_y = x;
|
||||
|
||||
// infinite loop; just so we don't leave this stack frame
|
||||
loop {}
|
||||
}
|
||||
1
discovery-master/f3discovery/src/05-led-roulette/target
Symbolic link
1
discovery-master/f3discovery/src/05-led-roulette/target
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../target
|
||||
|
|
@ -0,0 +1,152 @@
|
|||
# The challenge
|
||||
|
||||
You are now well armed to face a challenge! Your task will be to implement the application I showed
|
||||
you at the beginning of this chapter.
|
||||
|
||||
Here's the GIF again:
|
||||
|
||||
<p align="center">
|
||||
<img src="https://i.imgur.com/0k1r2Lc.gif">
|
||||
</p>
|
||||
|
||||
Also, this may help:
|
||||
|
||||
<p align="center">
|
||||
<img class="white_bg" src="../assets/timing-diagram.png">
|
||||
</p>
|
||||
|
||||
This is a timing diagram. It indicates which LED is on at any given instant of time and for how long
|
||||
each LED should be on. On the X axis we have the time in milliseconds. The timing diagram shows a
|
||||
single period. This pattern will repeat itself every 800 ms. The Y axis labels each LED with a
|
||||
cardinal point: North, East, etc. As part of the challenge you'll have to figure out how each
|
||||
element in the `Leds` array maps to these cardinal points (hint: `cargo doc --open` `;-)`).
|
||||
|
||||
Before you attempt this challenge, let me give you one additonal tip. Our GDB sessions always involve
|
||||
entering the same commands at the beginning. We can use a `.gdb` file to execute some commands
|
||||
right after GDB is started. This way you can save yourself the effort of having to enter them
|
||||
manually on each GDB session.
|
||||
|
||||
As it turns out we've already created `../openocd.gdb` and you can see it's doing
|
||||
pretty much what we did in the previous section plus a few other commands. Look at
|
||||
the comments for additional information:
|
||||
|
||||
``` console
|
||||
$ cat ../openocd.gdb
|
||||
# Connect to gdb remote server
|
||||
target remote :3333
|
||||
|
||||
# Load will flash the code
|
||||
load
|
||||
|
||||
# Eanble demangling asm names on disassembly
|
||||
set print asm-demangle on
|
||||
|
||||
# Enable pretty printing
|
||||
set print pretty on
|
||||
|
||||
# Disable style sources as the default colors can be hard to read
|
||||
set style sources off
|
||||
|
||||
# Initialize monitoring so iprintln! macro output
|
||||
# is sent from the itm port to itm.txt
|
||||
monitor tpiu config internal itm.txt uart off 8000000
|
||||
|
||||
# Turn on the itm port
|
||||
monitor itm port 0 on
|
||||
|
||||
# Set a breakpoint at main, aka entry
|
||||
break main
|
||||
|
||||
# Set a breakpiont at DefaultHandler
|
||||
break DefaultHandler
|
||||
|
||||
# Set a breakpiont at HardFault
|
||||
break HardFault
|
||||
|
||||
# Continue running and until we hit the main breakpoint
|
||||
continue
|
||||
|
||||
# Step from the trampoline code in entry into main
|
||||
step
|
||||
|
||||
```
|
||||
|
||||
Now we need to modify the `../.cargo/config.toml` file to execute `../openocd.gdb`
|
||||
``` console
|
||||
nano ../.cargo/config.toml
|
||||
```
|
||||
|
||||
Edit your `runner` command ` -x ../openocd.gdb`.
|
||||
Assuming you're using `arm-none-eabi-gdb` the diff is:
|
||||
``` diff
|
||||
~/embedded-discovery/src/05-led-roulette
|
||||
$ git diff ../.cargo/config.toml
|
||||
diff --git a/src/.cargo/config.toml b/src/.cargo/config.toml
|
||||
index ddff17f..02ac952 100644
|
||||
--- a/src/.cargo/config.toml
|
||||
+++ b/src/.cargo/config.toml
|
||||
@@ -1,5 +1,5 @@
|
||||
[target.thumbv7em-none-eabihf]
|
||||
-runner = "arm-none-eabi-gdb -q"
|
||||
+runner = "arm-none-eabi-gdb -q -x ../openocd.gdb"
|
||||
# runner = "gdb-multiarch -q"
|
||||
# runner = "gdb -q"
|
||||
rustflags = [
|
||||
```
|
||||
|
||||
And the full contents of `../.cargo/config.toml`, again
|
||||
assuming `arm-none-eabi-gdb`, is:
|
||||
``` toml
|
||||
[target.thumbv7em-none-eabihf]
|
||||
runner = "arm-none-eabi-gdb -q -x ../openocd.gdb"
|
||||
# runner = "gdb-multiarch -q"
|
||||
# runner = "gdb -q"
|
||||
rustflags = [
|
||||
"-C", "link-arg=-Tlink.x",
|
||||
]
|
||||
|
||||
[build]
|
||||
target = "thumbv7em-none-eabihf"
|
||||
|
||||
```
|
||||
|
||||
With that in place, you can now use a simple `cargo run` command which will build
|
||||
the ARM version of the code and run the `gdb` session. The `gdb` session will
|
||||
automatically flash the program and jump to the beginning of `main` as it `step`'s
|
||||
through the entry trampoline:
|
||||
|
||||
``` console
|
||||
cargo run
|
||||
```
|
||||
|
||||
``` console
|
||||
~/embedded-discovery/src/05-led-roulette (Update-05-led-roulette-WIP)
|
||||
$ cargo run
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
|
||||
Running `arm-none-eabi-gdb -q -x openocd.gdb ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/led-roulette`
|
||||
Reading symbols from ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/led-roulette...
|
||||
led_roulette::__cortex_m_rt_main_trampoline () at ~/embedded-discovery/src/05-led-roulette/src/main.rs:7
|
||||
7 #[entry]
|
||||
Loading section .vector_table, size 0x194 lma 0x8000000
|
||||
Loading section .text, size 0x52c0 lma 0x8000194
|
||||
Loading section .rodata, size 0xb50 lma 0x8005454
|
||||
Start address 0x08000194, load size 24484
|
||||
Transfer rate: 21 KB/sec, 6121 bytes/write.
|
||||
Breakpoint 1 at 0x8000202: file ~/embedded-discovery/src/05-led-roulette/src/main.rs, line 7.
|
||||
Note: automatically using hardware breakpoints for read-only addresses.
|
||||
|
||||
Breakpoint 1, led_roulette::__cortex_m_rt_main_trampoline ()
|
||||
at ~/embedded-discovery/src/05-led-roulette/src/main.rs:7
|
||||
7 #[entry]
|
||||
led_roulette::__cortex_m_rt_main () at ~/embedded-discovery/src/05-led-roulette/src/main.rs:9
|
||||
9 let (mut delay, mut leds): (Delay, LedArray) = aux5::init();
|
||||
```
|
||||
|
||||
## Fork the discovery book
|
||||
|
||||
If you haven't already ready, it's probably a good idea to fork
|
||||
the [embedded discovery book](https://github.com/rust-embedded/discovery) so you
|
||||
can save your changes in your own branch of your fork. We suggest creating
|
||||
your own branch and leaving the `master` branch alone so the `master` branch
|
||||
of your fork can stay in sync with the upstream repo. Also, it allows you to
|
||||
more easily create PR's and improve this book, **thank you in advance**!
|
||||
|
|
@ -0,0 +1,552 @@
|
|||
# The `Led` and `Delay` abstractions
|
||||
|
||||
Now, I'm going to introduce two high level abstractions that we'll use to implement the LED roulette
|
||||
application.
|
||||
|
||||
The auxiliary crate, `aux5`, exposes an initialization function called `init`. When called this
|
||||
function returns two values packed in a tuple: a `Delay` value and a `LedArray` value.
|
||||
|
||||
`Delay` can be used to block your program for a specified amount of milliseconds.
|
||||
|
||||
`LedArray` is an array of eight `Led`s. Each `Led` represents one of the LEDs on the F3 board,
|
||||
and exposes two methods: `on` and `off` which can be used to turn the LED on or off, respectively.
|
||||
|
||||
Let's try out these two abstractions by modifying the starter code to look like this:
|
||||
|
||||
``` rust
|
||||
{{#include examples/the-led-and-delay-abstractions.rs}}
|
||||
```
|
||||
|
||||
Now build it:
|
||||
``` console
|
||||
cargo build
|
||||
```
|
||||
|
||||
> **NOTE**: It's possible to forget to rebuild the program *before* starting a GDB session; this
|
||||
> omission can lead to very confusing debug sessions. To avoid this problem you can call just `cargo run`
|
||||
> instead of `cargo build`. The `cargo run` command will build *and* start a debug
|
||||
> session ensuring you never forget to recompile your program.
|
||||
|
||||
Now we'll run and repeat the flashing procedure as we did in the previous section
|
||||
but with the new program. I'll let you type in the `cargo run`, *this will get easier shortly*. :)
|
||||
|
||||
> **NOTE**: Don't forget to start ```openocd``` (debugger) on a separate terminal.
|
||||
> Otherwise `target remote :3333` won't work!
|
||||
|
||||
``` console
|
||||
$ cargo run
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
|
||||
Running `arm-none-eabi-gdb -q ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/led-roulette`
|
||||
Reading symbols from ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/led-roulette...
|
||||
|
||||
(gdb) target remote :3333
|
||||
Remote debugging using :3333
|
||||
led_roulette::__cortex_m_rt_main_trampoline () at ~/embedded-discovery/src/05-led-roulette/src/main.rs:7
|
||||
7 #[entry]
|
||||
|
||||
(gdb) load
|
||||
Loading section .vector_table, size 0x194 lma 0x8000000
|
||||
Loading section .text, size 0x52c0 lma 0x8000194
|
||||
Loading section .rodata, size 0xb50 lma 0x8005454
|
||||
Start address 0x08000194, load size 24484
|
||||
Transfer rate: 21 KB/sec, 6121 bytes/write.
|
||||
|
||||
(gdb) break main
|
||||
Breakpoint 1 at 0x8000202: file ~/embedded-discovery/src/05-led-roulette/src/main.rs, line 7.
|
||||
Note: automatically using hardware breakpoints for read-only addresses.
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, led_roulette::__cortex_m_rt_main_trampoline ()
|
||||
at ~/embedded-discovery/src/05-led-roulette/src/main.rs:7
|
||||
7 #[entry]
|
||||
|
||||
(gdb) step
|
||||
led_roulette::__cortex_m_rt_main () at ~/embedded-discovery/src/05-led-roulette/src/main.rs:9
|
||||
9 let (mut delay, mut leds): (Delay, LedArray) = aux5::init();
|
||||
|
||||
(gdb)
|
||||
```
|
||||
|
||||
OK. Let's step through the code. This time, we'll use the `next` command instead of `step`. The
|
||||
difference is that the `next` command will step *over* function calls instead of going inside them.
|
||||
```
|
||||
(gdb) next
|
||||
11 let half_period = 500_u16;
|
||||
|
||||
(gdb) next
|
||||
13 loop {
|
||||
|
||||
(gdb) next
|
||||
14 leds[0].on().ok();
|
||||
|
||||
(gdb) next
|
||||
15 delay.delay_ms(half_period);
|
||||
```
|
||||
|
||||
After executing the `leds[0].on().ok()` statement, you should see a red LED, the one pointing North,
|
||||
turn on.
|
||||
|
||||
Let's continue stepping over the program:
|
||||
|
||||
```
|
||||
(gdb) next
|
||||
17 leds[0].off().ok();
|
||||
|
||||
(gdb) next
|
||||
18 delay.delay_ms(half_period);
|
||||
```
|
||||
|
||||
The `delay_ms` call will block the program for half a second but you may not notice because the
|
||||
`next` command also takes some time to execute. However, after stepping over the `leds[0].off()`
|
||||
statement you should see the red LED turn off.
|
||||
|
||||
You can already guess what this program does. Let it run uninterrupted using the `continue` command.
|
||||
|
||||
```
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
```
|
||||
|
||||
Now, let's do something more interesting. We are going to modify the behavior of our program using
|
||||
GDB.
|
||||
|
||||
First, let's stop the infinite loop by hitting `Ctrl+C`. You'll probably end up somewhere inside
|
||||
`Led::on`, `Led::off` or `delay_ms`:
|
||||
|
||||
```
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
0x08003434 in core::ptr::read_volatile<u32> (src=0xe000e010)
|
||||
at ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:1053
|
||||
```
|
||||
|
||||
In my case, the program stopped its execution inside a `read_volatile` function. GDB output shows
|
||||
some interesting information about that: `core::ptr::read_volatile (src=0xe000e010)`. This means
|
||||
that the function comes from the `core` crate and that it was called with argument `src =
|
||||
0xe000e010`.
|
||||
|
||||
Just so you know, a more explicit way to show the arguments of a function is to use the `info args`
|
||||
command:
|
||||
|
||||
```
|
||||
(gdb) info args
|
||||
src = 0xe000e010
|
||||
```
|
||||
|
||||
Regardless of where your program may have stopped you can always look at the output of the
|
||||
`backtrace` command (`bt` for short) to learn how it got there:
|
||||
|
||||
```
|
||||
(gdb) backtrace
|
||||
#0 0x08003434 in core::ptr::read_volatile<u32> (src=0xe000e010)
|
||||
at ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:1053
|
||||
#1 0x08002d66 in vcell::VolatileCell<u32>::get<u32> (self=0xe000e010) at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/vcell-0.1.3/src/lib.rs:33
|
||||
#2 volatile_register::RW<u32>::read<u32> (self=0xe000e010) at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/volatile-register-0.2.0/src/lib.rs:75
|
||||
#3 cortex_m::peripheral::SYST::has_wrapped (self=0x20009fa4)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.6.4/src/peripheral/syst.rs:136
|
||||
#4 0x08003004 in stm32f3xx_hal::delay::{{impl}}::delay_us (self=0x20009fa4, us=500000)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f3xx-hal-0.5.0/src/delay.rs:58
|
||||
#5 0x08002f3e in stm32f3xx_hal::delay::{{impl}}::delay_ms (self=0x20009fa4, ms=500)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f3xx-hal-0.5.0/src/delay.rs:32
|
||||
#6 0x08002f80 in stm32f3xx_hal::delay::{{impl}}::delay_ms (self=0x20009fa4, ms=500)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f3xx-hal-0.5.0/src/delay.rs:38
|
||||
#7 0x0800024c in led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:15
|
||||
#8 0x08000206 in led_roulette::__cortex_m_rt_main_trampoline () at src/05-led-roulette/src/main.rs:7
|
||||
```
|
||||
|
||||
`backtrace` will print a trace of function calls from the current function down to main.
|
||||
|
||||
Back to our topic. To do what we are after, first, we have to return to the `main` function. We can
|
||||
do that using the `finish` command. This command resumes the program execution and stops it again
|
||||
right after the program returns from the current function. We'll have to call it several times.
|
||||
|
||||
```
|
||||
(gdb) finish
|
||||
Run till exit from #0 0x08003434 in core::ptr::read_volatile<u32> (src=0xe000e010)
|
||||
at ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:1053
|
||||
cortex_m::peripheral::SYST::has_wrapped (self=0x20009fa4)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.6.4/src/peripheral/syst.rs:136
|
||||
136 self.csr.read() & SYST_CSR_COUNTFLAG != 0
|
||||
Value returned is $1 = 5
|
||||
|
||||
(..)
|
||||
|
||||
(gdb) finish
|
||||
Run till exit from #0 0x08002f3e in stm32f3xx_hal::delay::{{impl}}::delay_ms (self=0x20009fa4, ms=500)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f3xx-hal-0.5.0/src/delay.rs:32
|
||||
0x08002f80 in stm32f3xx_hal::delay::{{impl}}::delay_ms (self=0x20009fa4, ms=500)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f3xx-hal-0.5.0/src/delay.rs:38
|
||||
38 self.delay_ms(u32(ms));
|
||||
|
||||
(gdb) finish
|
||||
Run till exit from #0 0x08002f80 in stm32f3xx_hal::delay::{{impl}}::delay_ms (self=0x20009fa4, ms=500)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/stm32f3xx-hal-0.5.0/src/delay.rs:38
|
||||
0x0800024c in led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:15
|
||||
15 delay.delay_ms(half_period);
|
||||
```
|
||||
|
||||
We are back in `main`. We have a local variable in here: `half_period`
|
||||
|
||||
```
|
||||
(gdb) print half_period
|
||||
$3 = 500
|
||||
```
|
||||
|
||||
Now, we are going to modify this variable using the `set` command:
|
||||
|
||||
```
|
||||
(gdb) set half_period = 100
|
||||
|
||||
(gdb) print half_period
|
||||
$5 = 100
|
||||
```
|
||||
|
||||
If you let program run free again using the `continue` command, you **might** see that the LED will
|
||||
blink at a much faster rate now, but more likely the blink rate didn't change. **What happened?**
|
||||
|
||||
Let's stop the program with `Ctrl+C` and then set a break point at `main:14`.
|
||||
``` console
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
core::cell::UnsafeCell<u32>::get<u32> (self=0x20009fa4)
|
||||
at ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/cell.rs:1711
|
||||
1711 pub const fn get(&self) -> *mut T {
|
||||
```
|
||||
|
||||
Then set a break point at `main.rs:14` and `continue`
|
||||
|
||||
``` console
|
||||
(gdb) break main.rs:14
|
||||
Breakpoint 2 at 0x8000236: file src/05-led-roulette/src/main.rs, line 14.
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 2, led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:14
|
||||
14 leds[0].on().ok();
|
||||
```
|
||||
|
||||
Now open your terminal window so it's about 80 lines long an 170 characters wide if possible.
|
||||
> **NOTE**: If you can't open the terminal that large, no problem you'll just see
|
||||
> `--Type <RET> for more, q to quit, c to continue without paging--` so just type return
|
||||
> until you see the `(gdb)` prompt. Then scroll your terminal window to
|
||||
> see the results.
|
||||
|
||||
``` console
|
||||
(gdb) disassemble /m
|
||||
Dump of assembler code for function _ZN12led_roulette18__cortex_m_rt_main17h51e7c3daad2af251E:
|
||||
8 fn main() -> ! {
|
||||
0x08000208 <+0>: push {r7, lr}
|
||||
0x0800020a <+2>: mov r7, sp
|
||||
0x0800020c <+4>: sub sp, #64 ; 0x40
|
||||
0x0800020e <+6>: add r0, sp, #32
|
||||
|
||||
9 let (mut delay, mut leds): (Delay, LedArray) = aux5::init();
|
||||
0x08000210 <+8>: bl 0x8000302 <aux5::init>
|
||||
0x08000214 <+12>: b.n 0x8000216 <led_roulette::__cortex_m_rt_main+14>
|
||||
0x08000216 <+14>: add r0, sp, #32
|
||||
0x08000218 <+16>: add r1, sp, #4
|
||||
0x0800021a <+18>: ldmia.w r0, {r2, r3, r4, r12, lr}
|
||||
0x0800021e <+22>: stmia.w r1, {r2, r3, r4, r12, lr}
|
||||
0x08000222 <+26>: ldr r0, [sp, #52] ; 0x34
|
||||
0x08000224 <+28>: ldr r1, [sp, #56] ; 0x38
|
||||
0x08000226 <+30>: str r1, [sp, #28]
|
||||
0x08000228 <+32>: str r0, [sp, #24]
|
||||
0x0800022a <+34>: mov.w r0, #500 ; 0x1f4
|
||||
|
||||
10
|
||||
11 let half_period = 500_u16;
|
||||
0x0800022e <+38>: strh.w r0, [r7, #-2]
|
||||
|
||||
12
|
||||
13 loop {
|
||||
0x08000232 <+42>: b.n 0x8000234 <led_roulette::__cortex_m_rt_main+44>
|
||||
0x08000234 <+44>: add r0, sp, #24
|
||||
0x08000268 <+96>: b.n 0x8000234 <led_roulette::__cortex_m_rt_main+44>
|
||||
|
||||
14 leds[0].on().ok();
|
||||
=> 0x08000236 <+46>: bl 0x80001ec <switch_hal::output::{{impl}}::on<stm32f3xx_hal::gpio::gpioe::PEx<stm32f3xx_hal::gpio::Output<stm32f3xx_hal::gpio::PushPull>>>>
|
||||
0x0800023a <+50>: b.n 0x800023c <led_roulette::__cortex_m_rt_main+52>
|
||||
0x0800023c <+52>: bl 0x8000594 <core::result::Result<(), core::convert::Infallible>::ok<(),core::convert::Infallible>>
|
||||
0x08000240 <+56>: b.n 0x8000242 <led_roulette::__cortex_m_rt_main+58>
|
||||
0x08000242 <+58>: add r0, sp, #4
|
||||
0x08000244 <+60>: mov.w r1, #500 ; 0x1f4
|
||||
|
||||
15 delay.delay_ms(half_period);
|
||||
0x08000248 <+64>: bl 0x8002f5c <stm32f3xx_hal::delay::{{impl}}::delay_ms>
|
||||
0x0800024c <+68>: b.n 0x800024e <led_roulette::__cortex_m_rt_main+70>
|
||||
0x0800024e <+70>: add r0, sp, #24
|
||||
|
||||
16
|
||||
17 leds[0].off().ok();
|
||||
0x08000250 <+72>: bl 0x800081a <switch_hal::output::{{impl}}::off<stm32f3xx_hal::gpio::gpioe::PEx<stm32f3xx_hal::gpio::Output<stm32f3xx_hal::gpio::PushPull>>>>
|
||||
0x08000254 <+76>: b.n 0x8000256 <led_roulette::__cortex_m_rt_main+78>
|
||||
0x08000256 <+78>: bl 0x8000594 <core::result::Result<(), core::convert::Infallible>::ok<(),core::convert::Infallible>>
|
||||
0x0800025a <+82>: b.n 0x800025c <led_roulette::__cortex_m_rt_main+84>
|
||||
0x0800025c <+84>: add r0, sp, #4
|
||||
0x0800025e <+86>: mov.w r1, #500 ; 0x1f4
|
||||
|
||||
18 delay.delay_ms(half_period);
|
||||
0x08000262 <+90>: bl 0x8002f5c <stm32f3xx_hal::delay::{{impl}}::delay_ms>
|
||||
0x08000266 <+94>: b.n 0x8000268 <led_roulette::__cortex_m_rt_main+96>
|
||||
|
||||
End of assembler dump.
|
||||
```
|
||||
|
||||
In the above dump the reason the delay didn't change was because the compiler
|
||||
recognized that half_period didn't change and instead in the two places where
|
||||
`delay.delay_ms(half_period);` is called we see `mov.w r1, #500`. So changing the
|
||||
value of `half_period` does nothing!
|
||||
|
||||
``` console
|
||||
0x08000244 <+60>: mov.w r1, #500 ; 0x1f4
|
||||
|
||||
15 delay.delay_ms(half_period);
|
||||
0x08000248 <+64>: bl 0x8002f5c <stm32f3xx_hal::delay::{{impl}}::delay_ms>
|
||||
|
||||
(..)
|
||||
|
||||
0x0800025e <+86>: mov.w r1, #500 ; 0x1f4
|
||||
|
||||
18 delay.delay_ms(half_period);
|
||||
0x08000262 <+90>: bl 0x8002f5c <stm32f3xx_hal::delay::{{impl}}::delay_ms>
|
||||
```
|
||||
|
||||
One solution to the problem is to wrap `half_period` in a `Volatile` as shown below.
|
||||
|
||||
``` console
|
||||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use volatile::Volatile;
|
||||
use aux5::{Delay, DelayMs, LedArray, OutputSwitch, entry};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (mut delay, mut leds): (Delay, LedArray) = aux5::init();
|
||||
|
||||
let mut half_period = 500_u16;
|
||||
let v_half_period = Volatile::new(&mut half_period);
|
||||
|
||||
loop {
|
||||
leds[0].on().ok();
|
||||
delay.delay_ms(v_half_period.read());
|
||||
|
||||
leds[0].off().ok();
|
||||
delay.delay_ms(v_half_period.read());
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Edit `Cargo.toml` adding `volatile = "0.4.3"` in the `[dependencies]` section.
|
||||
|
||||
``` console
|
||||
[dependencies]
|
||||
aux5 = { path = "auxiliary" }
|
||||
volatile = "0.4.3"
|
||||
```
|
||||
|
||||
With the above code using `Volatile` you can now change `half_period` and
|
||||
you'll be able to experiment with different values. Here is the list of
|
||||
commands followed by an explanation; `# xxxx` to demonstrate.
|
||||
|
||||
```
|
||||
$ cargo run --target thumbv7em-none-eabihf # Compile and load the program into gdb
|
||||
(gdb) target remote :3333 # Connect to STM32F3DISCOVERY board from PC
|
||||
(gdb) load # Flash program
|
||||
(gdb) break main.rs:16 # Set breakpoint 1 at top of loop
|
||||
(gdb) continue # Continue, will stop at main.rs:16
|
||||
(gdb) disable 1 # Disable breakpoint 1
|
||||
(gdb) set print asm-demangle on # Enable asm-demangle
|
||||
(gdb) disassemble /m # Disassemble main function
|
||||
(gdb) continue # Led blinking on for 1/2 sec then off 1/2 sec
|
||||
^C # Stop with Ctrl+C
|
||||
(gdb) enable 1 # Enable breakpoint 1
|
||||
(gdb) continue # Continue, will stop at main.rs:16
|
||||
(gdb) print half_period # Print half_period result is 500
|
||||
(gdb) set half_period = 2000 # Set half_period to 2000ms
|
||||
(gdb) print half_period # Print half_period and result is 2000
|
||||
(gdb) disable 1 # Disable breakpoint 1
|
||||
(gdb) continue # Led blinking on for 2 secs then off 2 sec
|
||||
^C # Stop with Ctrl+C
|
||||
(gdb) quit # Quit gdb
|
||||
```
|
||||
|
||||
The critical changes are at lines 13, 17 and 20 in the source code which
|
||||
you can see in the disassembly. At 13 we create `v_half_period` and then
|
||||
`read()` its value in lines 17 and 20. This means that when we `set half_period = 2000`
|
||||
the led will now be on for 2 seconds then off for 2 seconds.
|
||||
|
||||
``` console
|
||||
$ cargo run --target thumbv7em-none-eabihf
|
||||
Compiling led-roulette v0.2.0 (~/embedded-discovery/src/05-led-roulette)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.18s
|
||||
Running `arm-none-eabi-gdb -q ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/led-roulette`
|
||||
Reading symbols from ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/led-roulette...
|
||||
|
||||
(gdb) target remote :3333
|
||||
Remote debugging using :3333
|
||||
led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:16
|
||||
16 leds[0].on().ok();
|
||||
|
||||
(gdb) load
|
||||
Loading section .vector_table, size 0x194 lma 0x8000000
|
||||
Loading section .text, size 0x5258 lma 0x8000194
|
||||
Loading section .rodata, size 0xbd8 lma 0x80053ec
|
||||
Start address 0x08000194, load size 24516
|
||||
Transfer rate: 21 KB/sec, 6129 bytes/write.
|
||||
|
||||
(gdb) break main.rs:16
|
||||
Breakpoint 1 at 0x8000246: file src/05-led-roulette/src/main.rs, line 16.
|
||||
Note: automatically using hardware breakpoints for read-only addresses.
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:16
|
||||
16 leds[0].on().ok();
|
||||
|
||||
(gdb) disable 1
|
||||
|
||||
(gdb) set print asm-demangle on
|
||||
|
||||
(gdb) disassemble /m
|
||||
Dump of assembler code for function _ZN12led_roulette18__cortex_m_rt_main17he1f2bc7990b13731E:
|
||||
9 fn main() -> ! {
|
||||
0x0800020e <+0>: push {r7, lr}
|
||||
0x08000210 <+2>: mov r7, sp
|
||||
0x08000212 <+4>: sub sp, #72 ; 0x48
|
||||
0x08000214 <+6>: add r0, sp, #36 ; 0x24
|
||||
|
||||
10 let (mut delay, mut leds): (Delay, LedArray) = aux5::init();
|
||||
0x08000216 <+8>: bl 0x800036a <aux5::init>
|
||||
0x0800021a <+12>: b.n 0x800021c <led_roulette::__cortex_m_rt_main+14>
|
||||
0x0800021c <+14>: add r0, sp, #36 ; 0x24
|
||||
0x0800021e <+16>: add r1, sp, #8
|
||||
0x08000220 <+18>: ldmia.w r0, {r2, r3, r4, r12, lr}
|
||||
0x08000224 <+22>: stmia.w r1, {r2, r3, r4, r12, lr}
|
||||
0x08000228 <+26>: ldr r0, [sp, #56] ; 0x38
|
||||
0x0800022a <+28>: ldr r1, [sp, #60] ; 0x3c
|
||||
0x0800022c <+30>: str r1, [sp, #32]
|
||||
0x0800022e <+32>: str r0, [sp, #28]
|
||||
0x08000230 <+34>: mov.w r0, #500 ; 0x1f4
|
||||
|
||||
11
|
||||
12 let mut half_period = 500_u16;
|
||||
0x08000234 <+38>: strh.w r0, [r7, #-6]
|
||||
0x08000238 <+42>: subs r0, r7, #6
|
||||
|
||||
13 let v_half_period = Volatile::new(&mut half_period);
|
||||
0x0800023a <+44>: bl 0x800033e <volatile::Volatile<&mut u16, volatile::access::ReadWrite>::new<&mut u16>>
|
||||
0x0800023e <+48>: str r0, [sp, #68] ; 0x44
|
||||
0x08000240 <+50>: b.n 0x8000242 <led_roulette::__cortex_m_rt_main+52>
|
||||
|
||||
14
|
||||
15 loop {
|
||||
0x08000242 <+52>: b.n 0x8000244 <led_roulette::__cortex_m_rt_main+54>
|
||||
0x08000244 <+54>: add r0, sp, #28
|
||||
0x08000288 <+122>: b.n 0x8000244 <led_roulette::__cortex_m_rt_main+54>
|
||||
|
||||
16 leds[0].on().ok();
|
||||
=> 0x08000246 <+56>: bl 0x800032c <switch_hal::output::{{impl}}::on<stm32f3xx_hal::gpio::gpioe::PEx<stm32f3xx_hal::gpio::Output<stm32f3xx_hal::gpio::PushPull>>>>
|
||||
0x0800024a <+60>: b.n 0x800024c <led_roulette::__cortex_m_rt_main+62>
|
||||
0x0800024c <+62>: bl 0x80005fc <core::result::Result<(), core::convert::Infallible>::ok<(),core::convert::Infallible>>
|
||||
0x08000250 <+66>: b.n 0x8000252 <led_roulette::__cortex_m_rt_main+68>
|
||||
0x08000252 <+68>: add r0, sp, #68 ; 0x44
|
||||
|
||||
17 delay.delay_ms(v_half_period.read());
|
||||
0x08000254 <+70>: bl 0x800034a <volatile::Volatile<&mut u16, volatile::access::ReadWrite>::read<&mut u16,u16,volatile::access::ReadWrite>>
|
||||
0x08000258 <+74>: str r0, [sp, #4]
|
||||
0x0800025a <+76>: b.n 0x800025c <led_roulette::__cortex_m_rt_main+78>
|
||||
0x0800025c <+78>: add r0, sp, #8
|
||||
0x0800025e <+80>: ldr r1, [sp, #4]
|
||||
0x08000260 <+82>: bl 0x8002fc4 <stm32f3xx_hal::delay::{{impl}}::delay_ms>
|
||||
0x08000264 <+86>: b.n 0x8000266 <led_roulette::__cortex_m_rt_main+88>
|
||||
0x08000266 <+88>: add r0, sp, #28
|
||||
|
||||
18
|
||||
19 leds[0].off().ok();
|
||||
0x08000268 <+90>: bl 0x8000882 <switch_hal::output::{{impl}}::off<stm32f3xx_hal::gpio::gpioe::PEx<stm32f3xx_hal::gpio::Output<stm32f3xx_hal::gpio::PushPull>>>>
|
||||
0x0800026c <+94>: b.n 0x800026e <led_roulette::__cortex_m_rt_main+96>
|
||||
0x0800026e <+96>: bl 0x80005fc <core::result::Result<(), core::convert::Infallible>::ok<(),core::convert::Infallible>>
|
||||
0x08000272 <+100>: b.n 0x8000274 <led_roulette::__cortex_m_rt_main+102>
|
||||
0x08000274 <+102>: add r0, sp, #68 ; 0x44
|
||||
|
||||
20 delay.delay_ms(v_half_period.read());
|
||||
0x08000276 <+104>: bl 0x800034a <volatile::Volatile<&mut u16, volatile::access::ReadWrite>::read<&mut u16,u16,volatile::access::ReadWrite>>
|
||||
0x0800027a <+108>: str r0, [sp, #0]
|
||||
0x0800027c <+110>: b.n 0x800027e <led_roulette::__cortex_m_rt_main+112>
|
||||
0x0800027e <+112>: add r0, sp, #8
|
||||
0x08000280 <+114>: ldr r1, [sp, #0]
|
||||
0x08000282 <+116>: bl 0x8002fc4 <stm32f3xx_hal::delay::{{impl}}::delay_ms>
|
||||
0x08000286 <+120>: b.n 0x8000288 <led_roulette::__cortex_m_rt_main+122>
|
||||
|
||||
End of assembler dump.
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
0x080037b2 in core::cell::UnsafeCell<u32>::get<u32> (self=0x20009fa0) at ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/cell.rs:1716
|
||||
1716 }
|
||||
|
||||
(gdb) enable 1
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:16
|
||||
16 leds[0].on().ok();
|
||||
|
||||
(gdb) print half_period
|
||||
$2 = 500
|
||||
|
||||
(gdb) disable 1
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
0x08003498 in core::ptr::read_volatile<u32> (src=0xe000e010) at ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:1052
|
||||
1052 unsafe { intrinsics::volatile_load(src) }
|
||||
|
||||
(gdb) enable 1
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 1, led_roulette::__cortex_m_rt_main () at src/05-led-roulette/src/main.rs:16
|
||||
16 leds[0].on().ok();
|
||||
|
||||
(gdb) print half_period
|
||||
$3 = 500
|
||||
|
||||
(gdb) set half_period = 2000
|
||||
|
||||
(gdb) print half_period
|
||||
$4 = 2000
|
||||
|
||||
(gdb) disable 1
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
0x0800348e in core::ptr::read_volatile<u32> (src=0xe000e010) at ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:1046
|
||||
1046 pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||
|
||||
(gdb) q
|
||||
Detaching from program: ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/led-roulette, Remote target
|
||||
Ending remote debugging.
|
||||
[Inferior 1 (Remote target) detached]
|
||||
```
|
||||
|
||||
Question! What happens if you start lowering the value of `half_period`? At what value of
|
||||
`half_period` you can no longer see the LED blink?
|
||||
|
||||
Now, it's your turn to write a program.
|
||||
11
discovery-master/f3discovery/src/06-hello-world/Cargo.toml
Normal file
11
discovery-master/f3discovery/src/06-hello-world/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
authors = [
|
||||
"Jorge Aparicio <jorge@japaric.io>",
|
||||
"Wink Saville <wink@saville.com",
|
||||
]
|
||||
edition = "2018"
|
||||
name = "hello-world"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
aux6 = { path = "auxiliary" }
|
||||
144
discovery-master/f3discovery/src/06-hello-world/README.md
Normal file
144
discovery-master/f3discovery/src/06-hello-world/README.md
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
# Hello, world!
|
||||
|
||||
> **HEADS UP** The "solder bridge" SB10 (see back of the board) on the STM32F3DISCOVERY, which is
|
||||
> required to use the ITM and the `iprint!` macros shown below, is **not** soldered by default
|
||||
> (see page 21 of the [User Manual][]).
|
||||
> (To be more accurate: this actually depends on the board revision. If you have an old version of
|
||||
> the board as the [old User Manual][User Manual v3] said, the SB10 was soldered. Check your board
|
||||
> to decide whether you need to fix it.)
|
||||
|
||||
> **TL;DR** You have two options to fix this: Either **solder** the solder bridge SB10 or connect a
|
||||
> female to female jumper wire between SWO and PB3 as shown in the picture below.
|
||||
|
||||
[User Manual]: http://www.st.com/resource/en/user_manual/dm00063382.pdf
|
||||
[User Manual v3]: https://docs.rs-online.com/5192/0900766b814876f9.pdf
|
||||
|
||||
<p align="center">
|
||||
<img height=640 title="Manual SWD connection" src="../assets/f3-swd.png">
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Just a little more of helpful magic before we start doing low level stuff.
|
||||
|
||||
Blinking an LED is like the "Hello, world" of the embedded world.
|
||||
|
||||
But in this section, we'll run a proper "Hello, world" program that prints stuff to your computer
|
||||
console.
|
||||
|
||||
Go to the `06-hello-world` directory. There's some starter code in it:
|
||||
|
||||
``` rust
|
||||
{{#include src/main.rs}}
|
||||
```
|
||||
|
||||
The `iprintln` macro will format messages and output them to the microcontroller's *ITM*. ITM stands
|
||||
for Instrumentation Trace Macrocell and it's a communication protocol on top of SWD (Serial Wire
|
||||
Debug) which can be used to send messages from the microcontroller to the debugging host. This
|
||||
communication is only *one way*: the debugging host can't send data to the microcontroller.
|
||||
|
||||
OpenOCD, which is managing the debug session, can receive data sent through this ITM *channel* and
|
||||
redirect it to a file.
|
||||
|
||||
The ITM protocol works with *frames* (you can think of them as Ethernet frames). Each frame has a
|
||||
header and a variable length payload. OpenOCD will receive these frames and write them directly to a
|
||||
file without parsing them. So, if the microntroller sends the string "Hello, world!" using the
|
||||
`iprintln` macro, OpenOCD's output file won't exactly contain that string.
|
||||
|
||||
To retrieve the original string, OpenOCD's output file will have to be parsed. We'll use the
|
||||
`itmdump` program to perform the parsing as new data arrives.
|
||||
|
||||
You should have already installed the `itmdump` program during the [installation chapter].
|
||||
|
||||
[installation chapter]: ../03-setup/index.html#itmdump
|
||||
|
||||
In a new terminal, run this command inside the `/tmp` directory, if you are using a \*nix OS, or from
|
||||
within the `%TEMP%` directory, if you are running Windows. This should be the same directory from
|
||||
where you are running OpenOCD.
|
||||
|
||||
> **NOTE** It's very important that both `itmdump` and `openocd` are running
|
||||
from the same directory!
|
||||
|
||||
``` console
|
||||
$ # itmdump terminal
|
||||
|
||||
$ # *nix
|
||||
$ cd /tmp && touch itm.txt
|
||||
|
||||
$ # Windows
|
||||
$ cd %TEMP% && type nul >> itm.txt
|
||||
|
||||
$ # both
|
||||
$ itmdump -F -f itm.txt
|
||||
```
|
||||
|
||||
This command will block as `itmdump` is now watching the `itm.txt` file. Leave this terminal open.
|
||||
|
||||
Make sure that the STM32F3DISCOVERY board is connected to your computer. Open another terminal
|
||||
from `/tmp` directory (on Windows `%TEMP%`) to launch OpenOCD similar as described in [chapter 3].
|
||||
|
||||
[chapter 3]: ../03-setup/verify.html#first-openocd-connection
|
||||
|
||||
Alright. Now, let's build the starter code and flash it into the microcontroller.
|
||||
|
||||
We will now build and run the application, `cargo run`. And step through it using `next`.
|
||||
Since `openocd.gdb` contains the `monitor` commands in `openocd.gdb` OpenOCD will redirect
|
||||
the ITM output to itm.txt and `itmdump` will write it to its terminal window. Also, it setup
|
||||
break points and stepped through the trampoline we are at the first executable
|
||||
statement in `fn main()`:
|
||||
|
||||
``` console
|
||||
~/embedded-discovery/src/06-hello-world
|
||||
$ cargo run
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
|
||||
Running `arm-none-eabi-gdb -q -x ../openocd.gdb ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/hello-world`
|
||||
Reading symbols from ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/hello-world...
|
||||
hello_world::__cortex_m_rt_main () at ~/embedded-discovery/src/06-hello-world/src/main.rs:14
|
||||
14 loop {}
|
||||
Loading section .vector_table, size 0x194 lma 0x8000000
|
||||
Loading section .text, size 0x2828 lma 0x8000194
|
||||
Loading section .rodata, size 0x638 lma 0x80029bc
|
||||
Start address 0x08000194, load size 12276
|
||||
Transfer rate: 18 KB/sec, 4092 bytes/write.
|
||||
Breakpoint 1 at 0x80001f0: file ~/embedded-discovery/src/06-hello-world/src/main.rs, line 8.
|
||||
Note: automatically using hardware breakpoints for read-only addresses.
|
||||
Breakpoint 2 at 0x800092a: file /home/wink/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs, line 570.
|
||||
Breakpoint 3 at 0x80029a8: file /home/wink/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs, line 560.
|
||||
|
||||
Breakpoint 1, hello_world::__cortex_m_rt_main_trampoline () at ~/embedded-discovery/src/06-hello-world/src/main.rs:8
|
||||
8 #[entry]
|
||||
hello_world::__cortex_m_rt_main () at ~/embedded-discovery/src/06-hello-world/src/main.rs:10
|
||||
10 let mut itm = aux6::init();
|
||||
|
||||
(gdb)
|
||||
```
|
||||
|
||||
Now issue a `next` command which will execute `aux6::init()` and
|
||||
stop at the next executable statement in `main.rs`, which
|
||||
positions us at line 12:
|
||||
|
||||
``` text
|
||||
(gdb) next
|
||||
12 iprintln!(&mut itm.stim[0], "Hello, world!");
|
||||
```
|
||||
|
||||
Then issue another `next` command which will execute
|
||||
line 12, executing the `iprintln` and stop at line 14:
|
||||
|
||||
``` text
|
||||
(gdb) next
|
||||
14 loop {}
|
||||
```
|
||||
|
||||
Now since `iprintln` has been executed the output on the `itmdump`
|
||||
terminal window should be the `Hello, world!` string:
|
||||
|
||||
``` console
|
||||
$ itmdump -F -f itm.txt
|
||||
(...)
|
||||
Hello, world!
|
||||
```
|
||||
|
||||
Awesome, right? Feel free to use `iprintln` as a logging tool in the coming sections.
|
||||
|
||||
Next: That's not all! The `iprint!` macros are not the only thing that uses the ITM. `:-)`
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
authors = [
|
||||
"Jorge Aparicio <jorge@japaric.io>",
|
||||
"Wink Saville <wink@saville.com",
|
||||
]
|
||||
edition = "2018"
|
||||
name = "aux6"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.6.4"
|
||||
cortex-m-rt = "0.6.13"
|
||||
stm32f3-discovery = "0.5.0"
|
||||
panic-itm = "0.4.2"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
//! Initialization code
|
||||
|
||||
#![no_std]
|
||||
|
||||
pub use panic_itm;
|
||||
|
||||
pub use cortex_m_rt::entry;
|
||||
|
||||
// Need stm32f3xx_hal::prelude::* otherwise
|
||||
// 'Error(corex-m-rt): The interrupt vectors are missing`
|
||||
pub use stm32f3_discovery::stm32f3xx_hal::prelude::*;
|
||||
|
||||
pub use cortex_m::{asm::bkpt, iprint, iprintln, peripheral::ITM};
|
||||
|
||||
pub fn init() -> ITM {
|
||||
let p = cortex_m::Peripherals::take().unwrap();
|
||||
|
||||
p.ITM
|
||||
}
|
||||
147
discovery-master/f3discovery/src/06-hello-world/panic.md
Normal file
147
discovery-master/f3discovery/src/06-hello-world/panic.md
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
# `panic!`
|
||||
|
||||
The `panic!` macro also sends its output to the ITM!
|
||||
|
||||
Change the `main` function to look like this:
|
||||
|
||||
``` rust
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
panic!("Hello, world!");
|
||||
}
|
||||
```
|
||||
|
||||
Before running one other suggestion, I find it inconvenient to have to
|
||||
confirm when quitting gdb. Add the following file in your home
|
||||
directory `~/.gdbinit` so that it quits immediately:
|
||||
|
||||
``` console
|
||||
$ cat ~/.gdbinit
|
||||
define hook-quit
|
||||
set confirm off
|
||||
end
|
||||
```
|
||||
|
||||
OK, now use `cargo run` and it stops at the first line of `fn main()`:
|
||||
|
||||
``` console
|
||||
$ cargo run
|
||||
Compiling hello-world v0.2.0 (~/embedded-discovery/src/06-hello-world)
|
||||
Finished dev [unoptimized + debuginfo] target(s) in 0.11s
|
||||
Running `arm-none-eabi-gdb -q -x ../openocd.gdb ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/hello-world`
|
||||
Reading symbols from ~/embedded-discovery/target/thumbv7em-none-eabihf/debug/hello-world...
|
||||
hello_world::__cortex_m_rt_main () at ~/embedded-discovery/src/06-hello-world/src/main.rs:10
|
||||
10 panic!("Hello, world!");
|
||||
Loading section .vector_table, size 0x194 lma 0x8000000
|
||||
Loading section .text, size 0x20fc lma 0x8000194
|
||||
Loading section .rodata, size 0x554 lma 0x8002290
|
||||
Start address 0x08000194, load size 10212
|
||||
Transfer rate: 17 KB/sec, 3404 bytes/write.
|
||||
Breakpoint 1 at 0x80001f0: file ~/embedded-discovery/src/06-hello-world/src/main.rs, line 8.
|
||||
Note: automatically using hardware breakpoints for read-only addresses.
|
||||
Breakpoint 2 at 0x8000222: file ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs, line 570.
|
||||
Breakpoint 3 at 0x800227a: file ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs, line 560.
|
||||
|
||||
Breakpoint 1, hello_world::__cortex_m_rt_main_trampoline () at ~/embedded-discovery/src/06-hello-world/src/main.rs:8
|
||||
8 #[entry]
|
||||
hello_world::__cortex_m_rt_main () at ~/embedded-discovery/src/06-hello-world/src/main.rs:10
|
||||
10 panic!("Hello, world!");
|
||||
(gdb)
|
||||
```
|
||||
|
||||
We'll use short command names to save typing, enter `c` then the `Enter` or `Return` key:
|
||||
```
|
||||
(gdb) c
|
||||
Continuing.
|
||||
```
|
||||
|
||||
If all is well you'll see some new output in the `itmdump` terminal.
|
||||
|
||||
``` console
|
||||
$ # itmdump terminal
|
||||
(..)
|
||||
panicked at 'Hello, world!', src/06-hello-world/src/main.rs:10:5
|
||||
```
|
||||
|
||||
Then type `Ctrl-c` which breaks out of a loop in the runtime:
|
||||
``` text
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
0x0800115c in panic_itm::panic (info=0x20009fa0) at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-itm-0.4.2/src/lib.rs:57
|
||||
57 atomic::compiler_fence(Ordering::SeqCst);
|
||||
```
|
||||
|
||||
Ultimately, `panic!` is just another function call so you can see it leaves behind
|
||||
a trace of function calls. This allows you to use `backtrace` or just `bt` and to see
|
||||
call stack that caused the panic:
|
||||
|
||||
``` text
|
||||
(gdb) bt
|
||||
#0 panic_itm::panic (info=0x20009fa0) at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-itm-0.4.2/src/lib.rs:47
|
||||
#1 0x080005c2 in core::panicking::panic_fmt () at library/core/src/panicking.rs:92
|
||||
#2 0x0800055a in core::panicking::panic () at library/core/src/panicking.rs:50
|
||||
#3 0x08000210 in hello_world::__cortex_m_rt_main () at src/06-hello-world/src/main.rs:10
|
||||
#4 0x080001f4 in hello_world::__cortex_m_rt_main_trampoline () at src/06-hello-world/src/main.rs:8
|
||||
```
|
||||
|
||||
Another thing we can do is catch the panic *before* it does the logging.
|
||||
So we'll do several things; reset to the beginning, disable breakpoint 1,
|
||||
set a new breakpoint at `rust_begin_unwind`, list the break points and then continue:
|
||||
|
||||
``` text
|
||||
(gdb) monitor reset halt
|
||||
Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
Unable to match requested speed 1000 kHz, using 950 kHz
|
||||
adapter speed: 950 kHz
|
||||
target halted due to debug-request, current mode: Thread
|
||||
xPSR: 0x01000000 pc: 0x08000194 msp: 0x2000a000
|
||||
|
||||
(gdb) disable 1
|
||||
|
||||
(gdb) break rust_begin_unwind
|
||||
Breakpoint 4 at 0x800106c: file ~/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-itm-0.4.2/src/lib.rs, line 47.
|
||||
|
||||
(gdb) info break
|
||||
Num Type Disp Enb Address What
|
||||
1 breakpoint keep n 0x080001f0 in hello_world::__cortex_m_rt_main_trampoline
|
||||
at ~/prgs/rust/tutorial/embedded-discovery/src/06-hello-world/src/main.rs:8
|
||||
breakpoint already hit 1 time
|
||||
2 breakpoint keep y 0x08000222 in cortex_m_rt::DefaultHandler_
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:570
|
||||
3 breakpoint keep y 0x0800227a in cortex_m_rt::HardFault_
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:560
|
||||
4 breakpoint keep y 0x0800106c in panic_itm::panic
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-itm-0.4.2/src/lib.rs:47
|
||||
|
||||
(gdb) c
|
||||
Continuing.
|
||||
|
||||
Breakpoint 4, panic_itm::panic (info=0x20009fa0) at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-itm-0.4.2/src/lib.rs:47
|
||||
47 interrupt::disable();
|
||||
```
|
||||
|
||||
You'll notice that nothing got printed on the `itmdump` console this time. If
|
||||
you resume the program using `continue` then a new line will be printed.
|
||||
|
||||
In a later section we'll look into other simpler communication protocols.
|
||||
|
||||
Finally, enter the `q` command to quit and it quits immediately without asking for confirmation:
|
||||
|
||||
``` text
|
||||
(gdb) q
|
||||
Detaching from program: ~/prgs/rust/tutorial/embedded-discovery/target/thumbv7em-none-eabihf/debug/hello-world, Remote target
|
||||
Ending remote debugging.
|
||||
[Inferior 1 (Remote target) detached]
|
||||
```
|
||||
|
||||
As an even shorter sequence you can type `Ctrl-d`, which eliminates
|
||||
one keystroke!
|
||||
|
||||
> **NOTE** In this case the `(gdb)` prompt is overwritten with `quit)`
|
||||
|
||||
``` text
|
||||
quit)
|
||||
Detaching from program: ~/prgs/rust/tutorial/embedded-discovery/target/thumbv7em-none-eabihf/debug/hello-world, Remote target
|
||||
Ending remote debugging.
|
||||
[Inferior 1 (Remote target) detached]
|
||||
```
|
||||
15
discovery-master/f3discovery/src/06-hello-world/src/main.rs
Normal file
15
discovery-master/f3discovery/src/06-hello-world/src/main.rs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux6::{entry, iprint, iprintln};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut itm = aux6::init();
|
||||
|
||||
iprintln!(&mut itm.stim[0], "Hello, world!");
|
||||
|
||||
loop {}
|
||||
}
|
||||
1
discovery-master/f3discovery/src/06-hello-world/target
Symbolic link
1
discovery-master/f3discovery/src/06-hello-world/target
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../target
|
||||
11
discovery-master/f3discovery/src/07-registers/Cargo.toml
Normal file
11
discovery-master/f3discovery/src/07-registers/Cargo.toml
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
authors = [
|
||||
"Jorge Aparicio <jorge@japaric.io>",
|
||||
"Wink Saville <wink@saville.com",
|
||||
]
|
||||
edition = "2018"
|
||||
name = "registers"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
aux7 = { path = "auxiliary" }
|
||||
45
discovery-master/f3discovery/src/07-registers/README.md
Normal file
45
discovery-master/f3discovery/src/07-registers/README.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# Registers
|
||||
|
||||
It's time to explore what the `Led` API does under the hood.
|
||||
|
||||
In a nutshell, it just writes to some special memory regions. Go into the `07-registers` directory
|
||||
and let's run the starter code statement by statement.
|
||||
|
||||
``` rust
|
||||
{{#include src/main.rs}}
|
||||
```
|
||||
|
||||
What's this magic?
|
||||
|
||||
The address `0x48001018` points to a *register*. A register is a special region of memory that
|
||||
controls a *peripheral*. A peripheral is a piece of electronics that sits right next to the
|
||||
processor within the microcontroller package and provides the processor with extra functionality.
|
||||
After all, the processor, on its own, can only do math and logic.
|
||||
|
||||
This particular register controls General Purpose Input/Output (GPIO) *pins* (GPIO *is* a
|
||||
peripheral) and can be used to *drive* each of those pins *low* or *high*.
|
||||
|
||||
## An aside: LEDs, digital outputs and voltage levels
|
||||
|
||||
Drive? Pin? Low? High?
|
||||
|
||||
A pin is a electrical contact. Our microcontroller has several of them and some of them are
|
||||
connected to LEDs. An LED, a Light Emitting Diode, will only emit light when voltage is applied to
|
||||
it with a certain polarity.
|
||||
|
||||
<p align="center">
|
||||
<img class="white_bg" height=180 title="LED circuit" src="https://upload.wikimedia.org/wikipedia/commons/c/c9/LED_circuit.svg">
|
||||
</p>
|
||||
|
||||
Luckily for us, the microcontroller's pins are connected to the LEDs with the right polarity. All
|
||||
that we have to do is *output* some non-zero voltage through the pin to turn the LED on. The pins
|
||||
attached to the LEDs are configured as *digital outputs* and can only output two different voltage
|
||||
levels: "low", 0 Volts, or "high", 3 Volts. A "high" (voltage) level will turn the LED on whereas
|
||||
a "low" (voltage) level will turn it off.
|
||||
|
||||
These "low" and "high" states map directly to the concept of digital logic. "low" is `0` or `false`
|
||||
and "high" is `1` or `true`. This is why this pin configuration is known as digital output.
|
||||
|
||||
---
|
||||
|
||||
OK. But how can one find out what this register does? Time to RTRM (Read the Reference Manual)!
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
authors = [
|
||||
"Jorge Aparicio <jorge@japaric.io>",
|
||||
"Wink Saville <wink@saville.com",
|
||||
]
|
||||
edition = "2018"
|
||||
name = "aux7"
|
||||
version = "0.2.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.6.4"
|
||||
cortex-m-rt = "0.6.13"
|
||||
stm32f3-discovery = "0.6.0"
|
||||
panic-itm = "0.4.2"
|
||||
|
||||
[dependencies.stm32f3]
|
||||
version = "0.12.1"
|
||||
features = ["stm32f303", "rt"]
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
//! Initialization code
|
||||
|
||||
#![deny(warnings)]
|
||||
#![no_std]
|
||||
|
||||
use panic_itm as _; // panic handler
|
||||
|
||||
pub use cortex_m::{asm::bkpt, iprint, iprintln, peripheral::ITM};
|
||||
pub use cortex_m_rt::entry;
|
||||
|
||||
pub use stm32f3::stm32f303::{self, gpioc::RegisterBlock};
|
||||
pub use stm32f3_discovery::stm32f3xx_hal::pac::GPIOE;
|
||||
pub use stm32f3_discovery::{leds::Leds, stm32f3xx_hal};
|
||||
|
||||
use stm32f3xx_hal::prelude::*;
|
||||
pub use stm32f3xx_hal::stm32;
|
||||
|
||||
#[inline(never)]
|
||||
pub fn init() -> (ITM, &'static RegisterBlock) {
|
||||
let device_periphs = stm32::Peripherals::take().unwrap();
|
||||
let mut reset_and_clock_control = device_periphs.RCC.constrain();
|
||||
|
||||
// initialize user leds
|
||||
let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb);
|
||||
let _leds = Leds::new(
|
||||
gpioe.pe8,
|
||||
gpioe.pe9,
|
||||
gpioe.pe10,
|
||||
gpioe.pe11,
|
||||
gpioe.pe12,
|
||||
gpioe.pe13,
|
||||
gpioe.pe14,
|
||||
gpioe.pe15,
|
||||
&mut gpioe.moder,
|
||||
&mut gpioe.otyper,
|
||||
);
|
||||
|
||||
let core_periphs = cortex_m::Peripherals::take().unwrap();
|
||||
(core_periphs.ITM, unsafe { &*stm32f303::GPIOE::ptr() })
|
||||
}
|
||||
148
discovery-master/f3discovery/src/07-registers/bad-address.md
Normal file
148
discovery-master/f3discovery/src/07-registers/bad-address.md
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
# `0xBAAAAAAD` address
|
||||
|
||||
Not all the peripheral memory can be accessed. Look at this program.
|
||||
|
||||
``` rust
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::ptr;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux7::{entry, iprint, iprintln};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
aux7::init();
|
||||
|
||||
unsafe {
|
||||
ptr::read_volatile(0x4800_1800 as *const u32);
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
This address is close to the `GPIOE_BSRR` address we used before but this address is *invalid*.
|
||||
Invalid in the sense that there's no register at this address.
|
||||
|
||||
Now, let's try it.
|
||||
|
||||
``` console
|
||||
$ cargo run
|
||||
(..)
|
||||
Breakpoint 1, registers::__cortex_m_rt_main_trampoline () at src/07-registers/src/main.rs:9
|
||||
9 #[entry]
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Breakpoint 3, cortex_m_rt::HardFault_ (ef=0x20009fb0)
|
||||
at ~/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-rt-0.6.13/src/lib.rs:560
|
||||
560 loop {
|
||||
|
||||
(gdb)
|
||||
```
|
||||
|
||||
We tried to do an invalid operation, reading memory that doesn't exist, so the processor raised an
|
||||
*exception*, a *hardware* exception.
|
||||
|
||||
In most cases, exceptions are raised when the processor attempts to perform an invalid operation.
|
||||
Exceptions break the normal flow of a program and force the processor to execute an *exception
|
||||
handler*, which is just a function/subroutine.
|
||||
|
||||
There are different kind of exceptions. Each kind of exception is raised by different conditions and
|
||||
each one is handled by a different exception handler.
|
||||
|
||||
The `aux7` crate depends on the `cortex-m-rt` crate which defines a default
|
||||
*hard fault* handler, named `HardFault`, that handles the "invalid memory
|
||||
address" exception. `openocd.gdb` placed a breakpoint on `HardFault`; that's why
|
||||
the debugger halted your program while it was executing the exception handler.
|
||||
We can get more information about the exception from the debugger. Let's see:
|
||||
|
||||
```
|
||||
(gdb) list
|
||||
555 #[allow(unused_variables)]
|
||||
556 #[doc(hidden)]
|
||||
557 #[link_section = ".HardFault.default"]
|
||||
558 #[no_mangle]
|
||||
559 pub unsafe extern "C" fn HardFault_(ef: &ExceptionFrame) -> ! {
|
||||
560 loop {
|
||||
561 // add some side effect to prevent this from turning into a UDF instruction
|
||||
562 // see rust-lang/rust#28728 for details
|
||||
563 atomic::compiler_fence(Ordering::SeqCst);
|
||||
564 }
|
||||
```
|
||||
|
||||
`ef` is a snapshot of the program state right before the exception occurred. Let's inspect it:
|
||||
|
||||
```
|
||||
(gdb) print/x *ef
|
||||
$1 = cortex_m_rt::ExceptionFrame {
|
||||
r0: 0x48001800,
|
||||
r1: 0x80036b0,
|
||||
r2: 0x1,
|
||||
r3: 0x80000000,
|
||||
r12: 0xb,
|
||||
lr: 0x800020d,
|
||||
pc: 0x8001750,
|
||||
xpsr: 0xa1000200
|
||||
}
|
||||
```
|
||||
|
||||
There are several fields here but the most important one is `pc`, the Program Counter register.
|
||||
The address in this register points to the instruction that generated the exception. Let's
|
||||
disassemble the program around the bad instruction.
|
||||
|
||||
```
|
||||
(gdb) disassemble /m ef.pc
|
||||
Dump of assembler code for function core::ptr::read_volatile<u32>:
|
||||
1046 pub unsafe fn read_volatile<T>(src: *const T) -> T {
|
||||
0x0800174c <+0>: sub sp, #12
|
||||
0x0800174e <+2>: str r0, [sp, #4]
|
||||
|
||||
1047 if cfg!(debug_assertions) && !is_aligned_and_not_null(src) {
|
||||
1048 // Not panicking to keep codegen impact smaller.
|
||||
1049 abort();
|
||||
1050 }
|
||||
1051 // SAFETY: the caller must uphold the safety contract for `volatile_load`.
|
||||
1052 unsafe { intrinsics::volatile_load(src) }
|
||||
0x08001750 <+4>: ldr r0, [r0, #0]
|
||||
0x08001752 <+6>: str r0, [sp, #8]
|
||||
0x08001754 <+8>: ldr r0, [sp, #8]
|
||||
0x08001756 <+10>: str r0, [sp, #0]
|
||||
0x08001758 <+12>: b.n 0x800175a <core::ptr::read_volatile<u32>+14>
|
||||
|
||||
1053 }
|
||||
0x0800175a <+14>: ldr r0, [sp, #0]
|
||||
0x0800175c <+16>: add sp, #12
|
||||
0x0800175e <+18>: bx lr
|
||||
|
||||
End of assembler dump.
|
||||
```
|
||||
|
||||
The exception was caused by the `ldr r0, [r0, #0]` instruction, a read instruction. The instruction
|
||||
tried to read the memory at the address indicated by the `r0` register. By the way, `r0` is a CPU
|
||||
(processor) register not a memory mapped register; it doesn't have an associated address like, say,
|
||||
`GPIO_BSRR`.
|
||||
|
||||
Wouldn't it be nice if we could check what the value of the `r0` register was right at the instant
|
||||
when the exception was raised? Well, we already did! The `r0` field in the `ef` value we printed
|
||||
before is the value of `r0` register had when the exception was raised. Here it is again:
|
||||
|
||||
```
|
||||
(gdb) print/x *ef
|
||||
$1 = cortex_m_rt::ExceptionFrame {
|
||||
r0: 0x48001800,
|
||||
r1: 0x80036b0,
|
||||
r2: 0x1,
|
||||
r3: 0x80000000,
|
||||
r12: 0xb,
|
||||
lr: 0x800020d,
|
||||
pc: 0x8001750,
|
||||
xpsr: 0xa1000200
|
||||
}
|
||||
```
|
||||
|
||||
`r0` contains the value `0x4800_1800` which is the invalid address we called the `read_volatile`
|
||||
function with.
|
||||
11
discovery-master/f3discovery/src/07-registers/openocd.gdb
Normal file
11
discovery-master/f3discovery/src/07-registers/openocd.gdb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
target remote :3333
|
||||
set print asm-demangle on
|
||||
set print pretty on
|
||||
set style sources off
|
||||
monitor tpiu config internal itm.txt uart off 8000000
|
||||
monitor itm port 0 on
|
||||
load
|
||||
break main
|
||||
break DefaultHandler
|
||||
break HardFault
|
||||
continue
|
||||
221
discovery-master/f3discovery/src/07-registers/optimization.md
Normal file
221
discovery-master/f3discovery/src/07-registers/optimization.md
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
# (mis)Optimization
|
||||
|
||||
Reads/writes to registers are quite special. I may even dare to say that they are embodiment of side
|
||||
effects. In the previous example we wrote four different values to the same register. If you didn't
|
||||
know that address was a register, you may have simplified the logic to just write the final value `1
|
||||
<< (11 + 16)` into the register.
|
||||
|
||||
Actually, LLVM, the compiler's backend / optimizer, does not know we are dealing with a register and
|
||||
will merge the writes thus changing the behavior of our program. Let's check that really quick.
|
||||
|
||||
``` console
|
||||
$ cargo run --release
|
||||
(..)
|
||||
Breakpoint 1, registers::__cortex_m_rt_main_trampoline () at src/07-registers/src/main.rs:7
|
||||
7 #[entry]
|
||||
|
||||
(gdb) step
|
||||
registers::__cortex_m_rt_main () at src/07-registers/src/main.rs:9
|
||||
9 aux7::init();
|
||||
|
||||
(gdb) next
|
||||
25 *(GPIOE_BSRR as *mut u32) = 1 << (11 + 16);
|
||||
|
||||
(gdb) disassemble /m
|
||||
Dump of assembler code for function _ZN9registers18__cortex_m_rt_main17h45b1ef53e18aa8d0E:
|
||||
8 fn main() -> ! {
|
||||
0x08000248 <+0>: push {r7, lr}
|
||||
0x0800024a <+2>: mov r7, sp
|
||||
|
||||
9 aux7::init();
|
||||
0x0800024c <+4>: bl 0x8000260 <aux7::init>
|
||||
0x08000250 <+8>: movw r0, #4120 ; 0x1018
|
||||
0x08000254 <+12>: mov.w r1, #134217728 ; 0x8000000
|
||||
0x08000258 <+16>: movt r0, #18432 ; 0x4800
|
||||
|
||||
10
|
||||
11 unsafe {
|
||||
12 // A magic address!
|
||||
13 const GPIOE_BSRR: u32 = 0x48001018;
|
||||
14
|
||||
15 // Turn on the "North" LED (red)
|
||||
16 *(GPIOE_BSRR as *mut u32) = 1 << 9;
|
||||
17
|
||||
18 // Turn on the "East" LED (green)
|
||||
19 *(GPIOE_BSRR as *mut u32) = 1 << 11;
|
||||
20
|
||||
21 // Turn off the "North" LED
|
||||
22 *(GPIOE_BSRR as *mut u32) = 1 << (9 + 16);
|
||||
23
|
||||
24 // Turn off the "East" LED
|
||||
25 *(GPIOE_BSRR as *mut u32) = 1 << (11 + 16);
|
||||
=> 0x0800025c <+20>: str r1, [r0, #0]
|
||||
0x0800025e <+22>: b.n 0x800025e <registers::__cortex_m_rt_main+22>
|
||||
|
||||
End of assembler dump.
|
||||
```
|
||||
|
||||
The state of the LEDs didn't change this time! The `str` instruction is the one that writes a value
|
||||
to the register. Our *debug* (unoptimized) program had four of them, one for each write to the
|
||||
register, but the *release* (optimized) program only has one.
|
||||
|
||||
We can check that using `objdump` and capture the output to `out.asm`:
|
||||
|
||||
``` console
|
||||
# same as cargo objdump -- -d --no-show-raw-insn --print-imm-hex --source target/thumbv7em-none-eabihf/debug/registers
|
||||
cargo objdump --bin registers -- -d --no-show-raw-insn --print-imm-hex --source > debug.txt
|
||||
```
|
||||
|
||||
Then examine `debug.txt` looking for `main` and we see the 4 `str` instructions:
|
||||
```
|
||||
080001ec <main>:
|
||||
; #[entry]
|
||||
80001ec: push {r7, lr}
|
||||
80001ee: mov r7, sp
|
||||
80001f0: bl #0x2
|
||||
80001f4: trap
|
||||
|
||||
080001f6 <registers::__cortex_m_rt_main::hc2e3436fa38cd6f2>:
|
||||
; fn main() -> ! {
|
||||
80001f6: push {r7, lr}
|
||||
80001f8: mov r7, sp
|
||||
; aux7::init();
|
||||
80001fa: bl #0x3e
|
||||
80001fe: b #-0x2 <registers::__cortex_m_rt_main::hc2e3436fa38cd6f2+0xa>
|
||||
; *(GPIOE_BSRR as *mut u32) = 1 << 9;
|
||||
8000200: movw r0, #0x2640
|
||||
8000204: movt r0, #0x800
|
||||
8000208: ldr r0, [r0]
|
||||
800020a: movw r1, #0x1018
|
||||
800020e: movt r1, #0x4800
|
||||
8000212: str r0, [r1]
|
||||
; *(GPIOE_BSRR as *mut u32) = 1 << 11;
|
||||
8000214: movw r0, #0x2648
|
||||
8000218: movt r0, #0x800
|
||||
800021c: ldr r0, [r0]
|
||||
800021e: str r0, [r1]
|
||||
; *(GPIOE_BSRR as *mut u32) = 1 << (9 + 16);
|
||||
8000220: movw r0, #0x2650
|
||||
8000224: movt r0, #0x800
|
||||
8000228: ldr r0, [r0]
|
||||
800022a: str r0, [r1]
|
||||
; *(GPIOE_BSRR as *mut u32) = 1 << (11 + 16);
|
||||
800022c: movw r0, #0x2638
|
||||
8000230: movt r0, #0x800
|
||||
8000234: ldr r0, [r0]
|
||||
8000236: str r0, [r1]
|
||||
; loop {}
|
||||
8000238: b #-0x2 <registers::__cortex_m_rt_main::hc2e3436fa38cd6f2+0x44>
|
||||
800023a: b #-0x4 <registers::__cortex_m_rt_main::hc2e3436fa38cd6f2+0x44>
|
||||
(..)
|
||||
```
|
||||
|
||||
How do we prevent LLVM from misoptimizing our program? We use *volatile* operations instead of plain
|
||||
reads/writes:
|
||||
|
||||
``` rust
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::ptr;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux7::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
aux7::init();
|
||||
|
||||
unsafe {
|
||||
// A magic address!
|
||||
const GPIOE_BSRR: u32 = 0x48001018;
|
||||
|
||||
// Turn on the "North" LED (red)
|
||||
ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << 9);
|
||||
|
||||
// Turn on the "East" LED (green)
|
||||
ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << 11);
|
||||
|
||||
// Turn off the "North" LED
|
||||
ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << (9 + 16));
|
||||
|
||||
// Turn off the "East" LED
|
||||
ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << (11 + 16));
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Generate `release.txt` using with `--release` mode.
|
||||
|
||||
``` console
|
||||
cargo objdump --release --bin registers -- -d --no-show-raw-insn --print-imm-hex --source > release.txt
|
||||
```
|
||||
|
||||
Now find the `main` routine in `release.txt` and we see the 4 `str` instructions.
|
||||
```
|
||||
0800023e <main>:
|
||||
; #[entry]
|
||||
800023e: push {r7, lr}
|
||||
8000240: mov r7, sp
|
||||
8000242: bl #0x2
|
||||
8000246: trap
|
||||
|
||||
08000248 <registers::__cortex_m_rt_main::h45b1ef53e18aa8d0>:
|
||||
; fn main() -> ! {
|
||||
8000248: push {r7, lr}
|
||||
800024a: mov r7, sp
|
||||
; aux7::init();
|
||||
800024c: bl #0x22
|
||||
8000250: movw r0, #0x1018
|
||||
8000254: mov.w r1, #0x200
|
||||
8000258: movt r0, #0x4800
|
||||
; intrinsics::volatile_store(dst, src);
|
||||
800025c: str r1, [r0]
|
||||
800025e: mov.w r1, #0x800
|
||||
8000262: str r1, [r0]
|
||||
8000264: mov.w r1, #0x2000000
|
||||
8000268: str r1, [r0]
|
||||
800026a: mov.w r1, #0x8000000
|
||||
800026e: str r1, [r0]
|
||||
8000270: b #-0x4 <registers::__cortex_m_rt_main::h45b1ef53e18aa8d0+0x28>
|
||||
(..)
|
||||
```
|
||||
|
||||
We see that the four writes (`str` instructions) are preserved. If you run it using
|
||||
`gdb` you'll also see that we get the expected behavior.
|
||||
> NB: The last `next` will endlessly execute `loop {}`, use `Ctrl-c` to get
|
||||
> back to the `(gdb)` prompt.
|
||||
```
|
||||
$ cargo run --release
|
||||
(..)
|
||||
|
||||
Breakpoint 1, registers::__cortex_m_rt_main_trampoline () at src/07-registers/src/main.rs:9
|
||||
9 #[entry]
|
||||
|
||||
(gdb) step
|
||||
registers::__cortex_m_rt_main () at src/07-registers/src/main.rs:11
|
||||
11 aux7::init();
|
||||
|
||||
(gdb) next
|
||||
18 ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << 9);
|
||||
|
||||
(gdb) next
|
||||
21 ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << 11);
|
||||
|
||||
(gdb) next
|
||||
24 ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << (9 + 16));
|
||||
|
||||
(gdb) next
|
||||
27 ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << (11 + 16));
|
||||
|
||||
(gdb) next
|
||||
^C
|
||||
Program received signal SIGINT, Interrupt.
|
||||
0x08000270 in registers::__cortex_m_rt_main ()
|
||||
at ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:1124
|
||||
1124 intrinsics::volatile_store(dst, src);
|
||||
(gdb)
|
||||
```
|
||||
88
discovery-master/f3discovery/src/07-registers/rtrm.md
Normal file
88
discovery-master/f3discovery/src/07-registers/rtrm.md
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
# RTRM: Reading The Reference Manual
|
||||
|
||||
I mentioned that the microcontroller has several pins. For convenience, these pins are grouped in
|
||||
*ports* of 16 pins. Each port is named with a letter: Port A, Port B, etc. and the pins within each
|
||||
port are named with numbers from 0 to 15.
|
||||
|
||||
The first thing we have to find out is which pin is connected to which LED. This information is in
|
||||
the STM32F3DISCOVERY [User Manual] (You downloaded a copy, right?). In this particular section:
|
||||
|
||||
[User Manual]: http://www.st.com/resource/en/user_manual/dm00063382.pdf
|
||||
|
||||
> Section 6.4 LEDs - Page 18
|
||||
|
||||
The manual says:
|
||||
|
||||
- `LD3`, the North LED, is connected to the pin `PE9`. `PE9` is the short form of: Pin 9 on Port E.
|
||||
- `LD7`, the East LED, is connected to the pin `PE11`.
|
||||
|
||||
Up to this point, we know that we want to change the state of the pins PE9 and PE11 to turn the
|
||||
North/East LEDs on/off. These pins are part of Port E so we'll have to deal with the `GPIOE`
|
||||
peripheral.
|
||||
|
||||
Each peripheral has a register *block* associated to it. A register block is a collection of
|
||||
registers allocated in contiguous memory. The address at which the register block starts is known as
|
||||
its base address. We need to figure out what's the base address of the `GPIOE` peripheral. That
|
||||
information is in the following section of the microcontroller [Reference Manual]:
|
||||
|
||||
[Reference Manual]: http://www.st.com/resource/en/reference_manual/dm00043574.pdf
|
||||
|
||||
> Section 3.2.2 Memory map and register boundary addresses - Page 51
|
||||
|
||||
The table says that base address of the `GPIOE` register block is `0x4800_1000`.
|
||||
|
||||
Each peripheral also has its own section in the documentation. Each of these sections ends with a
|
||||
table of the registers that the peripheral's register block contains. For the `GPIO` family of
|
||||
peripheral, that table is in:
|
||||
|
||||
> Section 11.4.12 GPIO register map - Page 243
|
||||
|
||||
'BSRR' is the register which we will be using to set/reset. Its offset value is '0x18' from the base address
|
||||
of the 'GPIOE'. We can look up BSRR in the reference manual.
|
||||
GPIO Registers -> GPIO port bit set/reset register (GPIOx_BSRR).
|
||||
|
||||
Now we need to jump to the documentation of that particular register. It's a few pages above in:
|
||||
|
||||
> Section 11.4.7 GPIO port bit set/reset register (GPIOx_BSRR) - Page 240
|
||||
|
||||
Finally!
|
||||
|
||||
This is the register we were writing to. The documentation says some interesting things. First, this
|
||||
register is write only ... so let's try reading its value `:-)`.
|
||||
|
||||
We'll use GDB's `examine` command: `x`.
|
||||
|
||||
```
|
||||
(gdb) next
|
||||
16 *(GPIOE_BSRR as *mut u32) = 1 << 9;
|
||||
|
||||
(gdb) x 0x48001018
|
||||
0x48001018: 0x00000000
|
||||
|
||||
(gdb) # the next command will turn the North LED on
|
||||
(gdb) next
|
||||
19 *(GPIOE_BSRR as *mut u32) = 1 << 11;
|
||||
|
||||
(gdb) x 0x48001018
|
||||
0x48001018: 0x00000000
|
||||
```
|
||||
|
||||
Reading the register returns `0`. That matches what the documentation says.
|
||||
|
||||
The other thing that the documentation says is that the bits 0 to 15 can be used to *set* the
|
||||
corresponding pin. That is bit 0 sets the pin 0. Here, *set* means outputting a *high* value on
|
||||
the pin.
|
||||
|
||||
The documentation also says that bits 16 to 31 can be used to *reset* the corresponding pin. In this
|
||||
case, the bit 16 resets the pin number 0. As you may guess, *reset* means outputting a *low* value
|
||||
on the pin.
|
||||
|
||||
Correlating that information with our program, all seems to be in agreement:
|
||||
|
||||
- Writing `1 << 9` (`BS9 = 1`) to `BSRR` sets `PE9` *high*. That turns the North LED *on*.
|
||||
|
||||
- Writing `1 << 11` (`BS11 = 1`) to `BSRR` sets `PE11` *high*. That turns the East LED *on*.
|
||||
|
||||
- Writing `1 << 25` (`BR9 = 1`) to `BSRR` sets `PE9` *low*. That turns the North LED *off*.
|
||||
|
||||
- Finally, writing `1 << 27` (`BR11 = 1`) to `BSRR` sets `PE11` *low*. That turns the East LED *off*.
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
# Spooky action at a distance
|
||||
|
||||
`BSRR` is not the only register that can control the pins of Port E. The `ODR` register also lets
|
||||
you change the value of the pins. Furthermore, `ODR` also lets you retrieve the current output
|
||||
status of Port E.
|
||||
|
||||
`ODR` is documented in:
|
||||
|
||||
> Section 11.4.6 GPIO port output data register - Page 239
|
||||
|
||||
Let's look at this program. The key to this program
|
||||
is `fn iprint_odr`. This function prints the current
|
||||
value in `ODR` to the `ITM` console
|
||||
|
||||
``` rust
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::ptr;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux7::{entry, iprintln, ITM};
|
||||
|
||||
// Print the current contents of odr
|
||||
fn iprint_odr(itm: &mut ITM) {
|
||||
const GPIOE_ODR: u32 = 0x4800_1014;
|
||||
|
||||
unsafe {
|
||||
iprintln!(
|
||||
&mut itm.stim[0],
|
||||
"ODR = 0x{:04x}",
|
||||
ptr::read_volatile(GPIOE_ODR as *const u16)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let mut itm= aux7::init().0;
|
||||
|
||||
unsafe {
|
||||
// A magic addresses!
|
||||
const GPIOE_BSRR: u32 = 0x4800_1018;
|
||||
|
||||
// Print the initial contents of ODR
|
||||
iprint_odr(&mut itm);
|
||||
|
||||
// Turn on the "North" LED (red)
|
||||
ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << 9);
|
||||
iprint_odr(&mut itm);
|
||||
|
||||
// Turn on the "East" LED (green)
|
||||
ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << 11);
|
||||
iprint_odr(&mut itm);
|
||||
|
||||
// Turn off the "North" LED
|
||||
ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << (9 + 16));
|
||||
iprint_odr(&mut itm);
|
||||
|
||||
// Turn off the "East" LED
|
||||
ptr::write_volatile(GPIOE_BSRR as *mut u32, 1 << (11 + 16));
|
||||
iprint_odr(&mut itm);
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
If you run this program
|
||||
```
|
||||
$ cargo run
|
||||
(..)
|
||||
Breakpoint 1, registers::__cortex_m_rt_main_trampoline () at src/07-registers/src/main.rs:22
|
||||
22 #[entry]
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
```
|
||||
|
||||
You'll see on itmdump's console:
|
||||
|
||||
``` console
|
||||
$ # itmdump's console
|
||||
(..)
|
||||
ODR = 0x0000
|
||||
ODR = 0x0200
|
||||
ODR = 0x0a00
|
||||
ODR = 0x0800
|
||||
ODR = 0x0000
|
||||
```
|
||||
|
||||
Side effects! Although we are reading the same address multiple times without actually modifying it,
|
||||
we still see its value change every time `BSRR` is written to.
|
||||
29
discovery-master/f3discovery/src/07-registers/src/main.rs
Normal file
29
discovery-master/f3discovery/src/07-registers/src/main.rs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux7::{entry, iprint, iprintln};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
aux7::init();
|
||||
|
||||
unsafe {
|
||||
// A magic address!
|
||||
const GPIOE_BSRR: u32 = 0x48001018;
|
||||
|
||||
// Turn on the "North" LED (red)
|
||||
*(GPIOE_BSRR as *mut u32) = 1 << 9;
|
||||
|
||||
// Turn on the "East" LED (green)
|
||||
*(GPIOE_BSRR as *mut u32) = 1 << 11;
|
||||
|
||||
// Turn off the "North" LED
|
||||
*(GPIOE_BSRR as *mut u32) = 1 << (9 + 16);
|
||||
|
||||
// Turn off the "East" LED
|
||||
*(GPIOE_BSRR as *mut u32) = 1 << (11 + 16);
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
1
discovery-master/f3discovery/src/07-registers/target
Symbolic link
1
discovery-master/f3discovery/src/07-registers/target
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../target
|
||||
|
|
@ -0,0 +1,232 @@
|
|||
# Type safe manipulation
|
||||
|
||||
The last register we were working with, `ODR`, had this in its documentation:
|
||||
|
||||
> Bits 31:16 Reserved, must be kept at reset value
|
||||
|
||||
We are not supposed to write to those bits of the register or Bad Stuff May Happen.
|
||||
|
||||
There's also the fact the registers have different read/write permissions. Some of them are write
|
||||
only, others can be read and written to and there must be others that are read only.
|
||||
|
||||
Finally, directly working with hexadecimal addresses is error prone. You already saw that trying to
|
||||
access an invalid memory address causes an exception which disrupts the execution of our program.
|
||||
|
||||
Wouldn't it be nice if we had an API to manipulate registers in a "safe" manner? Ideally, the API
|
||||
should encode these three points I've mentioned: No messing around with the actual addresses, should
|
||||
respect read/write permissions and should prevent modification of the reserved parts of a register.
|
||||
|
||||
Well, we do! `aux7::init()` actually returns a value that provides a type safe API to manipulate the
|
||||
registers of the `GPIOE` peripheral.
|
||||
|
||||
As you may remember: a group of registers associated to a peripheral is called register block, and
|
||||
it's located in a contiguous region of memory. In this type safe API each register block is modeled
|
||||
as a `struct` where each of its fields represents a register. Each register field is a different
|
||||
newtype over e.g. `u32` that exposes a combination of the following methods: `read`, `write` or
|
||||
`modify` according to its read/write permissions. Finally, these methods don't take primitive values
|
||||
like `u32`, instead they take yet another newtype that can be constructed using the builder pattern
|
||||
and that prevent the modification of the reserved parts of the register.
|
||||
|
||||
The best way to get familiar with this API is to port our running example to it.
|
||||
|
||||
``` rust
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux7::{entry, iprintln, ITM, RegisterBlock};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let gpioe = aux7::init().1;
|
||||
|
||||
// Turn on the North LED
|
||||
gpioe.bsrr.write(|w| w.bs9().set_bit());
|
||||
|
||||
// Turn on the East LED
|
||||
gpioe.bsrr.write(|w| w.bs11().set_bit());
|
||||
|
||||
// Turn off the North LED
|
||||
gpioe.bsrr.write(|w| w.br9().set_bit());
|
||||
|
||||
// Turn off the East LED
|
||||
gpioe.bsrr.write(|w| w.br11().set_bit());
|
||||
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
||||
First thing you notice: There are no magic addresses involved. Instead we use a more human friendly
|
||||
way, for example `gpioe.bsrr`, to refer to the `BSRR` register in the `GPIOE` register block.
|
||||
|
||||
Then we have this `write` method that takes a closure. If the identity closure (`|w| w`) is used,
|
||||
this method will set the register to its *default* (reset) value, the value it had right after the
|
||||
microcontroller was powered on / reset. That value is `0x0` for the `BSRR` register. Since we want
|
||||
to write a non-zero value to the register, we use builder methods like `bs9` and `br9` to set some
|
||||
of the bits of the default value.
|
||||
|
||||
Let's run this program! There's some interesting stuff we can do *while* debugging the program.
|
||||
|
||||
`gpioe` is a reference to the `GPIOE` register block. `print gpioe` will return the base address of
|
||||
the register block.
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
(..)
|
||||
|
||||
Breakpoint 1, registers::__cortex_m_rt_main_trampoline () at src/07-registers/src/main.rs:7
|
||||
7 #[entry]
|
||||
|
||||
(gdb) step
|
||||
registers::__cortex_m_rt_main () at src/07-registers/src/main.rs:9
|
||||
9 let gpioe = aux7::init().1;
|
||||
|
||||
(gdb) next
|
||||
12 gpioe.bsrr.write(|w| w.bs9().set_bit());
|
||||
|
||||
(gdb) print gpioe
|
||||
$1 = (*mut stm32f3::stm32f303::gpioc::RegisterBlock) 0x48001000
|
||||
```
|
||||
|
||||
But if we instead `print *gpioe`, we'll get a *full view* of the register block: the value of each
|
||||
of its registers will be printed.
|
||||
|
||||
```
|
||||
(gdb) print *gpioe
|
||||
$2 = stm32f3::stm32f303::gpioc::RegisterBlock {
|
||||
moder: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_MODER> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 1431633920
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_MODER>
|
||||
},
|
||||
otyper: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_OTYPER> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_OTYPER>
|
||||
},
|
||||
ospeedr: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_OSPEEDR> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_OSPEEDR>
|
||||
},
|
||||
pupdr: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_PUPDR> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_PUPDR>
|
||||
},
|
||||
idr: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_IDR> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 204
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_IDR>
|
||||
},
|
||||
odr: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_ODR> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_ODR>
|
||||
},
|
||||
bsrr: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_BSRR> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_BSRR>
|
||||
},
|
||||
lckr: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_LCKR> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_LCKR>
|
||||
},
|
||||
afrl: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_AFRL> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_AFRL>
|
||||
},
|
||||
afrh: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_AFRH> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_AFRH>
|
||||
},
|
||||
brr: stm32f3::generic::Reg<u32, stm32f3::stm32f303::gpioc::_BRR> {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
_marker: core::marker::PhantomData<stm32f3::stm32f303::gpioc::_BRR>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
All these newtypes and closures sound like they'd generate large, bloated programs but, if you
|
||||
actually compile the program in release mode with [LTO] enabled, you'll see that it produces exactly
|
||||
the same instructions that the "unsafe" version that used `write_volatile` and hexadecimal addresses
|
||||
did!
|
||||
|
||||
[LTO]: https://en.wikipedia.org/wiki/Interprocedural_optimization
|
||||
|
||||
Use `cargo objdump` to grab the assembler code to `release.txt`:
|
||||
``` console
|
||||
cargo objdump --bin registers --release -- -d --no-show-raw-insn --print-imm-hex > release.txt
|
||||
```
|
||||
|
||||
Then search for `main` in `release.txt`
|
||||
```
|
||||
0800023e <main>:
|
||||
800023e: push {r7, lr}
|
||||
8000240: mov r7, sp
|
||||
8000242: bl #0x2
|
||||
8000246: trap
|
||||
|
||||
08000248 <registers::__cortex_m_rt_main::h199f1359501d5c71>:
|
||||
8000248: push {r7, lr}
|
||||
800024a: mov r7, sp
|
||||
800024c: bl #0x22
|
||||
8000250: movw r0, #0x1018
|
||||
8000254: mov.w r1, #0x200
|
||||
8000258: movt r0, #0x4800
|
||||
800025c: str r1, [r0]
|
||||
800025e: mov.w r1, #0x800
|
||||
8000262: str r1, [r0]
|
||||
8000264: mov.w r1, #0x2000000
|
||||
8000268: str r1, [r0]
|
||||
800026a: mov.w r1, #0x8000000
|
||||
800026e: str r1, [r0]
|
||||
8000270: b #-0x4 <registers::__cortex_m_rt_main::h199f1359501d5c71+0x28>
|
||||
```
|
||||
|
||||
The best part of all this is that nobody had to write a single line of code to implement the
|
||||
GPIOE API. All the code was automatically generated from a System View Description (SVD) file using the
|
||||
[svd2rust] tool. This SVD file is actually an XML file that microcontroller vendors provide and that
|
||||
contains the register maps of their microcontrollers. The file contains the layout of register
|
||||
blocks, the base addresses, the read/write permissions of each register, the layout of the
|
||||
registers, whether a register has reserved bits and lots of other useful information.
|
||||
|
||||
[svd2rust]: https://crates.io/crates/svd2rust
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
edition = "2018"
|
||||
name = "leds-again"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
aux8 = { path = "auxiliary" }
|
||||
117
discovery-master/f3discovery/src/08-leds-again/README.md
Normal file
117
discovery-master/f3discovery/src/08-leds-again/README.md
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# LEDs, again
|
||||
|
||||
In the last section, I gave you *initialized* (configured) peripherals (I initialized them in
|
||||
`aux7::init`). That's why just writing to `BSRR` was enough to control the LEDs. But, peripherals
|
||||
are not *initialized* right after the microcontroller boots.
|
||||
|
||||
In this section, you'll have more fun with registers. I won't do any initialization and you'll have
|
||||
to initialize and configure `GPIOE` pins as digital outputs pins so that you'll be able to drive LEDs
|
||||
again.
|
||||
|
||||
This is the starter code.
|
||||
|
||||
``` rust
|
||||
{{#include src/main.rs}}
|
||||
```
|
||||
|
||||
If you run the starter code, you'll see that nothing happens this time. Furthermore, if you print
|
||||
the `GPIOE` register block, you'll see that every register reads as zero even after the
|
||||
`gpioe.odr.write` statement was executed!
|
||||
|
||||
```
|
||||
$ cargo run
|
||||
Breakpoint 1, main () at src/08-leds-again/src/main.rs:9
|
||||
9 let (gpioe, rcc) = aux8::init();
|
||||
|
||||
(gdb) continue
|
||||
Continuing.
|
||||
|
||||
Program received signal SIGTRAP, Trace/breakpoint trap.
|
||||
0x08000f3c in __bkpt ()
|
||||
|
||||
(gdb) finish
|
||||
Run till exit from #0 0x08000f3c in __bkpt ()
|
||||
main () at src/08-leds-again/src/main.rs:25
|
||||
25 aux8::bkpt();
|
||||
|
||||
(gdb) p/x *gpioe
|
||||
$1 = stm32f30x::gpioc::RegisterBlock {
|
||||
moder: stm32f30x::gpioc::MODER {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
otyper: stm32f30x::gpioc::OTYPER {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
ospeedr: stm32f30x::gpioc::OSPEEDR {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
pupdr: stm32f30x::gpioc::PUPDR {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
idr: stm32f30x::gpioc::IDR {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
odr: stm32f30x::gpioc::ODR {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
bsrr: stm32f30x::gpioc::BSRR {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
lckr: stm32f30x::gpioc::LCKR {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
afrl: stm32f30x::gpioc::AFRL {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
afrh: stm32f30x::gpioc::AFRH {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
},
|
||||
brr: stm32f30x::gpioc::BRR {
|
||||
register: vcell::VolatileCell<u32> {
|
||||
value: core::cell::UnsafeCell<u32> {
|
||||
value: 0x0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
edition = "2018"
|
||||
name = "aux8"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.6.3"
|
||||
cortex-m-rt = "0.6.3"
|
||||
panic-itm = "0.4.0"
|
||||
|
||||
[dependencies.f3]
|
||||
features = ["rt"]
|
||||
version = "0.6.1"
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
//! Initialization code
|
||||
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust#53964
|
||||
extern crate panic_itm; // panic handler
|
||||
|
||||
pub use cortex_m::asm::bkpt;
|
||||
pub use cortex_m_rt::entry;
|
||||
pub use f3::hal::stm32f30x::{gpioc, rcc};
|
||||
|
||||
use f3::hal::stm32f30x::{self, GPIOE, RCC};
|
||||
|
||||
pub fn init() -> (&'static gpioc::RegisterBlock, &'static rcc::RegisterBlock) {
|
||||
// restrict access to the other peripherals
|
||||
(stm32f30x::Peripherals::take().unwrap());
|
||||
|
||||
unsafe { (&*GPIOE::ptr(), &*RCC::ptr()) }
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# Configuration
|
||||
|
||||
After turning on the GPIOE peripheral, it still needs to be configured. In this case, we
|
||||
want the pins to be configured as digital *outputs* so they can drive the LEDs; by default, most
|
||||
pins are configured as digital *inputs*.
|
||||
|
||||
You can find the list of registers in the `GPIOE` register block in:
|
||||
|
||||
> Section 11.4.12 - GPIO registers - Page 243 - Reference Manual
|
||||
|
||||
The register we'll have to deal with is: `MODER`.
|
||||
|
||||
Your task for this section is to further update the starter code to configure the *right* `GPIOE`
|
||||
pins as digital outputs. You'll have to:
|
||||
|
||||
- Figure out *which* pins you need to configure as digital outputs. (hint: check Section 6.4 LEDs of
|
||||
the *User Manual* (page 18)).
|
||||
- Read the documentation to understand what the bits in the `MODER` register do.
|
||||
- Modify the `MODER` register to configure the pins as digital outputs.
|
||||
|
||||
If successful, you'll see the 8 LEDs turn on when you run the program.
|
||||
10
discovery-master/f3discovery/src/08-leds-again/openocd.gdb
Normal file
10
discovery-master/f3discovery/src/08-leds-again/openocd.gdb
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
target remote :3333
|
||||
set print asm-demangle on
|
||||
set print pretty on
|
||||
monitor tpiu config internal itm.txt uart off 8000000
|
||||
monitor itm port 0 on
|
||||
load
|
||||
break DefaultHandler
|
||||
break HardFault
|
||||
break main
|
||||
continue
|
||||
32
discovery-master/f3discovery/src/08-leds-again/power.md
Normal file
32
discovery-master/f3discovery/src/08-leds-again/power.md
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# Power
|
||||
|
||||
Turns out that, to save power, most peripherals start in a powered off state -- that's their state
|
||||
right after the microcontroller boots.
|
||||
|
||||
The Reset and Clock Control (`RCC`) peripheral can be used to power on or off every other
|
||||
peripheral.
|
||||
|
||||
You can find the list of registers in the `RCC` register block in:
|
||||
|
||||
> Section 9.4.14 - RCC register map - Page 166 - Reference Manual
|
||||
|
||||
The registers that control the power status of other peripherals are:
|
||||
|
||||
- `AHBENR`
|
||||
- `APB1ENR`
|
||||
- `APB2ENR`
|
||||
|
||||
Each bit in these registers controls the power status of a single peripheral, including `GPIOE`.
|
||||
|
||||
Your task in this section is to power on the `GPIOE` peripheral. You'll have to:
|
||||
|
||||
- Figure out which of the three registers I mentioned before has the bit that controls the power
|
||||
status.
|
||||
- Figure out what value that bit must be set to,`0` or `1`, to power on the `GPIOE` peripheral.
|
||||
- Finally, you'll have to change the starter code to *modify* the right register to turn on the
|
||||
`GPIOE` peripheral.
|
||||
|
||||
If you are successful, you'll see that the `gpioe.odr.write` statement will now be able to modify
|
||||
the value of the `ODR` register.
|
||||
|
||||
Note that this won't be enough to actually turn on the LEDs.
|
||||
28
discovery-master/f3discovery/src/08-leds-again/src/main.rs
Normal file
28
discovery-master/f3discovery/src/08-leds-again/src/main.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use aux8::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (gpioe, rcc) = aux8::init();
|
||||
|
||||
// TODO initialize GPIOE
|
||||
|
||||
// Turn on all the LEDs in the compass
|
||||
gpioe.odr.write(|w| {
|
||||
w.odr8().set_bit();
|
||||
w.odr9().set_bit();
|
||||
w.odr10().set_bit();
|
||||
w.odr11().set_bit();
|
||||
w.odr12().set_bit();
|
||||
w.odr13().set_bit();
|
||||
w.odr14().set_bit();
|
||||
w.odr15().set_bit()
|
||||
});
|
||||
|
||||
aux8::bkpt();
|
||||
|
||||
loop {}
|
||||
}
|
||||
1
discovery-master/f3discovery/src/08-leds-again/target
Symbolic link
1
discovery-master/f3discovery/src/08-leds-again/target
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../target
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
# The solution
|
||||
|
||||
``` rust
|
||||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use aux8::entry;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (gpioe, rcc) = aux8::init();
|
||||
|
||||
// enable the GPIOE peripheral
|
||||
rcc.ahbenr.write(|w| w.iopeen().set_bit());
|
||||
|
||||
// configure the pins as outputs
|
||||
gpioe.moder.write(|w| {
|
||||
w.moder8().output();
|
||||
w.moder9().output();
|
||||
w.moder10().output();
|
||||
w.moder11().output();
|
||||
w.moder12().output();
|
||||
w.moder13().output();
|
||||
w.moder14().output();
|
||||
w.moder15().output()
|
||||
});
|
||||
|
||||
// Turn on all the LEDs in the compass
|
||||
gpioe.odr.write(|w| {
|
||||
w.odr8().set_bit();
|
||||
w.odr9().set_bit();
|
||||
w.odr10().set_bit();
|
||||
w.odr11().set_bit();
|
||||
w.odr12().set_bit();
|
||||
w.odr13().set_bit();
|
||||
w.odr14().set_bit();
|
||||
w.odr15().set_bit()
|
||||
});
|
||||
|
||||
aux8::bkpt();
|
||||
|
||||
loop {}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
edition = "2018"
|
||||
name = "clocks-and-timers"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
aux9 = { path = "auxiliary" }
|
||||
cortex-m-rt = "0.6.3"
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Clocks and timers
|
||||
|
||||
In this section, we'll re-implement the LED roulette application. I'm going to give you back the
|
||||
`Led` abstraction but this time I'm going to take away the `Delay` abstraction `:-)`.
|
||||
|
||||
Here's the starter code. The `delay` function is unimplemented so if you run this program the LEDs
|
||||
will blink so fast that they'll appear to always be on.
|
||||
|
||||
``` rust
|
||||
{{#include src/main.rs}}
|
||||
```
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
edition = "2018"
|
||||
name = "aux9"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.6.3"
|
||||
cortex-m-rt = "0.6.3"
|
||||
panic-itm = "0.4.0"
|
||||
stm32f3-discovery = "0.6.0"
|
||||
|
||||
[dependencies.stm32f3]
|
||||
version = "0.12.1"
|
||||
features = ["stm32f303", "rt"]
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
//! Initialization code
|
||||
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_extern_crates)] // NOTE(allow) rust-lang/rust#53964
|
||||
extern crate panic_itm; // panic handler
|
||||
|
||||
pub use cortex_m::asm::{bkpt, nop};
|
||||
pub use cortex_m_rt::entry;
|
||||
pub use stm32f3::stm32f303::{rcc, tim6, RCC, TIM6};
|
||||
pub use stm32f3_discovery::switch_hal;
|
||||
|
||||
use stm32f3_discovery::{
|
||||
leds::Leds,
|
||||
stm32f3xx_hal::{prelude::*, stm32},
|
||||
};
|
||||
|
||||
pub fn init() -> (
|
||||
Leds,
|
||||
&'static rcc::RegisterBlock,
|
||||
&'static tim6::RegisterBlock,
|
||||
) {
|
||||
let p = stm32::Peripherals::take().unwrap();
|
||||
|
||||
let mut rcc = p.RCC.constrain();
|
||||
|
||||
let mut gpioe = p.GPIOE.split(&mut rcc.ahb);
|
||||
|
||||
let leds = Leds::new(
|
||||
gpioe.pe8,
|
||||
gpioe.pe9,
|
||||
gpioe.pe10,
|
||||
gpioe.pe11,
|
||||
gpioe.pe12,
|
||||
gpioe.pe13,
|
||||
gpioe.pe14,
|
||||
gpioe.pe15,
|
||||
&mut gpioe.moder,
|
||||
&mut gpioe.otyper,
|
||||
);
|
||||
|
||||
(leds, unsafe { &*RCC::ptr() }, unsafe { &*TIM6::ptr() })
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# Busy waiting
|
||||
|
||||
The timer should now be properly initialized. All that's left is to implement the `delay` function
|
||||
using the timer.
|
||||
|
||||
First thing we have to do is set the autoreload register (`ARR`) to make the timer go off in `ms`
|
||||
milliseconds. Because the counter operates at 1 KHz, the autoreload value will be the same as `ms`.
|
||||
|
||||
``` rust
|
||||
// Set the timer to go off in `ms` ticks
|
||||
// 1 tick = 1 ms
|
||||
tim6.arr.write(|w| w.arr().bits(ms));
|
||||
```
|
||||
|
||||
Next, we need to enable the counter. It will immediately start counting.
|
||||
|
||||
``` rust
|
||||
// CEN: Enable the counter
|
||||
tim6.cr1.modify(|_, w| w.cen().set_bit());
|
||||
```
|
||||
|
||||
Now we need to wait until the counter reaches the value of the autoreload register, `ms`, then we'll
|
||||
know that `ms` milliseconds have passed. That condition is known as an *update event* and its
|
||||
indicated by the `UIF` bit of the status register (`SR`).
|
||||
|
||||
``` rust
|
||||
// Wait until the alarm goes off (until the update event occurs)
|
||||
while !tim6.sr.read().uif().bit_is_set() {}
|
||||
```
|
||||
|
||||
This pattern of just waiting until some condition is met, in this case that `UIF` becomes `1`, is
|
||||
known as *busy waiting* and you'll see it a few more times in this text `:-)`.
|
||||
|
||||
Finally, we must clear (set to `0`) this `UIF` bit. If we don't, next time we enter the `delay`
|
||||
function we'll think the update event has already happened and skip over the busy waiting part.
|
||||
|
||||
``` rust
|
||||
// Clear the update event flag
|
||||
tim6.sr.modify(|_, w| w.uif().clear_bit());
|
||||
```
|
||||
|
||||
Now, put this all together and check if it works as expected.
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
# `for` loop delays
|
||||
|
||||
The first challenge is to implement the `delay` function without using any peripheral and the
|
||||
obvious solution is to implement it as a `for` loop delay:
|
||||
|
||||
``` rust
|
||||
#[inline(never)]
|
||||
fn delay(tim6: &tim6::RegisterBlock, ms: u16) {
|
||||
for _ in 0..1_000 {}
|
||||
}
|
||||
```
|
||||
|
||||
Of course, the above implementation is wrong because it always generates the same delay for any
|
||||
value of `ms`.
|
||||
|
||||
In this section, you'll have to:
|
||||
|
||||
- Fix the `delay` function to generate delays proportional to its input `ms`.
|
||||
- Tweak the `delay` function to make the LED roulette spin at a rate of approximately 5 cycles in 4
|
||||
seconds (800 milliseconds period).
|
||||
- The processor inside the microcontroller is clocked at 72 MHz and executes most instructions in one
|
||||
"tick", a cycle of its clock. How many (`for`) loops do you *think* the `delay` function must do
|
||||
to generate a delay of 1 second?
|
||||
- How many `for` loops does `delay(1000)` actually do?
|
||||
- What happens if compile your program in release mode and run it?
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Initialization
|
||||
|
||||
As with every other peripheral, we'll have to initialize this timer before we can use it. And just
|
||||
as in the previous section, initialization is going to involve two steps: powering up the timer and
|
||||
then configuring it.
|
||||
|
||||
Powering up the timer is easy: We just have to set `TIM6EN` bit to 1. This bit is in the `APB1ENR`
|
||||
register of the `RCC` register block.
|
||||
|
||||
``` rust
|
||||
// Power on the TIM6 timer
|
||||
rcc.apb1enr.modify(|_, w| w.tim6en().set_bit());
|
||||
```
|
||||
|
||||
The configuration part is slightly more elaborate.
|
||||
|
||||
First, we'll have to configure the timer to operate in one pulse mode.
|
||||
|
||||
``` rust
|
||||
// OPM Select one pulse mode
|
||||
// CEN Keep the counter disabled for now
|
||||
tim6.cr1.write(|w| w.opm().set_bit().cen().clear_bit());
|
||||
```
|
||||
|
||||
Then, we'll like to have the `CNT` counter operate at a frequency of 1 KHz because our `delay`
|
||||
function takes a number of milliseconds as arguments and 1 KHz produces a 1 millisecond period. For
|
||||
that we'll have to configure the prescaler.
|
||||
|
||||
``` rust
|
||||
// Configure the prescaler to have the counter operate at 1 KHz
|
||||
tim6.psc.write(|w| w.psc().bits(psc));
|
||||
```
|
||||
|
||||
I'm going to let you figure out the value of the prescaler, `psc`. Remember that the frequency of
|
||||
the counter is `apb1 / (psc + 1)` and that `apb1` is 8 MHz.
|
||||
45
discovery-master/f3discovery/src/09-clocks-and-timers/nop.md
Normal file
45
discovery-master/f3discovery/src/09-clocks-and-timers/nop.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# NOP
|
||||
|
||||
If in the previous section you compiled the program in release mode and actually looked at the
|
||||
disassembly, you probably noticed that the `delay` function is optimized away and never gets called
|
||||
from within `main`.
|
||||
|
||||
LLVM decided that the function wasn't doing anything worthwhile and just removed it.
|
||||
|
||||
There is a way to prevent LLVM from optimizing the `for` loop delay: add a *volatile* assembly
|
||||
instruction. Any instruction will do but NOP (No OPeration) is a particular good choice in this case
|
||||
because it has no side effect.
|
||||
|
||||
Your `for` loop delay would become:
|
||||
|
||||
``` rust
|
||||
#[inline(never)]
|
||||
fn delay(_tim6: &tim6::RegisterBlock, ms: u16) {
|
||||
const K: u16 = 3; // this value needs to be tweaked
|
||||
for _ in 0..(K * ms) {
|
||||
aux9::nop()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And this time `delay` won't be compiled away by LLVM when you compile your program in release mode:
|
||||
|
||||
``` console
|
||||
$ cargo objdump --bin clocks-and-timers --release -- -d --no-show-raw-insn
|
||||
clocks-and-timers: file format ELF32-arm-little
|
||||
|
||||
Disassembly of section .text:
|
||||
clocks_and_timers::delay::h711ce9bd68a6328f:
|
||||
8000188: push {r4, r5, r7, lr}
|
||||
800018a: movs r4, #0
|
||||
800018c: adds r4, #1
|
||||
800018e: uxth r5, r4
|
||||
8000190: bl #4666
|
||||
8000194: cmp r5, #150
|
||||
8000196: blo #-14 <clocks_and_timers::delay::h711ce9bd68a6328f+0x4>
|
||||
8000198: pop {r4, r5, r7, pc}
|
||||
```
|
||||
|
||||
Now, test this: Compile the program in debug mode and run it, then compile the program in release
|
||||
mode and run it. What's the difference between them? What do you think is the main cause of the
|
||||
difference? Can you think of a way to make them equivalent or at least more similar again?
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
# One-shot timer
|
||||
|
||||
I hope that, by now, I have convinced you that `for` loop delays are a poor way to implement delays.
|
||||
|
||||
Now, we'll implement delays using a *hardware timer*. The basic function of a (hardware) timer is
|
||||
... to keep precise track of time. A timer is yet another peripheral that's available to the
|
||||
microcontroller; thus it can be controlled using registers.
|
||||
|
||||
The microcontroller we are using has several (in fact, more than 10) timers of different kinds
|
||||
(basic, general purpose, and advanced timers) available to it. Some timers have more *resolution*
|
||||
(number of bits) than others and some can be used for more than just keeping track of time.
|
||||
|
||||
We'll be using one of the *basic* timers: `TIM6`. This is one of the simplest timers available in
|
||||
our microcontroller. The documentation for basic timers is in the following section:
|
||||
|
||||
> Section 22 Timers - Page 670 - Reference Manual
|
||||
|
||||
Its registers are documented in:
|
||||
|
||||
> Section 22.4.9 TIM6/TIM7 register map - Page 682 - Reference Manual
|
||||
|
||||
The registers we'll be using in this section are:
|
||||
|
||||
- `SR`, the status register.
|
||||
- `EGR`, the event generation register.
|
||||
- `CNT`, the counter register.
|
||||
- `PSC`, the prescaler register.
|
||||
- `ARR`, the autoreload register.
|
||||
|
||||
We'll be using the timer as a *one-shot* timer. It will sort of work like an alarm clock. We'll set
|
||||
the timer to go off after some amount of time and then we'll wait until the timer goes off. The
|
||||
documentation refers to this mode of operation as *one pulse mode*.
|
||||
|
||||
Here's a description of how a basic timer works when configured in one pulse mode:
|
||||
|
||||
- The counter is enabled by the user (`CR1.CEN = 1`).
|
||||
- The `CNT` register resets its value to zero and, on each tick, its value gets incremented by one.
|
||||
- Once the `CNT` register has reached the value of the `ARR` register, the counter will be disabled
|
||||
by hardware (`CR1.CEN = 0`) and an *update event* will be raised (`SR.UIF = 1`).
|
||||
|
||||
`TIM6` is driven by the APB1 clock, whose frequency doesn't have to necessarily match the processor
|
||||
frequency. That is, the APB1 clock could be running faster or slower. The default, however, is that
|
||||
both APB1 and the processor are clocked at 8 MHz.
|
||||
|
||||
The tick mentioned in the functional description of the one pulse mode is *not* the same as one
|
||||
tick of the APB1 clock. The `CNT` register increases at a frequency of `apb1 / (psc + 1)`
|
||||
times per second, where `apb1` is the frequency of the APB1 clock and `psc` is the value of the
|
||||
prescaler register, `PSC`.
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
target remote :3333
|
||||
set print asm-demangle on
|
||||
set print pretty on
|
||||
monitor tpiu config internal itm.txt uart off 8000000
|
||||
monitor itm port 0 on
|
||||
load
|
||||
break DefaultHandler
|
||||
break HardFault
|
||||
break main
|
||||
continue
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Putting it all together
|
||||
|
||||
``` rust
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use aux9::{entry, switch_hal::OutputSwitch, tim6};
|
||||
|
||||
#[inline(never)]
|
||||
fn delay(tim6: &tim6::RegisterBlock, ms: u16) {
|
||||
// Set the timer to go off in `ms` ticks
|
||||
// 1 tick = 1 ms
|
||||
tim6.arr.write(|w| w.arr().bits(ms));
|
||||
|
||||
// CEN: Enable the counter
|
||||
tim6.cr1.modify(|_, w| w.cen().set_bit());
|
||||
|
||||
// Wait until the alarm goes off (until the update event occurs)
|
||||
while !tim6.sr.read().uif().bit_is_set() {}
|
||||
|
||||
// Clear the update event flag
|
||||
tim6.sr.modify(|_, w| w.uif().clear_bit());
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (leds, rcc, tim6) = aux9::init();
|
||||
let mut leds = leds.into_array();
|
||||
|
||||
// Power on the TIM6 timer
|
||||
rcc.apb1enr.modify(|_, w| w.tim6en().set_bit());
|
||||
|
||||
// OPM Select one pulse mode
|
||||
// CEN Keep the counter disabled for now
|
||||
tim6.cr1.write(|w| w.opm().set_bit().cen().clear_bit());
|
||||
|
||||
// Configure the prescaler to have the counter operate at 1 KHz
|
||||
// APB1_CLOCK = 8 MHz
|
||||
// PSC = 7999
|
||||
// 8 MHz / (7999 + 1) = 1 KHz
|
||||
// The counter (CNT) will increase on every millisecond
|
||||
tim6.psc.write(|w| w.psc().bits(7_999));
|
||||
|
||||
let ms = 50;
|
||||
loop {
|
||||
for curr in 0..8 {
|
||||
let next = (curr + 1) % 8;
|
||||
|
||||
leds[next].on().unwrap();
|
||||
delay(tim6, ms);
|
||||
leds[curr].off().unwrap();
|
||||
delay(tim6, ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use aux9::{entry, switch_hal::OutputSwitch, tim6};
|
||||
|
||||
#[inline(never)]
|
||||
fn delay(tim6: &tim6::RegisterBlock, ms: u16) {
|
||||
// TODO implement this
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (leds, rcc, tim6) = aux9::init();
|
||||
let mut leds = leds.into_array();
|
||||
|
||||
// TODO initialize TIM6
|
||||
|
||||
let ms = 50;
|
||||
loop {
|
||||
for curr in 0..8 {
|
||||
let next = (curr + 1) % 8;
|
||||
|
||||
leds[next].on().unwrap();
|
||||
delay(tim6, ms);
|
||||
leds[curr].off().unwrap();
|
||||
delay(tim6, ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
discovery-master/f3discovery/src/09-clocks-and-timers/target
Symbolic link
1
discovery-master/f3discovery/src/09-clocks-and-timers/target
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../../target
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
# Serial communication
|
||||
|
||||
<a href="https://en.wikipedia.org/wiki/File:Serial_port.jpg">
|
||||
<p align="center">
|
||||
<img height="240" title="Standard serial port connector DE-9" src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Serial_port.jpg/800px-Serial_port.jpg">
|
||||
</p>
|
||||
</a>
|
||||
|
||||
<p align="center">
|
||||
<em>This is what we'll be using. I hope your computer has one!</em>
|
||||
</p>
|
||||
|
||||
Nah, don't worry. This connector, the DE-9, went out of fashion on PCs quite some time ago; it got
|
||||
replaced by the Universal Serial Bus (USB). We won't be dealing with the DE-9 connector itself but
|
||||
with the communication protocol that this cable is/was usually used for.
|
||||
|
||||
So what's this [*serial communication*][ASC]? It's an *asynchronous* communication protocol where two
|
||||
devices exchange data *serially*, as in one bit at a time, using two data lines (plus a common
|
||||
ground). The protocol is asynchronous in the sense that neither of the shared lines carries a clock
|
||||
signal. Instead both parties must agree on how fast data will be sent along the wire *before* the
|
||||
communication occurs. This protocol allows *duplex* communication as data can be sent from A to B
|
||||
and from B to A simultaneously.
|
||||
|
||||
We'll be using this protocol to exchange data between the microcontroller and your computer. In
|
||||
contrast to the ITM protocol we have used before, with the serial communication protocol you can
|
||||
send data from your computer to the microcontroller.
|
||||
|
||||
The next practical question you probably want to ask is: How fast can we send data through this
|
||||
protocol?
|
||||
|
||||
This protocol works with frames. Each frame has one *start* bit, 5 to 9 bits of payload (data) and 1
|
||||
to 2 *stop bits*. The speed of the protocol is known as *baud rate* and it's quoted in bits per
|
||||
second (bps). Common baud rates are: 9600, 19200, 38400, 57600 and 115200 bps.
|
||||
|
||||
To actually answer the question: With a common configuration of 1 start bit, 8 bits of data, 1
|
||||
stop bit and a baud rate of 115200 bps one can, in theory, send 11,520 frames per second. Since each
|
||||
one frame carries a byte of data that results in a data rate of 11.52 KB/s. In practice, the data
|
||||
rate will probably be lower because of processing times on the slower side of the communication (the
|
||||
microcontroller).
|
||||
|
||||
Today's computers don't support the serial communication protocol. So you can't directly connect
|
||||
your computer to the microcontroller. But that's where the serial module comes in. This module will
|
||||
sit between the two and expose a serial interface to the microcontroller and an USB interface to
|
||||
your computer. The microcontroller will see your computer as another serial device and your computer
|
||||
will see the microcontroller as a virtual serial device.
|
||||
|
||||
Now, let's get familiar with the serial module and the serial communication tools that your OS
|
||||
offers. Pick a route:
|
||||
|
||||
- [\*nix](nix-tooling.md)
|
||||
- [Windows](windows-tooling.md)
|
||||
|
||||
[ASC]: https://en.wikipedia.org/wiki/Asynchronous_serial_communication
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
# Loopbacks
|
||||
|
||||
We've tested sending data. It's time to test receiving it. Except that there's no other device that
|
||||
can send us some data ... or is there?
|
||||
|
||||
Enter: loopbacks
|
||||
|
||||
<p align="center">
|
||||
<img title="Serial module loopback" src="../assets/serial-loopback.png">
|
||||
</p>
|
||||
|
||||
You can send data to yourself! Not very useful in production but very useful for debugging.
|
||||
|
||||
## Older board revision / external serial module
|
||||
|
||||
Connect the `TXO` and the `RXI` pins of the serial module together using a male to male jumper wire
|
||||
as shown above.
|
||||
|
||||
Now enter some text into minicom/PuTTY and observe. What happens?
|
||||
|
||||
You should see three things:
|
||||
|
||||
- As before, the TX (red) LED blinks on each key press.
|
||||
- But now the RX (green) LED blinks on each key press as well! This indicates that the serial module
|
||||
is receiving some data; the one it just sent.
|
||||
- Finally, on the minicom/PuTTY console, you should see that what you type echoes back to the
|
||||
console.
|
||||
|
||||
## Newer board revision
|
||||
|
||||
If you have a newer revision of the board you can set up a loopback by shorting
|
||||
the PC4 and PC5 pins using a female to female jumper wire, like [you did for the
|
||||
SWO pin](../06-hello-world/index.html).
|
||||
|
||||
You should now be able to send data to yourself.
|
||||
|
||||
Now try to enter some text into minicom/PuTTY and observe.
|
||||
|
||||
> **NOTE**: To rule out the possibility of the existing firmware doing weird
|
||||
> things to the serial pins (PC4 and PC5) we recommend *holding* the reset
|
||||
> button while you enter text into minicom/PuTTY.
|
||||
|
||||
If all is working you should see what you type echoed back to minicom/PuTTY
|
||||
console.
|
||||
|
||||
---
|
||||
|
||||
Now that you are familiar with sending and receiving data over serial port using minicom/PuTTY,
|
||||
let's make your microcontroller and your computer talk!
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
# \*nix tooling
|
||||
|
||||
## Newer revisions of the discovery board
|
||||
|
||||
With newer revisions, if you connect the discovery board to your computer you
|
||||
should see a new TTY device appear in `/dev`.
|
||||
|
||||
``` console
|
||||
$ # Linux
|
||||
$ dmesg | tail | grep -i tty
|
||||
[13560.675310] cdc_acm 1-1.1:1.2: ttyACM0: USB ACM device
|
||||
```
|
||||
|
||||
This is the USB <-> Serial device. On Linux, it's named `tty*` (usually
|
||||
`ttyACM*` or `ttyUSB*`).
|
||||
|
||||
If you don't see the device appear then you probably have an older revision of
|
||||
the board; check the next section, which contains instructions for older
|
||||
revisions. If you do have a newer revision skip the next section and move to the
|
||||
"minicom" section.
|
||||
|
||||
## Older revisions of the discovery board / external serial module
|
||||
|
||||
Connect the serial module to your computer and let's find out what name the OS assigned to it.
|
||||
|
||||
> **NOTE** On macs, the USB device will named like this: `/dev/cu.usbserial-*`. You won't
|
||||
> find it using `dmesg`, instead use `ls -l /dev | grep cu.usb` and adjust the following
|
||||
> commands accordingly!
|
||||
|
||||
``` console
|
||||
$ dmesg | grep -i tty
|
||||
(..)
|
||||
[ +0.000155] usb 3-2: FTDI USB Serial Device converter now attached to ttyUSB0
|
||||
```
|
||||
|
||||
But what's this `ttyUSB0` thing? It's a file of course! Everything is a file in \*nix:
|
||||
|
||||
``` console
|
||||
$ ls -l /dev/ttyUSB0
|
||||
crw-rw-rw- 1 root uucp 188, 0 Oct 27 00:00 /dev/ttyUSB0
|
||||
```
|
||||
|
||||
> **NOTE** if the permissions above is `crw-rw----`, the udev rules have not been set correctly
|
||||
> see [udev rules](../03-setup/linux.html#udev-rules)
|
||||
|
||||
You can send out data by simply writing to this file:
|
||||
|
||||
``` console
|
||||
$ echo 'Hello, world!' > /dev/ttyUSB0
|
||||
```
|
||||
|
||||
You should see the TX (red) LED on the serial module blink, just once and very fast!
|
||||
|
||||
## All revisions: minicom
|
||||
|
||||
Dealing with serial devices using `echo` is far from ergonomic. So, we'll use the program `minicom`
|
||||
to interact with the serial device using the keyboard.
|
||||
|
||||
We must configure `minicom` before we use it. There are quite a few ways to do that but we'll use a
|
||||
`.minirc.dfl` file in the home directory. Create a file in `~/.minirc.dfl` with the following
|
||||
contents:
|
||||
|
||||
``` console
|
||||
$ cat ~/.minirc.dfl
|
||||
pu baudrate 115200
|
||||
pu bits 8
|
||||
pu parity N
|
||||
pu stopbits 1
|
||||
pu rtscts No
|
||||
pu xonxoff No
|
||||
```
|
||||
|
||||
> **NOTE** Make sure this file ends in a newline! Otherwise, `minicom` will fail to read it.
|
||||
|
||||
That file should be straightforward to read (except for the last two lines), but nonetheless let's
|
||||
go over it line by line:
|
||||
|
||||
- `pu baudrate 115200`. Sets baud rate to 115200 bps.
|
||||
- `pu bits 8`. 8 bits per frame.
|
||||
- `pu parity N`. No parity check.
|
||||
- `pu stopbits 1`. 1 stop bit.
|
||||
- `pu rtscts No`. No hardware control flow.
|
||||
- `pu xonxoff No`. No software control flow.
|
||||
|
||||
Once that's in place, we can launch `minicom`.
|
||||
|
||||
``` console
|
||||
$ # NOTE you may need to use a different device here
|
||||
$ minicom -D /dev/ttyACM0 -b 115200
|
||||
```
|
||||
|
||||
This tells `minicom` to open the serial device at `/dev/ttyACM0` and set its
|
||||
baud rate to 115200. A text-based user interface (TUI) will pop out.
|
||||
|
||||
<p align="center">
|
||||
<img title="minicom" src="../assets/minicom.png">
|
||||
</p>
|
||||
|
||||
You can now send data using the keyboard! Go ahead and type something. Note that
|
||||
the TUI will *not* echo back what you type but, if you are using an external
|
||||
module, you *may* see some LED on the module blink with each keystroke.
|
||||
|
||||
## `minicom` commands
|
||||
|
||||
`minicom` exposes commands via keyboard shortcuts. On Linux, the shortcuts start with `Ctrl+A`. On
|
||||
mac, the shortcuts start with the `Meta` key. Some useful commands below:
|
||||
|
||||
- `Ctrl+A` + `Z`. Minicom Command Summary
|
||||
- `Ctrl+A` + `C`. Clear the screen
|
||||
- `Ctrl+A` + `X`. Exit and reset
|
||||
- `Ctrl+A` + `Q`. Quit with no reset
|
||||
|
||||
> **NOTE** mac users: In the above commands, replace `Ctrl+A` with `Meta`.
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
# Windows tooling
|
||||
|
||||
Start by unplugging your discovery board.
|
||||
|
||||
Before plugging the discovery board or the serial module, run the following command on
|
||||
the terminal:
|
||||
|
||||
``` console
|
||||
$ mode
|
||||
```
|
||||
|
||||
It will print a list of devices that are connected to your computer. The ones that start with `COM` in
|
||||
their names are serial devices. This is the kind of device we'll be working with. Take note of all
|
||||
the `COM` *ports* `mode` outputs *before* plugging the serial module.
|
||||
|
||||
Now, plug the discovery board and run the `mode` command again. If you see a new
|
||||
`COM` port appear on the list then you have a newer revision of the discovery
|
||||
and that's the COM port assigned to the serial functionality on the discovery.
|
||||
You can skip the next paragraph.
|
||||
|
||||
If you didn't get a new COM port then you probably have an older revision of the
|
||||
discovery. Now plug the serial module; you should see new COM port appear;
|
||||
that's the COM port of the serial module.
|
||||
|
||||
Now launch `putty`. A GUI will pop out.
|
||||
|
||||
<p align="center">
|
||||
<img title="PuTTY settings" src="../assets/putty-session-choose-serial.png">
|
||||
</p>
|
||||
|
||||
On the starter screen, which should have the "Session" category open, pick "Serial" as the
|
||||
"Connection type". On the "Serial line" field enter the `COM` device you got on the previous step,
|
||||
for example `COM3`.
|
||||
|
||||
<p align="center">
|
||||
<img title="PuTTY settings" src="../assets/putty-settings.png">
|
||||
</p>
|
||||
|
||||
Next, pick the "Connection/Serial" category from the menu on the left. On this new view, make sure
|
||||
that the serial port is configured as follows:
|
||||
|
||||
- "Speed (baud)": 115200
|
||||
- "Data bits": 8
|
||||
- "Stop bits": 1
|
||||
- "Parity": None
|
||||
- "Flow control": None
|
||||
|
||||
Finally, click the Open button. A console will show up now:
|
||||
|
||||
<p align="center">
|
||||
<img title="PuTTY console" src="../assets/putty-console.png">
|
||||
</p>
|
||||
|
||||
If you type on this console, the TX (red) LED on the Serial module should blink. Each key stroke
|
||||
should make the LED blink once. Note that the console won't echo back what you type so the screen
|
||||
will remain blank.
|
||||
14
discovery-master/f3discovery/src/11-usart/Cargo.toml
Normal file
14
discovery-master/f3discovery/src/11-usart/Cargo.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
edition = "2018"
|
||||
name = "usart"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies.aux11]
|
||||
path = "auxiliary"
|
||||
# enable this if you are going to use an external serial adapter
|
||||
# features = ["adapter"]
|
||||
|
||||
[dependencies.heapless]
|
||||
default-features = false
|
||||
version = "0.7.1"
|
||||
68
discovery-master/f3discovery/src/11-usart/README.md
Normal file
68
discovery-master/f3discovery/src/11-usart/README.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# USART
|
||||
|
||||
The microcontroller has a peripheral called USART, which stands for Universal
|
||||
Synchronous/Asynchronous Receiver/Transmitter. This peripheral can be configured to work with
|
||||
several communication protocols like the serial communication protocol.
|
||||
|
||||
Throughout this chapter, we'll use serial communication to exchange information between the
|
||||
microcontroller and your computer. But before we do that we have to wire up everything.
|
||||
|
||||
I mentioned before that this protocol involves two data lines: TX and RX. TX stands for transmitter
|
||||
and RX stands for receiver. Transmitter and receiver are relative terms though; which line is the
|
||||
transmitter and which line is the receiver depends from which side of the communication you are
|
||||
looking at the lines.
|
||||
|
||||
### Newer board revisions
|
||||
|
||||
If you have a newer revision of the board and are using the on-board USB <->
|
||||
Serial functionality then the `auxiliary` crate will set pin `PC4` as the TX
|
||||
line and pin `PC5` as the RX line.
|
||||
|
||||
If you had previously connected the PC4 and PC4 pins in order to test the [loopback functionality](../10-serial-communication/loopbacks.md) in the previous section,
|
||||
make sure to remove that wire, or the upcoming serial communication will fail silently.
|
||||
|
||||
Everything is already wired on the board so you don't need to wire anything yourself.
|
||||
You can move on to the [next section](send-a-single-byte.html).
|
||||
|
||||
### Older board revisions / external serial module
|
||||
|
||||
If you are using an external USB <-> Serial module then you will **need** to
|
||||
enable the `adapter` feature of the `aux11` crate dependency in `Cargo.toml`.
|
||||
|
||||
``` toml
|
||||
[dependencies.aux11]
|
||||
path = "auxiliary"
|
||||
# enable this if you are going to use an external serial adapter
|
||||
features = ["adapter"] # <- uncomment this
|
||||
```
|
||||
|
||||
We'll be using the pin `PA9` as the microcontroller's TX line and `PA10` as its RX line. In other
|
||||
words, the pin `PA9` outputs data onto its wire whereas the pin `PA10` listens for data on its
|
||||
wire.
|
||||
|
||||
We could have used a different pair of pins as the TX and RX pins. There's a table in page 44 of the
|
||||
[Data Sheet] that list all the other possible pins we could have used.
|
||||
|
||||
[Data Sheet]: http://www.st.com/resource/en/datasheet/stm32f303vc.pdf
|
||||
|
||||
The serial module also has TX and RX pins. We'll have to *cross* these pins: that is connect the
|
||||
microcontroller's TX pin to the serial module's RX pin and the micro's RX pin to the serial module's
|
||||
TX pin. The wiring diagram below shows all the necessary connections.
|
||||
|
||||
<p align="center">
|
||||
<img height=640 title="F3 <-> Serial connection" src="../assets/f3-serial.png">
|
||||
</p>
|
||||
|
||||
These are the recommended steps to connect the microcontroller and the serial module:
|
||||
|
||||
- Close OpenOCD and `itmdump`
|
||||
- Disconnect the USB cables from the F3 and the serial module.
|
||||
- Connect one of F3 GND pins to the GND pin of the serial module using a female to male (F/M) wire.
|
||||
Preferably, a black one.
|
||||
- Connect the PA9 pin on the back of the F3 to the RXI pin of the serial module using a F/M wire.
|
||||
- Connect the PA10 pin on the back of the F3 to the TXO pin of the serial module using a F/M wire.
|
||||
- Now connect the USB cable to the F3.
|
||||
- Finally connect the USB cable to the Serial module.
|
||||
- Re-launch OpenOCD and `itmdump`
|
||||
|
||||
Everything's wired up! Let's proceed to send data back and forth.
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
authors = ["Jorge Aparicio <jorge@japaric.io>"]
|
||||
edition = "2018"
|
||||
name = "aux11"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.7.2"
|
||||
cortex-m-rt = "0.6.14"
|
||||
panic-itm = "0.4.2"
|
||||
stm32f3-discovery = "0.7.0"
|
||||
|
||||
[features]
|
||||
adapter = []
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
//! Initialization code
|
||||
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_extern_crates)] // NOTE(allow) bug rust-lang/rust53964
|
||||
extern crate panic_itm; // panic handler
|
||||
|
||||
pub use cortex_m::{asm::bkpt, iprint, iprintln, peripheral::ITM};
|
||||
pub use cortex_m_rt::entry;
|
||||
pub use stm32f3_discovery::stm32f3xx_hal::pac::usart1;
|
||||
|
||||
pub mod monotimer;
|
||||
|
||||
use stm32f3_discovery::stm32f3xx_hal::{
|
||||
prelude::*,
|
||||
serial::Serial,
|
||||
pac::{self, USART1},
|
||||
};
|
||||
use monotimer::MonoTimer;
|
||||
|
||||
pub fn init() -> (&'static mut usart1::RegisterBlock, MonoTimer, ITM) {
|
||||
let cp = cortex_m::Peripherals::take().unwrap();
|
||||
let dp = pac::Peripherals::take().unwrap();
|
||||
|
||||
let mut flash = dp.FLASH.constrain();
|
||||
let mut rcc = dp.RCC.constrain();
|
||||
|
||||
let clocks = rcc.cfgr.freeze(&mut flash.acr);
|
||||
|
||||
let (tx, rx) = match () {
|
||||
#[cfg(feature = "adapter")]
|
||||
() => {
|
||||
let mut gpioa = dp.GPIOA.split(&mut rcc.ahb);
|
||||
|
||||
let tx = gpioa.pa9.into_af7_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrh);
|
||||
let rx = gpioa.pa10.into_af7_push_pull(&mut gpioa.moder, &mut gpioa.otyper, &mut gpioa.afrh);
|
||||
|
||||
(tx, rx)
|
||||
}
|
||||
#[cfg(not(feature = "adapter"))]
|
||||
() => {
|
||||
let mut gpioc = dp.GPIOC.split(&mut rcc.ahb);
|
||||
|
||||
let tx = gpioc.pc4.into_af7_push_pull(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrl);
|
||||
let rx = gpioc.pc5.into_af7_push_pull(&mut gpioc.moder, &mut gpioc.otyper, &mut gpioc.afrl);
|
||||
|
||||
(tx, rx)
|
||||
}
|
||||
};
|
||||
|
||||
Serial::new(dp.USART1, (tx, rx), 115_200.Bd(), clocks, &mut rcc.apb2);
|
||||
// If you are having trouble sending/receiving data to/from the
|
||||
// HC-05 bluetooth module, try this configuration instead:
|
||||
// Serial::usart1(dp.USART1, (tx, rx), 9600.bps(), clocks, &mut rcc.apb2);
|
||||
|
||||
unsafe {
|
||||
(
|
||||
&mut *(USART1::ptr() as *mut _),
|
||||
MonoTimer::new(cp.DWT, clocks),
|
||||
cp.ITM,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
use stm32f3_discovery::stm32f3xx_hal as hal;
|
||||
|
||||
use cortex_m::peripheral::DWT;
|
||||
use hal::{
|
||||
rcc::Clocks,
|
||||
time::rate::Hertz,
|
||||
};
|
||||
|
||||
/// A monotonic nondecreasing timer. This is a resurrection of MonoTimer from
|
||||
/// the stm32f3xx-hal where it got removed after 0.6.1.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MonoTimer {
|
||||
frequency: Hertz,
|
||||
}
|
||||
|
||||
// TODO: What about a refactoring to implement Clock from embedded-time?
|
||||
impl MonoTimer {
|
||||
/// Creates a new `Monotonic` timer
|
||||
pub fn new(mut dwt: DWT, clocks: Clocks) -> Self {
|
||||
dwt.enable_cycle_counter();
|
||||
|
||||
// now the CYCCNT counter can't be stopped or resetted
|
||||
drop(dwt);
|
||||
|
||||
MonoTimer {
|
||||
frequency: clocks.hclk(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the frequency at which the monotonic timer is operating at
|
||||
pub fn frequency(self) -> Hertz {
|
||||
self.frequency
|
||||
}
|
||||
|
||||
/// Returns an `Instant` corresponding to "now"
|
||||
pub fn now(self) -> Instant {
|
||||
Instant {
|
||||
now: DWT::get_cycle_count(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A measurement of a monotonically nondecreasing clock
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Instant {
|
||||
now: u32,
|
||||
}
|
||||
|
||||
impl Instant {
|
||||
/// Ticks elapsed since the `Instant` was created
|
||||
pub fn elapsed(self) -> u32 {
|
||||
DWT::get_cycle_count().wrapping_sub(self.now)
|
||||
}
|
||||
}
|
||||
87
discovery-master/f3discovery/src/11-usart/buffer-overrun.md
Normal file
87
discovery-master/f3discovery/src/11-usart/buffer-overrun.md
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# Overruns
|
||||
|
||||
If you wrote your program like this:
|
||||
|
||||
``` rust
|
||||
{{#include examples/buffer-overrun.rs}}
|
||||
```
|
||||
|
||||
You probably received something like this on your computer when you executed the program compiled in
|
||||
debug mode.
|
||||
|
||||
``` console
|
||||
$ # minicom's terminal
|
||||
(..)
|
||||
The uic brwn oxjums oer helaz do.
|
||||
```
|
||||
|
||||
And if you compiled in release mode, you probably only got something like this:
|
||||
|
||||
``` console
|
||||
$ # minicom's terminal
|
||||
(..)
|
||||
T
|
||||
```
|
||||
|
||||
What went wrong?
|
||||
|
||||
You see, sending bytes over the wire takes a relatively large amount of time. I already did the math
|
||||
so let me quote myself:
|
||||
|
||||
> With a common configuration of 1 start bit, 8 bits of data, 1 stop bit and a baud rate of 115200
|
||||
> bps one can, in theory, send 11,520 frames per second. Since each one frame carries a byte of data
|
||||
> that results in a data rate of 11.52 KB/s
|
||||
|
||||
Our pangram has a length of 45 bytes. That means it's going to take, at least, 3,900 microseconds
|
||||
(`45 bytes / (11,520 bytes/s) = 3,906 us`) to send the string. The processor is working at 8 MHz,
|
||||
where executing an instruction takes 125 nanoseconds, so it's likely going to be done with the `for`
|
||||
loop in less than 3,900 microseconds.
|
||||
|
||||
We can actually time how long it takes to execute the `for` loop. `aux11::init()` returns a
|
||||
`MonoTimer` (monotonic timer) value that exposes an `Instant` API that's similar to the one in
|
||||
`std::time`.
|
||||
|
||||
``` rust
|
||||
{{#include examples/buffer-overrun-timed.rs}}
|
||||
```
|
||||
|
||||
In debug mode, I get:
|
||||
|
||||
``` console
|
||||
$ # itmdump terminal
|
||||
(..)
|
||||
`for` loop took 22415 ticks (2801.875 us)
|
||||
```
|
||||
|
||||
This is less than 3,900 microseconds but it's not that far off and that's why only a few bytes of
|
||||
information are lost.
|
||||
|
||||
In conclusion, the processor is trying to send bytes at a faster rate than what the hardware can
|
||||
actually handle and this results in data loss. This condition is known as buffer *overrun*.
|
||||
|
||||
How do we avoid this? The status register (`ISR`) has a flag, `TXE`, that indicates if it's "safe"
|
||||
to write to the `TDR` register without incurring in data loss.
|
||||
|
||||
Let's use that to slowdown the processor.
|
||||
|
||||
``` rust
|
||||
{{#include examples/buffer-overrun-txe.rs}}
|
||||
```
|
||||
|
||||
This time, running the program in debug or release mode should result in a complete string on the
|
||||
receiving side.
|
||||
|
||||
``` console
|
||||
$ # minicom/PuTTY's console
|
||||
(..)
|
||||
The quick brown fox jumps over the lazy dog.
|
||||
```
|
||||
|
||||
The timing of the `for` loop should be closer to the theoretical 3,900 microseconds as well. The
|
||||
timing below is for the debug version.
|
||||
|
||||
``` console
|
||||
$ # itmdump terminal
|
||||
(..)
|
||||
`for` loop took 30499 ticks (3812.375 us)
|
||||
```
|
||||
7
discovery-master/f3discovery/src/11-usart/echo-server.md
Normal file
7
discovery-master/f3discovery/src/11-usart/echo-server.md
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Echo server
|
||||
|
||||
Let's merge transmission and reception into a single program and write an echo server. An echo
|
||||
server sends back to the client the same text it sent. For this application, the microcontroller
|
||||
will be the server and you and your computer will be the client.
|
||||
|
||||
This should be straightforward to implement. (hint: do it byte by byte)
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux11::{entry, iprint, iprintln};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (usart1, mono_timer, mut itm) = aux11::init();
|
||||
|
||||
let instant = mono_timer.now();
|
||||
// Send a string
|
||||
for byte in b"The quick brown fox jumps over the lazy dog.".iter() {
|
||||
usart1.tdr.write(|w| w.tdr().bits(u16::from(*byte)));
|
||||
}
|
||||
let elapsed = instant.elapsed(); // in ticks
|
||||
|
||||
iprintln!(
|
||||
&mut itm.stim[0],
|
||||
"`for` loop took {} ticks ({} us)",
|
||||
elapsed,
|
||||
elapsed as f32 / mono_timer.frequency().0 as f32 * 1e6
|
||||
);
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux11::{entry, iprint, iprintln};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (usart1, mono_timer, mut itm) = aux11::init();
|
||||
|
||||
let instant = mono_timer.now();
|
||||
// Send a string
|
||||
for byte in b"The quick brown fox jumps over the lazy dog.".iter() {
|
||||
// wait until it's safe to write to TDR
|
||||
while usart1.isr.read().txe().bit_is_clear() {} // <- NEW!
|
||||
|
||||
usart1
|
||||
.tdr
|
||||
.write(|w| w.tdr().bits(u16::from(*byte)));
|
||||
}
|
||||
let elapsed = instant.elapsed(); // in ticks
|
||||
|
||||
iprintln!(
|
||||
&mut itm.stim[0],
|
||||
"`for` loop took {} ticks ({} us)",
|
||||
elapsed,
|
||||
elapsed as f32 / mono_timer.frequency().0 as f32 * 1e6
|
||||
);
|
||||
|
||||
loop {}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux11::{entry, iprint, iprintln};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (usart1, _mono_timer, _itm) = aux11::init();
|
||||
|
||||
// Send a string
|
||||
for byte in b"The quick brown fox jumps over the lazy dog.".iter() {
|
||||
usart1
|
||||
.tdr
|
||||
.write(|w| w.tdr().bits(u16::from(*byte)));
|
||||
}
|
||||
|
||||
loop {}
|
||||
}
|
||||
48
discovery-master/f3discovery/src/11-usart/examples/echo.rs
Normal file
48
discovery-master/f3discovery/src/11-usart/examples/echo.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux11::{entry, iprint, iprintln};
|
||||
use heapless::Vec;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (usart1, _mono_timer, _itm) = aux11::init();
|
||||
|
||||
// A buffer with 32 bytes of capacity
|
||||
let mut buffer: Vec<u8, 32> = Vec::new();
|
||||
|
||||
loop {
|
||||
buffer.clear();
|
||||
|
||||
loop {
|
||||
while usart1.isr.read().rxne().bit_is_clear() {}
|
||||
let byte = usart1.rdr.read().rdr().bits() as u8;
|
||||
|
||||
if buffer.push(byte).is_err() {
|
||||
// buffer full
|
||||
for byte in b"error: buffer full\n\r" {
|
||||
while usart1.isr.read().txe().bit_is_clear() {}
|
||||
usart1
|
||||
.tdr
|
||||
.write(|w| w.tdr().bits(u16::from(*byte)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Carriage return
|
||||
if byte == 13 {
|
||||
// Respond
|
||||
for byte in buffer.iter().rev().chain(&[b'\n', b'\r']) {
|
||||
while usart1.isr.read().txe().bit_is_clear() {}
|
||||
usart1
|
||||
.tdr
|
||||
.write(|w| w.tdr().bits(u16::from(*byte)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux11::{entry, iprint, iprintln};
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (usart1, _mono_timer, _itm) = aux11::init();
|
||||
|
||||
loop {
|
||||
// Wait until there's data available
|
||||
while usart1.isr.read().rxne().bit_is_clear() {}
|
||||
|
||||
// Retrieve the data
|
||||
let _byte = usart1.rdr.read().rdr().bits() as u8;
|
||||
|
||||
aux11::bkpt();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux11::{entry, iprint, iprintln};
|
||||
use heapless::Vec;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (usart1, _mono_timer, _itm) = aux11::init();
|
||||
|
||||
// A buffer with 32 bytes of capacity
|
||||
let mut buffer: Vec<u8, 32> = Vec::new();
|
||||
|
||||
loop {
|
||||
buffer.clear();
|
||||
|
||||
// TODO Receive a user request. Each user request ends with ENTER
|
||||
// NOTE `buffer.push` returns a `Result`. Handle the error by responding
|
||||
// with an error message.
|
||||
|
||||
// TODO Send back the reversed string
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#![deny(unsafe_code)]
|
||||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::fmt::{self, Write};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use aux11::{entry, iprint, iprintln, usart1};
|
||||
|
||||
macro_rules! uprint {
|
||||
($serial:expr, $($arg:tt)*) => {
|
||||
$serial.write_fmt(format_args!($($arg)*)).ok()
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! uprintln {
|
||||
($serial:expr, $fmt:expr) => {
|
||||
uprint!($serial, concat!($fmt, "\n"))
|
||||
};
|
||||
($serial:expr, $fmt:expr, $($arg:tt)*) => {
|
||||
uprint!($serial, concat!($fmt, "\n"), $($arg)*)
|
||||
};
|
||||
}
|
||||
|
||||
struct SerialPort {
|
||||
usart1: &'static mut usart1::RegisterBlock,
|
||||
}
|
||||
|
||||
impl fmt::Write for SerialPort {
|
||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||
// TODO implement this
|
||||
// hint: this will look very similar to the previous program
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
let (usart1, _mono_timer, _itm) = aux11::init();
|
||||
|
||||
let mut serial = SerialPort { usart1 };
|
||||
|
||||
uprintln!(serial, "The answer is {}", 40 + 2);
|
||||
|
||||
loop {}
|
||||
}
|
||||
5
discovery-master/f3discovery/src/11-usart/my-solution.md
Normal file
5
discovery-master/f3discovery/src/11-usart/my-solution.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# My solution
|
||||
|
||||
```rust
|
||||
{{#include examples/echo.rs}}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue