ATTiny44 array declared and initialized has wrong values

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am having the weirdest problem with an array initialization, and nothing I've tried has solved it. I have a global uint8_t array declared and initialized at the same time like so:

uint8_t registers[] = {
    0x00, 0xA0, 0x64, 0x32,
    0x00, 0xA0, 0x64, 0x32,
    0x00, 0xA0, 0x64, 0x32,
    0x00, 0xA0, 0x64, 0x32,
    0x00, 0x64, 0x32, 0x06, 0xE0, 0x06, 0xE0
};

And then later, I have a series of assignments and comparisons to translate bit fields into distinct control variables, like so:

void applyRegisterConfig() {
    redMode         =  registers[0x00] & 0x07;
    redInvert       =  registers[0x00] & 0x08;
    redBrightMin    =  registers[0x01] & 0x0F;
    redBrightMax    = (registers[0x01] & 0xF0) >> 4;
    redPeriod       =  registers[0x02];
    redSquare       =  registers[0x03] & 0x7F;
    .
    .
    .
}

But the behavior is not as expected. The rest of the code does not modify either the registers array contents or any of the red... or other control variables, but for example redMode does not contain zero even though it is being assigned registers[0] & 0x07 (which should be zero as far as I can tell.

Further testing by direct comparison shows that this condition matches, and redMode is set to 1:

if (registers[0x00] == 255) { redMode = 1; }

This makes no sense at all to me. What would be causing the initialized variable to be 255 instead of 0? I'm using the ATTiny44, so I have ~4K of program space and I'm using a little more than half. The full code is available here for inspection:

https://github.com/jrowberg/keyglove/blob/master/module_code/kgm_feedback/kgm_feedback.c

It's an ATTiny44-powered I2C slave for a unique RGB/vibe/piezo feedback module. This is the only roadblock I've come across so far, but I can't figure it out for anything.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My guess is you are running out of RAM. While the register[] array is stored in FLASH, it is copied into RAM by the C initialization code. Depending on how many variables you have declared and how deep your function calls go, the stack may be overwriting registers[].

Read the "Data in Program Space" chapter of the libc manual to learn how declare and access registers[] without using any RAM:

http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

Gamu The Killer Narwhal
Portland, OR, US
_________________
Atmel Studio 6.2
Windows 8.1 Pro
Xplained boards mostly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

GTKNarwhal wrote:
Depending on how many variables you have declared and how deep your function calls go, the stack may be overwriting registers[].

Thanks for the tip, and the tutorial link. I followed the instructions, and I was able to get the code to work as intended, though there is now a new issue to overcome--but a design consideration rather than unintended behavior.

The registers must be writable to change the configuration, so storing them in program memory only is too limiting. There are only 22 bytes to keep track of, so would EEPROM be a suitable storage place to keep the register settings if they need to be persistent? This may not be necessary, but if possible, it would be nice. Is there a reason--complexity, maximum R/W cycles on EEPROM, etc.--why that would be a bad idea?

In the mean time, since it is not strictly necessary to have settings persist across power cycles, I'm using the program memory to store the power-on default register values, then loading them into the individual control variables (redMode, redInvert, etc.). So far, so good.

Separately, I noticed that the repeated usage of pgm_read_byte() sent the program code size through the roof (adding nearly 1kB with 32 calls). Are the pgm_read_* macros especially memory intensive? I have restructured my code to use a loop with conditional control variable assignment depending on the read byte's index, such as:

if (reg == 0x00) {
    redMode = b & 0x07;
    redInvert = b & 0x08;
} else if (reg == 0x01) {
    redBrightMin = b & 0x0F;
    redBrightMax = (b & 0xF0) >> 4;
} else ...

This has saved a good 500 bytes or so and is functionally the same. I'm not used to optimizing this much! :-) I'm sure there are many ways I could further improve the efficiency.

Hopefully the small RAM availability will not be a huge deal. The code is really not all that complicated and does not use a zillion variables or recursion or deeply nested function calls.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

There are only 22 bytes to keep track of, so would EEPROM be a suitable storage place to keep the register settings if they need to be persistent?

It'd be a perfect choice for this kind of thing.
Quote:

I'm using the program memory to store the power-on default register values, then loading them into the individual control variables

Then why not do what you were attempting initially? An initialised array (that is not PROGMEM) will be copied out of flash to RAM before the C runtime starts main(). You end up using the same amount of RAM either way, it's just using this technique the CRT just has a single LPM in a loop to do all the copying that's required (rather than multiple pgm_read_byte calls which each set up to do an LPM).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The cause of your initial problem is the objcopy command in the Makefile. You also need to export the .data section to the hex file.

Stefan Ernst

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sternst wrote:
The cause of your initial problem is the objcopy command in the Makefile. You also need to export the .data section to the hex file.

Interesting. I just copied and modified the Makefile from a project online (not mine). What should that objcopy command be instead, since I'm not really familiar with what it does in the first place? Or, do you have any link(s) you can point me to so I can learn exactly what "you also need to export the .data section to the hex file" means? It is definitely not my intention to make you guys do all the work.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
What should that objcopy command be instead,

There must be something in the air tonight. I just posted this to another thread not 10 minutes ago:

https://www.avrfreaks.net/index.p...

If everyone would use Mfile rather than half baked, roll-your-own Makefiles many hours of frustration could have been saved.

BTW you have two options on objcopy. Either -j and list all that SHOULD be included or -R and list all that SHOULDN'T be included.

I seem to remember that Mfile chooses to use -j's while Studio creates Makefiles that use -R's (or is that the otehr way round?)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
If everyone would use Mfile rather than half baked, roll-your-own Makefiles many hours of frustration could have been saved. ...

Perfect! I changed the Makefile, tweaked the code back to regular storage instead of PROGMEM storage, and it still works beautifully. Thanks! And I promise to read up on Mfile and what each command is actually doing to help avoid this kind of problem in the future.

Not a bad opportunity to learn more about PROGMEM either though.

Thanks, everyone!