Skip to content

Creating custom board variant

paradajz edited this page Feb 18, 2024 · 100 revisions

OpenDeck firmware uses YAML files as descriptors of the entire board. YAML file specifies what hardware the board uses, what is supported on board (displays, DIN MIDI, I/O etc.) and which pins on MCU are used for what. This document will describe all available options when creating new board variant.

File location

YAML descriptor file should be placed in config/target directory in root directory of the OpenDeck repository. The file name represents variant name and is also used in build process (make TARGET=yaml_file_name_without_extension). YAML files must start with three dashes followed by new line, eg.:

---
key: value

In OpenDeck descriptor files, value can be string, in which case double quotation marks are needed ("value"), boolean (true or false) or integer values (0, 1 etc.).

MCU

For MCU definition, only mcu key is used. Check config/mcu directory for supported microcontrollers. The value of the key can be any filename present in mentioned directory without extension and with quotes, eg. "stm32f405rg", "atmega2560" etc. For more details on supported microcontrollers, check the dedicated wiki page.

Additional important key for MCU is extClockMhz. This key defines the value of crystal in Mhz. The value is integer. For AVR MCUs, the only allowed value is 16. For STM32 MCUs, the following values are supported:

  • 8
  • 12
  • 16
  • 25

Make sure to enter the actual value of crystal connected to the MCU.

Example to configure ATmega2560 MCU:

  mcu: "atmega2560"
  extClockMhz: 16

Peripherals

Keys listed below can be used to define all MCU peripherals used on custom board variant. All peripheral keys are optional. If any of the keys below is omitted in the YAML file, omitted peripheral will not be initialized and used.

  • usb - Used to define whether or not the board has USB connection. Boolean. Allowed values are:
    • true
    • false
  • uart - Used to configure various supported UART peripherals:
    • dinMIDI - Used to define whether or not the board has DIN MIDI connection. If unused, don't specify at all. This key has the following sub-key:
      • channel - Specifies UART channel used for DIN MIDI transmitter. Integer. See Supported microcontrollers page for availability of specific channels for specified MCU.
    • touchscreen - Used to define whether or not the board supports UART touchscreen connection. If unused, don't specify at all. This key has the following sub-keys:
      • channel - Specifies UART channel on MCU on which touchscreen is connected to. Integer. See Supported microcontrollers page for availability of specific channels for specified MCU.
      • components - Total number of supported touchscreen component. Touchscreen component can be button, analog input and indicator, all at the same time. Integer.
  • i2c - Used to configure various supported I2C peripherals:
    • display - Used to specify that the board support I2C displays. If unused, don't specify at all. This key has the following sub-key:
      • channel - Specifies I2C channel used for display. Integer. See Supported microcontrollers page for availability of specific channels for specified MCU.

In the example below, USB is enabled, DIN MIDI is used on channel 2, display is used on I2C channel 0 and touchscreen is disabled:

  usb: true
  uart:
    dinMIDI:
      channel: 2
  i2c:
    display:
      channel: 0

Note: If DIN MIDI or touchscreen share the same UART channel, only one of those peripherals can be used at any given time. I2C peripherals don't have this restriction as many peripherals can share the same channel.

Buttons

There are several supported ways using which buttons can be connected to the boards running OpenDeck firmware. This is described using type sub-key:

  • type - String.
    • "native" - Buttons are connected directly to MCU pins.
    • "shiftRegister" - 74HC165 shift register(s) are used to read button states.
    • "matrix" - Matrix setup is used with 74HC165 shift register to read rows and 74HC138 decoder to select active column.

Native

If "native" type for buttons is used, buttons are connected directly to MCU pins. The following argument can be used for buttons if this type is specified:

  • extPullups - Specifies whether to use external pull-up resistors or not. Boolean. If set to false or not specified, internal pull-ups on MCU will be used. Used only if type sub-key has value "native", otherwise it's ignored.

  • pins - List of pins on which buttons are connected to MCU. Each pin has port and index keys.

Complete example of configuration in this mode with 2 buttons:

 buttons:
   type: "native"
   extPullups: true
   pins:
   -
     port: "B"
     index: 15
   -
     port: "B"
     index: 14

Shift register

In this mode, 74HC165 shift registers are used to read button states. The following sub-keys are needed:

  • shiftRegisters - Specifies the amount of connected registers. 74HC165 registers can be daisy-chained by connecting DS pin (pin 10) from one shift register to Q7 pin (pin 9) to another. Maximum amount of registers in this setup isn't specified, but reasonable number shall be used since time needed to process all the buttons increases with added amount of registers.

  • pins - Specifies pins on which 74HC165 registers are connected to MCU. Each pin has port and index keys. Required pins are the following:

    • data
    • clock
    • latch

