atsamd-rs / atsamd

Target atsamd microcontrollers using Rust
https://matrix.to/#/#atsamd-rs:matrix.org
Apache License 2.0
557 stars 199 forks source link

ItsyBitsy M4 support #89

Closed nic-hartley closed 5 years ago

nic-hartley commented 5 years ago

I've been eyeing the ItsyBitsy M4 lately, because I already know I can deploy to M4 but I want something smaller. Are there any plans to add support for it?

I'm happy to do it myself. There's one slight issue, which is that I don't know how. I could go through the other M4 and M0 crates and try to adapt itsybitsy_m0, but I'm not sure if that'd miss something important or not, and I likely won't use enough of the HAL to catch subtler issues.

sajattack commented 5 years ago

I wrote a guide on adding new boards https://github.com/atsamd-rs/atsamd/wiki/Adding-a-new-board itsybitsy m4 is ATSAMD51G19A which we already have the PAC for. Should be fairly straightforward. Let me know if you run into any issues.

nic-hartley commented 5 years ago

@sajattack Cool, thanks! I'll start working on it this week, but I'm not sure when exactly I'll get it done -- the semester just started and I'm a bit busy.

twitchyliquid64 commented 5 years ago

I just ordered two boards and also happy to help on this!

I think the ItsyBitsy M4 Express is a small form factor version of the Metro M4, so hears to hoping copy-pasta works.

sajattack commented 5 years ago

It's actually a slightly different chip to the Metro.

twitchyliquid64 commented 5 years ago

Really? The product page states:

It's the same chip as the Adafruit Metro M4 but really really small.

I couldn't find any mention on adafruit as to the exact chip, but the markings on the uC for my ItsyBitsy M4 Express reads:

ATSAMD51G
19A-U
1845 A
1846T7S TW
sajattack commented 5 years ago

Yup. The Metro is an ATSAMD51J19A while the ItsyBitsy is an ATSAMD51G19A. Fairly similar, but not identical. Anyway, thanks for offering to help.

nic-hartley commented 5 years ago

I've been poking at this, and it looks like copy-paste will mostly work, with just a few tweaks to the HAL (GPIO pins, &c). I haven't had a chance to actually test it yet, though. In the meantime, I've made a fork. Right now, the itsybitsy_m4 directory is just a copy-paste of the Metro -- I'll get to actually making it this weekend. I'd appreciate another pair of eyes on it once I do get a chance to do some work, or even PRs (or I can add y'all as contributors)

twitchyliquid64 commented 5 years ago

Sure thing, let me know how I can help!

Looks like its the same core in a different form factor[1] (possibly even the same die? idk how that works):

[1] p 1728 - states the G means 48 pins, whereas the J is 64 pins.

sajattack commented 5 years ago

If I may make a suggestion, y'all are probably better off copying the feather_m4, since I do most of my work in the metro_m4 and as a result there are many examples for it that would need to be adjusted.

sajattack commented 5 years ago

Fewer pins, fewer timers don't remember what else. You're better off getting the datasheet from microchip's site as they're on revision E now not revision A. Anyway, the HAL enabling and disabling has been done for that chip so yeah you're pretty much set to copy-paste.

sajattack commented 5 years ago

image Configuration summary for the curious.

tarcieri commented 5 years ago

Note that the trellis_m4 has a ATSAMD51G19A (which I made the PAC for)

nic-hartley commented 5 years ago

@tarcieri Oh, cool! Just switched it to be based on the Trellis instead, and I'll start working on "fixing" the HAL this weekend. Thanks!

twitchyliquid64 commented 5 years ago

I've gone through the schematic [0] and the pinout reference [1] and come up with this pinout, PTAL.


