14: Added GPIO IRQ interface, refactoring r=robamu a=robamu

- Adds the IRQ interface to configure interrupts on output and input pins
- Moved the `FilterClkSel` struct to the `clock` module, reexporting in `gpio`
- Added function to set clock divisor registers
- Clearing output state at initialization of Output pins
- Added utility function to set up millisecond timer with `TIM0`

Co-authored-by: Robin Mueller <robin.mueller.m@gmail.com>
This commit is contained in:
bors[bot] 2021-11-13 13:53:12 +00:00 committed by GitHub
commit 030b555a7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 314 additions and 105 deletions

View File

@ -8,9 +8,19 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [unreleased] ## [unreleased]
## [0.2.1]
### Added
- Adds the IRQ interface to configure interrupts on output and input pins
- Utility function to set up millisecond timer with `TIM0`
- Function to set clock divisor registers in `clock` module
### Changed ### Changed
- Minor optimizations and tweaks for GPIO module - Minor optimizations and tweaks for GPIO module
- Moved the `FilterClkSel` struct to the `clock` module, re-exporting in `gpio`
- Clearing output state at initialization of Output pins
## [0.2.0] ## [0.2.0]

View File

@ -1,6 +1,6 @@
[package] [package]
name = "va108xx-hal" name = "va108xx-hal"
version = "0.2.0" version = "0.2.1"
authors = ["Robin Mueller <robin.mueller.m@gmail.com>"] authors = ["Robin Mueller <robin.mueller.m@gmail.com>"]
edition = "2021" edition = "2021"
description = "HAL for the Vorago VA108xx family of microcontrollers" description = "HAL for the Vorago VA108xx family of microcontrollers"

View File

@ -12,7 +12,7 @@ use va108xx_hal::{
pac::{self, interrupt}, pac::{self, interrupt},
prelude::*, prelude::*,
time::Hertz, time::Hertz,
timer::{CountDownTimer, Event}, timer::{set_up_ms_timer, CountDownTimer, Event},
}; };
#[allow(dead_code)] #[allow(dead_code)]
@ -65,23 +65,21 @@ fn main() -> ! {
} }
} }
LibType::Hal => { LibType::Hal => {
let mut ms_timer = set_up_ms_timer(
CountDownTimer::tim0(&mut dp.SYSCONFIG, get_sys_clock().unwrap(), dp.TIM0);
let mut second_timer =
CountDownTimer::tim1(&mut dp.SYSCONFIG, get_sys_clock().unwrap(), dp.TIM1);
ms_timer.listen(
Event::TimeOut,
&mut dp.SYSCONFIG, &mut dp.SYSCONFIG,
&mut dp.IRQSEL, &mut dp.IRQSEL,
50.mhz().into(),
dp.TIM0,
interrupt::OC0, interrupt::OC0,
); );
let mut second_timer =
CountDownTimer::tim1(&mut dp.SYSCONFIG, get_sys_clock().unwrap(), dp.TIM1);
second_timer.listen( second_timer.listen(
Event::TimeOut, Event::TimeOut,
&mut dp.SYSCONFIG, &mut dp.SYSCONFIG,
&mut dp.IRQSEL, &mut dp.IRQSEL,
interrupt::OC1, interrupt::OC1,
); );
ms_timer.start(1000.hz());
second_timer.start(1.hz()); second_timer.start(1.hz());
unmask_irqs(); unmask_irqs();
} }

View File