Complete configuration example of board which uses 2 shift registers for a total of 16 buttons:

  buttons:
    type: "shiftRegister"
    shiftRegisters: 2
    pins:
      data:
        port: "D"
        index: 3
      clock:
        port: "D"
        index: 4
      latch:
        port: "D"
        index: 5

Note: Even if multiple shift registers are used, only 3 pins are required on MCU.

Matrix

In matrix setup, row scanning is used. 74HC165 register is used to read all rows (therefore maximum amount of supported rows is currently 8) and 74HCC138 decoder is used to control columns in rows (therefore maximum amount of supported columns is currently 8). 8x8 matrix gives a total of 64 buttons. In firmware, it takes 500μs to scan entire matrix, so switching frequency is 2kHz.

The following sub-keys are needed in this setup:

  • rows: Defines total number of rows. Integer. Maximum is 8.
  • columns: Defines total number of columns. Integer. Maximum is 8.

Following pins need to be configured in this setup:

  • Rows: 74HC165 shift register is used with the following pins:
    • data
    • clock
    • latch
  • Columns: 74HC138 decoder is used with the following pins
    • decA0
    • decA1
    • decA2

Complete configuration example of board which uses 6 rows / 8 columns button matrix setup for a total of 48 buttons:

  buttons:
    type: "matrix"
    rows: 6
    columns: 8
    pins:
      data:
        port: "G"
        index: 2
      clock:
        port: "G"
        index: 1
      latch:
        port: "G"
        index: 0
      decA0:
        port: "L"
        index: 2
      decA1:
        port: "L"
        index: 1
      decA2:
        port: "L"
        index: 0

LEDs

There are two groups of LEDs which can be configured on OpenDeck boards: internal and external. Internal LEDs are used to configure integrated LEDs on board which are used to indicate data traffic. For more details about types of traffic and the behavior of indicators, see LED Indicators page. External LEDs are LEDs which are connected to the MCU by user. In both modes, the following key can be used:

  • invert - Specifies whether to use inverted logic to control LEDs. Boolean. Optional. If set to true, high logic signal is used to turn the LED off. If set to false or omitted, high logic signal is used to turn the LED on.

External LEDs

Keys to configure external LEDs go under external key for LED configuration in YAML file.

There are several supported ways using which external LEDs can be connected to the boards running OpenDeck firmware. This is described using type sub-key:

  • type - String.
    • "native" - LEDs are connected directly to MCU pins.
    • "shiftRegister" - 74HC595 shift register(s) are used to set LED states.
    • "matrix" - Matrix setup is used with 74HC238 decoder and ULN2803 Darlington array to control columns and mosfets are connected directly to MCU pins.

Native

If "native" type for LEDs is used, LEDs are connected directly to MCU pins.

The following method of specifying pins on which LEDs are connected to MCU shall be used in this mode:

pins:
-
  port: "A"
  index: 0
-
  port: "B"
  index: 0

Complete example of external LED configuration in this mode with 2 LEDs:

 leds:
   external:
     type: "native"
     invert: false
     pins:
     -
       port: "B"
       index: 15
     -
       port: "B"
       index: 14

Shift register

In this mode, 74HC595 shift registers are used to set LED states. The following sub-keys are needed:

  • shiftRegisters - Specifies the amount of connected registers. 74HC595 registers can be daisy-chained by connecting Q7S pin (pin 9) from one shift register to DS pin (pin 14) to another. Maximum amount of registers in this setup isn't specified, but reasonable number shall be used.

  • pins - Specifies pins on which 74HC595 registers are connected to MCU. Each pin has port and index keys. Required pins are the following:

    • data
    • clock
    • latch
    • enable

Note: Pin 10 on all connected shift registers (Reset) must be connected to Vcc.

Complete configuration example of board which uses 2 shift registers for a total of 16 LEDs:

  leds:
    external:
      type: "shiftRegister"
      shiftRegisters: 2
      invert: false
      pins:
        data:
          port: "D"
          index: 2
        clock:
          port: "D"
          index: 0
        latch:
          port: "D"
          index: 1
        enable:
          port: "C"
          index: 7

Note: Even if multiple shift registers are used, only 4 pins are required on MCU.

Matrix

In matrix setup, row scanning is used. 74HC238 decoder is used in combination with ULN2803A sink to control columns (therefore maximum amount of supported columns is currently 8). Row pins are connected either directly to MCU or via MOSFETS (recommended to avoid sourcing current from MCU). In both cases, single resistor should be used between MCU pin and row. Small value is recommended (100 ohms or less). Column is switched every 1ms, which translates to 125Hz switching frequency.

The following sub-keys are needed in this setup:

  • rows: Defines total number of rows. Integer.
  • columns: Defines total number of columns. Integer. Maximum is 8.