define_pins!(
    /// Maps the pins to their arduino names and
    /// the numbers printed on the board.
    struct Pins,
    target_device: target_device,

    /// Analog pin 0. Can act as a true analog output
    /// as it has a DAC (which is not currently supported
    /// by this hal) as well as input.
    pin a0 = a2,

    /// Analog Pin 1.  Can act as a true analog output
    /// as it has a DAC (which is not currently supported
    /// by this hal) as well as input.
    pin a1 = a5,
    /// Analog Pin 2
    pin a2 = b8,
    /// Analog Pin 3
    pin a3 = b9,
    /// Analog Pin 4, PWM capable
    pin a4 = a4,
    /// Analog Pin 5, PWM capable
    pin a5 = a6,

    /// Pin 0, rx
    pin d0 = a16,
    /// Pin 1, tx
    pin d1 = a17,
    /// Pin 2, PWM capable, analog input capable
    pin d2 = a7,
    /// Pin 3
    pin d3 = b22,
    /// Pin 4, PWM capable
    pin d4 = a14,

    /// Pin 5, Output only with rail-to-rail HI level. PWM capable
    pin d5 = a15,

    /// Dotstar Clock Pin
    pin dotstar_clk = b2,
    /// Dotstar Data Pin
    pin dotstar_data = b3,

    /// Pin 7, PWM capable
    pin d7 = a18,

    /// Pin 9, PWM capable
    pin d9 = a19,
    /// Pin 10, PWM capable
    pin d10 = a20,
    /// Pin 11, PWM capable
    pin d11 = a21,
    /// Pin 12, PWM capable
    pin d12 = a23,
    /// Pin 13, which is also attached to
    /// the red LED.  PWM capable.
    pin d13 = a22,

    /// The I2C data line
    pin sda = a12,
    /// The I2C clock line
    pin scl = a13,

    /// The SPI SCK, PWM capable
    pin sck = a1,
    /// The SPI MOSI, PWM capable
    pin mosi = a0,
    /// The SPI MISO
    pin miso = b23,

    /// The USB D- pad
    pin usb_dm = a24,
    /// The USB D+ pad
    pin usb_dp = a25,
);

It compiles fine if I drop it in-place to feather_m4/src/lib.rs. Do we need to examine the helper functions (ie: uart, i2c_master)? Am I right in thinking they should 'just work' as the uC is the same?

EDIT: Blink example also works on real hardware.

Gonna try some basic examples once I wrangle bossa and find a USB cable.

[0] - https://cdn-learn.adafruit.com/assets/assets/000/055/481/original/adafruit_products_schem.png?1529261754

[1] - https://learn.adafruit.com/introducing-adafruit-itsybitsy-m4/pinouts

nic-hartley commented 5 years ago

@twitchyliquid64 I think a lot more will need to be reworked -- there's a bunch of stuff on the NeoTrellis that the ItsyBitsy doesn't have (e.g. a JST STEMMA port, NeoPixels, etc.) which, though it might compile to leave it, will be misleading. Right now I've been going through the peripherals and jotting down all of them, and the schematic to see what's connected to what, and figure out what protocols should be supported where. The pins themselves look good, though!

sajattack commented 5 years ago

The trellis_m4 crate is a bit overcomplicated :P If you want to base on feather_m4, just remember to change atsamd51j19a to atsamd51g19a.

twitchyliquid64 commented 5 years ago

Managed to get it working reliably on real hardware with the above pinout + change to the example's clock config. Forgot that the itsybitsy_m4 has no external oscillator (it would only work after I touched it and I couldnt figure out why - looks like the PLL + crosstalk combo is damn good).

Would it make sense to not expose the GenericClockController::with_external_osc() function given the board doesnt have it? or do we need to leave it, in-case someone solders a crystal on MOSI/SCK?

I also seem to crash my kernel (4.20.0 - tainted by Nvidia) after flashing + replugging it ~2 dozen times. Must be a bug somewhere in the kernel USB code ...

sajattack commented 5 years ago

The external_osc function is at the HAL level, it can't be removed at the BSP level. It could be removed for all atsamd51g19a but that doesn't make sense because there could be other boards with that chip with external oscillators.

nic-hartley commented 5 years ago

@twitchyliquid64 You honestly seem a lot more on top of this than I am -- do you want to take over? Just be sure to go over the ItsyBitsy M4's peripherals and make sure you're accounting for all of them -- not just external pins. e.g. I don't see any pins for the QSPI Flash chip on the board.

EDIT: I just pushed up the pins on my fork, if you want to take a look.

twitchyliquid64 commented 5 years ago

@nic-hartley I'm pretty trash at rust (still learning) but I'm happy to help out. That said, it looks like you're almost done?

nic-hartley commented 5 years ago

@twitchyliquid64 Possibly -- I'm still going through the examples to see what's what and trying to figure out if QSPI is already supported, or if I'll need to add it. That Flash chip is really useful.

twitchyliquid64 commented 5 years ago

Heres some copy-pasta-modify for powering up the SPI peripheral on the labelled pins. I'll test it tonight.


