Skip to content

Commit

Permalink
Add atmega328pb as an extension to atmega328p
Browse files Browse the repository at this point in the history
  • Loading branch information
jkristell authored and Rahix committed Oct 29, 2020
1 parent f26144b commit 9f9d697
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 4 deletions.
2 changes: 1 addition & 1 deletion boards/arduino-uno/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ rt = ["atmega328p-hal/rt"]
arduino-nano = ["atmega328p-hal/adc-pins"]

[dependencies]
atmega328p-hal = { path = "../../chips/atmega328p-hal/" }
atmega328p-hal = { path = "../../chips/atmega328p-hal/", features = ["atmega328p"] }
avr-hal-generic = { path = "../../avr-hal-generic/" }

[dev-dependencies]
Expand Down
4 changes: 3 additions & 1 deletion chips/atmega328p-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ edition = "2018"
rt = ["avr-device/rt"]
# Package exposes the ADC6 and ADC7 pins (only 32TQFP, 32MLF, 32UFBGA)
adc-pins = []
device-selected = []
atmega328p = ["avr-device/atmega328p", "device-selected"]
atmega328pb = ["avr-device/atmega328pb", "device-selected"]

[dependencies]
avr-hal-generic = { path = "../../avr-hal-generic/" }

[dependencies.avr-device]
version = "0.2.2"
features = ["atmega328p"]
154 changes: 152 additions & 2 deletions chips/atmega328p-hal/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,23 @@
#![no_std]

#[cfg(not(feature = "device-selected"))]
compile_error!(
"This crate requires you to specify your target chip as a feature.
Please select one of the following
* atmega328p
* atmega328pb
"
);


/// Reexport of `atmega328p` from `avr-device`
#[cfg(feature = "atmega328p")]
pub use avr_device::atmega328p as pac;
/// Reexport of `atmega328pb` from `avr-device`
#[cfg(feature = "atmega328pb")]
pub use avr_device::atmega328pb as pac;

/// See [`avr_device::entry`](https://docs.rs/avr-device/latest/avr_device/attr.entry.html).
#[cfg(feature = "rt")]
Expand All @@ -10,17 +26,22 @@ pub use avr_device::entry;
pub use avr_hal_generic::clock;
pub use avr_hal_generic::delay;

#[cfg(feature = "device-selected")]
pub mod port;

#[cfg(feature = "device-selected")]
pub mod adc;
#[cfg(feature = "device-selected")]
pub mod pwm;
#[cfg(feature = "device-selected")]
pub mod wdt;

#[cfg(feature = "device-selected")]
pub mod prelude {
pub use avr_hal_generic::prelude::*;
pub use crate::port::PortExt as _;
}

#[cfg(feature = "atmega328p")]
/// I2C Bus
pub mod i2c {
use crate::port::portc;
Expand Down Expand Up @@ -53,6 +74,66 @@ pub mod i2c {
}
}

#[cfg(feature = "atmega328pb")]
/// I2C Bus
pub mod i2c {
use crate::port::{portc, porte};
pub use avr_hal_generic::i2c::*;

avr_hal_generic::impl_twi_i2c! {
/// I2C based on ATmega328P's TWI peripheral
pub struct I2c0 {
peripheral: crate::pac::TWI0,
pins: {
sda: portc::PC4,
scl: portc::PC5,
},
registers: {
control: twcr {
enable: twen,
ack: twea,
int: twint,
start: twsta,
stop: twsto,
},
status: twsr {
prescaler: twps,
status: tws,
},
bitrate: twbr,
data: twdr,
},
}
}

avr_hal_generic::impl_twi_i2c! {
/// I2C based on ATmega328P's TWI peripheral
pub struct I2c1 {
peripheral: crate::pac::TWI1,
pins: {
sda: porte::PE0,
scl: porte::PE1,
},
registers: {
control: twcr {
enable: twen,
ack: twea,
int: twint,
start: twsta,
stop: twsto,
},
status: twsr {
prescaler: twps,
status: tws,
},
bitrate: twbr,
data: twdr,
},
}
}
}