Following pins need to be configured in this setup:

  • Rows: All MCU pins on which rows are connected shall be listed here
  • Columns: 74HC238 decoder is used with the following pins:
    • decA0
    • decA1
    • decA2

Complete configuration example of board which uses 6 rows / 8 columns LED matrix setup for a total of 48 LEDs:

  leds:
    external:
      type: "matrix"
      columns: 8
      rows: 6
      pins:
        decA0:
          port: "L"
          index: 5
        decA1:
          port: "L"
          index: 4
        decA2:
          port: "L"
          index: 3
        rows:
          -
            port: "H"
            index: 3
          -
            port: "H"
            index: 4
          -
            port: "H"
            index: 5
          -
            port: "E"
            index: 3
          -
            port: "E"
            index: 4
          -
            port: "E"
            index: 5

MAX7219

MAX7219 is a LED driver which can drive up to 64 LEDs individually. It's controlled via SPI, so it requires only 3 pins on MCU. The LED connections are arranged in 8 rows and 8 columns (matrix setup). A single resistor is used to control the current across all the connected LEDs. Recommended value is 10k. For more details on this driver, consult the datasheet.

The following sub-keys are needed in this setup:

  • drivers - Specifies the number of daisy chained MAX7219 drivers.
  • pins - Specifies pins on which MAX7219 driver is connected to MCU. Each pin has port and index keys. Required pins are the following:
    • data
    • clock
    • latch

Notes:

  • If your custom board uses both MAX7219 for LEDs and 74HC165 for inputs, the clock pins can be shared between the two.
  • MAX7219 supports 15 different brightness levels, however, the level is applied globally for all LEDs - individual brightness control is not possible. OpenDeck firmware currently hardcodes this level to 10 with no ability to configure it. This might change in the future firmware releases.

Complete configuration example of board which uses MAX7219 driver:

  leds:
    external:
      type: "max7219"
      drivers: 1
      invert: false
      pins:
        data:
          port: "1"
          index: 8
        clock:
          port: "0"
          index: 4
        latch:
          port: "1"
          index: 11

Internal LEDs

Keys to configure internal LEDs go under internal key for LED configuration in YAML file.

If no internal LEDs are used on board, this section can be omitted. Otherwise, the following keys shall be set:

  • present - Specifies whether internal LEDs are used or nor. Boolean. If set to true, internal LEDs are used.
  • pins - Specifies pins on internal LEDs are connected. Pins are split into several groups:
    • usb - Specifies LEDs used to indicate USB traffic
    • uart - Specifies LEDs used to indicate UART traffic
    • ble - Specifies LEDs used to indicate BLE traffic

Each group is further split into following groups:

  • rx - Defines LED used to indicate incoming traffic.
  • tx - Defines LED used to indicate outgoing traffic.

rx and tx groups require port and index keys to define pin.

Complete configuration example of board which uses internal LEDs:

  leds:
    internal:
      invert: true
      pins:
        usb:
          rx:
            port: "0"
            index: 24
          tx:
            port: "0"
            index: 16
        uart:
          rx:
            port: "0"
            index: 24
          tx:
            port: "0"
            index: 16
        ble:
          rx:
            port: "0"
            index: 24
          tx:
            port: "0"
            index: 16

Note: same pins can be used for defining different traffic type indicators.

Analog

There are several supported ways using which analog components can be connected to the boards running OpenDeck firmware. This is described using type sub-key:

  • type - String.
    • "native" - Analog components are connected directly to MCU pins.
    • "4067" - Analog components are connected to 4067 analog multiplexer(s).
    • "4051" - Analog components are connected to 4051 analog multiplexer(s).

The following keys can be used regardless of analog component type:

  • extReference - Specifies whether the MCU uses built-in analog reference voltage or external one. Boolean. If set to true, external voltage reference will be used. If set to false or if omitted, internal reference voltage will be used.
  • filter sub-key:
    • median - Specifies whether median filter is used for analog values. If set to false, median filter isn't used. If set to true or omitted, filter is used. Median filter works by sampling 3 analog values and picking the middle one. This is, however, used only when analog component is still for a while. On AVR boards this filter can introduce lag when larger amount of analog inputs is used, so it can be disabled (which will also save some RAM as an consequence).
    • ema - Specifies whether exponential moving average filter is used for analog values. If set to false, EMA filter isn't used. If set to true or omitted, filter is used. EMA filter works by making the current (new) value depend on old one. This smooths the response coming out of ADC. As with median filter, disabling this will reduce the RAM usage (which is mostly a concern on AVR boards).

Native

