Communication Protocols for Display Modules

Bit-Banged Implementation and Hardware-Based Implementation

For some microcontrollers, the CowPi_stdio library has implementations of SPI and of I2C that make use of the microcontroller’s SPI hardware and I2C hardware. For all microcontrollers, the CowPi_stdio library has bit-banged (i.e., software-only) implementations of SPI and of I2C. The latter is useful for rapidly porting the library to new microcontrollers, while the former is useful for reducing the size of the program and having faster communication with the display module.

By default, the hardware-based implementation is used when available. Four functions are available to specify which implementations you wish to use:

void cowpi_use_spi_bitbang()

Assigns to cowpi_spi_initialize, cowpi_spi_transmit, and cowpi_spi_finalize pure software (“bit-banged”) implementations that do not use the microcontroller’s built-in SPI hardware.

Bit-banged implementations have no restrictions on the choice of data and clock pins.

bool cowpi_use_spi_hardware()

Assigns to cowpi_spi_initialize, cowpi_spi_transmit, and cowpi_spi_finalize implementations that use the microcontroller’s built-in SPI hardware.

Using the SPI hardware may limit the choice of data and clock pins. Attempting to use the SPI hardware while specifying pins that cannot be used by the SPI hardware may result in unexpected behavior.

The CowPi_stdio library does not have SPI hardware implementations for all microcontrollers. If a hardware implementation is unavailable, this function will return false and will assign a bit-banged implementation instead.

Returns:

true if hardware implementations have been assigned to the CowPi’s SPI function pointers

Returns:

false if hardware implementations have not been assigned to the CowPi’s SPI function pointers

void cowpi_use_i2c_bitbang()

Assigns to cowpi_i2c_initialize, cowpi_i2c_transmit, and cowpi_i2c_finalize pure software (“bit-banged”) implementations that do not use the microcontroller’s built-in I2C hardware.

Bit-banged implementations have no restrictions on the choice of data and clock pins.

bool cowpi_use_i2c_hardware()

Assigns to cowpi_i2c_initialize, cowpi_i2c_transmit, and cowpi_i2c_finalize implementations that use the microcontroller’s built-in I2C (aka IIC, aka TWI) hardware.

Using the I2C hardware may limit the choice of data and clock pins. Attempting to use the I2C hardware while specifying pins that cannot be used by the I2C hardware may result in unexpected behavior.

The CowPi_stdio library does not have I2C hardware implementations for all microcontrollers. If a hardware implementation is unavailable, this function will return false and will assign a bit-banged implementation instead.

Returns:

true if hardware implementations have been assigned to the CowPi’s I2C function pointers

Returns:

false if hardware implementations have not been assigned to the CowPi’s I2C function pointers

No Protocol

Principally, the NO_PROTOCOL choice is there to be a default value to detect that the application programmer failed to chose SPI or I2C. The MORSE_CODE “display,” however, legitimately uses neither SPI nor I2C. If the protocol is NO_PROTOCOL and the display module is any option other than MORSE_CODE, then cowpi_add_display_module() will return NULL.

SPI

The Serial-Parallel Interface (SPI) protocol requires that the cowpi_display_module_protocol_t.data_pin, clock_pin, and the select_pin fields be specified; however, the data_pin and the clock_pin have default values, meaning that only the select_pin must be specified in all cases.

  • If the default bit-banged implementation is used, then the data and clock pins can be any available pins.

  • If the microcontroller’s SPI hardware is used, then your choice of data and clock pins may be limited.

  • The select_pin can still be any available pin, regardless of the implementation being used; this gives you the option of having multiple SPI devices.

Even if the bit-banged implementation is used, the default data_pin and clock_pin values are the pins that the microcontroller’s hardware uses (or uses by default when the hardware allows options).

For HD44780-based LCD character displays, the adapter_mapping field may also be specified; its default value is COWPI_DEFAULT.

Terminology