#[cfg(feature = "atmega328p")]
pub mod spi {
//! Implementation of the Rust Embedded-HAL SPI FullDuplex trait for AVR.
//!
Expand Down Expand Up @@ -93,9 +174,63 @@ pub mod spi {
}
}

#[cfg(feature = "atmega328pb")]
pub mod spi {
//! Implementation of the Rust Embedded-HAL SPI FullDuplex trait for AVR.
//!
//! The interface can be instantiated with the `new` method, and used directly
//! or passed into a driver. Example usage:
//!
//! ```
//! pins.d10.into_output(&mut pins.ddr);// SS must be set to output mode
//! // create SPI interface
//! let mut spi = Spi0::new(
//! dp.SPI,// SPI peripheral
//! pins.d13.into_output(&mut pins.ddr),// SCLK
//! pins.d11.into_output(&mut pins.ddr),// MOSI output pin
//! pins.d12.into_pull_up_input(&mut pins.ddr),// MISO input pin
//! Settings::default(),
//! );
//!
//! // Send a byte
//! let sent = 0b10101010;
//! spi.send(sent).unwrap();
//! let response = spi.read().unwrap();
//! ```
//! In the example above, all of the settings are left at the default. You can
//! also instantiate a Settings object with the other options available.
use crate::port::{portb, portc, porte};
pub use avr_hal_generic::spi::*;

avr_hal_generic::impl_spi! {
pub struct Spi0 {
peripheral: crate::pac::SPI0,
pins: {
sclk: portb::PB5,
mosi: portb::PB3,
miso: portb::PB4,
}
}
}

avr_hal_generic::impl_spi! {
pub struct Spi1 {
peripheral: crate::pac::SPI1,
pins: {
sclk: portc::PC1,
mosi: porte::PE3,
miso: portc::PC0,
}
}
}
}

/// Serial interface using USART
#[cfg(feature = "device-selected")]
pub mod usart {
use crate::port::portd;
#[allow(unused_imports)]
use crate::port::{portb, portd};
pub use avr_hal_generic::serial::*;

avr_hal_generic::impl_usart! {
Expand All @@ -111,4 +246,19 @@ pub mod usart {
register_suffix: 0,
}
}

#[cfg(feature = "atmega328pb")]
avr_hal_generic::impl_usart! {
/// Serial interface based on ATmega328PB's USART0 peripheral
///
/// Maximum baudrate seems to be 57600
pub struct Usart1 {
peripheral: crate::pac::USART1,
pins: {
rx: portb::PB4,
tx: portb::PB3,
},
register_suffix: 1,
}
}
}
30 changes: 30 additions & 0 deletions chips/atmega328p-hal/src/port.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub trait PortExt {
fn split(self) -> Self::Parts;
}

#[cfg(feature = "atmega328p")]
avr_hal_generic::impl_generic_pin! {
pub enum Pin {
B(crate::pac::PORTB, portb, pinb, ddrb),
Expand All @@ -20,6 +21,17 @@ avr_hal_generic::impl_generic_pin! {
}
}

#[cfg(feature = "atmega328pb")]
avr_hal_generic::impl_generic_pin! {
pub enum Pin {
B(crate::pac::PORTB, portb, pinb, ddrb),
C(crate::pac::PORTC, portc, pinc, ddrc),
D(crate::pac::PORTD, portd, pind, ddrd),
E(crate::pac::PORTE, porte, pine, ddre),
}
}


avr_hal_generic::impl_port! {
pub mod portb {
#[port_ext]
Expand Down Expand Up @@ -85,3 +97,21 @@ avr_hal_generic::impl_port! {
}
}

#[cfg(feature = "atmega328pb")]
avr_hal_generic::impl_port! {
pub mod porte {
#[port_ext]
use super::PortExt;

#[generic_pin]
use Pin::E;

impl PortExt for crate::pac::PORTE {
regs: (pine, ddre, porte),
pe0: (PE0, 0),
pe1: (PE1, 1),
pe2: (PE2, 2),
pe3: (PE3, 3),
}
}
}
104 changes: 104 additions & 0 deletions chips/atmega328p-hal/src/pwm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,3 +172,107 @@ avr_hal_generic::impl_pwm! {
},
}
}

