[chbot] Programming 8-bit microcontrollers
Volker Kuhlmann
list0570 at paradise.net.nz
Mon Sep 26 03:47:37 BST 2016
OK lets have some fun with 8 bit...
The highly suspicious can rest at ease, there are no intentional traps,
though perhaps the unintentional one of this being only a fragment. It's
the result of 3 minutes of stripping down to th essentials.
The layout of the class is fine on 8 bit, but on 16 or 32 bit one would
of course order members such that gaps caused by alignment are
minimised. If you're wondering about the purpose, the class is the bunch
of runtime settings, and the initialised copy is the default that gets
copied over. I could have made it a struct, there is no practical
difference I can see. In both cases there is no kind of checking by the
compiler for the initialised copy. It doesn't even check there are
enough values supplied - it only barfs if there are too many. Pathetic.
Half of first prize goes to Synco, who spotted one of the problems
first. Mark spotted it as well.
On the AVR, an int is 16 bit, which has more implications then is at
first obvious and it's not just variable widths. long btw is 32 bits.
> static const uint16_t setting = 15*60;
> class values {
> uint8_t a, b, c;
> uint32_t d;
> uint8_t e;
> uint16_t f;
> };
> static const values defaults {
> 50, 1, 10, 0xFFB070, 5, 0x03FF
> };
> void func() {
> unsigned long ms;
> ms = setting * 1000;
> ...
> }
First problem:
ms = setting * 1000;
Even though the result is assigned to 32 bit, that doesn't matter when
evaluating the expression, which is uint16_t times, well, int...
Any scalar constant that doesn't have an explicit type gets type int!!
So it's 16 bits times 16 bits, with a result of 16 bits, truncated of
course, which is THEN stuffed into 32 bits. Ooops.
In C++ casting is frowned upon, so instead of "(uint32_t) setting"
perhaps the better programming is
ms = setting * 1000UL;
which makes one operand 32 bits, forcing the multiplication to be at 32
bits, with the expected result.
Second problem:
No-one saw it.
static const values defaults {
50, 1, 10, 0xFFB070, 5, 0x03FF
Not even a value large enough to bust the size of an int persuades the
compiler to make it bigger than an int! Long oops. Nothing like some
corner cases to force you to learn your programming language properly...
So, the compiler will allocate 32 bits of memory, and then stuff
0x0000B070 into it...
Correctly programmed as:
static const values defaults {
50, 1, 10, 0xFFB070UL, 5, 0x03FF
Richard no it's not exactly what I want. I want one initialised
read-only (it never changes) instance, all other instances need no
initialisation so no code wastage if possible.
Paul I don't know if the aggregate initialisation works for private
members (it should), the class should still be assignable, privates
included. But yes I stripped down 1 line too many, and it should be
class values {
public:
...
What I wanted to point out is that int being 16 bit trips us up when
we're sloppy, having got used to 32 bit a long time ago. There is
updated firmware for the night light now.
Charles' answer is a good one too - ditch the 8 bit stuff and go ARM.
I did consider it, but it has other drawbacks.
Volker
--
Volker Kuhlmann
http://volker.top.geek.nz/ Please do not CC list postings to me.
More information about the Chchrobotics
mailing list