The data pin historically has been called MOSI (there is also MISO, but not for the purposes of this library) from the “master/slave” paradigm. In 2020, the Open Source Hardware Association (OSHWA) proposed changing this to SDO (Serial Data Out) for devices that are strictly data-out on this pin, and to COPI (Controller-Out/Peripheral-In) for devices whose pin direction changes depending on their role as controller or peripheral. In 2022, OSHWA changed its proposal to SDO and PICO (Peripheral-In/Controller-Out) after discovering that the abbreviation for Controller-In/Peripheral-Out is a vulgar and offensive word in some parts of the world. The clock pin has been, and continues to be, referred to as SCK or CLK. The select pin historically has been called SS, and the OSHWA’s proposal renames it as CS (Chip Select). Another common re-definition of MOSI and MISO use the terms “main” and “sub-node”. NXP Semiconductor, which owns Motorola’s intellectual property that covers SPI, has adopted COTI, CITO, and TS, using the terms “controller” and “target”.

We prefer the terms “controller” and “peripheral”, as we think these are the most-descriptive terms for the components. Unfortunately, the potential for confusion with the “Pico” shorthand for “Raspberry Pi Pico” dissuades us from using PICO. Fortunately, this library uses the data pin in only one direction, and so we may refer to it as SDO but will typically refer to it as the “data pin”.

As of June 2023, Arduino has adopted “COPI” on the hardware side but still uses “MOSI” on the software side. The Raspberry Pi Pico uses “TX” (Transmit). Legacy datasheets still have the legacy terminology.

I2C

The Inter-Integrated Circuit (I2C or IIC) protocol, also known as the Two-Wire Interface (TWI) protocol, requires that the data_pin (SDA), clock_pin (SCL) and i2c_address fields be specified; however, the data_pin and the clock_pin have default values, meaning that only the i2c_address must be specified in all cases.

  • If the default bit-banged implementation is used, then the data and clock pins can be any available pins.

  • If the microcontroller’s I2C hardware is used, then your choice of data and clock pins may be limited.

  • You have the option of having multiple I2C devices if the devices have different addresses.

Even if the bit-banged implementation is used, the default data_pin and clock_pin values are the pins that the microcontroller’s hardware uses (or uses by default when the hardware allows options).

For HD44780-based LCD character displays, the adapter_mapping field may also be specified; its default value is COWPI_DEFAULT.

Specifying the Peripheral’s I2C Address

When specifying the display module’s’s I2C address, you may, of course, hard-code the address if you know it. Alternatively, if only one peripheral is on the I2C bus, then you may use the cowpi_discover_i2c_address() function inline to assign the address; see the scan_i2c example for a demonstration of cowpi_discover_i2c_address()’s functionality.

int8_t cowpi_discover_i2c_address(uint8_t i2c_data_pin, uint8_t i2c_clock_pin)

Reports the address of the I2C peripheral if there is exactly one I2C peripheral attached to the designated pins.

Parameters:
  • i2c_data_pin – the I2C serial data pin

  • i2c_clock_pin – the I2C serial clock pin

Returns:

  • 0 if no I2C peripherals are detected

  • -1 if multiple I2C peripherals are detected

  • the peripheral’s I2C address otherwise

Note

If there are multiple peripherals on the I2C bus then cowpi_discover_i2c_address() does not return a usable address. (Similarly, if there are no peripherals on the I2C bus, then it does not return a usable address, either.)

If you need to determine the addresses of multiple peripherals, then we recommend that you run the Arduino Wire library’s i2c_scanner example to print the addresses of all devices on the I2C bus.

Terminology

While the I2C lines don’t reference the “master/slave” paradigm – they are SDA (“Serial DAta”) and SCL (“Serial CLock”), legacy datasheets use the “master/slave” terminology when describing the components’ roles. NXP Semiconductor, which owns Philip Semiconductor’s intellectual property that covers I2C, has adopted the terms “controller/target”. OSHWA has not proposed any changes.

As with SPI, we prefer the terms “controller” and “peripheral”, as we think these are the most-descriptive terms for the components.