I
think I figured out what the problem was. I believe it is a problem with the compiler generating machine code that doesn't quite do things in the same order as the C code.
The assembly code generated for the
led_flash_start() function code as previously given was as follows:
000000ca <led_flash_start>:
ca: 87 72 andi r24, 0x27 ; 39
cc: 2f b7 in r18, 0x3f ; 63
ce: f8 94 cli
d0: 90 91 60 00 lds r25, 0x0060 ; 0x800060 <__TEXT_REGION_LENGTH__+0x7e0060>
d4: 98 61 ori r25, 0x18 ; 24
d6: 90 93 60 00 sts 0x0060, r25 ; 0x800060 <__TEXT_REGION_LENGTH__+0x7e0060>
da: 82 32 cpi r24, 0x22 ; 34
dc: 08 f0 brcs .+2 ; 0xe0 <led_flash_start+0x16>
de: 81 e2 ldi r24, 0x21 ; 33
e0: 80 64 ori r24, 0x40 ; 64
e2: 80 93 60 00 sts 0x0060, r24 ; 0x800060 <__TEXT_REGION_LENGTH__+0x7e0060>
e6: 2f bf out 0x3f, r18 ; 63
e8: 08 95 ret
I don't really know what I'm doing with assembly, but I can recognise what I think are the instructions writing to the WDTCSR register (
sts 0x0060... - 0x60 being the address of the reg), and there are
4 instructions between the two instances. Given that the datasheet specifies that the changes to the watchdog configuration must be done within four clock cycles, this code would appear to overrun that timing requirement!
I'm not 100% sure what the stuff in-between is, but it looks like (because of the branch instruction) it might be the
if() statement that ensures the
frequency variable is not a reserved value.
If I comment that
if() line out and re-compile, the generated assembly code is drastically different:
000000ca <led_flash_start>:
ca: 2f b7 in r18, 0x3f ; 63
cc: f8 94 cli
ce: e0 e6 ldi r30, 0x60 ; 96
d0: f0 e0 ldi r31, 0x00 ; 0
d2: 90 81 ld r25, Z
d4: 98 61 ori r25, 0x18 ; 24
d6: 90 83 st Z, r25
d8: 87 72 andi r24, 0x27 ; 39
da: 80 64 ori r24, 0x40 ; 64
dc: 80 83 st Z, r24
de: 2f bf out 0x3f, r18 ; 63
e0: 08 95 ret
I don't quite recognise what's going on - the only reference to the memory address of the WDTCSR register (0x60) is in loading that value to another register - but hazarding a guess that the two
st instructions are the ones writing to WDTCSR, there are now only two instructions separating them, which now complies with the timing requirements. And, this code actually works!
Bloody compilers, re-arranging my code!
I think I'll just remove the
if() statement, as it's not strictly necessary, just a sanity check against if one were to pass some random value into the function.
I also think I'll add a line to reset the watchdog at the beginning of the atomic section, as the datasheet notes that when changing the timer period to a shorter value, you might get a premature time-out, and resetting prior will mitigate that.