HALFRED
0.4.0
|
The most important IO module interface is the IODevice interface. It provides an abstraction of a generic input/output device general enough to represent peripherals such as UART or SPI allowing data buffering, signaling transaction events including transfer errors, utilizing DMA transfers and possible cooperation with an operating system.
IODevice implementation for each peripheral instance in use is provided by the port. Every IODevice shares the same unified application programming interface (API). This API is presented in table below. The picture shows a visualization of the abstract IODevice model.
![]() | ||
IODEV_Init | Initializes the IODevice | |
IODEV_Deinit | Deinitializes the IODevice | |
IODEV_IOCtl | Allows additional device-specific control | |
IODEV_EnableRead | Enables reception for the specified IODevice | |
IODEV_EnableWrite | Enables transmission for the specified IODevice | |
IODEV_DisableRead | Disables reception for the specified IODevice | |
IODEV_DisableWrite | Disables transmission for the specified IODevice | |
IODEV_GetReadCount | Gets the amount of data available for reading in the IODevice | |
IODEV_GetWriteSpace | Gets the amount of free buffering space in the IODevice | |
IODEV_Read | Reads data from IODevice | |
IODEV_Write | Writes data to IODevice | |
IODEV_Lock | Locks the IODevice to prevent concurrent access | |
IODEV_Unlock | Unlocks the IODevice |
Advantages of using IODevices.
The IODevice provides an unified interface for several different peripherals. It makes it possible for the application or component code to provide generic functions that rely on the realization of the IODevice interface (supplied by the port). While being generic, the IODevice interface provides means to implement rich I/O functionality, including operating system support.
Configuring and initializing IODevices.
IODevice objects are supplied by the port. In case of microcontrollers, the port usually supplies the actual objects. For example the AVR family port supplies IODevice objects named IO_USART0, IO_USART1 etc., depending on how many actual USART ports are available in a particular microcontroller. The port may also supply a function that dynamically creates the IODevices. This is the case for example in the PC ports, where a function is provided to create an IODevice for a particular serial port, since the number of available ports is not constant.
Once the IODevice object is available, it must be initialized before it is used. To do that IODEV_Init function must be called. The responsibility of this function is to generally prepare the IODevice for operation. This usually means configuring it's power supply and clocking options, preparing buffers etc. A complementary function IODEV_Deinit deinitializes the IODevice, bringing it back to the initial state, usually freeing all the possible resources taken.
The following example shows the init-deinit pattern.
Controlling the flow of data.
Application can enable or disable the receive or transmit path for the IODevice. By default the both signal paths should be disabled, and must be enabled prior use. Calling IODEV_EnableWrite enables the transmit path, which means that the data written to the IODevice will be available to the outside world. Similarly, calling IODEV_EnableRead enabled the IODevice to receive input from the outside world. Disabling the transmit path by calling IODEV_DisableWrite results in no output from the microcontroller. That means there is no data flow. However, it does not mean that application cannot write to the IODevice. The application can write to it as long as there is enough buffering space. Application may check that space it by calling IODEV_GetWriteSpace. This is particularly useful when the underlying driver supports buffering, where data can be prepared before it is actually sent, especially if the preparing time is quite long. When disabling the receive path by calling IODEV_DisableRead, the external data will not be received any longer. However, the data that came before the disable call, but have not been processed can still be read from the IODevice. The IODEV_GetReadCount function always shows how much data is left in the IODevice to read.
Please note, that all the parameters and result values that indicate the amount of data are expressed as number of elements not bytes. In some cases a single element may be larger than one byte. One example is using 9-bit UART frames. In this case the IODevice implementation may expect a 16-bit element size. Please consult the documentation for the particular IODevice supplied by the port.
Writing to an IODevice.
The IODevice interface supplies only one function for writing called IODEV_Write. This function writes data to the IODevice. If the transmit path of the IODevice is enabled (see IODEV_EnableWrite) then the data is immediately available at the output. If the transmit path is disabled (by default or by the call to IODEV_DisableWrite) then the data is written to a possible internal IODevice output buffer. Note however, that not all IODevices have the capability to queue output data. For example, many IODevice implementations working in polling mode will not be able to buffer the data, unless the hardware supports this, as they operate directly on the peripheral registers. The available buffering capability can always be checked by a call to IODEV_GetWriteSpace.
The following example shows an efficient way of sending a data array. The example assumes that the IODevice named "iodev" is already initialized, and the "arraySize" is the size of the "array" expressed in characters.
Reading from an IODevice.
Similar to the case of writing, the IODevice interface supplies only one function for reading called IODEV_Write. This function reads data from the IODevice. If the receive path of the IODevice is enabled (see IODEV_EnableRead) then the data comming from the outside world is immediately made available for reading. If the receive path is disabled (by default or by the call to IODEV_DisableRead) then the no new data can be gathered from the outside world. However the data acquired prior to the disable call is still available for reading depending on the buffering capabilities of the particular IODevice. Note however, that not all IODevices have the capability to queue input data. For example, many IODevice implementations working in polling mode will not be able to buffer the data, unless the hardware supports this, as they operate directly on the peripheral registers. In such case the data must be usually read before new data comes. The size of the data available for reading can always be checked by a call to IODEV_GetReadCount.
The following example shows the read pattern.
Timeouts.
Both the IODEV_Write and IODEV_Read functions have a timeout parameter. This parameter is used when operating system is present (see HAL_ENABLE_OS in HAL configuration file) and when OS integration mode is enabled for the IO module (see HAL_IO_OS_INTEGRATION definition in HAL configuration file). In this case the IO module can use OS module and implement the timeout mechanism based on the OSNotifier interface.
If OS integration mode is not available, the timeout parameter is omitted and should be set to 0 in all calls to IODEV_Read and IODEV_Write. If OS integration mode is enabled, the timeout parameter for IODEV_Read calls is used to specify how long should the function wait for the given amount of data. The same applies to IODEV_Write, where the timeout parameter is used to specify how long to wait until the given amount of data is actually sent out. Specifying timeout parameter as 0 means that there is no timeout applied and the operation may finish at any given time (may even possibly take forever to execute).
The timeout mechanism relies on the port implementation of the IODevice interface to properly signal write and read events. For details please refer to IODevice internals.
Please also note, that in OS integration mode every read and write operation with non-zero timeout require a dedicated OSNotifier interface instance. The HAL_OS_NOTIFIER_POOL_SIZE parameter in HAL configuration file specifies how many OS notifier instances are available to the application.
Concurrent access and thread-safety.
The IODevice API calls are not immediately thread safe and care should be taken not to let concurrent access to the IODevice. To make it easier, each IODevice has a built-in lock-unlock functionality utilizing the OSMutex interface. The basic pattern is to lock the IODevice (including checking if the lock was successful) before usage, and then unlock it. The following code shows such example
Typedefs | |
typedef OSTime | IOTime |
typedef struct IODeviceDesc * | IODevice |
Functions | |
HALRESULT | IODEV_Init (IODevice iodevice) |
HALRESULT | IODEV_Deinit (IODevice iodevice) |
HALRESULT | IODEV_IOCtl (IODevice iodevice, unsigned int request,...) |
HALRESULT | IODEV_EnableWrite (IODevice iodevice) |
HALRESULT | IODEV_DisableWrite (IODevice iodevice) |
HALRESULT | IODEV_EnableRead (IODevice iodevice) |
HALRESULT | IODEV_DisableRead (IODevice iodevice) |
size_t | IODEV_GetReadCount (IODevice iodevice) |
size_t | IODEV_GetWriteSpace (IODevice iodevice) |
size_t | IODEV_Read (IODevice iodevice, void *data, size_t size, IOTime timeout) |
size_t | IODEV_Write (IODevice iodevice, const void *data, size_t size, IOTime timeout) |
int | IODEV_Lock (IODevice iodevice, unsigned int timeout) |
void | IODEV_Unlock (IODevice iodevice) |
typedef OSTime IOTime |
Definition of time type used in IO module. This evaluates to OSTime if HAL_IO_OS_INTEGRATION is defined or simple unsigned int otherwise.
typedef struct IODeviceDesc* IODevice |
The actual definition of an IODevice type. This is actually a pointer to a private structure IODeviceDesc. For details see IODevice internals.
Initializes the hardware peripheral, represented by the the IODevice.
iodevice | handle of the IODevice |
Deinitializes the hardware peripheral, associated with the IODevice.
iodevice | handle of the IODevice |
Executes a low level control function on the provided IODevice.
iodevice | handle of the IODevice |
request | request code |
Enables the transmit path.
iodevice | handle of the IODevice |
Disables the transmit path.
iodevice | handle of the IODevice |
Enables the receive path.
iodevice | handle of the IODevice |
Disables the receive path.
iodevice | handle of the IODevice |
size_t IODEV_GetReadCount | ( | IODevice | iodevice | ) |
Returns the number of bytes available for reading from the device.
iodevice | handle of the IODevice |
size_t IODEV_GetWriteSpace | ( | IODevice | iodevice | ) |
Returns the number of bytes that can be written to the device.
iodevice | handle of the IODevice |
Reads a specified number of elements from the iodevice.
iodevice | handle of the IODevice |
data | pointer to the location where data will be stored |
size | size of data to read |
timeout | timeout value (in miliseconds). This parameter is ignored and should be set to 0 when OS integration is disabled. |
Referenced by TXTDEV_ReadString().
Writes a specified number of elements to the iodevice.
iodevice | handle of the IODevice |
data | pointer to the data to write |
size | size of the data to write (in bytes) |
timeout | timeout value (in miliseconds). This parameter is ignored and should be set to 0 when OS integration is disabled. |
Referenced by TXTDEV_ReadString(), TXTDEV_WriteData(), TXTDEV_WriteDate(), TXTDEV_WriteFLOAT(), TXTDEV_WriteINT32(), TXTDEV_WriteINT64(), TXTDEV_WriteNL(), TXTDEV_WriteString(), TXTDEV_WriteTime(), TXTDEV_WriteUINT32(), and TXTDEV_WriteUINT64().
int IODEV_Lock | ( | IODevice | iodevice, |
unsigned int | timeout | ||
) |
Locks access to a specified IODevice prohibiting concurrent access. This function is used to guard access to shared IODevice. After acquiring lock it must be released through a call to IODEV_Unlock. This function requires HAL_IO_OS_INTEGRATION to be set to non-zero value in hal_config.h
iodevice | handle of the IODevice |
timeout | maximum time waiting for device |
void IODEV_Unlock | ( | IODevice | iodevice | ) |
Unlocks access to a specified IODevice re-enabling access to it. This function requires HAL_IO_OS_INTEGRATION to be set to non-zero value in hal_config.h
iodevice | handle of the IODevice |