If "native" type for buttons is used, analog components are connected directly to MCU pins. The following keys shall be used in this mode:

  • pins - List of pins on which buttons are connected to MCU. Each pin has port and index keys.

Complete example of configuration in this mode with 2 analog components:

 analog:
   type: "native"
   extReference: true
   filter:
     median: true
     ema: true
   pins:
   -
     port: "F"
     index: 0
   -
     port: "F"
     index: 1

4067

In this mode, 4067 analog multiplexers are used to connect analog components. The following keys shall be used in this mode:

  • multiplexers - Specifies amount of used multiplexers. Integer. Maximum amount of multiplexers in this setup isn't specified, but reasonable number shall be used since time needed to process all the analog components increases with added amount of multiplexers.

Following pins need to be configured in this setup:

  • s0 - Select pin 0 on multiplexer.
  • s1 - Select pin 1 on multiplexer.
  • s2 - Select pin 2 on multiplexer.
  • s3 - Select pin 3 on multiplexer.
  • z0 - Common multiplexer pin which is connected to analog input on MCU. Amount of these pins depends on specified number of multiplexers. If 2 multiplexers are used, z1 shall be defined as well. For three, z2 must be specified etc.

Note: Select pins (sx) are shared between all multiplexers.

Complete example of configuration in this mode with 2 4067 multiplexers:

analog:
  extReference: true
  filter:
    median: true
    ema: true
  type: "4067"
  multiplexers: 2
  pins:
    s0:
      port: "B"
      index: 3
    s1:
      port: "B"
      index: 1
    s2:
      port: "E"
      index: 6
    s3:
      port: "B"
      index: 2
    z0:
      port: "F"
      index: 7
    z1:
      port: "F"
      index: 6

4051

Exactly the same as 4067, but without s3 pin being defined.

Mux on mux

In this setup, one 4051 or 4067 multiplexer is used as an "controller" on which more multiplexers ("nodes") are connected. Analog output from each multiplexer node is connected to a input on controller multiplexer. This setup can be used on MCUs with very limited amount of analog inputs, such as RP2040. This setup requires only one analog pin, however, it requires 4 pins for select pins on 4067 multiplexer nodes and up to 4 pins for select pins on controller. At least 2 select pins need to be defined for controller multiplexer. Below is an example of circuit which uses a single 4051 multiplexer to control 4 4067 multiplexers.

Example of configuration using this connection scheme:

  analog:
    type: "muxonmux"
    pins:
      controller:
        s0:
          port: "0"
          index: 26
        s1:
          port: "0"
          index: 27
        z:
          port: "0"
          index: 28
      nodes:
        s0:
          port: "0"
          index: 24
        s1:
          port: "0"
          index: 25
        s2:
          port: "0"
          index: 23
        s3:
          port: "0"
          index: 22

A note on multiplexers

While most 4051/4067 multiplexers work with 3.3V signaling, some may not. In this case, their control pins need to be controlled with 5V and the multiplexer needs to be powered with 5V as well. Since 3V/3.3V systems such as STM32, nRF52 or RP2040 cannot output 5V, level translator might be required. Below is an example of circuit that can be used to convert 3V/3.3V Sx signals from MCU to 5V signals needed for multiplexer. The circuit uses 74HCT125 IC (not to be confused with 74HC125). IMPORTANT: Even if circuit below is used, make sure never to connect analog signals to 5V if the MCU works with 3V/3.3V! Maximum voltage from analog signal should never go beyond the ADC reference voltage.

Unused / fixed state pins

Unused or fixed state pins are pins which are initialized on board power on, but aren't used in firmware for any specific function. To define unused pin, unused-io key is used. If this key is specified, a list of unused pins and its states is required. Each list entry requires pin port, pin index and mode in which the specified pin should be configured. These modes are the following:

  • out-low (Output / low)
  • out-high (Output / high)
  • in-pull (Input / pull-up)

Example configuration:

unused-io:
  -
    port: "H"
    index: 5
    mode: "out-low"
  -
    port: "H"
    index: 6
    mode: "out-high"
  -
    port: "H"
    index: 7
    mode: "in-pull"

Examples

For examples of different configurations, check the config/target directory in repository. For examples of schematics used in actual controllers, including the schematic of the official OpenDeck board, check the bin/sch directory.

Building custom configuration

If the configuration is correctly specified, it can be built using the make build system. For example, if the configuration named my_custom_conf.yml exists in config/target directory, in order to build it, execute the following command from the root directory of the repository:

make TARGET=my_custom_conf

Important: Each time YAML file is changed, make clean should be re-issued so that everything is properly re-generated.

Build process creates binaries in build directory. For more information about building the firmware, check Building OpenDeck code wiki page. For instructions on how to flash compiled binary to the board, follow this page.

Clone this wiki locally