Home2L - C/C++ API  v1.2-2-ga4fe (2023-04-15)
Smart Tools for a Private Home
Classes | Macros
Microcontroller: API

Description

Microcontroller firmware basic modules, relevant for implementing new features.

This module describes some internal interfaces of the Brownie firmware. They are relevant for extending the firmware by, for example, adding new feature modules.

To add a new feature module foobar, the following steps have to be done (replace foobar with the name of your module):

  1. In source directory 'brownies/avr', add a new file foobar.h with the following content:
    #ifndef _FOOBAR_
    #define _FOOBAR_
    #include "core.h"
    #if !WITH_FOOBAR
    EMPTY_MODULE(Foobar)
    #else // WITH_FOOBAR
    void FoobarInit ();
    void FoobarIterate ();
    void FoobarOnRegRead (uint8_t reg);
    void FoobarOnRegWrite (uint8_t reg, uint8_t val);
    #endif // WITH_FOOBAR
    #endif // _FOOBAR_
    #define EMPTY_MODULE(NAME)
    Helper to declare an empty / deactivated module (see Microcontroller: API).
    Definition: core.h:39
  2. In the same directory, add a new file foobar.c with the following content:

    #if WITH_FOOBAR
    #include "foobar.h"
    ... put your code here ...
    void FoobarInit () {
    ... put your code here ...
    }
    void FoobarIterate () {
    ... put your code here ...
    }
    void FoobarOnRegRead (uint8_t reg) {
    ... put your code here ...
    }
    void FoobarOnRegWrite (uint8_t reg, uint8_t val) {
    ... put your code here ...
    }
    #endif // WITH_FOOBAR

    The documentation of CoreInit() and friends describe what the interface functions must do.

  3. Add the source file foobar.c to the list of source files'SRC' in 'brownies/avr/Makefile'.
  4. In file configure.h, add a new section for the compile-time parameters of your module. At least one parameter, 'WITH_FOOBAR' must be defined. Other parameters must be prefixed with 'FOOBAR_'.

    In the same file, please also extend the sections "Features: Auto-Completion", "Ports: Auto-Completion", "Ports: Checks", and "MCU: Reset Pin Configuration".

  5. If the module foobar requires new MCU pins: Add them in section "MCU Port Assignments" in file configure.h.
  6. If the module foobar requires new registers: Go to section "Register Map" in file interface.h (starting with BR_REG_CHANGED), identify a resonable unused register number and add the definition of your register(s) there. Name the registers 'BR_REG_FOOBAR_...', and name constants (e.g. bit masks) related to your registers like 'BR_FOOBAR_...'.
Collaboration diagram for Microcontroller: API:

Classes

struct  SBrEeprom
 Structure to describe the complete EEPROM content. More...
 

Macros

#define EMPTY_MODULE(NAME)
 Helper to declare an empty / deactivated module (see Microcontroller: API).
 

Memory and Registers ...

const __flash TBrFeatureRecord brFeatureRecord
 The Brownie feature recourd.
 
#define brConfigRecord   ((const TBrConfigRecord) _brConfigRecord)
 The Brownie config recourd.
 
#define RegGet(REG)   (_regFile[REG])
 Get a register value.
 
#define RegSet(REG, VAL)   do { _regFile[REG] = (VAL); } while (0)
 Set a register to a new value.
 

Persistent Storage (EEPROM) ...

struct SBrEeprom brEeprom
 Complete EEPROM contents.
 

Change Reporting ...

uint8_t chgShadow
 
static void ReportChange (uint8_t mask)
 Set (a) bit(s) in the BR_REG_CHANGED register.
 
void ReportChangeAndNotify (uint8_t mask)
 Set (a) bit(s) in the BR_REG_CHANGED register and issue a TWI host notification.
 

TWI Master ...

Note: These functions may only be called if the hub functionality is disabled.

void TwiMaInit ()
 Init all TWI master ports.
 
static void TwiMaSelectPort (int8_t port)
 Select the TWI master port for the next operations.
 
void TwiMaSendStart ()
 Send a start condition.
 
void TwiMaSendStop ()
 Send a stop condition.
 
bool TwiMaSendByte (uint8_t data)
 Send a byte (returns the ACK bit).
 
uint8_t TwiMaReceiveByte (bool ack)
 Receive a byte ('ack' to be set to 1 if more bytes are expected).
 

Timer ...

uint16_t TimerNow ()
 Get current time in ticks. More...
 

Mini-Timer ...

8-bit counter with a cycle time of 8µs to be used locally in modules. Presently, the mini-timer is used by the ISRs of the UART and the 'temperature' modules. Minitimers must be used with interrupts disabled.

Note: MinitimerReset() and MinitimerNow() are implemented as macros to ensure that they are inlined. Otherwise, ISRs may get a very long prologue saving many registers. This causes problems in 'TemperatureISR()' on the ATtiny861.

void MinitimerStart (int clockScale)
 Start the minitimer (pass any MINI_CLOCK_SCALE_* constants)
 
void MinitimerStop ()
 Stop the minitimer.
 
void MinitimerReset ()
 Reset the minitimer.
 
uint8_t MinitimerNow ()
 Get the current minitimer value.
 
#define MINITICKS_OF_US(X)   (X * (BR_CPU_FREQ / 1000000) / 8)
 Number of 8-bit timer ticks of a microsecond value for a clock selection of clk_io/8.
 
#define MINI_CLOCK_SCALE_1
 Minitimer clock period of 1/BR_CPU_FREQ.
 
#define MINI_CLOCK_SCALE_8
 Minitimer clock period of 8 * 1/BR_CPU_FREQ.
 
