Rahix / avr-device

Register access crate for AVR microcontrollers
Apache License 2.0
168 stars 67 forks source link

Arduino hangs when trying to read FUSE bits #129

Open Ayrdim opened 1 year ago

Ayrdim commented 1 year ago

When trying to read the fuse bits via dp.FUSE.low.read().bits(); (it seems like .read() may be causing the issue) the Arduino Uno halts on that line, never continuing or panicking. It seems like this also happens when reading other fuse bits.

Is this to be expected or is this a bug? I know that the fuse bits are special however I assumed that if they are accessible in then they should also be readable, or at the very least cause a panic.

Edit: When running in release mode the program does not hang, however I've noticed that the register values aren't consistent, E.g. printing the ext register in hex will make the low registers value 0, whereas printing it as a u8 will cause the low register to be 250.

    let _val = dp.FUSE.high.read().bits();
    ufmt::uwriteln!(&mut serial, "high! {}", _val).unwrap();
    let _val = dp.FUSE.extended.read().bits();
    ufmt::uwriteln!(&mut serial, "ext! {}", _val).unwrap();
    let _val = dp.FUSE.low.read().bits();
    ufmt::uwriteln!(&mut serial, "low! {}", _val).unwrap();
Rahix commented 1 year ago

Hi,

can you run a quick test whether other registers behave differently? I have a feeling this might be an issue on compiler side...

Ayrdim commented 1 year ago

Sure can! Everything seems to work fine when playing around with the interrupt registers:

    dp.TC0.tccr0a.write(|w| w.wgm0().ctc());
    dp.TC0.tccr0b.write(|w| w.cs0().prescale_1024());
    dp.TC0.ocr0a.write(|w| w.bits(255));
    dp.TC0.timsk0.write(|w| w.ocie0a().set_bit());

    let _val = dp.TC0.tccr0a.read().bits();
    ufmt::uwriteln!(&mut serial, "tccr0a.bits() {}", _val).unwrap();
    let _val = dp.TC0.tccr0b.read().bits();
    ufmt::uwriteln!(&mut serial, "tccr0b.bits() {}", _val).unwrap();
    let _val = dp.TC0.ocr0a.read().bits();
    ufmt::uwriteln!(&mut serial, "ocr0a.bits() {}", _val).unwrap();
    let _val = dp.TC0.timsk0.read().bits();
    ufmt::uwriteln!(&mut serial, "timsk0.bits() {}", _val).unwrap();

This will output the following:

tccr0a.bits() 2
tccr0b.bits() 5
ocr0a.bits() 255
timsk0.bits() 2

Appreciate the help!

Patryk27 commented 9 months ago

Depending on your uC, reading fuse bits might require extra trickery - e.g. datasheet for atmega328p says:

25.2.2 Reading the Fuse and Lock Bits from Software: It is possible to read both the fuse and lock bits from software. To read the lock bits, load the Z-pointer with 0x0001 and set the BLBSET and SELFPRGEN bits in SPMCSR. When an LPM instruction is executed within three CPU cycles after the BLBSET and SELFPRGEN bits are set in SPMCSR, the value of the Lock bits will be loaded in the destination register. The BLBSET and SELFPRGEN bits will auto-clear upon completion of reading the lock bits or if no LPM instruction is executed within three CPU cycles or no SPM instruction is executed within four CPU cycles. When BLBSET and SELFPRGEN are cleared, LPM will work as described in the Instruction set manual.