Friday, August 11, 2017

Variables and Parameters

The first thing I pulled into the main code was the real time clock / timestamp library, and it turned out to be a good choice because it was non-trivial. Most classes have private and public members, but in the case of 'time', it needs to be accessible to other libraries, like logging and scheduling.

One way is to simply call rtcupdate() to fetch valid entries for a time struct, then add a parameter for it in the function calls. In memory constrained situations this isn't ideal. The stack is only so large and garbage collection is seldom merciful (or timely).

Early on I decided I prefer to create a register array of bytes for my 'globals'. This means I can look down a list of #defines and see which globals I have and how they are organized. It also means that I don't create a lot of near-duplicates (or outright duplicates), pass a lot of useless stuff on the stack, and generally keep things tidy. This is really important on the ATTINY 85 (and 84), but also handy on the 328's. It also means that turning a project into an I2C slave is trivial, since the registers already exist.

The implementation when mixed with several libraries is less obvious. Class files aren't aware of globals; they don't 'see' the uint8_t reg[64] declaration. There are two ways around this; pointers and extern. Both work, but the way I did it, pointers was just ever so slightly clunkier than using extern as I had to pass the address of the global reg[] array in the class .begin() call.

This allows for the following basic rules:

  1. class / private variables: never seen outside the class implementation. Can use get/set syntax for access
  2. class / public variables: never needed outside of either the class or loop(); preferred if no get/set syntax is helpful for housekeeping
  3. shared globals via extern: used to make global scope variables visible to class implementations.
To simplify point 3 even more, the only variable I plan on using this way is the reg[] array. We'll see if that holds up.


I should probably document how this works... after looking online I see that there is a lot of incomplete examples out there.

main (where setup and loop are):

#include "registers.h"
uint8_t reg[ REG_SIZE ];

void loop() {
  Serial.print( reg [ DT_MINUTE ] );
  Serial.print( " - ");
  Serial.println( reg [ DT_SECOND ] );

 delay(1000); // for demo, doesn't use RTC for timing


// fragment only:
#define REG_SIZE 64

#define DT_YEAR   16
#define DT_MONTH  17
#define DT_DATE   18
#define DT_HOUR   19
#define DT_MINUTE 20
#define DT_SECOND 21

Now the library (there is only a reference in the .cpp file, not in the .h):

timestamp.cpp (uses ds3231.h lib for actual i2c comms):

#include "registers.h"
extern uint8_t reg[  ];

void timestamp::getRTC( void ) {
 // fragment
  reg[ DT_MINUTE ] = Clock.getMinute();
  reg[ DT_SECOND ] = Clock.getSecond();

No comments:

Post a Comment