The Raspberry Pi Hardware Abstraction Layer (HAL) is used with the C/C++ bindings for microcontrollers to communicate with Bricklets over SPI.
This HAL was tested with the Raspberry Pi and Pi Zero with the HAT Brick or HAT Zero Brick. It will only work with Raspberry Pi compatible devices, that use the BCM2835 chip.
Note
The Raspberry Pi 3, 4 and Zero scale their clock speed dynamically. Unfortunately this also scales the SPI clock speed. You have lock the GPU core frequency to make sure, the SPI clock is stable. We recommend setting the core_freq in /boot/config.txt to 250. See here for details, especially for the Pi 4B, where the core_freq is influenced by other boot options.
If you select another core_freq than 250, you have to compensate by multiplying
BRICKLET_STACK_SPI_CONFIG_MAX_SPEED_HZ by 250 / [your core_freq in MHz].
This HAL includes an example driver sketch that can be used to run any example provided with the bindings, as well as a Makefile to compile any example. To use the Makefile, create the following folder layout:
[your main folder]/
example_driver.c [from the hal_raspberry_pi folder]
[copy the example .c file here]
Makefile
src/
bindings/
[copy the contents of the bindings folder here]
hal_raspberry_pi/
[copy the contents of the hal_raspberry_pi folder here]
After creating the folder structure, you have to modify the Makefile for your example:
List the device used in your selected example under SOURCES_DEVICES, for example for the Industrial Digital In 4 2.0 Bricklet
SOURCES_DEVICES := src/bindings/bricklet_industrial_digital_in_4_v2.c
and add the example source file itself to SOURCES_EXAMPLE, for example
SOURCES_EXAMPLE := example_edge_count.c
Next you have to modify the port assignment in the example driver to fit to your set-up (see this section). If you connect multiple Bricklets to the same SPI bus (this is only possible with a tri-state buffer chip), you have to connect all their chip select pins to the Board and list them in the port assignment, even if you don't want to communicate with the Bricklets yet. This makes sure the signals are correctly separated.
The HAT Brick or HAT Zero Brick already contain the required buffer chip and their port assignment is listed in the example driver.
You can then compile the program with make.
If you want to cross-compile from another machine, use make CROSS_COMPILE=[compiler-prefix]
for example make CROSS_COMPILE=arm-linux-gnueabihf- for the Raspberry Pi.
To run the compiled program, make sure that no Brick Daemon is running on the device and
start ./uc_example.
A port is specified as an instance of the TF_Port structure:
struct TF_Port {
int chip_select_pin;
char port_name;
}
The chip_select_pin is the pin that has to be pulled to be able to communicate to the port.
The port_name is a single character that identifies the port. The name is injected into the
result of tf_[device]_get_identity calls if the device is connected directly to the host.
The example_driver.c contains an example port specification.
Most functions of the HAL return an error code (e_code).
Possible error codes are (as defined in errors.h):
TF_E_OK = 0
TF_E_TIMEOUT = -1
TF_E_INVALID_PARAMETER = -2
TF_E_NOT_SUPPORTED = -3
TF_E_UNKNOWN_ERROR_CODE = -4
TF_E_STREAM_OUT_OF_SYNC = -5
TF_E_INVALID_CHAR_IN_UID = -6
TF_E_UID_TOO_LONG = -7
TF_E_UID_OVERFLOW = -8
TF_E_TOO_MANY_DEVICES = -9
TF_E_DEVICE_NOT_FOUND = -10
TF_E_WRONG_DEVICE_TYPE = -11
TF_E_LOCKED = -12
TF_E_PORT_NOT_FOUND = -13
TF_E_NULL = -14
TF_E_DEVICE_ALREADY_IN_USE = -15
TF_E_WRONG_RESPONSE_LENGTH = -16
TF_E_NOT_INITIALIZED = -17
The HAL defines the following further error codes:
TF_E_BCM2835_INIT_FAILED = -100
TF_E_BCM2835_SPI_BEGIN_FAILED = -101
Use tf_hal_strerror() to get
an error string for an error code.
Creates a HAL context that can be used to list the available devices. It is also required for the constructor of Bricks and Bricklets.
ports is an array of port specifications, as described here
port_count is the length of the ports array.
Destroys the given TF_HAL.
Sets the timeout in microseconds for getters and for setters for which the response expected flag is activated.
The default timeout is 2500000 (2.5 seconds).
Returns the timeout as set by tf_hal_set_timeout().
Returns the UID, port and device identifier of the nth (=index) detected device.
This function returns TF_E_DEVICE_NOT_FOUND if the index was equal or higher than the number of detected devices.
To list all devices you can call this function in a loop with growing index until TF_E_DEVICE_NOT_FOUND is returned once.
Polls for callbacks on all devices with an registered callback handler. Will block for the given timeout in microseconds.
This function can be used in a non-blocking fashion by calling it with a timeout of 0. The bindings will then poll a single device for one callback, by clocking out a single byte over SPI, returning immediately if no callback is available. If the device starts sending a callback packet, it will be received, acked and the callback handler will be called.
The polling is scheduled round-robin over multiple calls, so even if you only poll with a timeout of 0, all devices will be polled as fairly as possble.
Returns true if the deadline in microseconds is elapsed, false otherwise. Robust against
overflows up to UINT32_MAX / 2.
Returns the error counters for the given port. The following errors are counted:
spitfp_error_count_checksum: Received SPITFP packets that where ignored because of a wrong checksum
spitfp_error_count_frame: Received SPITFP packets with an invalid length
tfp_error_count_frame: Received TFP packets with an invalid length
tfp_error_count_unexpected: Received TFP packets that where unexpected because they responded to unknown requests
Logs an error if the log level in bindings/config.h is TF_LOG_LEVEL_ERROR or more.
Supports a subset of the standard printf syntax. See tf_hal_printf() for details.
Logs an information if the log level in bindings/config.h is TF_LOG_LEVEL_INFO or more.
Supports a subset of the standard printf syntax. See tf_hal_printf() for details.
Logs a debug message if the log level in bindings/config.h is TF_LOG_LEVEL_DEBUG or more.
Supports a subset of the standard printf syntax. See tf_hal_printf() for details.
This function is a minimalistic printf implementation. The following placeholders are supported:
%[prefix]u: An unsigned integer value printed in base 10
%[prefix]d: A signed integer value printed in base 10
%[prefix]b: An unsigned integer value printed in base 2
%[prefix]x and %[prefix]X: An unsigned integer value printed in base 16, in both cases with lower-case letters
%c: A single character
%s: A zero terminated string
%%: A percent sign
With the prefix, you can control the width of printed integers.
Valid prefixes are I8, I16, I32 and I64.
For example using the placeholder %I16x will print a 16 bit integer hexadecimal.
No padding, grouping , l-modifiers or similar, or floats are supported.
The newline character \n is translated to the platform specific newline character(s).
Returns an error string for the given error code.
Returns the display name for the given device identifier.