GPIO init #2
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,3 +12,5 @@ Cargo.lock
|
|||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
|
/app.map
|
||||||
|
76
README.md
76
README.md
@ -1,11 +1,18 @@
|
|||||||
# Vorago VA416xx Rust Workspace
|
Vorago VA416xx Rust Support
|
||||||
|
=========
|
||||||
|
|
||||||
After cloning, run
|
This crate collection provided support to write Rust applications for the VA416XX family
|
||||||
|
of devices.
|
||||||
|
|
||||||
```sh
|
## List of crates
|
||||||
git submodule init
|
|
||||||
git submodule update
|
This workspace contains the following crates:
|
||||||
```
|
|
||||||
|
- The `va416xx` PAC crate containing basic low-level register definition
|
||||||
|
- The `va416xx-hal` HAL crate containing higher-level abstractions on top of
|
||||||
|
the PAC register crate.
|
||||||
|
- The `vorago-peb1` BSP crate containing support for the PEB1 development
|
||||||
|
board.
|
||||||
|
|
||||||
## Using the `.cargo/config.toml` file
|
## Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
@ -17,3 +24,60 @@ cp .cargo/def-config.toml .cargo/config.toml
|
|||||||
|
|
||||||
You then can adapt the `config.toml` to your needs. For example, you can configure runners
|
You then can adapt the `config.toml` to your needs. For example, you can configure runners
|
||||||
to conveniently flash with `cargo run`.
|
to conveniently flash with `cargo run`.
|
||||||
|
|
||||||
|
## Using the sample VS Code files
|
||||||
|
|
||||||
|
Use the following command to have a starting configuration for VS Code:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp vscode .vscode -r
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then adapt the files in `.vscode` to your needs.
|
||||||
|
|
||||||
|
## Flashing, running and debugging with the command line
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
|
||||||
|
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
|
||||||
|
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
|
||||||
|
|
||||||
|
### Flashing and debugging the blinky application
|
||||||
|
|
||||||
|
You can build the blinky example application with the following command
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build -p va416xx-hal --example blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the GDB server first. The server needs to be started with a certain configuration and with
|
||||||
|
a JLink script to disable ROM protection.
|
||||||
|
For example, on Debian based system the following command can be used to do this (this command
|
||||||
|
is also run when running the `jlink-gdb.sh` script)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
JLinkGDBServer -select USB -device Cortex-M4 -endian little -if SWD -speed 2000 \
|
||||||
|
-LocalhostOnly -vd -jlinkscriptfile ./jlink/JLinkSettings.JLinkScript
|
||||||
|
```
|
||||||
|
|
||||||
|
After this, you can flash and debug the application with the following command
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv7em-none-eabihf/debug/examples/blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that you can automate all steps except starting the GDB server by using a cargo
|
||||||
|
runner configuration, for example with the following lines in your `.cargo/config.toml` file:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
runner = "gdb-multiarch -q -x jlink/jlink.gdb"
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, you can simply use `cargo run -p va416xx-hal --example blinky` to flash the blinky
|
||||||
|
example.
|
||||||
|
|
||||||
|
## Flashing, running and debugging with VS Code
|
||||||
|
|
||||||
|
TODO
|
||||||
|
26
va416xx-hal/.vscode/launch.json
vendored
26
va416xx-hal/.vscode/launch.json
vendored
@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"type": "cortex-debug",
|
|
||||||
"request": "launch",
|
|
||||||
"name": "Debug LED Blinky",
|
|
||||||
// The user should start the J-Link server themselves for now. This is because the
|
|
||||||
// Cortex-Debug will issue a reset command, which is problematic even with
|
|
||||||
// a valid JLinkScript file
|
|
||||||
"servertype": "external",
|
|
||||||
"gdbTarget": "localhost:2331",
|
|
||||||
"gdbPath": "/usr/bin/gdb-multiarch",
|
|
||||||
"cwd": "${workspaceRoot}",
|
|
||||||
"device": "Cortex-M4",
|
|
||||||
"svdFile": "../va416xx/svd/va416xx-base.svd",
|
|
||||||
"preLaunchTask": "rust: cargo build led blinky",
|
|
||||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky",
|
|
||||||
"interface": "swd",
|
|
||||||
"runToMain": true,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
19
va416xx-hal/.vscode/tasks.json
vendored
19
va416xx-hal/.vscode/tasks.json
vendored
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
|
||||||
// for the documentation about the tasks.json format
|
|
||||||
"version": "2.0.0",
|
|
||||||
"tasks": [
|
|
||||||
{
|
|
||||||
"label": "rust: cargo build led blinky",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
|
||||||
"args": [
|
|
||||||
"build", "--example", "blinky"
|
|
||||||
],
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
@ -14,6 +14,9 @@ categories = ["embedded", "no-std", "hardware-support"]
|
|||||||
cortex-m = "0.7"
|
cortex-m = "0.7"
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
nb = "1"
|
nb = "1"
|
||||||
|
paste = "1"
|
||||||
|
embedded-hal = "1"
|
||||||
|
typenum = "1.12.0"
|
||||||
|
|
||||||
[dependencies.va416xx]
|
[dependencies.va416xx]
|
||||||
path = "../va416xx"
|
path = "../va416xx"
|
||||||
|
34
va416xx-hal/examples/blinky-pac.rs
Normal file
34
va416xx-hal/examples/blinky-pac.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
//! Simple blinky example
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use panic_halt as _;
|
||||||
|
use va416xx_hal::pac;
|
||||||
|
|
||||||
|
// Mask for the LED
|
||||||
|
const LED_PG5: u32 = 1 << 5;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
// SAFETY: Peripherals are only stolen once here.
|
||||||
|
let dp = unsafe { pac::Peripherals::steal() };
|
||||||
|
// Enable all peripheral clocks
|
||||||
|
dp.sysconfig
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|_, w| unsafe { w.bits(0xffffffff) });
|
||||||
|
dp.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) });
|
||||||
|
dp.portg
|
||||||
|
.datamask()
|
||||||
|
.modify(|_, w| unsafe { w.bits(LED_PG5) });
|
||||||
|
for _ in 0..10 {
|
||||||
|
dp.portg.clrout().write(|w| unsafe { w.bits(LED_PG5) });
|
||||||
|
cortex_m::asm::delay(2_000_000);
|
||||||
|
dp.portg.setout().write(|w| unsafe { w.bits(LED_PG5) });
|
||||||
|
cortex_m::asm::delay(2_000_000);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) });
|
||||||
|
cortex_m::asm::delay(2_000_000);
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +1,21 @@
|
|||||||
//! Simple blinky example
|
//! Simple blinky example using the HAL
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::digital::StatefulOutputPin;
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use va416xx_hal::pac;
|
use va416xx_hal::{gpio::PinsG, pac};
|
||||||
|
|
||||||
// Mask for the LED
|
|
||||||
const LED_PG5: u32 = 1 << 5;
|
|
||||||
|
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
// SAFETY: Peripherals are only stolen once here.
|
// SAFETY: Peripherals are only stolen once here.
|
||||||
let dp = unsafe { pac::Peripherals::steal() };
|
let mut dp = unsafe { pac::Peripherals::steal() };
|
||||||
// Enable all peripheral clocks
|
let portg = PinsG::new(&mut dp.sysconfig, Some(dp.ioconfig), dp.portg);
|
||||||
dp.sysconfig
|
let mut led = portg.pg5.into_readable_push_pull_output();
|
||||||
.peripheral_clk_enable()
|
//let mut delay = CountDownTimer::new(&mut dp.SYSCONFIG, 50.mhz(), dp.TIM0);
|
||||||
.modify(|_, w| unsafe { w.bits(0xffffffff) });
|
|
||||||
dp.portg.dir().modify(|_, w| unsafe { w.bits(LED_PG5) });
|
|
||||||
dp.portg
|
|
||||||
.datamask()
|
|
||||||
.modify(|_, w| unsafe { w.bits(LED_PG5) });
|
|
||||||
for _ in 0..10 {
|
|
||||||
dp.portg.clrout().write(|w| unsafe { w.bits(LED_PG5) });
|
|
||||||
cortex_m::asm::delay(2_000_000);
|
|
||||||
dp.portg.setout().write(|w| unsafe { w.bits(LED_PG5) });
|
|
||||||
cortex_m::asm::delay(2_000_000);
|
|
||||||
}
|
|
||||||
loop {
|
loop {
|
||||||
dp.portg.togout().write(|w| unsafe { w.bits(LED_PG5) });
|
|
||||||
cortex_m::asm::delay(2_000_000);
|
cortex_m::asm::delay(2_000_000);
|
||||||
|
led.toggle().ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,20 @@ pub enum PeripheralSelect {
|
|||||||
PortG = 30,
|
PortG = 30,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type PeripheralClocks = PeripheralSelect;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum FilterClkSel {
|
||||||
|
SysClk = 0,
|
||||||
|
Clk1 = 1,
|
||||||
|
Clk2 = 2,
|
||||||
|
Clk3 = 3,
|
||||||
|
Clk4 = 4,
|
||||||
|
Clk5 = 5,
|
||||||
|
Clk6 = 6,
|
||||||
|
Clk7 = 7,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn enable_peripheral_clock(syscfg: &mut Sysconfig, clock: PeripheralSelect) {
|
pub fn enable_peripheral_clock(syscfg: &mut Sysconfig, clock: PeripheralSelect) {
|
||||||
syscfg
|
syscfg
|
||||||
.peripheral_clk_enable()
|
.peripheral_clk_enable()
|
||||||
|
453
va416xx-hal/src/gpio/dynpin.rs
Normal file
453
va416xx-hal/src/gpio/dynpin.rs
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
use embedded_hal::digital::{ErrorKind, ErrorType, InputPin, OutputPin, StatefulOutputPin};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
reg::RegisterInterface, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, Pin, PinId,
|
||||||
|
PinMode, PinState,
|
||||||
|
};
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPinMode configurations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Value-level `enum` for disabled configurations
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynDisabled {
|
||||||
|
Floating,
|
||||||
|
PullDown,
|
||||||
|
PullUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level `enum` for input configurations
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynInput {
|
||||||
|
Floating,
|
||||||
|
PullDown,
|
||||||
|
PullUp,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level `enum` for output configurations
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynOutput {
|
||||||
|
PushPull,
|
||||||
|
OpenDrain,
|
||||||
|
ReadablePushPull,
|
||||||
|
ReadableOpenDrain,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type DynAlternate = crate::FunSel;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPinMode
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Value-level `enum` representing pin modes
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynPinMode {
|
||||||
|
Input(DynInput),
|
||||||
|
Output(DynOutput),
|
||||||
|
Alternate(DynAlternate),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for floating input mode
|
||||||
|
pub const DYN_FLOATING_INPUT: DynPinMode = DynPinMode::Input(DynInput::Floating);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for pull-down input mode
|
||||||
|
pub const DYN_PULL_DOWN_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullDown);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for pull-up input mode
|
||||||
|
pub const DYN_PULL_UP_INPUT: DynPinMode = DynPinMode::Input(DynInput::PullUp);
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for push-pull output mode
|
||||||
|
pub const DYN_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::PushPull);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for open-drain output mode
|
||||||
|
pub const DYN_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::OpenDrain);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for readable push-pull output mode
|
||||||
|
pub const DYN_RD_PUSH_PULL_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadablePushPull);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for readable opendrain output mode
|
||||||
|
pub const DYN_RD_OPEN_DRAIN_OUTPUT: DynPinMode = DynPinMode::Output(DynOutput::ReadableOpenDrain);
|
||||||
|
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 1
|
||||||
|
pub const DYN_ALT_FUNC_1: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel1);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 2
|
||||||
|
pub const DYN_ALT_FUNC_2: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel2);
|
||||||
|
/// Value-level variant of [`DynPinMode`] for function select 3
|
||||||
|
pub const DYN_ALT_FUNC_3: DynPinMode = DynPinMode::Alternate(DynAlternate::Sel3);
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynGroup & DynPinId
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Value-level `enum` for pin groups
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub enum DynGroup {
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
D,
|
||||||
|
E,
|
||||||
|
F,
|
||||||
|
G,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Value-level `struct` representing pin IDs
|
||||||
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct DynPinId {
|
||||||
|
pub group: DynGroup,
|
||||||
|
pub num: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// DynRegisters
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`DynPin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`DynPinId`] and provides an API to
|
||||||
|
/// access the corresponding regsiters.
|
||||||
|
struct DynRegisters {
|
||||||
|
id: DynPinId,
|
||||||
|
}
|
||||||
|
|
||||||
|
// [`DynRegisters`] takes ownership of the [`DynPinId`], and [`DynPin`]
|
||||||
|
// guarantees that each pin is a singleton, so this implementation is safe.
|
||||||
|
unsafe impl RegisterInterface for DynRegisters {
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> DynPinId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynRegisters {
|
||||||
|
/// Create a new instance of [`DynRegisters`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users must never create two simultaneous instances of this `struct` with
|
||||||
|
/// the same [`DynPinId`]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new(id: DynPinId) -> Self {
|
||||||
|
DynRegisters { id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Error
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// GPIO error type
|
||||||
|
///
|
||||||
|
/// [`DynPin`]s are not tracked and verified at compile-time, so run-time
|
||||||
|
/// operations are fallible. This `enum` represents the corresponding errors.
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct InvalidPinTypeError(pub(crate) ());
|
||||||
|
|
||||||
|
impl embedded_hal::digital::Error for InvalidPinTypeError {
|
||||||
|
fn kind(&self) -> embedded_hal::digital::ErrorKind {
|
||||||
|
ErrorKind::Other
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// DynPin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// A value-level pin, parameterized by [`DynPinId`] and [`DynPinMode`]
|
||||||
|
///
|
||||||
|
/// This type acts as a type-erased version of [`Pin`]. Every pin is represented
|
||||||
|
/// by the same type, and pins are tracked and distinguished at run-time.
|
||||||
|
pub struct DynPin {
|
||||||
|
regs: DynRegisters,
|
||||||
|
mode: DynPinMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynPin {
|
||||||
|
/// Create a new [`DynPin`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Each [`DynPin`] must be a singleton. For a given [`DynPinId`], there
|
||||||
|
/// must be at most one corresponding [`DynPin`] in existence at any given
|
||||||
|
/// time. Violating this requirement is `unsafe`.
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new(id: DynPinId, mode: DynPinMode) -> Self {
|
||||||
|
DynPin {
|
||||||
|
regs: DynRegisters::new(id),
|
||||||
|
mode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the pin ID
|
||||||
|
#[inline]
|
||||||
|
pub fn id(&self) -> DynPinId {
|
||||||
|
self.regs.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a copy of the pin mode
|
||||||
|
#[inline]
|
||||||
|
pub fn mode(&self) -> DynPinMode {
|
||||||
|
self.mode
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin to the requested [`DynPinMode`]
|
||||||
|
#[inline]
|
||||||
|
pub fn into_mode(&mut self, mode: DynPinMode) {
|
||||||
|
// Only modify registers if we are actually changing pin mode
|
||||||
|
if mode != self.mode {
|
||||||
|
self.regs.change_mode(mode);
|
||||||
|
self.mode = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_1(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_2(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_3(&mut self) {
|
||||||
|
self.into_mode(DYN_ALT_FUNC_3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a floating input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_floating_input(&mut self) {
|
||||||
|
self.into_mode(DYN_FLOATING_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled down input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_down_input(&mut self) {
|
||||||
|
self.into_mode(DYN_PULL_DOWN_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled up input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_up_input(&mut self) {
|
||||||
|
self.into_mode(DYN_PULL_UP_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_push_pull_output(&mut self) {
|
||||||
|
self.into_mode(DYN_PUSH_PULL_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_open_drain_output(&mut self) {
|
||||||
|
self.into_mode(DYN_OPEN_DRAIN_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_push_pull_output(&mut self) {
|
||||||
|
self.into_mode(DYN_RD_PUSH_PULL_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_open_drain_output(&mut self) {
|
||||||
|
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
common_reg_if_functions!();
|
||||||
|
|
||||||
|
/// See p.53 of the programmers guide for more information.
|
||||||
|
/// Possible delays in clock cycles:
|
||||||
|
/// - Delay 1: 1
|
||||||
|
/// - Delay 2: 2
|
||||||
|
/// - Delay 1 + Delay 2: 3
|
||||||
|
#[inline]
|
||||||
|
pub fn delay(self, delay_1: bool, delay_2: bool) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.regs.delay(delay_1, delay_2);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.52 of the programmers guide for more information.
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
pub fn pulse_mode(
|
||||||
|
self,
|
||||||
|
enable: bool,
|
||||||
|
default_state: PinState,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.regs.pulse_mode(enable, default_state);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.37 and p.38 of the programmers guide for more information.
|
||||||
|
#[inline]
|
||||||
|
pub fn filter_type(
|
||||||
|
self,
|
||||||
|
filter: FilterType,
|
||||||
|
clksel: FilterClkSel,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) => {
|
||||||
|
self.regs.filter_type(filter, clksel);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
|
self.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb();
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_level(
|
||||||
|
mut self,
|
||||||
|
level_type: InterruptLevel,
|
||||||
|
) -> Result<Self, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DynPinMode::Output(_) => {
|
||||||
|
self.regs.interrupt_level(level_type);
|
||||||
|
self.irq_enb();
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn _read(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Input(_) | DYN_RD_OPEN_DRAIN_OUTPUT | DYN_RD_PUSH_PULL_OUTPUT => {
|
||||||
|
Ok(self.regs.read_pin())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _write(&mut self, bit: bool) -> Result<(), InvalidPinTypeError> {
|
||||||
|
match self.mode {
|
||||||
|
DynPinMode::Output(_) => {
|
||||||
|
self.regs.write_pin(bit);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(InvalidPinTypeError(())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn _is_low(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self._read().map(|v| !v)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _is_high(&self) -> Result<bool, InvalidPinTypeError> {
|
||||||
|
self._read()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _set_low(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
self._write(false)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn _set_high(&mut self) -> Result<(), InvalidPinTypeError> {
|
||||||
|
self._write(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Convert between Pin and DynPin
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
impl<I, M> From<Pin<I, M>> for DynPin
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
/// Erase the type-level information in a [`Pin`] and return a value-level
|
||||||
|
/// [`DynPin`]
|
||||||
|
#[inline]
|
||||||
|
fn from(_pin: Pin<I, M>) -> Self {
|
||||||
|
// The `Pin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `DynPin`
|
||||||
|
unsafe { DynPin::new(I::DYN, M::DYN) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, M> TryFrom<DynPin> for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
type Error = InvalidPinTypeError;
|
||||||
|
|
||||||
|
/// Try to recreate a type-level [`Pin`] from a value-level [`DynPin`]
|
||||||
|
///
|
||||||
|
/// There is no way for the compiler to know if the conversion will be
|
||||||
|
/// successful at compile-time. We must verify the conversion at run-time
|
||||||
|
/// or refuse to perform it.
|
||||||
|
#[inline]
|
||||||
|
fn try_from(pin: DynPin) -> Result<Self, Self::Error> {
|
||||||
|
if pin.regs.id == I::DYN && pin.mode == M::DYN {
|
||||||
|
// The `DynPin` is consumed, so it is safe to replace it with the
|
||||||
|
// corresponding `Pin`
|
||||||
|
Ok(unsafe { Self::new() })
|
||||||
|
} else {
|
||||||
|
Err(InvalidPinTypeError(()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Embedded HAL v1 traits
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
impl ErrorType for DynPin {
|
||||||
|
type Error = InvalidPinTypeError;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputPin for DynPin {
|
||||||
|
#[inline]
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._set_high()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._set_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputPin for DynPin {
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_high()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_low()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatefulOutputPin for DynPin {
|
||||||
|
#[inline]
|
||||||
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_high()
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
self._is_low()
|
||||||
|
}
|
||||||
|
}
|
82
va416xx-hal/src/gpio/mod.rs
Normal file
82
va416xx-hal/src/gpio/mod.rs
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
//! # API for the GPIO peripheral
|
||||||
|
//!
|
||||||
|
//! The implementation of this GPIO module is heavily based on the
|
||||||
|
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/latest/atsamd_hal/gpio/index.html).
|
||||||
|
//!
|
||||||
|
//! This API provides two different submodules, [`mod@pins`] and [`dynpins`],
|
||||||
|
//! representing two different ways to handle GPIO pins. The default, [`mod@pins`],
|
||||||
|
//! is a type-level API that tracks the state of each pin at compile-time. The
|
||||||
|
//! alternative, [`dynpins`] is a type-erased, value-level API that tracks the
|
||||||
|
//! state of each pin at run-time.
|
||||||
|
//!
|
||||||
|
//! The type-level API is strongly preferred. By representing the state of each
|
||||||
|
//! pin within the type system, the compiler can detect logic errors at
|
||||||
|
//! compile-time. Furthermore, the type-level API has absolutely zero run-time
|
||||||
|
//! cost.
|
||||||
|
//!
|
||||||
|
//! If needed, [`dynpins`] can be used to erase the type-level differences
|
||||||
|
//! between pins. However, by doing so, pins must now be tracked at run-time,
|
||||||
|
//! and each pin has a non-zero memory footprint.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-hal/src/branch/main/examples/blinky.rs)
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
pub struct IsMaskedError(pub(crate) ());
|
||||||
|
|
||||||
|
macro_rules! common_reg_if_functions {
|
||||||
|
() => {
|
||||||
|
paste::paste!(
|
||||||
|
#[inline]
|
||||||
|
pub fn datamask(&self) -> bool {
|
||||||
|
self.regs.datamask()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clear_datamask(self) -> Self {
|
||||||
|
self.regs.clear_datamask();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_datamask(self) -> Self {
|
||||||
|
self.regs.set_datamask();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_high_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.read_pin_masked()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_low_masked(&self) -> Result<bool, crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.read_pin_masked().map(|v| !v)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_high_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.write_pin_masked(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_low_masked(&mut self) -> Result<(), crate::gpio::IsMaskedError> {
|
||||||
|
self.regs.write_pin_masked(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn irq_enb(&mut self) {
|
||||||
|
self.regs.enable_irq();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub mod pin;
|
||||||
|
pub use pin::*;
|
||||||
|
|
||||||
|
pub mod dynpin;
|
||||||
|
pub use dynpin::*;
|
||||||
|
|
||||||
|
mod reg;
|
826
va416xx-hal/src/gpio/pin.rs
Normal file
826
va416xx-hal/src/gpio/pin.rs
Normal file
@ -0,0 +1,826 @@
|
|||||||
|
use core::{convert::Infallible, marker::PhantomData, mem::transmute};
|
||||||
|
|
||||||
|
pub use crate::clock::FilterClkSel;
|
||||||
|
use crate::Sealed;
|
||||||
|
use embedded_hal::digital::{ErrorType, InputPin, OutputPin, StatefulOutputPin};
|
||||||
|
use va416xx::{Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
reg::RegisterInterface, DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode,
|
||||||
|
};
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Errors and Definitions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum InterruptEdge {
|
||||||
|
HighToLow,
|
||||||
|
LowToHigh,
|
||||||
|
BothEdges,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum InterruptLevel {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum PinState {
|
||||||
|
Low = 0,
|
||||||
|
High = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Input configuration
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for input configurations
|
||||||
|
///
|
||||||
|
/// The valid options are [`Floating`], [`PullDown`] and [`PullUp`].
|
||||||
|
pub trait InputConfig: Sealed {
|
||||||
|
/// Corresponding [`DynInput`](super::DynInput)
|
||||||
|
const DYN: DynInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Floating {}
|
||||||
|
pub enum PullDown {}
|
||||||
|
pub enum PullUp {}
|
||||||
|
|
||||||
|
impl InputConfig for Floating {
|
||||||
|
const DYN: DynInput = DynInput::Floating;
|
||||||
|
}
|
||||||
|
impl InputConfig for PullDown {
|
||||||
|
const DYN: DynInput = DynInput::PullDown;
|
||||||
|
}
|
||||||
|
impl InputConfig for PullUp {
|
||||||
|
const DYN: DynInput = DynInput::PullUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for Floating {}
|
||||||
|
impl Sealed for PullDown {}
|
||||||
|
impl Sealed for PullUp {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for floating input mode
|
||||||
|
pub type InputFloating = Input<Floating>;
|
||||||
|
/// Type-level variant of [`PinMode`] for pull-down input mode
|
||||||
|
pub type InputPullDown = Input<PullDown>;
|
||||||
|
/// Type-level variant of [`PinMode`] for pull-up input mode
|
||||||
|
pub type InputPullUp = Input<PullUp>;
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for input modes
|
||||||
|
///
|
||||||
|
/// Type `C` is one of three input configurations: [`Floating`], [`PullDown`] or
|
||||||
|
/// [`PullUp`]
|
||||||
|
pub struct Input<C: InputConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: InputConfig> Sealed for Input<C> {}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum FilterType {
|
||||||
|
SystemClock = 0,
|
||||||
|
DirectInputWithSynchronization = 1,
|
||||||
|
FilterOneClockCycle = 2,
|
||||||
|
FilterTwoClockCycles = 3,
|
||||||
|
FilterThreeClockCycles = 4,
|
||||||
|
FilterFourClockCycles = 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Output configuration
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
pub trait OutputConfig: Sealed {
|
||||||
|
const DYN: DynOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ReadableOutput: Sealed {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a push-pull configuration
|
||||||
|
pub enum PushPull {}
|
||||||
|
/// Type-level variant of [`OutputConfig`] for an open drain configuration
|
||||||
|
pub enum OpenDrain {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a readable push-pull configuration
|
||||||
|
pub enum ReadablePushPull {}
|
||||||
|
/// Type-level variant of [`OutputConfig`] for a readable open-drain configuration
|
||||||
|
pub enum ReadableOpenDrain {}
|
||||||
|
|
||||||
|
impl Sealed for PushPull {}
|
||||||
|
impl Sealed for OpenDrain {}
|
||||||
|
impl Sealed for ReadableOpenDrain {}
|
||||||
|
impl Sealed for ReadablePushPull {}
|
||||||
|
impl ReadableOutput for ReadableOpenDrain {}
|
||||||
|
impl ReadableOutput for ReadablePushPull {}
|
||||||
|
|
||||||
|
impl OutputConfig for PushPull {
|
||||||
|
const DYN: DynOutput = DynOutput::PushPull;
|
||||||
|
}
|
||||||
|
impl OutputConfig for OpenDrain {
|
||||||
|
const DYN: DynOutput = DynOutput::OpenDrain;
|
||||||
|
}
|
||||||
|
impl OutputConfig for ReadablePushPull {
|
||||||
|
const DYN: DynOutput = DynOutput::ReadablePushPull;
|
||||||
|
}
|
||||||
|
impl OutputConfig for ReadableOpenDrain {
|
||||||
|
const DYN: DynOutput = DynOutput::ReadableOpenDrain;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for output modes
|
||||||
|
///
|
||||||
|
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
|
||||||
|
/// their respective readable versions
|
||||||
|
pub struct Output<C: OutputConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: OutputConfig> Sealed for Output<C> {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for push-pull output mode
|
||||||
|
pub type PushPullOutput = Output<PushPull>;
|
||||||
|
/// Type-level variant of [`PinMode`] for open drain output mode
|
||||||
|
pub type OutputOpenDrain = Output<OpenDrain>;
|
||||||
|
|
||||||
|
pub type OutputReadablePushPull = Output<ReadablePushPull>;
|
||||||
|
pub type OutputReadableOpenDrain = Output<ReadableOpenDrain>;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Alternate configurations
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for alternate peripheral function configurations
|
||||||
|
pub trait AlternateConfig: Sealed {
|
||||||
|
const DYN: DynAlternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Funsel1 {}
|
||||||
|
pub enum Funsel2 {}
|
||||||
|
pub enum Funsel3 {}
|
||||||
|
|
||||||
|
impl AlternateConfig for Funsel1 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel1;
|
||||||
|
}
|
||||||
|
impl AlternateConfig for Funsel2 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel2;
|
||||||
|
}
|
||||||
|
impl AlternateConfig for Funsel3 {
|
||||||
|
const DYN: DynAlternate = DynAlternate::Sel3;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sealed for Funsel1 {}
|
||||||
|
impl Sealed for Funsel2 {}
|
||||||
|
impl Sealed for Funsel3 {}
|
||||||
|
|
||||||
|
/// Type-level variant of [`PinMode`] for alternate peripheral functions
|
||||||
|
///
|
||||||
|
/// Type `C` is an [`AlternateConfig`]
|
||||||
|
pub struct Alternate<C: AlternateConfig> {
|
||||||
|
cfg: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: AlternateConfig> Sealed for Alternate<C> {}
|
||||||
|
|
||||||
|
pub type AltFunc1 = Alternate<Funsel1>;
|
||||||
|
pub type AltFunc2 = Alternate<Funsel2>;
|
||||||
|
pub type AltFunc3 = Alternate<Funsel3>;
|
||||||
|
|
||||||
|
/// Type alias for the [`PinMode`] at reset
|
||||||
|
pub type Reset = InputFloating;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin modes
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum representing pin modes
|
||||||
|
///
|
||||||
|
/// The valid options are [`Input`], [`Output`] and [`Alternate`].
|
||||||
|
pub trait PinMode: Sealed {
|
||||||
|
/// Corresponding [`DynPinMode`](super::DynPinMode)
|
||||||
|
const DYN: DynPinMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: InputConfig> PinMode for Input<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Input(C::DYN);
|
||||||
|
}
|
||||||
|
impl<C: OutputConfig> PinMode for Output<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Output(C::DYN);
|
||||||
|
}
|
||||||
|
impl<C: AlternateConfig> PinMode for Alternate<C> {
|
||||||
|
const DYN: DynPinMode = DynPinMode::Alternate(C::DYN);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin IDs
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Type-level enum for pin IDs
|
||||||
|
pub trait PinId: Sealed {
|
||||||
|
/// Corresponding [`DynPinId`](super::DynPinId)
|
||||||
|
const DYN: DynPinId;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! pin_id {
|
||||||
|
($Group:ident, $Id:ident, $NUM:literal) => {
|
||||||
|
// Need paste macro to use ident in doc attribute
|
||||||
|
paste::paste! {
|
||||||
|
#[doc = "Pin ID representing pin " $Id]
|
||||||
|
pub enum $Id {}
|
||||||
|
impl Sealed for $Id {}
|
||||||
|
impl PinId for $Id {
|
||||||
|
const DYN: DynPinId = DynPinId {
|
||||||
|
group: DynGroup::$Group,
|
||||||
|
num: $NUM,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// A type-level GPIO pin, parameterized by [`PinId`] and [`PinMode`] types
|
||||||
|
|
||||||
|
pub struct Pin<I: PinId, M: PinMode> {
|
||||||
|
pub(in crate::gpio) regs: Registers<I>,
|
||||||
|
mode: PhantomData<M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, M: PinMode> Pin<I, M> {
|
||||||
|
/// Create a new [`Pin`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Each [`Pin`] must be a singleton. For a given [`PinId`], there must be
|
||||||
|
/// at most one corresponding [`Pin`] in existence at any given time.
|
||||||
|
/// Violating this requirement is `unsafe`.
|
||||||
|
#[inline]
|
||||||
|
pub(crate) unsafe fn new() -> Pin<I, M> {
|
||||||
|
Pin {
|
||||||
|
regs: Registers::new(),
|
||||||
|
mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the pin to the requested [`PinMode`]
|
||||||
|
#[inline]
|
||||||
|
pub fn into_mode<N: PinMode>(mut self) -> Pin<I, N> {
|
||||||
|
// Only modify registers if we are actually changing pin mode
|
||||||
|
// This check should compile away
|
||||||
|
if N::DYN != M::DYN {
|
||||||
|
self.regs.change_mode::<N>();
|
||||||
|
}
|
||||||
|
// Safe because we drop the existing Pin
|
||||||
|
unsafe { Pin::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 1. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_1(self) -> Pin<I, AltFunc1> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 2. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_2(self) -> Pin<I, AltFunc2> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin for function select 3. See Programmer Guide p.40 for the function table
|
||||||
|
#[inline]
|
||||||
|
pub fn into_funsel_3(self) -> Pin<I, AltFunc3> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a floating input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_floating_input(self) -> Pin<I, InputFloating> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled down input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_down_input(self) -> Pin<I, InputPullDown> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a pulled up input
|
||||||
|
#[inline]
|
||||||
|
pub fn into_pull_up_input(self) -> Pin<I, InputPullUp> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_push_pull_output(self) -> Pin<I, PushPullOutput> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a readable push-pull output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_push_pull_output(self) -> Pin<I, OutputReadablePushPull> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure the pin to operate as a readable open-drain output
|
||||||
|
#[inline]
|
||||||
|
pub fn into_readable_open_drain_output(self) -> Pin<I, OutputReadableOpenDrain> {
|
||||||
|
self.into_mode()
|
||||||
|
}
|
||||||
|
|
||||||
|
common_reg_if_functions!();
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _set_high(&mut self) {
|
||||||
|
self.regs.write_pin(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _set_low(&mut self) {
|
||||||
|
self.regs.write_pin(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _is_low(&self) -> bool {
|
||||||
|
!self.regs.read_pin()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn _is_high(&self) -> bool {
|
||||||
|
self.regs.read_pin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// AnyPin
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Type class for [`Pin`] types
|
||||||
|
///
|
||||||
|
/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
|
||||||
|
/// [`Pin`] types. See the `AnyKind` documentation for more details on the
|
||||||
|
/// pattern.
|
||||||
|
///
|
||||||
|
/// ## `v1` Compatibility
|
||||||
|
///
|
||||||
|
/// Normally, this trait would use `Is<Type = SpecificPin<Self>>` as a super
|
||||||
|
/// trait. But doing so would restrict implementations to only the `v2` `Pin`
|
||||||
|
/// type in this module. To aid in backwards compatibility, we want to implement
|
||||||
|
/// `AnyPin` for the `v1` `Pin` type as well. This is possible for a few
|
||||||
|
/// reasons. First, both structs are zero-sized, so there is no meaningful
|
||||||
|
/// memory layout to begin with. And even if there were, the `v1` `Pin` type is
|
||||||
|
/// a newtype wrapper around a `v2` `Pin`, and single-field structs are
|
||||||
|
/// guaranteed to have the same layout as the field, even for `repr(Rust)`.
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||||
|
/// [type class]: crate::typelevel#type-classes
|
||||||
|
pub trait AnyPin
|
||||||
|
where
|
||||||
|
Self: Sealed,
|
||||||
|
Self: From<SpecificPin<Self>>,
|
||||||
|
Self: Into<SpecificPin<Self>>,
|
||||||
|
Self: AsRef<SpecificPin<Self>>,
|
||||||
|
Self: AsMut<SpecificPin<Self>>,
|
||||||
|
{
|
||||||
|
/// [`PinId`] of the corresponding [`Pin`]
|
||||||
|
type Id: PinId;
|
||||||
|
/// [`PinMode`] of the corresponding [`Pin`]
|
||||||
|
type Mode: PinMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, M> Sealed for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, M> AnyPin for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
type Id = I;
|
||||||
|
type Mode = M;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias to recover the specific [`Pin`] type from an implementation of
|
||||||
|
/// [`AnyPin`]
|
||||||
|
///
|
||||||
|
/// See the [`AnyKind`] documentation for more details on the pattern.
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: crate::typelevel#anykind-trait-pattern
|
||||||
|
pub type SpecificPin<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
|
||||||
|
|
||||||
|
impl<P: AnyPin> AsRef<P> for SpecificPin<P> {
|
||||||
|
#[inline]
|
||||||
|
fn as_ref(&self) -> &P {
|
||||||
|
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||||
|
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
|
||||||
|
// both are zero-sized, and single-field, newtype structs are guaranteed
|
||||||
|
// to have the same layout as the field anyway, even for repr(Rust).
|
||||||
|
unsafe { transmute(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<P: AnyPin> AsMut<P> for SpecificPin<P> {
|
||||||
|
#[inline]
|
||||||
|
fn as_mut(&mut self) -> &mut P {
|
||||||
|
// SAFETY: This is guaranteed to be safe, because P == SpecificPin<P>
|
||||||
|
// Transmuting between `v1` and `v2` `Pin` types is also safe, because
|
||||||
|
// both are zero-sized, and single-field, newtype structs are guaranteed
|
||||||
|
// to have the same layout as the field anyway, even for repr(Rust).
|
||||||
|
unsafe { transmute(self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Additional functionality
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
|
pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Self {
|
||||||
|
self.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self {
|
||||||
|
self.regs.interrupt_level(level_type);
|
||||||
|
self.irq_enb();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
|
||||||
|
/// See p.53 of the programmers guide for more information.
|
||||||
|
/// Possible delays in clock cycles:
|
||||||
|
/// - Delay 1: 1
|
||||||
|
/// - Delay 2: 2
|
||||||
|
/// - Delay 1 + Delay 2: 3
|
||||||
|
#[inline]
|
||||||
|
pub fn delay(self, delay_1: bool, delay_2: bool) -> Self {
|
||||||
|
self.regs.delay(delay_1, delay_2);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See p.52 of the programmers guide for more information.
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
pub fn pulse_mode(self, enable: bool, default_state: PinState) -> Self {
|
||||||
|
self.regs.pulse_mode(enable, default_state);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_edge(mut self, edge_type: InterruptEdge) -> Self {
|
||||||
|
self.regs.interrupt_edge(edge_type);
|
||||||
|
self.irq_enb();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn interrupt_level(mut self, level_type: InterruptLevel) -> Self {
|
||||||
|
self.regs.interrupt_level(level_type);
|
||||||
|
self.irq_enb();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
|
||||||
|
/// See p.37 and p.38 of the programmers guide for more information.
|
||||||
|
#[inline]
|
||||||
|
pub fn filter_type(self, filter: FilterType, clksel: FilterClkSel) -> Self {
|
||||||
|
self.regs.filter_type(filter, clksel);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Embedded HAL traits
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
impl<I, M> ErrorType for Pin<I, M>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
M: PinMode,
|
||||||
|
{
|
||||||
|
type Error = Infallible;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId, C: OutputConfig> OutputPin for Pin<I, Output<C>> {
|
||||||
|
#[inline]
|
||||||
|
fn set_high(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._set_high();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn set_low(&mut self) -> Result<(), Self::Error> {
|
||||||
|
self._set_low();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> InputPin for Pin<I, Input<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: InputConfig,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I, C> StatefulOutputPin for Pin<I, Output<C>>
|
||||||
|
where
|
||||||
|
I: PinId,
|
||||||
|
C: OutputConfig + ReadableOutput,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_high())
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
|
||||||
|
Ok(self._is_low())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Registers
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Provide a safe register interface for [`Pin`]s
|
||||||
|
///
|
||||||
|
/// This `struct` takes ownership of a [`PinId`] and provides an API to
|
||||||
|
/// access the corresponding registers.
|
||||||
|
pub(in crate::gpio) struct Registers<I: PinId> {
|
||||||
|
id: PhantomData<I>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// [`Registers`] takes ownership of the [`PinId`], and [`Pin`] guarantees that
|
||||||
|
// each pin is a singleton, so this implementation is safe.
|
||||||
|
unsafe impl<I: PinId> RegisterInterface for Registers<I> {
|
||||||
|
#[inline]
|
||||||
|
fn id(&self) -> DynPinId {
|
||||||
|
I::DYN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: PinId> Registers<I> {
|
||||||
|
/// Create a new instance of [`Registers`]
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users must never create two simultaneous instances of this `struct` with
|
||||||
|
/// the same [`PinId`]
|
||||||
|
#[inline]
|
||||||
|
unsafe fn new() -> Self {
|
||||||
|
Registers { id: PhantomData }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provide a type-level equivalent for the
|
||||||
|
/// [`RegisterInterface::change_mode`] method.
|
||||||
|
#[inline]
|
||||||
|
pub(in crate::gpio) fn change_mode<M: PinMode>(&mut self) {
|
||||||
|
RegisterInterface::change_mode(self, M::DYN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Pin definitions
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
macro_rules! pins {
|
||||||
|
(
|
||||||
|
$Port:ident, $PinsName:ident, $($Id:ident,)+,
|
||||||
|
) => {
|
||||||
|
paste::paste!(
|
||||||
|
/// Collection of all the individual [`Pin`]s for a given port (PORTA or PORTB)
|
||||||
|
pub struct $PinsName {
|
||||||
|
iocfg: Option<va416xx::Ioconfig>,
|
||||||
|
port: $Port,
|
||||||
|
$(
|
||||||
|
#[doc = "Pin " $Id]
|
||||||
|
pub [<$Id:lower>]: Pin<$Id, Reset>,
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $PinsName {
|
||||||
|
/// Create a new struct containing all the Pins. Passing the IOCONFIG peripheral
|
||||||
|
/// is optional because it might be required to create pin definitions for both
|
||||||
|
/// ports.
|
||||||
|
#[inline]
|
||||||
|
pub fn new(
|
||||||
|
syscfg: &mut va416xx::Sysconfig,
|
||||||
|
iocfg: Option<va416xx::Ioconfig>,
|
||||||
|
port: $Port
|
||||||
|
) -> $PinsName {
|
||||||
|
syscfg.peripheral_clk_enable().modify(|_, w| {
|
||||||
|
w.[<$Port:lower>]().set_bit();
|
||||||
|
w.ioconfig().set_bit()
|
||||||
|
});
|
||||||
|
$PinsName {
|
||||||
|
iocfg,
|
||||||
|
port,
|
||||||
|
// Safe because we only create one `Pin` per `PinId`
|
||||||
|
$(
|
||||||
|
[<$Id:lower>]: unsafe { Pin::new() },
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the peripheral ID
|
||||||
|
/// Safety: Read-only register
|
||||||
|
pub fn get_perid() -> u32 {
|
||||||
|
let port = unsafe { &(*$Port::ptr()) };
|
||||||
|
port.perid().read().bits()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Consumes the Pins struct and returns the port definitions
|
||||||
|
pub fn release(self) -> (Option<va416xx::Ioconfig>, $Port) {
|
||||||
|
(self.iocfg, self.port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! declare_pins {
|
||||||
|
(
|
||||||
|
$Group:ident, $PinsName:ident, $Port:ident, [$(($Id:ident, $NUM:literal),)+]
|
||||||
|
) => {
|
||||||
|
pins!($Port, $PinsName, $($Id,)+,);
|
||||||
|
$(
|
||||||
|
pin_id!($Group, $Id, $NUM);
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
A,
|
||||||
|
PinsA,
|
||||||
|
Porta,
|
||||||
|
[
|
||||||
|
(PA0, 0),
|
||||||
|
(PA1, 1),
|
||||||
|
(PA2, 2),
|
||||||
|
(PA3, 3),
|
||||||
|
(PA4, 4),
|
||||||
|
(PA5, 5),
|
||||||
|
(PA6, 6),
|
||||||
|
(PA7, 7),
|
||||||
|
(PA8, 8),
|
||||||
|
(PA9, 9),
|
||||||
|
(PA10, 10),
|
||||||
|
(PA11, 11),
|
||||||
|
(PA12, 12),
|
||||||
|
(PA13, 13),
|
||||||
|
(PA14, 14),
|
||||||
|
(PA15, 15),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
B,
|
||||||
|
PinsB,
|
||||||
|
Portb,
|
||||||
|
[
|
||||||
|
(PB0, 0),
|
||||||
|
(PB1, 1),
|
||||||
|
(PB2, 2),
|
||||||
|
(PB3, 3),
|
||||||
|
(PB4, 4),
|
||||||
|
(PB5, 5),
|
||||||
|
(PB6, 6),
|
||||||
|
(PB7, 7),
|
||||||
|
(PB8, 8),
|
||||||
|
(PB9, 9),
|
||||||
|
(PB10, 10),
|
||||||
|
(PB11, 11),
|
||||||
|
(PB12, 12),
|
||||||
|
(PB13, 13),
|
||||||
|
(PB14, 14),
|
||||||
|
(PB15, 15),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
C,
|
||||||
|
PinsC,
|
||||||
|
Portc,
|
||||||
|
[
|
||||||
|
(PC0, 0),
|
||||||
|
(PC1, 1),
|
||||||
|
(PC2, 2),
|
||||||
|
(PC3, 3),
|
||||||
|
(PC4, 4),
|
||||||
|
(PC5, 5),
|
||||||
|
(PC6, 6),
|
||||||
|
(PC7, 7),
|
||||||
|
(PC8, 8),
|
||||||
|
(PC9, 9),
|
||||||
|
(PC10, 10),
|
||||||
|
(PC11, 11),
|
||||||
|
(PC12, 12),
|
||||||
|
(PC13, 13),
|
||||||
|
(PC14, 14),
|
||||||
|
(PC15, 15),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
D,
|
||||||
|
PinsD,
|
||||||
|
Portd,
|
||||||
|
[
|
||||||
|
(PD0, 0),
|
||||||
|
(PD1, 1),
|
||||||
|
(PD2, 2),
|
||||||
|
(PD3, 3),
|
||||||
|
(PD4, 4),
|
||||||
|
(PD5, 5),
|
||||||
|
(PD6, 6),
|
||||||
|
(PD7, 7),
|
||||||
|
(PD8, 8),
|
||||||
|
(PD9, 9),
|
||||||
|
(PD10, 10),
|
||||||
|
(PD11, 11),
|
||||||
|
(PD12, 12),
|
||||||
|
(PD13, 13),
|
||||||
|
(PD14, 14),
|
||||||
|
(PD15, 15),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
E,
|
||||||
|
PinsE,
|
||||||
|
Porte,
|
||||||
|
[
|
||||||
|
(PE0, 0),
|
||||||
|
(PE1, 1),
|
||||||
|
(PE2, 2),
|
||||||
|
(PE3, 3),
|
||||||
|
(PE4, 4),
|
||||||
|
(PE5, 5),
|
||||||
|
(PE6, 6),
|
||||||
|
(PE7, 7),
|
||||||
|
(PE8, 8),
|
||||||
|
(PE9, 9),
|
||||||
|
(PE10, 10),
|
||||||
|
(PE11, 11),
|
||||||
|
(PE12, 12),
|
||||||
|
(PE13, 13),
|
||||||
|
(PE14, 14),
|
||||||
|
(PE15, 15),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
F,
|
||||||
|
PinsF,
|
||||||
|
Portf,
|
||||||
|
[
|
||||||
|
(PF0, 0),
|
||||||
|
(PF1, 1),
|
||||||
|
(PF2, 2),
|
||||||
|
(PF3, 3),
|
||||||
|
(PF4, 4),
|
||||||
|
(PF5, 5),
|
||||||
|
(PF6, 6),
|
||||||
|
(PF7, 7),
|
||||||
|
(PF8, 8),
|
||||||
|
(PF9, 9),
|
||||||
|
(PF10, 10),
|
||||||
|
(PF11, 11),
|
||||||
|
(PF12, 12),
|
||||||
|
(PF13, 13),
|
||||||
|
(PF14, 14),
|
||||||
|
(PF15, 15),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
declare_pins!(
|
||||||
|
G,
|
||||||
|
PinsG,
|
||||||
|
Portg,
|
||||||
|
[
|
||||||
|
(PG0, 0),
|
||||||
|
(PG1, 1),
|
||||||
|
(PG2, 2),
|
||||||
|
(PG3, 3),
|
||||||
|
(PG4, 4),
|
||||||
|
(PG5, 5),
|
||||||
|
(PG6, 6),
|
||||||
|
(PG7, 7),
|
||||||
|
]
|
||||||
|
);
|
387
va416xx-hal/src/gpio/reg.rs
Normal file
387
va416xx-hal/src/gpio/reg.rs
Normal file
@ -0,0 +1,387 @@
|
|||||||
|
use crate::FunSel;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
dynpin::{self, DynGroup, DynPinId},
|
||||||
|
DynPinMode, FilterClkSel, FilterType, InterruptEdge, InterruptLevel, IsMaskedError, PinState,
|
||||||
|
};
|
||||||
|
use va416xx::{ioconfig, porta, Ioconfig, Porta, Portb, Portc, Portd, Porte, Portf, Portg};
|
||||||
|
|
||||||
|
/// Type definition to avoid confusion: These register blocks are identical
|
||||||
|
type PortRegisterBlock = porta::RegisterBlock;
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// ModeFields
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/// Collect all fields needed to set the [`PinMode`](super::PinMode)
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ModeFields {
|
||||||
|
dir: bool,
|
||||||
|
opendrn: bool,
|
||||||
|
pull_en: bool,
|
||||||
|
/// true for pullup, false for pulldown
|
||||||
|
pull_dir: bool,
|
||||||
|
funsel: u8,
|
||||||
|
enb_input: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DynPinMode> for ModeFields {
|
||||||
|
#[inline]
|
||||||
|
fn from(mode: DynPinMode) -> Self {
|
||||||
|
let mut fields = Self::default();
|
||||||
|
use DynPinMode::*;
|
||||||
|
match mode {
|
||||||
|
Input(config) => {
|
||||||
|
use dynpin::DynInput::*;
|
||||||
|
fields.dir = false;
|
||||||
|
fields.funsel = FunSel::Sel0 as u8;
|
||||||
|
match config {
|
||||||
|
Floating => (),
|
||||||
|
PullUp => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
fields.pull_dir = true;
|
||||||
|
}
|
||||||
|
PullDown => {
|
||||||
|
fields.pull_en = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Output(config) => {
|
||||||
|
use dynpin::DynOutput::*;
|
||||||
|
fields.dir = true;
|
||||||
|
fields.funsel = FunSel::Sel0 as u8;
|
||||||
|
match config {
|
||||||
|
PushPull => (),
|
||||||
|
OpenDrain => {
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
ReadableOpenDrain => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
fields.opendrn = true;
|
||||||
|
}
|
||||||
|
ReadablePushPull => {
|
||||||
|
fields.enb_input = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Alternate(config) => {
|
||||||
|
fields.funsel = config as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// RegisterInterface
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
pub type PortReg = ioconfig::Porta;
|
||||||
|
|
||||||
|
/// Provide a safe register interface for pin objects
|
||||||
|
///
|
||||||
|
/// [`PORT`], like every PAC `struct`, is [`Send`] but not [`Sync`], because it
|
||||||
|
/// points to a `RegisterBlock` of `VolatileCell`s. Unfortunately, such an
|
||||||
|
/// interface is quite restrictive. Instead, it would be ideal if we could split
|
||||||
|
/// the [`PORT`] into independent pins that are both [`Send`] and [`Sync`].
|
||||||
|
///
|
||||||
|
/// [`PORT`] is a single, zero-sized marker `struct` that provides access to
|
||||||
|
/// every [`PORT`] register. Instead, we would like to create zero-sized marker
|
||||||
|
/// `struct`s for every pin, where each pin is only allowed to control its own
|
||||||
|
/// registers. Furthermore, each pin `struct` should be a singleton, so that
|
||||||
|
/// exclusive access to the `struct` also guarantees exclusive access to the
|
||||||
|
/// corresponding registers. Finally, the pin `struct`s should not have any
|
||||||
|
/// interior mutability. Together, these requirements would allow the pin
|
||||||
|
/// `struct`s to be both [`Send`] and [`Sync`].
|
||||||
|
///
|
||||||
|
/// This trait creates a safe API for accomplishing these goals. Implementers
|
||||||
|
/// supply a pin ID through the [`id`] function. The remaining functions provide
|
||||||
|
/// a safe API for accessing the registers associated with that pin ID. Any
|
||||||
|
/// modification of the registers requires `&mut self`, which destroys interior
|
||||||
|
/// mutability.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Users should only implement the [`id`] function. No default function
|
||||||
|
/// implementations should be overridden. The implementing type must also have
|
||||||
|
/// "control" over the corresponding pin ID, i.e. it must guarantee that a each
|
||||||
|
/// pin ID is a singleton.
|
||||||
|
///
|
||||||
|
/// [`id`]: Self::id
|
||||||
|
pub(super) unsafe trait RegisterInterface {
|
||||||
|
/// Provide a [`DynPinId`] identifying the set of registers controlled by
|
||||||
|
/// this type.
|
||||||
|
fn id(&self) -> DynPinId;
|
||||||
|
|
||||||
|
const PORTA: *const PortRegisterBlock = Porta::ptr();
|
||||||
|
const PORTB: *const PortRegisterBlock = Portb::ptr();
|
||||||
|
const PORTC: *const PortRegisterBlock = Portc::ptr();
|
||||||
|
const PORTD: *const PortRegisterBlock = Portd::ptr();
|
||||||
|
const PORTE: *const PortRegisterBlock = Porte::ptr();
|
||||||
|
const PORTF: *const PortRegisterBlock = Portf::ptr();
|
||||||
|
const PORTG: *const PortRegisterBlock = Portg::ptr();
|
||||||
|
|
||||||
|
/// Change the pin mode
|
||||||
|
#[inline]
|
||||||
|
fn change_mode(&mut self, mode: DynPinMode) {
|
||||||
|
let ModeFields {
|
||||||
|
dir,
|
||||||
|
funsel,
|
||||||
|
opendrn,
|
||||||
|
pull_dir,
|
||||||
|
pull_en,
|
||||||
|
enb_input,
|
||||||
|
} = mode.into();
|
||||||
|
let (portreg, iocfg) = (self.port_reg(), self.iocfg_port());
|
||||||
|
iocfg.write(|w| {
|
||||||
|
w.opendrn().bit(opendrn);
|
||||||
|
w.pen().bit(pull_en);
|
||||||
|
w.plevel().bit(pull_dir);
|
||||||
|
w.iewo().bit(enb_input);
|
||||||
|
unsafe { w.funsel().bits(funsel) }
|
||||||
|
});
|
||||||
|
let mask = self.mask_32();
|
||||||
|
unsafe {
|
||||||
|
if dir {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
|
||||||
|
// Clear output
|
||||||
|
portreg.clrout().write(|w| w.bits(mask));
|
||||||
|
} else {
|
||||||
|
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn port_reg(&self) -> &PortRegisterBlock {
|
||||||
|
match self.id().group {
|
||||||
|
DynGroup::A => unsafe { &(*Self::PORTA) },
|
||||||
|
DynGroup::B => unsafe { &(*Self::PORTB) },
|
||||||
|
DynGroup::C => unsafe { &(*Self::PORTC) },
|
||||||
|
DynGroup::D => unsafe { &(*Self::PORTD) },
|
||||||
|
DynGroup::E => unsafe { &(*Self::PORTE) },
|
||||||
|
DynGroup::F => unsafe { &(*Self::PORTF) },
|
||||||
|
DynGroup::G => unsafe { &(*Self::PORTG) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn iocfg_port(&self) -> &PortReg {
|
||||||
|
let ioconfig = unsafe { Ioconfig::ptr().as_ref().unwrap() };
|
||||||
|
match self.id().group {
|
||||||
|
DynGroup::A => ioconfig.porta(self.id().num as usize),
|
||||||
|
DynGroup::B => ioconfig.portb0(self.id().num as usize),
|
||||||
|
DynGroup::C => ioconfig.portc0(self.id().num as usize),
|
||||||
|
DynGroup::D => ioconfig.portd0(self.id().num as usize),
|
||||||
|
DynGroup::E => ioconfig.porte0(self.id().num as usize),
|
||||||
|
DynGroup::F => ioconfig.portf0(self.id().num as usize),
|
||||||
|
DynGroup::G => ioconfig.portg0(self.id().num as usize),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn mask_32(&self) -> u32 {
|
||||||
|
1 << self.id().num
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn enable_irq(&self) {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_enb()
|
||||||
|
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
/// Read the logic level of an output pin
|
||||||
|
fn read_pin(&self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
((portreg.datainraw().read().bits() >> self.id().num) & 0x01) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get DATAMASK bit for this particular pin
|
||||||
|
#[inline(always)]
|
||||||
|
fn datamask(&self) -> bool {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
(portreg.datamask().read().bits() >> self.id().num) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read a pin but use the masked version but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
#[inline(always)]
|
||||||
|
fn read_pin_masked(&self) -> Result<bool, IsMaskedError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(IsMaskedError(()))
|
||||||
|
} else {
|
||||||
|
Ok(((self.port_reg().datain().read().bits() >> self.id().num) & 0x01) == 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin
|
||||||
|
#[inline(always)]
|
||||||
|
fn write_pin(&mut self, bit: bool) {
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the logic level of an output pin but check whether the datamask for the pin is
|
||||||
|
/// cleared as well
|
||||||
|
#[inline]
|
||||||
|
fn write_pin_masked(&mut self, bit: bool) -> Result<(), IsMaskedError> {
|
||||||
|
if !self.datamask() {
|
||||||
|
Err(IsMaskedError(()))
|
||||||
|
} else {
|
||||||
|
// Safety: SETOUT is a "mask" register, and we only write the bit for
|
||||||
|
// this pin ID
|
||||||
|
unsafe {
|
||||||
|
if bit {
|
||||||
|
self.port_reg().setout().write(|w| w.bits(self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg().clrout().write(|w| w.bits(self.mask_32()));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for interrupt pins. Configure whether to use edges or level as interrupt soure
|
||||||
|
/// When using edge mode, it is possible to generate interrupts on both edges as well
|
||||||
|
#[inline]
|
||||||
|
fn interrupt_edge(&mut self, edge_type: InterruptEdge) {
|
||||||
|
unsafe {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_sen()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
match edge_type {
|
||||||
|
InterruptEdge::HighToLow => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
InterruptEdge::LowToHigh => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
InterruptEdge::BothEdges => {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_edge()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configure which edge or level type triggers an interrupt
|
||||||
|
#[inline]
|
||||||
|
fn interrupt_level(&mut self, level: InterruptLevel) {
|
||||||
|
unsafe {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_sen()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
if level == InterruptLevel::Low {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
} else {
|
||||||
|
self.port_reg()
|
||||||
|
.irq_evt()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for input pins
|
||||||
|
#[inline]
|
||||||
|
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) {
|
||||||
|
self.iocfg_port().modify(|_, w| {
|
||||||
|
// Safety: Only write to register for this Pin ID
|
||||||
|
unsafe {
|
||||||
|
w.flttype().bits(filter as u8);
|
||||||
|
w.fltclk().bits(clksel as u8)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set DATAMASK bit for this particular pin. 1 is the default
|
||||||
|
/// state of the bit and allows access of the corresponding bit
|
||||||
|
#[inline(always)]
|
||||||
|
fn set_datamask(&self) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
portreg
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear DATAMASK bit for this particular pin. This prevents access
|
||||||
|
/// of the corresponding bit for output and input operations
|
||||||
|
#[inline(always)]
|
||||||
|
fn clear_datamask(&self) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
portreg
|
||||||
|
.datamask()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for output pins
|
||||||
|
/// See p.52 of the programmers guide for more information.
|
||||||
|
/// When configured for pulse mode, a given pin will set the non-default state for exactly
|
||||||
|
/// one clock cycle before returning to the configured default state
|
||||||
|
fn pulse_mode(&self, enable: bool, default_state: PinState) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
if enable {
|
||||||
|
portreg
|
||||||
|
.pulse()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.pulse()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
if default_state == PinState::Low {
|
||||||
|
portreg
|
||||||
|
.pulsebase()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.pulsebase()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Only useful for output pins
|
||||||
|
fn delay(&self, delay_1: bool, delay_2: bool) {
|
||||||
|
let portreg = self.port_reg();
|
||||||
|
unsafe {
|
||||||
|
if delay_1 {
|
||||||
|
portreg
|
||||||
|
.delay1()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.delay1()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
if delay_2 {
|
||||||
|
portreg
|
||||||
|
.delay2()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | self.mask_32()));
|
||||||
|
} else {
|
||||||
|
portreg
|
||||||
|
.delay2()
|
||||||
|
.modify(|r, w| w.bits(r.bits() & !self.mask_32()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,45 @@
|
|||||||
|
|
||||||
pub use va416xx;
|
pub use va416xx;
|
||||||
pub use va416xx as pac;
|
pub use va416xx as pac;
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod time;
|
pub mod gpio;
|
||||||
|
pub mod time;
|
||||||
|
pub mod typelevel;
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, Copy, Clone, PartialEq)]
|
||||||
|
pub enum FunSel {
|
||||||
|
Sel0 = 0b00,
|
||||||
|
Sel1 = 0b01,
|
||||||
|
Sel2 = 0b10,
|
||||||
|
Sel3 = 0b11,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic IRQ config which can be used to specify whether the HAL driver will
|
||||||
|
/// use the IRQSEL register to route an interrupt, and whether the IRQ will be unmasked in the
|
||||||
|
/// Cortex-M0 NVIC. Both are generally necessary for IRQs to work, but the user might perform
|
||||||
|
/// this steps themselves
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct IrqCfg {
|
||||||
|
/// Interrupt target vector. Should always be set, might be required for disabling IRQs
|
||||||
|
pub irq: pac::Interrupt,
|
||||||
|
/// Specfiy whether IRQ should be routed to an IRQ vector using the IRQSEL peripheral
|
||||||
|
pub route: bool,
|
||||||
|
/// Specify whether the IRQ is unmasked in the Cortex-M NVIC
|
||||||
|
pub enable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IrqCfg {
|
||||||
|
pub fn new(irq: pac::Interrupt, route: bool, enable: bool) -> Self {
|
||||||
|
IrqCfg { irq, route, enable }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
/// Super trait used to mark traits with an exhaustive set of
|
||||||
|
/// implementations
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use private::Sealed;
|
||||||
|
1
va416xx-hal/src/prelude.rs
Normal file
1
va416xx-hal/src/prelude.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
155
va416xx-hal/src/typelevel.rs
Normal file
155
va416xx-hal/src/typelevel.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
//! Module supporting type-level programming
|
||||||
|
//!
|
||||||
|
//! This module is identical to the
|
||||||
|
//! [atsamd typelevel](https://docs.rs/atsamd-hal/latest/atsamd_hal/typelevel/index.html).
|
||||||
|
|
||||||
|
use core::ops::{Add, Sub};
|
||||||
|
|
||||||
|
use typenum::{Add1, Bit, Sub1, UInt, Unsigned, B1, U0};
|
||||||
|
|
||||||
|
mod private {
|
||||||
|
/// Super trait used to mark traits with an exhaustive set of
|
||||||
|
/// implementations
|
||||||
|
pub trait Sealed {}
|
||||||
|
|
||||||
|
impl Sealed for u8 {}
|
||||||
|
impl Sealed for i8 {}
|
||||||
|
impl Sealed for u16 {}
|
||||||
|
impl Sealed for i16 {}
|
||||||
|
impl Sealed for u32 {}
|
||||||
|
impl Sealed for i32 {}
|
||||||
|
impl Sealed for f32 {}
|
||||||
|
|
||||||
|
/// Mapping from an instance of a countable type to its successor
|
||||||
|
pub trait Increment {
|
||||||
|
/// Successor type of `Self`
|
||||||
|
type Inc;
|
||||||
|
/// Consume an instance of `Self` and return its successor
|
||||||
|
fn inc(self) -> Self::Inc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mapping from an instance of a countable type to its predecessor
|
||||||
|
pub trait Decrement {
|
||||||
|
/// Predecessor type of `Self`
|
||||||
|
type Dec;
|
||||||
|
/// Consume an instance of `Self` and return its predecessor
|
||||||
|
fn dec(self) -> Self::Dec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) use private::Decrement as PrivateDecrement;
|
||||||
|
pub(crate) use private::Increment as PrivateIncrement;
|
||||||
|
pub(crate) use private::Sealed;
|
||||||
|
|
||||||
|
/// Type-level version of the [`None`] variant
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NoneT;
|
||||||
|
|
||||||
|
impl Sealed for NoneT {}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Is
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Marker trait for type identity
|
||||||
|
///
|
||||||
|
/// This trait is used as part of the [`AnyKind`] trait pattern. It represents
|
||||||
|
/// the concept of type identity, because all implementors have
|
||||||
|
/// `<Self as Is>::Type == Self`. When used as a trait bound with a specific
|
||||||
|
/// type, it guarantees that the corresponding type parameter is exactly the
|
||||||
|
/// specific type. Stated differently, it guarantees that `T == Specific` in
|
||||||
|
/// the following example.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// where T: Is<Type = Specific>
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Moreover, the super traits guarantee that any instance of or reference to a
|
||||||
|
/// type `T` can be converted into the `Specific` type.
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// fn example<T>(mut any: T)
|
||||||
|
/// where
|
||||||
|
/// T: Is<Type = Specific>,
|
||||||
|
/// {
|
||||||
|
/// let specific_mut: &mut Specific = any.as_mut();
|
||||||
|
/// let specific_ref: &Specific = any.as_ref();
|
||||||
|
/// let specific: Specific = any.into();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// [`AnyKind`]: #anykind-trait-pattern
|
||||||
|
pub trait Is
|
||||||
|
where
|
||||||
|
Self: Sealed,
|
||||||
|
Self: From<IsType<Self>>,
|
||||||
|
Self: Into<IsType<Self>>,
|
||||||
|
Self: AsRef<IsType<Self>>,
|
||||||
|
Self: AsMut<IsType<Self>>,
|
||||||
|
{
|
||||||
|
type Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type alias for [`Is::Type`]
|
||||||
|
pub type IsType<T> = <T as Is>::Type;
|
||||||
|
|
||||||
|
impl<T> Is for T
|
||||||
|
where
|
||||||
|
T: Sealed + AsRef<T> + AsMut<T>,
|
||||||
|
{
|
||||||
|
type Type = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// Counting
|
||||||
|
//==============================================================================
|
||||||
|
|
||||||
|
/// Implement `Sealed` for [`U0`]
|
||||||
|
impl Sealed for U0 {}
|
||||||
|
|
||||||
|
/// Implement `Sealed` for all type-level, [`Unsigned`] integers *except* [`U0`]
|
||||||
|
impl<U: Unsigned, B: Bit> Sealed for UInt<U, B> {}
|
||||||
|
|
||||||
|
/// Trait mapping each countable type to its successor
|
||||||
|
///
|
||||||
|
/// This trait maps each countable type to its corresponding successor type. The
|
||||||
|
/// actual implementation of this trait is contained within `PrivateIncrement`.
|
||||||
|
/// Access to `PrivateIncrement` is restricted, so that safe HAL APIs can be
|
||||||
|
/// built with it.
|
||||||
|
pub trait Increment: PrivateIncrement {}
|
||||||
|
|
||||||
|
impl<T: PrivateIncrement> Increment for T {}
|
||||||
|
|
||||||
|
/// Trait mapping each countable type to its predecessor
|
||||||
|
///
|
||||||
|
/// This trait maps each countable type to its corresponding predecessor type.
|
||||||
|
/// The actual implementation of this trait is contained within
|
||||||
|
/// `PrivateDecrement`. Access to `PrivateDecrement` is restricted, so that safe
|
||||||
|
/// HAL APIs can be built with it.
|
||||||
|
pub trait Decrement: PrivateDecrement {}
|
||||||
|
|
||||||
|
impl<T: PrivateDecrement> Decrement for T {}
|
||||||
|
|
||||||
|
impl<N> PrivateIncrement for N
|
||||||
|
where
|
||||||
|
N: Unsigned + Add<B1>,
|
||||||
|
Add1<N>: Unsigned,
|
||||||
|
{
|
||||||
|
type Inc = Add1<N>;
|
||||||
|
#[inline]
|
||||||
|
fn inc(self) -> Self::Inc {
|
||||||
|
Self::Inc::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<N> PrivateDecrement for N
|
||||||
|
where
|
||||||
|
N: Unsigned + Sub<B1>,
|
||||||
|
Sub1<N>: Unsigned,
|
||||||
|
{
|
||||||
|
type Dec = Sub1<N>;
|
||||||
|
#[inline]
|
||||||
|
fn dec(self) -> Self::Dec {
|
||||||
|
Self::Dec::default()
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,6 @@
|
|||||||
|
// Manually inserted.
|
||||||
|
#![allow(clippy::identity_op)]
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[doc = "Register block"]
|
#[doc = "Register block"]
|
||||||
pub struct RegisterBlock {
|
pub struct RegisterBlock {
|
||||||
|
@ -3,12 +3,9 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::digital::v2::{OutputPin, ToggleableOutputPin};
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
use va416xx_hal::{
|
use va416xx_hal::{gpio::PinsG, pac};
|
||||||
pac,
|
|
||||||
gpio::PinsG
|
|
||||||
};
|
|
||||||
use embedded_hal::digital::v2::{ToggleableOutputPin, OutputPin};
|
|
||||||
|
|
||||||
// Mask for the LED
|
// Mask for the LED
|
||||||
const LED_PG5: u32 = 1 << 5;
|
const LED_PG5: u32 = 1 << 5;
|
||||||
@ -54,7 +51,7 @@ fn main() -> ! {
|
|||||||
led.set_low().ok();
|
led.set_low().ok();
|
||||||
cortex_m::asm::delay(5_000_000);
|
cortex_m::asm::delay(5_000_000);
|
||||||
led.set_high().ok();
|
led.set_high().ok();
|
||||||
};
|
}
|
||||||
loop {
|
loop {
|
||||||
led.toggle().ok();
|
led.toggle().ok();
|
||||||
cortex_m::asm::delay(25_000_000);
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
12
vscode/extensions.json
Normal file
12
vscode/extensions.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
||||||
|
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
||||||
|
// List of extensions which should be recommended for users of this workspace.
|
||||||
|
"recommendations": [
|
||||||
|
"rust-lang.rust",
|
||||||
|
"marus25.cortex-debug"
|
||||||
|
// "probe-rs.probe-rs-debugger"
|
||||||
|
],
|
||||||
|
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||||
|
"unwantedRecommendations": []
|
||||||
|
}
|
@ -15,11 +15,11 @@
|
|||||||
"gdbTarget": "localhost:2331",
|
"gdbTarget": "localhost:2331",
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M4",
|
"device": "Cortex-M4",
|
||||||
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx-base.svd",
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
"preLaunchTask": "rust: cargo build led blinky pac",
|
"preLaunchTask": "blinky-pac-example",
|
||||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky-pac",
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky-pac",
|
||||||
"interface": "swd",
|
"interface": "swd",
|
||||||
"runToMain": true,
|
"runToEntryPoint": "main",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
@ -29,11 +29,11 @@
|
|||||||
"gdbTarget": "localhost:2331",
|
"gdbTarget": "localhost:2331",
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M4",
|
"device": "Cortex-M4",
|
||||||
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx-base.svd",
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
"preLaunchTask": "rust: cargo build led blinky",
|
"preLaunchTask": "blinky-example",
|
||||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky",
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/blinky",
|
||||||
"interface": "swd",
|
"interface": "swd",
|
||||||
"runToMain": true,
|
"runToEntryPoint": "main",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "cortex-debug",
|
"type": "cortex-debug",
|
||||||
@ -43,11 +43,11 @@
|
|||||||
"gdbTarget": "localhost:2331",
|
"gdbTarget": "localhost:2331",
|
||||||
"cwd": "${workspaceRoot}",
|
"cwd": "${workspaceRoot}",
|
||||||
"device": "Cortex-M4",
|
"device": "Cortex-M4",
|
||||||
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx-base.svd",
|
"svdFile": "${workspaceFolder}/va416xx/svd/va416xx.svd.patched",
|
||||||
"preLaunchTask": "rust: cargo build rtt",
|
"preLaunchTask": "rtt-log-example",
|
||||||
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/rtt-log",
|
"executable": "${workspaceFolder}/target/thumbv7em-none-eabihf/debug/examples/rtt-log",
|
||||||
"interface": "swd",
|
"interface": "swd",
|
||||||
"runToMain": true,
|
"runToEntryPoint": "main",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -4,11 +4,15 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "rust: cargo build led blinky pac",
|
"label": "blinky-pac-example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build", "-p", "va416xx-hal", "--example", "blinky-pac"
|
"build",
|
||||||
|
"-p",
|
||||||
|
"va416xx-hal",
|
||||||
|
"--example",
|
||||||
|
"blinky-pac"
|
||||||
],
|
],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
@ -16,11 +20,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "rust: cargo build rtt",
|
"label": "rtt-log-example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build", "-p", "va416xx-hal", "--example", "rtt-log"
|
"build",
|
||||||
|
"-p",
|
||||||
|
"va416xx-hal",
|
||||||
|
"--example",
|
||||||
|
"rtt-log"
|
||||||
],
|
],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
@ -28,11 +36,15 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "rust: cargo build led blinky",
|
"label": "blinky-example",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
|
||||||
"args": [
|
"args": [
|
||||||
"build", "-p", "vorago-peb1", "--example", "blinky"
|
"build",
|
||||||
|
"-p",
|
||||||
|
"va416xx-hal",
|
||||||
|
"--example",
|
||||||
|
"blinky"
|
||||||
],
|
],
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
|
Loading…
Reference in New Issue
Block a user