#define MINI_CLOCK_SCALE_64
 Minitimer clock period of 64 * 1/BR_CPU_FREQ.
 
#define MINI_CLOCK_SCALE_256
 Minitimer clock period of 256 * 1/BR_CPU_FREQ.
 
#define MINI_CLOCK_SCALE_1024
 Minitimer clock period of 1024 * 1/BR_CPU_FREQ.
 

Interface Functions of the "core" Module ...

The feature modules (e.g. gpio.[hc], shades.[hc]) have a common set of interface functions. The following functions are the interface for the pseudo-module "core", which implements some core functionality.

If you are developing a feature module: Never call these functions directly. They are only documented here in order to document the general module interface functions.

void CoreInit ()
 Initialize the module. More...
 
static void CoreIterate ()
 Iterate the module. More...
 
void CoreOnRegRead (uint8_t reg)
 Update a register when it is read. More...
 
void CoreOnRegWrite (uint8_t reg, uint8_t val)
 Write a register. More...
 

Basic Helpers ...

#define INIT(NAME, VAL)   if ((VAL) != 0) NAME = (VAL);
 Variable assignment for '*Init()' functions which are only called once on startup. More...
 
#define LO(X)   ((X) & 0xff)
 Get low byte of a 16-bit word-.
 
#define HI(X)   ((X) >> 8)
 Get high byte of a 16-bit word.
 
#define HILO(H, L)   ((((uint16_t) (H)) << 8) | (L))
 Compose a 16-bit word from high and low byte.
 
#define SHIFT_OF_MASK(X)
 Get the number of shifts to obtain a certain byte mask (inverse of '1 << N').
 

MCU Port Access Macros ...

#define P_A0   0x0001
 
#define P_A1   0x0002
 
#define P_A2   0x0004
 
#define P_A3   0x0008
 
#define P_A4   0x0010
 
#define P_A5   0x0020
 
#define P_A6   0x0040
 
#define P_A7   0x0080
 
#define P_B0   0x0100
 
#define P_B1   0x0200
 
#define P_B2   0x0400
 
#define P_B3   0x0800
 
#define P_B4   0x1000
 
#define P_B5   0x2000
 
#define P_B6   0x4000
 
#define P_B7   0x8000
 
#define P_IN(P)   (LO(P) ? (PINA & LO(P)) : HI(P) ? (PINB & HI(P)) : 0)
 Read a single pin; result is either 0 or non-zero, depending on whether the pin is set.
 
#define P_IN_MULTI(MASK)   ((LO(MASK) ? (PINA & LO(MASK)) : 0) | (HI(MASK) ? ((uint16_t) (PINB & HI(MASK))) << 8 : 0))
 Read multiple pins, selected by PMASK; result is 16-bit vector.
 
#define P_OUT_0(P)   do { if (LO(P)) PORTA &= ~LO(P); if (HI(P)) PORTB &= ~HI(P); } while(0)
 
#define P_OUT_1(P)   do { if (LO(P)) PORTA |= LO(P); if (HI(P)) PORTB |= HI(P); } while(0)
 Set/reset pin(s). Usually, only a single bit should be passed here to make the compiler generate 'sbi' and 'cbi' instructions.
 
#define P_OUT_MULTI(MASK, P)
 Set multiple pins, selected by 'MASK', to 'P'.
 
#define P_DDR_IN(P)   do { if (LO(P)) DDRA &= ~LO(P); if (HI(P)) DDRB &= ~HI(P); } while(0)
 Set port(s) as input.
 
#define P_DDR_OUT(P)   do { if (LO(P)) DDRA |= LO(P); if (HI(P)) DDRB |= HI(P); } while(0)
 Set port(s) as output.
 

MCU Types ...

#define MCU_TYPE
 MCU type compiled for; is set to any 'BR_MCU_*' value (see BR_MCU_NONE and friends).
 

Macro Definition Documentation

◆ INIT

#define INIT (   NAME,
  VAL 
)    if ((VAL) != 0) NAME = (VAL);

Variable assignment for '*Init()' functions which are only called once on startup.

This is to wrap normal variable assignemts, which are optimized out if the value is zero (BSS initialization has done the job already).

Definition at line 134 of file base.h.

Function Documentation

◆ TimerNow()

uint16_t TimerNow ( )

Get current time in ticks.

One tick is approx. 1ms (1024µs for a calibrated clock). => Counter overflow is in approx. one minute (~65 seconds). This function never returns 0 (= BR_TICKS_NEVER) (if necessary by rounding away from that value).

◆ CoreInit()

void CoreInit ( )

Initialize the module.

In this case, the timers and the "changed" register logic are initialized.

◆ CoreIterate()

static void CoreIterate ( )
inlinestatic

Iterate the module.

 This function is called regularly from the main event loop and may do some
 regular housekeeping.

Definition at line 295 of file core.h.

◆ CoreOnRegRead()

void CoreOnRegRead ( uint8_t  reg)

Update a register when it is read.

The ...OnRegRegRead() functions are called whenever the Brownie has received a "register read" request just before it is answered. The function must check if 'reg' references a register maintained by this module and may then update the register content. For example, the GPIO module reads the GPIO inputs then.

This function handles reads from the BR_REG_CHANGED register (i.e. auto-resets it on read) and reads from the timer registers (i.e. latches the 16-bit time value on access to BR_REG_TICKS_LO).

◆ CoreOnRegWrite()

void CoreOnRegWrite ( uint8_t  reg,
uint8_t  val 
)

Write a register.

The ...OnRegRegWrite() functions are called whenever the Brownie has received a "register write" request. The function must check, if 'reg' references a register maintained by this module and perform the repective action. Also, the passed value must be written into the respective register.

This function handles writes to the BR_REG_CHANGED register.