#[cfg(feature = "atmega328pb")]
avr_hal_generic::impl_pwm! {
/// Use `TC3` for PWM (pins `PD0`, `PD2`)
///
/// # Example
/// ```
/// let mut portd = dp.PORTD.split();
/// let mut timer3 = Timer3Pwm::new(dp.TC3, pwm::Prescaler::Prescale64);
///
/// let mut pb1 = portd.pd1.into_output(&mut portd.ddr).into_pwm(&mut timer3);
/// let mut pb2 = portd.pd2.into_output(&mut portd.ddr).into_pwm(&mut timer3);
///
/// pd1.set_duty(128);
/// pd1.enable();
/// ```
pub struct Timer3Pwm {
timer: crate::pac::TC3,
init: |tim, prescaler| {
tim.tccr3a.modify(|_, w| w.wgm3().bits(0b01));
tim.tccr3b.modify(|_, w| {
//TODO: Figure out if svdtool can mark this as safe (as for Tc1)
unsafe { w.wgm3().bits(0b01) };
match prescaler {
Prescaler::Direct => w.cs3().direct(),
Prescaler::Prescale8 => w.cs3().prescale_8(),
Prescaler::Prescale64 => w.cs3().prescale_64(),
Prescaler::Prescale256 => w.cs3().prescale_256(),
Prescaler::Prescale1024 => w.cs3().prescale_1024(),
}
});
},
pins: {
portd::PD0: {
ocr: ocr3a,
into_pwm: |tim| if enable {
tim.tccr3a.modify(|_, w| w.com3a().match_clear());
} else {
tim.tccr3a.modify(|_, w| w.com3a().disconnected());
},
},
portd::PD2: {
ocr: ocr3b,
into_pwm3: |tim| if enable {
tim.tccr3a.modify(|_, w| w.com3b().match_clear());
} else {
tim.tccr3a.modify(|_, w| w.com3b().disconnected());
},
},
},
}
}

#[cfg(feature = "atmega328pb")]
avr_hal_generic::impl_pwm! {
/// Use `TC4` for PWM (pins `PD1`, `PD2`)
///
/// # Example
/// ```
/// let mut portd = dp.PORTD.split();
/// let mut timer4 = Timer4Pwm::new(dp.TC4, pwm::Prescaler::Prescale64);
///
/// let mut pd1 = portd.pd1.into_output(&mut portd.ddr).into_pwm(&mut timer4);
/// let mut pd2 = portd.pd2.into_output(&mut portd.ddr).into_pwm(&mut timer4);
///
/// pd1.set_duty(128);
/// pd1.enable();
/// ```
pub struct Timer4Pwm {
timer: crate::pac::TC4,
init: |tim, prescaler| {
tim.tccr4a.modify(|_, w| w.wgm4().bits(0b01));
tim.tccr4b.modify(|_, w| {
//TODO: Figure out if svdtool can mark this as safe (as for Tc1)
unsafe { w.wgm4().bits(0b01) };
match prescaler {
Prescaler::Direct => w.cs4().direct(),
Prescaler::Prescale8 => w.cs4().prescale_8(),
Prescaler::Prescale64 => w.cs4().prescale_64(),
Prescaler::Prescale256 => w.cs4().prescale_256(),
Prescaler::Prescale1024 => w.cs4().prescale_1024(),
}
});
},
pins: {
portd::PD1: {
ocr: ocr4a,
into_pwm: |tim| if enable {
tim.tccr4a.modify(|_, w| w.com4a().match_clear());
} else {
tim.tccr4a.modify(|_, w| w.com4a().disconnected());
},
},
portd::PD2: {
ocr: ocr4b,
into_pwm4: |tim| if enable {
tim.tccr4a.modify(|_, w| w.com4b().match_clear());
} else {
tim.tccr4a.modify(|_, w| w.com4b().disconnected());
},
},
},
}
}

0 comments on commit 9f9d697

Please sign in to comment.