目次
Middleware
What is middleware?
In this article, I will explain about middleware. Middleware is software located in the middle of a real-time OS and an application. Specifically, it is a module that adds functions to a real-time OS, such as a file system or network protocol stack (TCP/IP). Device drivers may not be included in middleware in some cases, but since they have a lot in common, we will include them here.
In embedded systems development, applications are developed on top of a real-time OS and middleware, but because the hardware of an embedded device is generally specific to that device, it is not uncommon for users to develop and port the middleware and device drivers themselves by themselves. In this article, the operation and mechanism of device drivers in T-Kernel will be explained in the first half of this article, and T2EX, an open-source basic middleware developed for T-Kernel, will be discussed in the second half.
Calling Device Drivers
What is a device driver
Embedded devices, for example, include a variety of devices as shown in Table 1. The software that controls the hardware of such devices is called a device driver (hereinafter referred to simply as “driver”). Each driver is called by the application that controls the entire embedded device (*1).
(*1) Drivers are called not only by applications but also by other middleware, but the concept is the same, so we will use an example of a case where the driver is called by an application.
(a) Input Devices | Buttons, touch panels, microphones, etc. |
---|---|
(b) Output Device | LEDs, LCD screens, speakers, etc. |
(c) Communication devices | Wireless LAN, Bluetooth, serial port (RS-232C), etc. |
(d) Storage device | Flash memory, hard disk, etc. |
Creating Device Driver Specifications
In addition to the application program, you may need to develop the device driver itself in addition to the application program because embedded devices often control hardware and devices specific to that device. Therefore, before starting the development of the driver, you must first create the driver specification.
The API (Application Program Interface) to invoke the device driver is defined in the T-Kernel specification, but this is the “framework” that is common to all devices, and specifications depending on individual devices must be determined for each device (*2).
T-Kernel’s device drivers use a negative integer called “attribute data number” to specify special device-dependent processes. APIs such as tk_rea_dev
and tk_srea_dev
for reading from the device and tk_wri_dev
and tk_swri_dev
for writing to the device. has a parameter start
that indicates the start position for reading and writing, and by specifying an attribute data number here, device specific processing can be performed.
Therefore, defining a device driver specification is equivalent to defining the value of the attribute data number to be used by the device and the device-specific processing for that value. You also need to determine the device name to be used as the name to open the device. These definitions are documented in a device driver specification, attribute data numbers are labeled with easy-to-understand mnemonics, and header files are created for use in the program. By clarifying the device driver specifications and header files, we can divide the development of the application and the development of the device drivers.
As an example of a relatively simple device driver, we will develop a driver to control the lighting of LEDs. Table 2 shows an example of an external specification for this device driver.
(*2) Specifications depending on standard devices such as LAN and disk are also defined for each device.See “T-Engine Standard Device Driver Specification“.
Device name | “LED” |
---|---|
attribute data number | DN_LED_CHIP = -100 : 16-bit (UH-type) data Each bit corresponds to the state of the chip LEDs. |
DN_LED_SEG = -101 : 16-bit (UH-type) data Compatible with the numbers displayed on the 7-segment LED |
API for device drivers
First, let’s look at how to call the driver using the T-Kernel API from the perspective of an application developer who will be using this driver.
【Listing 1: Example of Calling the Driver from an Application】
ID dd; W asize; UH data; dd = tk_opn_dev("LED", TD_UPDATE); data = 4; // write data tk_swri_dev(dd, DN_LED_SEG, &data, sizeof(data), &asize); tk_cls_dev(dd, 0);
- Open the driver with
tk_opn_dev
(meaning T-Kernel open device). The string “LED” is the device name. - Read data from the device with
tk_srea_dev
(T-Kernel synchronous read device) ortk_swri_dev
(T-Kernel synchronous write device) The constantDN_LED_SEG
is the attribute data number. The constantDN_LED_SEG
is the attribute data number. - Close the driver with
tk_cls_dev
(T-Kernel close device).
Synchronous and Asynchronous
The “s” in tk_srea_dev
and tk_swri_dev
stands for “synchronous”. In this case, execution of the application is suspended until reading and writing are completed in the driver. On the other hand, tk_rea_dev
and tk_wri_dev
, which do not have an “s”, are asynchronous, and the application runs in parallel while reading and writing are being done in the driver. In this case, tk_wai_dev
(T-Kernel wait device) waits for the completion of reading and writing (Figure 1).
By using asynchronous mode, you may be able to improve the performance by using CPU efficiently. For example, when outputting compressed audio data while decompressing, the application can use the free time of the CPU for DMA transfer, etc., to decompress the next audio data in advance while the driver is running.
Internal structure of the device driver
Three layers of device drivers
Next, let’s take a look at the internal structure of the driver from a device driver developer’s point of view. The driver has a three-layered structure as shown in Figure 2.
- When an application makes a read or write request, the driver’s interface layer accepts the request via T-Kernel/SM (System Manager).
- The interface layer calls the logical layer, with exclusive control of multiple requests.
- The logical layer performs common operations independent of the hardware details of the device. The logical layer calls the physical layer.
- The physical layer directly controls the hardware of the device.
As a basic concept, the logical layer of the driver is implemented for each type of device, while the physical layer is implemented for each piece of hardware. For example, in the case of a LAN driver, the part that performs common processing as a LAN driver is implemented as a logical layer, and the part that depends on each NIC (NetworkInterface Card) or LAN control chip is implemented as a physical layer.
インタフェース層の選択
The following two types of interface layers are provided as libraries for T-Kernel, and you can usually choose one of them to use.
SDI (simple device driver interface layer)
The request packet passed to the driver is processed immediately on the fly. The logical layer is a function call structure. It is not possible to enter into an indefinite wait in the driver, and it is assumed that reading and writing will be completed promptly. It is not possible to abort processing of a given request. Only synchronous read/write is possible (*3).
GDI (general device driver interface layer)
Once passed to the driver, the request packets are stored in a queue. A task in the driver retrieves the request packets from the queue and processes them. It is possible to wait for an indefinite period of time in the driver and to abort a waiting request. Not only synchronous but also asynchronous reads and writes are possible.
An indefinite wait is a state of waiting that occurs when waiting for a change in the status of a device or communication partner. For example, in the receiving process in the wireless LAN driver, processing does not proceed until the data sent from the other party arrives, so the driver enters an indefinite wait until the actual data arrives. In this case, the driver is implemented using GDI.
In contrast, the LED driver process described in this example, for example, only sets the data to the memory address connected to the LED. In other words, the driver program only needs to proceed unilaterally and does not need to wait for the device to change state. This type of device driver can be implemented with SDI.
(*3) It is also possible to use tk_rea_dev
or tk_wri_dev
without an “s” for a driver implemented by SDI, but even in that case, the application is suspended until the end of reading or writing and runs in synchronous mode.
Implementation of device drivers using SDI
Implementation of the logic layer
As an example of a driver implementation using SDI, the main part of the LED driver logic layer program is shown in Listing 2. The read function, which is responsible for reading, and the write function, which is responsible for writing, are defined as callback functions and registered in SDefDevice (SDI definedevice). The read and write functions call the physical layer corresponding to the attribute data number passed by the application.
【List 2: Example of Logic Layer Implementation with SDI (excerpt of main points only)】
// read function INT read_fn( ID devid, INT start, INT size, void *data, SDI sdi ) { switch (start) { // Classification by attribute data number case DN_LED_CHIP: return read_chip( (UH*)data ); // Physical Layer: Chip LED reading case DN_LED_SEG: return read_seg( (UH*)data ); // Physical Layer: 7 segment LED reading default: return E_PAR; // Parameter Error } } // light function INT write_fn( ID devid, INT start, INT size, void *data, SDI sdi ) { switch (start) { // Classification by attribute data number case DN_LED_CHIP: return write_chip( *(UH*)data ); // Physical Layer: Chip LED writing case DN_LED_SEG: return write_seg( *(UH*)data ); // Physical layer: 7 segment LED writing default: return E_PAR; // Parameter Error } } // Driver start process ER init_led_driver( void ) { SDefDev ddev = { .devnm = "LED", // device name .blksz = 1, // block size .read = read_fn, // read function .write = write_fn, // light function }; init_chip(); // Physical Layer Initialization init_seg(); return SDefDevice( &ddev, NULL, &sdi ); // Driver Registration }
Implementation of the physical layer
The physical layer implements each of the routines (init_chip, init_seg, read_chip, write_chip, read_seg, write_seg
) called from the above logical layer. This part depends on the hardware details of the LEDs.
Listing 3 shows an example of write_seg
(7-segment LED write) on some hardware. 7-segment LEDs consist of 7 segments (8 segments including the decimal point) from a to g, as shown in Figure 3. For example, to display “4”, b, c, f, and g are lit and the remaining a, d, e, and decimal point are turned off. In this implementation example, the pattern of lights on and lights off is output to the LEDs by writing out_w
to a specific address of the I/O mapped in memory space.
【List 3: Example of Physical Layer Implementation (excerpt only)】
const UW ptn[] = { // 7-segment LED display pattern SEGa | SEGb | SEGc | SEGd | SEGe | SEGf, // '0' SEGb | SEGc, // '1' SEGa | SEGb | SEGd | SEGe | SEGg, // '2' SEGa | SEGb | SEGc | SEGd | SEGg, // '3' SEGb | SEGc | SEGf | SEGg, // '4' SEGa | SEGc | SEGd | SEGf | SEGg, // '5' SEGa | SEGc | SEGd | SEGe | SEGf | SEGg, // '6' SEGa | SEGb | SEGc, // '7' SEGa | SEGb | SEGc | SEGd | SEGe | SEGf | SEGg, // '8' SEGa | SEGb | SEGc | SEGd | SEGf | SEGg, // '9' }; INT write_seg( UH data ) // 7 Segment LED writing { if (data > 9) // out of bounds return E_PAR; UW p = ptn[data]; out_w( PIO_SODR(SEG_PIO), p ); // lighting section out_w( PIO_CODR(SEG_PIO), ~p & SEG_ALL ); // extinction lamp return sizeof(data); }
Implementation of device drivers using GDI
Request Processing Tasks
As an example of a driver implementation using GDI, Figure 4 shows an example driver for a communication device such as a wireless LAN. Here, the event flags (3rd reference) and an interrupt (10th reference).
- The driver’s logical layer allows a “request processing task” to run in parallel with the application.
- The request processing task uses
GDI_Accept
to retrieve request packets from the queue one by one and calls the corresponding physical layer according to the contents of the request packet. The request packet contains information passed by the application as API parameters, such as the type of request (read/write distinction) andtk_rea_dev
. - In the physical layer, it is possible to enter an irregular wait. For example, when processing a receive request, you can set up an interrupt to occur when data from the other party comes in, then wait for
tk_wai_flg
until one of the bits of the event flag is set (TWF_ORW
), then wait fortk_wai_flg
- When data arrives and an interrupt occurs,
tk_set_flg
is issued in the interrupt handler and an event flag is set. - The wait for
tk_wai_flg
is released and the request processing task retrieves the received data. - The request processing task returns the incoming data to the application in
GDI_Reply
.
Cancel the request
On the other hand, if the device is closed while waiting to receive, or if the application encounters a task exception such as an exit request, it is necessary to abort the wait in the driver and return the error to the requesting application, GDI uses abort handling to handle these situations . The abort process is implemented as follows
- If there is an abort request before the “4” interrupt occurs, set another bit of the event flag in
tk_set_flg
. - 5″ This clears the wait for
tk_wai_flg
in the request processing task. By referring to the bit pattern of the event flags, we can see whether data could be received or whether an abort request was made. - The “6” request processing task returns an abort error to the application in
GDI_Reply
.
Summary so far
The application calls the device driver with an open/read/write type API. To implement a device driver, select either SDI or GDI as the interface layer; when using SDI, the driver can be implemented simply by defining a callback function. On the other hand, when GDI is used, a request processing task in the driver takes care of it. Therefore, flexible processing can be achieved, such as waiting in the driver.
What is T2EX?
The world of embedded systems continues to evolve, with the advent of the Cortex family of processors, as well as many new features and implementations, such as big. The reason for this evolution of embedded platforms is that actual embedded systems are evolving rapidly. The reason for this evolution of embedded platforms is that actual embedded systems are evolving rapidly.
In recent years, there has been a remarkable increase in the sophistication and scale of information processing in embedded systems. Even if we take a microwave oven as an example, many of them nowadays use sensors instead of a turntable to cook evenly, or have various cooking menus. In addition, there are now products that have the ability to suggest and display recipes and set the cooking time, temperature, etc. according to those recipes, and proceed with cooking to some extent automatically.
Therefore, software with advanced information processing functions, such as those found in an information-oriented OS, is needed, while satisfying the unique requirements of embedded systems, such as real-time performance and memory saving. T2EX, introduced here, is the answer to this need. As we introduced in the first feature article, T2EX stands for T-Kernel 2.0 Extension, which can be translated directly as “T-Kernel 2.0 Extension. As the name suggests, T2EX is a collection of extensions for adding advanced functions of an information-oriented OS to the real-time OS called T-Kernel 2.0.
T2EX and add-on architecture
T2EX is realized as a collection of subsystems running on T-Kernel 2.0 and their complementary libraries. T2EX provides the following features
- File management
- Network communication (network communication function)
- Memory protection
- Calendar (calendar function)
- Program load (program load function)
- Standard C compatible library (except for standard input/output)
- Standard input/output (standard input/output function)
Figure 5 shows the overall structure of the T2EX architecture. The gray enclosure labeled “T2EX” represents the entire T2EX architecture, and the red blocks within it represent each module included in T2EX.
Modules in T2EX can be added and removed within the limits of the dependencies shown by the vertical relationship of the blocks in Figure 5. Removing unused functions can reduce the amount of ROM/RAM used and can also reduce the start-up time. Let’s take a digital camera, for example. Let’s assume that this digital camera uses the file management function to save the taken photo data to the SD card and the calendar function to display and record the date and time of the shot, but does not use the network communication function or the program load function. In this case, the system configuration can be reduced in weight as shown in Figure 6.
A feature of the T2EX add-on architecture is that it is designed to allow functions to be added and removed in module units. On the other hand, in an information-oriented OS such as Linux, functions such as files and networks are deeply intertwined with the basic functions of the OS (the parts that relate to the basics of operation, such as scheduling), and it is not easy to remove these functions. For this reason, even so-called “embedded Linux” products generally require several MB or more of ROM/RAM. On the other hand, in T2EX, each module’s program size is small (for example, the file management function is less than 100 KB) and unused functional modules can be removed, so that even a system such as the digital camera mentioned above will often require only a few hundred KB of ROM/RAM. .
Let’s make a digital photo frame
As a concrete example of an embedded application using T2EX, let’s create a digital photo frame that does the following
- Read one JPEG file in the SD card inserted into the unit.
- Display the image of the read JPEG file on the screen. (Use the screen driver)
- Stay intact for a period of time.
- Back to “1”
Select the required T2EX modules
First, select the T2EX module to be used in this system. In this section, we will use the memory protection function as well as the file management function and the standard input/output function, because it handles external data. To use only these three modules, edit kernel/sysmain/build_t2ex/tef_em1d/Makefile and add ‘#’ to the beginning of the line specifying modules not to be used and comment them out. Specifically, you can comment out the
# use T2EX file management T2EX_FS = yes # use T2EX network communication T2EX_NET = yes # use T2EX calendar T2EX_DT = yes # use T2EX program load T2EX_PM = yes
The part with a specification like
# use T2EX file management T2EX_FS = yes # use T2EX network communication #T2EX_NET = yes # use T2EX calendar #T2EX_DT = yes # use T2EX program load #T2EX_PM = yes
You can remove the network communication, calendar, and program load functions by rewriting it as
Drivers and libraries can also be removed from the system by editing kernel/usermain_t2ex/Makefile.usermain and commenting out the ones you don’t use. This time, we will not use the network driver ( netdrv.o
), so we will comment out the following lines.
#I_OBJ += $(BD)/driver/$(TETYPE)_$(MACHINE)/netdrv.o
Programming Using T2EX T2EX is an extension of T-Kernel 2.0, so the basic style of system programming using T2EX is the same as the system using T-Kernel 2.0 only. So, the application is created by rewriting the contents of the main body of T-Kernel 2.0 and T2EX.
The API of T-Kernel 2.0 and the API of T2EX can be programmed together in the usermain function. By combining them, you will be able to create applications that use advanced features such as file systems and TCP/IP while performing real-time processing.
Note that the T2EX API is similar to the API of POSIX, but there are some differences as follows
The API name of T2EX is prefixed with a shortened name of the module name of T2EX. For example, the API for the file management function is prefixed with fs_
and the API for the network communication function is prefixed with so_
. A negative value indicates an error code. The error code can take various values depending on the meaning of the error. Many POSIX functions, on the other hand, use only (-1) as the return value in case of an error. Consider the following POSIX program as an example
fd = open("/path/to/file", O_RDONLY); if ( fd == -1 ) { fprintf(stderr, "open failed: errno = %d\n", errno); goto error; }
A program that performs the same process in T2EX is as follows.
er = fs_open("/path/to/file", O_RDONLY); if ( er < E_OK ) { fprintf(stderr, "open failed: errno = %d\n", ERRNO(er)); goto error; } fd = er;
This program can be written together with the programs for T-Kernel 2.0 that have been introduced so far.
Programming Examples
We will implement the processes corresponding to the behavior of the aforementioned digital photo frame in the usermain function one by one.
Due to the convenience of the magazine, the actual program list and explanation are omitted here.
This sample includes the device management function of T-Kernel 2.0, the screen driver specification, and an example of the use of the file management function of T2EX, so there are many useful points to reference.
“もっと見る” カテゴリーなし
Mbed TLS overview and features
In this article, I'd like to discuss Mbed TLS, which I've touched on a few times in the past, Transport …
What is an “IoT device development platform”?
I started using Mbed because I wanted a microcontroller board that could connect natively to the Internet. At that time, …
Mbed OS overview and features
In this article, I would like to write about one of the components of Arm Mbed, and probably the most …