dart-archive / sdk

The Dartino project was an experiment seeking to improve productivity when writing application code for embedded devices.
https://dartino.org
Other
332 stars 36 forks source link

Why do we use objects for storing global state (like STM32F746GDiscovery)? #559

Open kasperl opened 8 years ago

kasperl commented 8 years ago

Right now, we insist on all users of important system wide properties to first create an instance of a specific class through something like new STM32F746GDiscovery(). Why?

Does it make sense for two such objects to co-exist within the same process? How much state does the objects you can grab hold from from the STM32F746GDiscovery instance have?

I think most users would prefer this kind of board-support library:

library stm32.stm32f746g_discovery;
final Gpio gpio = new STM32Gpio();
final FrameBuffer frameBuffer = new FrameBuffer();
...

or maybe even insist on making all the objects involved compile-time constants that hold any necessary state in static fields.

jakobr-google commented 8 years ago

The main 'board' should really be a singleton.

There's not much state in these objects (since most of it's held in the hardware), but it doesn't really make sense to have more than one instance (per hardware unit - one board might have 3 ADC units, for example).

I'm not sure I'd make them complete compile-time constants, though, since that'll tie the code that uses them to a specific implementation.

What I'd really like, is to be able to inject the concrete board-specific class at compile time, maybe through a project/hardware manifest, so the rest of the code can be as generic as possible, making it easier to support multiple boards with the same application code.

sigurdm commented 8 years ago

I think having a main library per board that passes a board-object to the rest of the program is the right way to 'inject' the dependency.

sgjesse commented 8 years ago

I agree that having board objects as instantiatable objects is not the right solution. Having more than one instance (of any board) in a given application does not make sense.

We might use Interface Libraries (configuration specific imports) to abstract over board differences to create a program that can run on several boards.

File board_interface.dart

import 'package:gpio/gpio.dart';
// Main LED.
GpioOutputPin get led {}

File board.dart

export 'board_interface.dart'
    if (board.stm32f746g_discovery) 'board_stm32f746g_discovery.dart'
    if (board.stm32f411re_nucleo) 'board_stm32f411re_nucleo.dart'
    show led;

File board_stm32f746g_discovery.dart

import 'package:stm32/stm32f746g_disco.dart';
// Use LED1 as main LED.
final led = gpio.initOutput(LED1);

File board_stm32f411re_nucleo.dart

import 'package:stm32/stm32f411re_nucleo.dart';
// Use LED2 as main LED.
final led = gpio.initOutput(LED2);

And the main program

import 'dart:dartino'
import 'board.dart'
main() {
  led.toggle();
  sleep(500);
}

The passing either -Dboard.stm32f746g_discovery or -Dboard.stm32f411re_nucleo at compile-time should make the main file compile for both boards.

If we get the board selection integrated into the compilation/project setup the the -D options should be set automatically when compiling from project configuration.

karlklose commented 8 years ago

Another solution to allow for generic programs is to make the board classes form a class hierarchy and have a factory constructor instatiate the concrete object using an environment constant. For example, new Stm32Board() would return either a Stm32F7DiscoveryBoard or Stm32F411RENucleoBoard (depending on the target platform specified), but a new Stm32F411RENucleoBoard would throw an error if the STM32F746G board is specified as target.