-
Notifications
You must be signed in to change notification settings - Fork 223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a HAL implementation for SPI master #13
Conversation
Can you elaborate on why you did that? |
@Rahix Those terms carry a strong association with human slavery, usage of it can be insensitive to those who have been directly or indirectly impacted by that practice. See this section on Wikipedia on other projects that have migrated away from it and why. https://en.wikipedia.org/wiki/Master/slave_(technology)#Terminology_concerns |
@Rahix The concrete implementation is "done". I still need to test it and turn it into a macro, but please review it to make sure it seems sane at this point. Please offer any desired critique of the interface too. |
Pretty happy with the current state of the concrete implementation. Need to test it next, then make a macro out of it. |
Having trouble testing this implementation. I've been trying to verify it at a simple level by creating an example that connects POSI directly to PISO to read and write the same byte. However it's not working, it just reads a 0. Continuing debugging. |
I've simplified my example to cut out the SPI implementation entirely and use the registers directly, but it still does not work as expected.
Some output
Oddly sometimes the byte will come back as 0b11111111. No idea why. I've tweaked a bunch of the control register settings and even checked that the config (87) matches what I expect according to the datasheet. At this point I'm wondering if this is even possible (connecting PISO to POSI). I'll probably try integrating with the Ethernet module I have next. I wanted to wait as long as possible on that, since it's a less primitive test and will be more difficult to debug. |
I did make an attempt at getting the Ethernet module to work, but it wouldn't even initialize before panicking. According to this StackExchange answer though, my idea of looping PISO to POSI should work. https://arduino.stackexchange.com/questions/66942/spi-test-by-connecting-mosi-to-miso/66950#66950 I'll continue debugging with that simple setup. |
Finally had an epiphany and solved the problem. The problem was that the POSI pin was not in output mode. Should have the SPI library working and ready soon now. Here's a super basic (working) example: #![no_std]
#![no_main]
#![feature(proc_macro_hygiene)]
extern crate panic_halt;
use arduino_uno::prelude::*;
#[no_mangle]
pub extern fn main() -> ! {
let dp = arduino_uno::Peripherals::take().unwrap();
let mut delay = arduino_uno::Delay::new();
let mut pins = arduino_uno::Pins::new(
dp.PORTB,
dp.PORTC,
dp.PORTD,
);
pins.d10.into_output(&mut pins.ddr);// POSI pin must be made an ouptput
pins.d11.into_output(&mut pins.ddr);// secondary select pin must be made an output
let mut serial = arduino_uno::Serial::new(
dp.USART0,
pins.d0,
pins.d1.into_output(&mut pins.ddr),
57600,
);
dp.SPI.spcr.write(|w| {
w.spie().clear_bit();
w.spe().set_bit();// must enable SPI
w.dord().clear_bit();
w.mstr().set_bit();// must set to primary mode
w.cpol().clear_bit();
w.cpha().clear_bit();
w.spr().val_0x00()
});
dp.SPI.spsr.write(|w| w.spi2x().clear_bit());
loop {
dp.SPI.spdr.write(|w| w.bits(0b10101010));
while dp.SPI.spsr.read().spif().bit_is_clear() {}
let read_data = dp.SPI.spdr.read().bits();
ufmt::uwriteln!(&mut serial, "data: {}\r", read_data).unwrap();
delay.delay_ms(1000);
}
} |
Successfully moved the working SPI code into the atmega328p library and added an example that uses it. Next up I need to add more docs to the example, then move it back into the macro. |
Documentation is ready, all that's left is turning it into a macro. |
@Rahix Please see the comment I left above. |
I updated the examples to use the No idea why uwriteln is failing on a dynamic u8 parameter, but at least the important parts here are working. This PR is ready as far as I'm concerned. Please LMK if there's anything else that needs to be tweaked. |
Ran cargo fmt on SPI examples
This does sound like a compiler issue ... We had something similar happen before, which I pushed a workaround for in commit cab8613 ("Add workaround for ufmt related codegen bug"). Which version of ufmt are you using right now? Is that commit in your branch as well? |
This is from Cargo.lock [[patch.unused]]
name = "ufmt"
version = "0.1.0-beta.5"
source = "git+https://github.com/Rahix/ufmt.git?rev=5a0f82991c74becb81415d3c2b74fba872496ab6#5a0f82991c74becb81415d3c2b74fba872496ab6" I've pulled the latest from your master branch as of yesterday, so if your commit is there, I have it. Edit: checked, your commit is included. |
Hm, not sure if this is correct. It says |
Damn, I see the same thing; the patched version is not included in the build. I'll dig a bit and check what causes this. |
I pushed commit 2cb668d ("Update to ufmt 0.1.0"). Can you try pulling that into your branch, then do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A few small things, with those fixed I think we are ready for merge. :) If anything doesn't make much sense, let me know. I can try explaining it a bit more ...
I'll give that a try tonight. Thank you for patiently explaining all of this to me. I'll work on another round of revisions tonight. |
Reverted examples now that ufmt patch is in place
… send does not block unless another write is already pending
Changed implementation of SPI to match nb::Result contract correctly:…
Removed debug trait from SPI
@Rahix I applied your suggestions, and the patch to ufmt works perfectly. |
fn read(&mut self) -> $crate::nb::Result<u8, Self::Error> { | ||
self.flush()?; | ||
Ok(self.peripheral.spdr.read().bits()) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch adding the flush()
here! I guess you already read the docs but just for reference (for me and potential others): This works because every read has to happen after a send by the traits design.
I'm wondering whether we should add an assertion that this is upheld by the caller ... (which would only be enabled in debug builds)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe, I'm inclined to leave it though. It will work if they read first, they'll just read 0b00000000
. Plus we'd either have to use the is_write_in_progress flag in the assertion which has the disadvantage of only allowing one read after a write, OR add another boolean flag, which has a run-time impact. Not sure if the compiler can remove an entire field or not.
I don't have much time today unfortunately, but hopefully I'll get around to setting up a logic-analyzer and testing this in depth in the coming days. Thank you so much for working on this (and sticking around for so long :D)! |
Sounds good! |
How's it looking @Rahix ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems to work fine from what I tested!
Woohoo! Thanks @Rahix! 🎉 🎉 🎉 |
In an attempt to execute #5 , I've begun on this skeleton of a SPI-implementing macro. I've only written a few lines of Rust, and never a macro, so this is based on reading the relevant documentation and following the examples of the other macros in the directory (namely
serial.rs
). I haven't even tried to compile it yet. It's a Work in Progress, and not yet ready to merge.Please give any and all feedback. I will continue to work on fleshing out the details.
Note that I've intentionally substituted the original "master"/"slave" terminology for the alternate "primary"/"secondary" terminology. Hence "piso" and "posi" instead of "miso" and "mosi" respectively.