Open dsvensson opened 7 years ago
I just tried out this framework on the leonardo/promicro/atmega32u4 and had to change some addresses around to get for example serial working
What addresses were these? Did you have to run a custom linking command?
AVR-LLVM itself supports hundreds of different AVR microcontrollers, so the problem is likely that Rust needs to pass extra arguments to the linker.
UDR0, UBRR0H, UBRR0L, UCSR0C, UCSR0B, UCSR0A etc
I'm getting my repositories mixed up haha, cc @shepmaster
Do you have any nice idea on how to have different boards in the same code base?
Yeah, that's a big question for this library!
My hope was that basically the giant list of registers could be specified for each chip, perhaps in a module. That should work nowadays.
The hard part comes with selecting the appropriate chipset. My first idea would be to have a feature flag that switches them around (and maybe enables certain parts like what serial features are available).
@dsvensson Could you maybe publish your fork somewhere so we could see what kind of diff you had to make? That might help guide what kind of swapability we need to enable.
Going sligthly off-topic... I have very little experience in this area, but wouldn't it make sense to try to align this to the route @japaric is taking with embedded-hal etc, or is his arm efforts just too far away? Thinking otherwise it would be nice to get some cross pollination?
wouldn't it make sense to try to align this to the route @japaric is taking with embedded-hal etc, or is his arm efforts just too far away? Thinking otherwise it would be nice to get some cross pollination?
I'm always of two minds on this kind of thing. Having two parallel approaches has the potential for independent discovery of useful ideas. It also has the downside of duplicated effort for the common stuff.
For example, back in ye olde days, there was zinc.rs which offered one way of this type of abstraction. I haven't even looked at the ARM HAL stuff, so I don't know if it's the same idea as zinc or if they started from the same place.
However, @japaric is a smart embedded programmer, so I'd appreciate any input from them!
Found a git repo with the datasheets in XML format, so here's a generated set of registers (and the script). Up for debate what the names should be... and maybe newer versions of Atmel Studio has updated XML files that could be used instead for better naming.
Thanks. Would you mind running the script for the atmega328p as well? That way I can compare it to the checked-in version. Any idea about 16-bit register pairs or aliases?
Heh, didn't know about collapsable parts in comments before! Here you go:
Any idea about 16-bit register pairs or aliases?
I'm generating them as H/L, with bits from 0...15, here they are:
// atmega32u4
pub const ADC: *mut u16 = ADCL as *mut u16;
pub const EEAR: *mut u16 = EEARL as *mut u16;
pub const ICR1: *mut u16 = ICR1L as *mut u16;
pub const ICR3: *mut u16 = ICR3L as *mut u16;
pub const OCR1A: *mut u16 = OCR1AL as *mut u16;
pub const OCR1B: *mut u16 = OCR1BL as *mut u16;
pub const OCR1C: *mut u16 = OCR1CL as *mut u16;
pub const OCR3A: *mut u16 = OCR3AL as *mut u16;
pub const OCR3B: *mut u16 = OCR3BL as *mut u16;
pub const OCR3C: *mut u16 = OCR3CL as *mut u16;
pub const SP: *mut u16 = SPL as *mut u16;
pub const TCNT1: *mut u16 = TCNT1L as *mut u16;
pub const TCNT3: *mut u16 = TCNT3L as *mut u16;
pub const UBRR1: *mut u16 = UBRR1L as *mut u16;
pub const UDFNUM: *mut u16 = UDFNUML as *mut u16;
// atmega328p
pub const ADC: *mut u16 = ADCL as *mut u16;
pub const EEAR: *mut u16 = EEARL as *mut u16;
pub const ICR1: *mut u16 = ICR1L as *mut u16;
pub const OCR1A: *mut u16 = OCR1AL as *mut u16;
pub const OCR1B: *mut u16 = OCR1BL as *mut u16;
pub const SP: *mut u16 = SPL as *mut u16;
pub const TCNT1: *mut u16 = TCNT1L as *mut u16;
pub const UBRR0: *mut u16 = UBRR0L as *mut u16;
And here is the updated script.
Side note, does this library have support for the Mega? It does have an increased number of pins compared to the Uno and other similar boards, as well as having (2 iirc) more serial ports, which run on certain pins.
@dylanmckay any idea if avrd can be of use here? Specifically, I don't see any of the bits available there.
AVRd does have the pins afaik. It could solve the problem.
AVRd does have the pins afaik
It appears it has access to the pins (bitfield
), but isn't currently making use of them.
What do you mean? The README indicates that you can use them like you would using the arduino library:
What do you mean? The README indicates that you can use them like you would using the arduino library:
Yes, but this library currently also relies on the pins associated
register!(0x25, PORTB, [PORTB7, PORTB6, PORTB5, PORTB4, PORTB3, PORTB2, PORTB1, PORTB0 ]);
I don't see PORTB7
in those docs.
Yeah, we definitely have access to the bitfields from the pack files.
At the moment, the library only uses them in generated documentation.
We can definitely use them to generate PORT[A-Z][0-9]
constants.
Can confirm that AVRd has definitions for every register across every device.
Just because it was in my head, I wonder what kinds of type safety and structuring we could employ. For example:
struct PORTB;
impl PORTB {
const PORTB7: u8 = 1 << 0;
}
struct Register(*const u8);
impl From<PORTB> for Register {
fn from(_: PORTB) -> Self { Register(0x20 as _) }
}
This would make each register its own type, and then we could choose to implement traits across them.
I did something similar locally:
pub trait Information {
const DDR: *mut u8;
const IO: *mut u8;
const PIN: *mut u8;
}
pub struct B;
impl Information for B {
const DDR: *mut u8 = ::DDRB;
const IO: *mut u8 = ::PORTB;
const PIN: *mut u8 = ::PINB;
}
I was thinking if we want the avrd
library to do something like this.
I'd really like the library to be quite low level that other devices built atop of, but if we create a trait like this then we can auto generate it for all devices, rather than having to rebuild all of it for the Arduino library.
Another option might be to extract most of avrd
to a library. With that, we could add it as a build dependency for Arduino and then use it to generate structures ourselves.
Another option might be to extract most of avrd to a library.
That seems like it might be the appropriate path. A rough idea would be that the arduino
library would have only generic things like
pub trait Information {
const DDR: *mut u8;
const IO: *mut u8;
const PIN: *mut u8;
}
The user uses avrd
(or something built on top of it) in their own build script (insert hand-waving here), generating code like
pub struct B;
impl Information for B {
const DDR: *mut u8 = ::DDRB;
const IO: *mut u8 = ::PORTB;
const PIN: *mut u8 = ::PINB;
}
Which accounts for their specific board (e.g. which PORTx
are available). They can then use the combination of the two libraries, effectively dependency-injecting the concrete board information.
This could also allow for the user to choose a serial implementation for macros like println!
to use.
A big question in my mind is exactly how abstract we can be. Aren't there some boards that have like... "half" of a PORT defined? Will things like that cause massive pain?
We could abstract it on the GPIO level rather than the port level.
The pack files do say the number of pins supported on each port.
Something like
trait Pin {
const DDR: *mut u8;
const PORT: *mut u8;
const PIN: *mut u8;
const MASK: u8;
}
struct PB5;
impl Pin for PB5 {
const DDR: *mut u8 = ::DDRB;
const PORT: *mut u8 = ::PORTB;
const PIN: *mut u8 = ::PINB;
const MASK: u8 = 1<<5;
}
I remember there also being some sort of special IO register that 1
can be written to which will toggle the pin, regardless of its current state.
Because the XOR is done in hardware, it's faster than using PORTB
to toggle.
If we abstract this on the pin level, we can write a toggle
method to do this optimisation transparently.
I remember there also being some sort of special IO register that 1 can be written to which will toggle the pin, regardless of its current state.
Yep, that was one of the things that tripped me up earlier this year, and why I asked you about coalescing 8 consecutive sbi
instructions for the same register.
You can see my current set of changes in the ports
branch. TL;DR yeah, there's a toggle
function.
I've got my own local branch as well, exposing a Pin
trait that gets implementations generated per mcu.
I'm also creating a HardwareSerial
trait, and if I can understand enough about timers, will hopefully do that too.
Here's my WIP PR avr-rust/arduino#4
Related: could this be done for serial ports?
Example gist: https://gist.github.com/Restioson/47976cc261a1a9b788114283e98f2308 (Apologies for any mistakes)
Yeah, I've done it for SPI so far.
Great :smile:
I just tried out this framework on the leonardo/promicro/atmega32u4 and had to change some addresses around to get for example serial working, but I guess the rest need some tweaking too. Do you have any nice idea on how to have different boards in the same code base?