@ -25,6 +25,18 @@ pub enum PeripheralClocks {
Gpio = 24, Gpio = 24,
} }
#[derive(Debug, PartialEq)]
pub enum FilterClkSel {
SysClk = 0,
Clk1 = 1,
Clk2 = 2,
Clk3 = 3,
Clk4 = 4,
Clk5 = 5,
Clk6 = 6,
Clk7 = 7,
}
/// The Vorago in powered by an external clock which might have different frequencies. /// The Vorago in powered by an external clock which might have different frequencies.
/// The clock can be set here so it can be used by other software components as well. /// The clock can be set here so it can be used by other software components as well.
/// The clock can be set exactly once /// The clock can be set exactly once
@ -39,6 +51,19 @@ pub fn get_sys_clock() -> Option<Hertz> {
interrupt::free(|cs| SYS_CLOCK.borrow(cs).get().copied()) interrupt::free(|cs| SYS_CLOCK.borrow(cs).get().copied())
} }
pub fn set_clk_div_register(syscfg: &mut SYSCONFIG, clk_sel: FilterClkSel, div: u32) {
match clk_sel {
FilterClkSel::SysClk => (),
FilterClkSel::Clk1 => syscfg.ioconfig_clkdiv1.write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk2 => syscfg.ioconfig_clkdiv2.write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk3 => syscfg.ioconfig_clkdiv3.write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk4 => syscfg.ioconfig_clkdiv4.write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk5 => syscfg.ioconfig_clkdiv5.write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk6 => syscfg.ioconfig_clkdiv6.write(|w| unsafe { w.bits(div) }),
FilterClkSel::Clk7 => syscfg.ioconfig_clkdiv7.write(|w| unsafe { w.bits(div) }),
}
}
pub fn enable_peripheral_clock(syscfg: &mut SYSCONFIG, clock: PeripheralClocks) { pub fn enable_peripheral_clock(syscfg: &mut SYSCONFIG, clock: PeripheralClocks) {
syscfg syscfg
.peripheral_clk_enable .peripheral_clk_enable

View File

@ -57,9 +57,17 @@
//! operation, the trait functions will return //! operation, the trait functions will return
//! [`InvalidPinType`](Error::InvalidPinType). //! [`InvalidPinType`](Error::InvalidPinType).
use super::pins::{FilterClkSel, FilterType, Pin, PinError, PinId, PinMode, PinState}; use super::pins::{
common_reg_if_functions, FilterType, InterruptEdge, InterruptLevel, Pin, PinError, PinId,
PinMode, PinState,
};
use super::reg::RegisterInterface; use super::reg::RegisterInterface;
use crate::{
clock::FilterClkSel,
pac::{self, IRQSEL, SYSCONFIG},
};
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
use paste::paste;
//================================================================================================== //==================================================================================================
// DynPinMode configurations // DynPinMode configurations
@ -293,42 +301,7 @@ impl DynPin {
self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT); self.into_mode(DYN_RD_OPEN_DRAIN_OUTPUT);
} }
#[inline] common_reg_if_functions!();
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, PinError> {
self.regs.read_pin_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, PinError> {
self.regs.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), PinError> {
self.regs.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), PinError> {
self.regs.write_pin_masked(false)
}
/// See p.53 of the programmers guide for more information. /// See p.53 of the programmers guide for more information.
/// Possible delays in clock cycles: /// Possible delays in clock cycles:
@ -371,6 +344,40 @@ impl DynPin {
} }
} }
pub fn interrupt_edge(
mut self,
edge_type: InterruptEdge,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
) -> Result<Self, PinError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type);
Ok(self)
}
_ => Err(PinError::InvalidPinType),
}
}
pub fn interrupt_level(
mut self,
level_type: InterruptLevel,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: crate::pac::Interrupt,
) -> Result<Self, PinError> {
match self.mode {
DynPinMode::Input(_) | DynPinMode::Output(_) => {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type);
Ok(self)
}
_ => Err(PinError::InvalidPinType),
}
}
#[inline] #[inline]
fn _read(&self) -> Result<bool, PinError> { fn _read(&self) -> Result<bool, PinError> {
match self.mode { match self.mode {

View File

@ -89,9 +89,11 @@
use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode}; use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
use super::reg::RegisterInterface; use super::reg::RegisterInterface;
use crate::pac::{IOCONFIG, PORTA, PORTB, SYSCONFIG}; use crate::{
use crate::typelevel::Is; pac::{self, IOCONFIG, IRQSEL, PORTA, PORTB, SYSCONFIG},
use crate::Sealed; typelevel::Is,
Sealed,
};
use core::convert::Infallible; use core::convert::Infallible;
use core::marker::PhantomData; use core::marker::PhantomData;
use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin}; use embedded_hal::digital::v2::{InputPin, OutputPin, ToggleableOutputPin};
@ -101,6 +103,19 @@ use paste::paste;
// Errors and Definitions // Errors and Definitions
//================================================================================================== //==================================================================================================
#[derive(Debug, PartialEq)]
pub enum InterruptEdge {
HighToLow,
LowToHigh,
BothEdges,
}
#[derive(Debug, PartialEq)]
pub enum InterruptLevel {
Low = 0,
High = 1,
}
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum PinState { pub enum PinState {
Low = 0, Low = 0,
@ -164,6 +179,7 @@ pub struct Input<C: InputConfig> {
impl<C: InputConfig> Sealed for Input<C> {} impl<C: InputConfig> Sealed for Input<C> {}
#[derive(Debug, PartialEq)]
pub enum FilterType { pub enum FilterType {
SystemClock = 0, SystemClock = 0,
DirectInputWithSynchronization = 1, DirectInputWithSynchronization = 1,
@ -173,16 +189,7 @@ pub enum FilterType {
FilterFourClockCycles = 5, FilterFourClockCycles = 5,
} }
pub enum FilterClkSel { pub use crate::clock::FilterClkSel;
SysClk = 0,
Clk1 = 1,
Clk2 = 2,
Clk3 = 3,
Clk4 = 4,
Clk5 = 5,
Clk6 = 6,
Clk7 = 7,
}
//================================================================================================== //==================================================================================================
// Output configuration // Output configuration
@ -347,6 +354,77 @@ impl<I: PinId, M: PinMode> AnyPin for Pin<I, M> {
type Mode = M; type Mode = M;
} }
macro_rules! common_reg_if_functions {
() => {
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, PinError> {
self.regs.read_pin_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, PinError> {
self.regs.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), PinError> {
self.regs.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), PinError> {
self.regs.write_pin_masked(false)
}
fn _irq_enb(
&mut self,
syscfg: Option<&mut va108xx::SYSCONFIG>,
irqsel: &mut va108xx::IRQSEL,
interrupt: va108xx::Interrupt,
) {
if syscfg.is_some() {
crate::clock::enable_peripheral_clock(
syscfg.unwrap(),
crate::clock::PeripheralClocks::Irqsel,
);
}
self.regs.enable_irq();
match self.regs.id().group {
// Set the correct interrupt number in the IRQSEL register
DynGroup::A => {
irqsel.porta[self.regs.id().num as usize]
.write(|w| unsafe { w.bits(interrupt as u32) });
}
DynGroup::B => {
irqsel.portb[self.regs.id().num as usize]
.write(|w| unsafe { w.bits(interrupt as u32) });
}
}
}
);
};
}
pub(crate) use common_reg_if_functions;
impl<I: PinId, M: PinMode> Pin<I, M> { impl<I: PinId, M: PinMode> Pin<I, M> {
/// Create a new [`Pin`] /// Create a new [`Pin`]
/// ///
@ -429,42 +507,7 @@ impl<I: PinId, M: PinMode> Pin<I, M> {
self.into_mode() self.into_mode()
} }
#[inline] common_reg_if_functions!();
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, PinError> {
self.regs.read_pin_masked()
}
#[inline]
pub fn is_low_masked(&self) -> Result<bool, PinError> {
self.regs.read_pin_masked().map(|v| !v)
}
#[inline]
pub fn set_high_masked(&mut self) -> Result<(), PinError> {
self.regs.write_pin_masked(true)
}
#[inline]
pub fn set_low_masked(&mut self) -> Result<(), PinError> {
self.regs.write_pin_masked(false)
}
#[inline] #[inline]
pub(crate) fn _set_high(&mut self) { pub(crate) fn _set_high(&mut self) {
@ -526,6 +569,32 @@ impl<I: PinId, M: PinMode> AsMut<Self> for Pin<I, M> {
// Additional functionality // Additional functionality
//================================================================================================== //==================================================================================================
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {
pub fn interrupt_edge(
mut self,
edge_type: InterruptEdge,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type);
self
}
pub fn interrupt_level(
mut self,
level_type: InterruptLevel,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type);
self
}
}
impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> { impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
/// See p.53 of the programmers guide for more information. /// See p.53 of the programmers guide for more information.
/// Possible delays in clock cycles: /// Possible delays in clock cycles:
@ -545,6 +614,30 @@ impl<I: PinId, C: OutputConfig> Pin<I, Output<C>> {
self.regs.pulse_mode(enable, default_state); self.regs.pulse_mode(enable, default_state);
self self
} }
pub fn interrupt_edge(
mut self,
edge_type: InterruptEdge,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_edge(edge_type);
self
}
pub fn interrupt_level(
mut self,
level_type: InterruptLevel,
syscfg: Option<&mut SYSCONFIG>,
irqsel: &mut IRQSEL,
interrupt: pac::Interrupt,
) -> Self {
self._irq_enb(syscfg, irqsel, interrupt);
self.regs.interrupt_level(level_type);
self
}
} }
impl<I: PinId, C: InputConfig> Pin<I, Input<C>> { impl<I: PinId, C: InputConfig> Pin<I, Input<C>> {

View File

@ -1,5 +1,6 @@
use super::dynpins::{self, DynGroup, DynPinId, DynPinMode}; use super::dynpins::{self, DynGroup, DynPinId, DynPinMode};
use super::pins::{FilterClkSel, FilterType, PinError, PinState}; use super::pins::{FilterType, InterruptEdge, InterruptLevel, PinError, PinState};
use crate::clock::FilterClkSel;
use va108xx::{ioconfig, porta, IOCONFIG, PORTA, PORTB}; use va108xx::{ioconfig, porta, IOCONFIG, PORTA, PORTB};
/// Type definition to avoid confusion: These register blocks are identical /// Type definition to avoid confusion: These register blocks are identical
@ -31,16 +32,13 @@ impl From<DynPinMode> for ModeFields {
use dynpins::DynInput::*; use dynpins::DynInput::*;
fields.dir = false; fields.dir = false;
match config { match config {
Floating => { Floating => (),
fields.pull_en = false;
}
PullUp => { PullUp => {
fields.pull_en = true; fields.pull_en = true;
fields.pull_dir = true; fields.pull_dir = true;
} }
PullDown => { PullDown => {
fields.pull_en = true; fields.pull_en = true;
fields.pull_dir = false;
} }
} }
} }
@ -48,9 +46,7 @@ impl From<DynPinMode> for ModeFields {
use dynpins::DynOutput::*; use dynpins::DynOutput::*;
fields.dir = true; fields.dir = true;
match config { match config {
PushPull => { PushPull => (),
fields.opendrn = false;
}
OpenDrain => { OpenDrain => {
fields.opendrn = true; fields.opendrn = true;
} }
@ -60,7 +56,6 @@ impl From<DynPinMode> for ModeFields {
} }
ReadablePushPull => { ReadablePushPull => {
fields.enb_input = true; fields.enb_input = true;
fields.opendrn = false;
} }
} }
} }
@ -154,6 +149,8 @@ pub(super) unsafe trait RegisterInterface {
unsafe { unsafe {
if dir { if dir {
portreg.dir().modify(|r, w| w.bits(r.bits() | mask)); portreg.dir().modify(|r, w| w.bits(r.bits() | mask));
// Clear output
portreg.clrout().write(|w| w.bits(mask));
} else { } else {
portreg.dir().modify(|r, w| w.bits(r.bits() & !mask)); portreg.dir().modify(|r, w| w.bits(r.bits() & !mask));
} }
@ -200,6 +197,20 @@ pub(super) unsafe trait RegisterInterface {
} }
} }
#[inline]
fn enable_irq(&self) {
self.port_reg()
.irq_enb
.modify(|r, w| unsafe { w.bits(r.bits() | self.mask_32()) });
}
#[inline]
fn disable_irq(&self) {
self.port_reg()
.irq_enb
.modify(|r, w| unsafe { w.bits(r.bits() & !self.mask_32()) });
}
#[inline] #[inline]
fn get_perid(&self) -> u32 { fn get_perid(&self) -> u32 {
let portreg = self.port_reg(); let portreg = self.port_reg();
@ -273,6 +284,53 @@ pub(super) unsafe trait RegisterInterface {
unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) }; unsafe { self.port_reg().togout().write(|w| w.bits(self.mask_32())) };
} }
/// 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 /// Only useful for input pins
#[inline] #[inline]
fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) { fn filter_type(&self, filter: FilterType, clksel: FilterClkSel) {

View File

@ -5,7 +5,10 @@
//! - [MS and second tick implementation](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/timer-ticks.rs) //! - [MS and second tick implementation](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/timer-ticks.rs)
use crate::{ use crate::{
clock::{enable_peripheral_clock, PeripheralClocks}, clock::{enable_peripheral_clock, PeripheralClocks},
pac,
prelude::*,
time::Hertz, time::Hertz,
timer,
}; };
use embedded_hal::timer::{Cancel, CountDown, Periodic}; use embedded_hal::timer::{Cancel, CountDown, Periodic};
use va108xx::{Interrupt, IRQSEL, SYSCONFIG}; use va108xx::{Interrupt, IRQSEL, SYSCONFIG};
@ -160,6 +163,20 @@ macro_rules! timers {
} }
} }
// Set up a millisecond timer on TIM0. Please note that you still need to unmask the related IRQ
// and provide an IRQ handler yourself
pub fn set_up_ms_timer(
syscfg: &mut pac::SYSCONFIG,
irqsel: &mut pac::IRQSEL,
sys_clk: Hertz,
tim0: TIM0,
irq: pac::Interrupt,
) {
let mut ms_timer = CountDownTimer::tim0(syscfg, sys_clk, tim0);
ms_timer.listen(timer::Event::TimeOut, syscfg, irqsel, irq);
ms_timer.start(1000.hz());
}
timers! { timers! {
TIM0: (tim0, 0), TIM0: (tim0, 0),
TIM1: (tim1, 1), TIM1: (tim1, 1),

View File

@ -1,6 +1,7 @@
//! # API for the UART peripheral //! # API for the UART peripheral
//! //!
//! ## Examples //! ## Examples
//!
//! - [UART example](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/uart.rs) //! - [UART example](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/uart.rs)
use core::{convert::Infallible, ptr}; use core::{convert::Infallible, ptr};
use core::{marker::PhantomData, ops::Deref}; use core::{marker::PhantomData, ops::Deref};