/// Convenience for setting up the labelled SPI peripheral.
/// This powers up SERCOM1 and configures it for use as an
/// SPI Master in SPI Mode 0.
pub fn spi_master<F: Into<Hertz>>(
    clocks: &mut GenericClockController,
    bus_speed: F,
    sercom1: pac::SERCOM1,
    mclk: &mut pac::MCLK,
    sck: gpio::Pa1<Input<Floating>>,
    mosi: gpio::Pa0<Input<Floating>>,
    miso: gpio::Pb23<Input<Floating>>,
    port: &mut Port,
) -> SPIMaster1<
        hal::sercom::Sercom1Pad3<gpio::Pb23<gpio::PfC>>,
        hal::sercom::Sercom1Pad0<gpio::Pa0<gpio::PfD>>,
        hal::sercom::Sercom1Pad1<gpio::Pa1<gpio::PfD>>
    > {
    let gclk0 = clocks.gclk0();
    SPIMaster1::new(
        &clocks.sercom1_core(&gclk0).unwrap(),
        bus_speed.into(),
        hal::hal::spi::Mode {
            phase: hal::hal::spi::Phase::CaptureOnFirstTransition,
            polarity: hal::hal::spi::Polarity::IdleLow,
        },
        sercom1,
        mclk,
        (miso.into_pad(port), mosi.into_pad(port), sck.into_pad(port)),
    )
}

Use like:


use hal::time::MegaHertz;
use hal::spi_master;

// ...

let mut ss = spi_master(
    &mut clocks,
    MegaHertz(1),
    peripherals.SERCOM1,
    &mut peripherals.MCLK,
    pins.sck,
    pins.mosi,
    pins.miso,
    &mut pins.port,
);
sajattack commented 5 years ago

QSPI is not supported.

nic-hartley commented 5 years ago

@sajattack I'm gonna open another issue for it once I'm home. (and start working on it, once I have more time) Access to the QSPI flash chip would be very handy.

twitchyliquid64 commented 5 years ago

@nic-hartley if Q/D SPI is going to be too much of a hassle, we could just talk regular SPI to it - looks like it does support that (though not all the commands).

sajattack commented 5 years ago

@twitchyliquid64 the spi-capable pins and the qspi-capable pins often don't match up.

twitchyliquid64 commented 5 years ago

SPI on labelled pins works.

image

twitchyliquid64 commented 5 years ago

Disappointingly, the dotstar (which the schematic says is on SERCOM4) is not on a pinout that corresponds with any valid SPI pad configuration. As such, it looks like we have to bit bang it.

Its late and I have work tomorrow so I'll post what I have. This appears to work (I changed the constants and got a blue/red/green), but needs an actual API.

// dotstar.rs
use super::hal;
use hal::gpio::{self, *};
use hal::prelude::*;
use cortex_m::asm::delay as cycle_delay;

pub struct Dotstar {
    clk: Pb2<Output<PushPull>>,
    data: Pb3<Output<PushPull>>,
}

impl Dotstar {
    pub fn new(
        clk: gpio::Pb2<Input<Floating>>,
        data: gpio::Pb3<Input<Floating>>,
        port: &mut Port,
    ) -> Dotstar {
        let clk_pin = clk.into_push_pull_output(port);
        let data_pin = data.into_push_pull_output(port);
        Self{clk: clk_pin, data: data_pin}
    }

    pub fn send(&mut self, mut b: u8) {
        for _ in 0..8 {
            if b & 1 != 0 {
                self.data.set_high().unwrap();
            } else {
                self.data.set_low().unwrap();
            }

            self.clk.set_low().unwrap();
            cycle_delay(24);

            b = b >> 1;

            self.clk.set_high().unwrap();
            cycle_delay(24);
        }
    }

    pub fn red(mut self) {
        // Preamble.
        self.send(0);
        self.send(0);
        self.send(0);
        self.send(0);

        // LED 0 data packet.
        self.send(0xff); // brightness bit - always max to avoid flicker.
        self.send(0);    // Blue.
        self.send(0);    // Green.
        self.send(0x72); // Red. 0xFF hurts my eyes.

        // Postamble.
        self.send(0xff);
        self.send(0xff);
        self.send(0xff);
        self.send(0xff);
    }
}

This took me way too long as I'm learning rust, so would appreciate any feedback.

sajattack commented 5 years ago

You could try bitbang-hal + smart-leds/apa102-spi, but last I heard it was broken. Feel free to raise your PR to this repo rather than Nic's.