<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Leo Liu</title>
    <description>The latest articles on DEV Community by Leo Liu (@sienovoleo).</description>
    <link>https://dev.to/sienovoleo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3909612%2F830db132-9003-4ab2-9291-22d84be18f91.png</url>
      <title>DEV Community: Leo Liu</title>
      <link>https://dev.to/sienovoleo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sienovoleo"/>
    <language>en</language>
    <item>
      <title>Autonomous Obstacle Avoidance System for UAVs Based on OMAPL138 DSP+ARM+FPGA, Millimeter-Wave Radar, and Monocular Vision Fusion</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Wed, 10 Jun 2026 07:25:53 +0000</pubDate>
      <link>https://dev.to/sienovoleo/autonomous-obstacle-avoidance-system-for-uavs-based-on-omapl138-dsparmfpga-millimeter-wave-57ia</link>
      <guid>https://dev.to/sienovoleo/autonomous-obstacle-avoidance-system-for-uavs-based-on-omapl138-dsparmfpga-millimeter-wave-57ia</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In recent years, civil small unmanned aerial vehicles (UAVs) have received increasing attention and application, becoming widely used in fields such as express delivery, food delivery, and power line inspection. UAVs equipped with autonomous obstacle avoidance capabilities hold broad application prospects in social production, scientific research, and aerial photography operations. Such systems not only ensure stable flight but also enable UAVs to effectively avoid obstacles during complex missions, reducing damage from collisions and associated secondary hazards. Scholars worldwide have conducted extensive research on UAV autonomous obstacle avoidance. Conventional UAV obstacle avoidance solutions typically rely on pre-existing 3D elevation maps, multi-camera visual sensors, or combinations of multiple sensors. However, existing obstacle avoidance systems generally suffer from drawbacks such as large size, high power consumption, heavy weight, and high cost. Autonomous obstacle avoidance systems that are compact, low-power, lightweight, and low-cost—suitable for installation on small UAVs such as multirotor drones—remain an area requiring further research.&lt;/p&gt;

&lt;p&gt;Millimeter-wave radar offers advantages including small size, light weight, and long-distance ranging capability, but it suffers from low accuracy in measuring obstacle azimuth angles. Monocular vision sensors can estimate the azimuth angles of obstacles in the UAV's flight environment and, compared to multi-camera systems, require less computational effort. However, monocular vision performs poorly in measuring obstacle distance. Integrating millimeter-wave radar with a monocular vision sensor on a small UAV enables the acquisition of high-precision obstacle distance and azimuth angle information, achieving complementary strengths between the two sensors. Moreover, the computational load of this solution is low, making it suitable for general embedded microprocessors and ideal for deployment on small UAVs.&lt;/p&gt;

&lt;p&gt;Based on this UAV autonomous obstacle avoidance approach, the main research contributions of this paper are as follows:&lt;br&gt;&lt;br&gt;
1) A review of the current state of research and development in UAV autonomous obstacle avoidance systems is presented, followed by the proposal of a fusion-based system combining millimeter-wave radar and monocular vision sensors.&lt;br&gt;&lt;br&gt;
2) The architecture of millimeter-wave radar and its fundamental principles for obstacle distance measurement are introduced. Methods for applying digital image processing theory to monocular vision-based obstacle angle estimation are investigated. A method for fusing millimeter-wave radar distance measurements with monocular vision angle measurements to achieve 3D obstacle localization is proposed. Additionally, a technique for simplifying complex 3D path planning into 2D path planning is discussed.&lt;br&gt;&lt;br&gt;
3) Based on the Sienovo OMAPL138 DSP+ARM+FPGA hardware platform, a millimeter-wave radar platform, and the DJI Naza UAV platform, a demonstration system for UAV autonomous obstacle avoidance using millimeter-wave radar and monocular vision fusion was developed. Obstacle avoidance flight experiments were conducted using this system, providing preliminary validation of the feasibility of the proposed fusion approach in UAV autonomous flight.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5qs7nixzuovsro8erxcq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5qs7nixzuovsro8erxcq.png" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdbhbb2my1zd6zx342yu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdbhbb2my1zd6zx342yu.png" width="671" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Evaluation Board Overview
Based on the TI OMAP-L138 (fixed/floating-point DSP C674x + ARM9) and Xilinx Spartan-6 FPGA processor;
OMAP-L138 and FPGA are connected via uPP, EMIFA, and I2C buses, supporting communication speeds up to 228 MByte/s;
OMAP-L138 operates at a maximum clock frequency of 456 MHz, delivering up to 3648 MIPS and 2746 MFLOPS computational performance;
FPGA supports Xilinx Spartan-6 XC6SLX9/16/25/45, offering strong platform scalability;
The board exposes abundant peripherals, including Gigabit Ethernet, SATA, EMIFA, uPP, USB 2.0, and other high-speed data interfaces, as well as common interfaces such as GPIO, I2C, RS232, PWM, and McBSP;
Certified for high and low temperature operation, suitable for harsh working environments;
The DSP+ARM+FPGA tri-core SOM measures 66mm × 38.6mm and uses industrial-grade B2B connectors to ensure signal integrity;
Supports bare-metal, SYS/BIOS, and Linux operating systems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegdfy2xh7ymdi5rbndvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fegdfy2xh7ymdi5rbndvw.png" width="693" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bimv8yb7nv7v2c9dzxd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bimv8yb7nv7v2c9dzxd.png" width="781" height="500"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Figure 1 Front and side views of the evaluation board&lt;/p&gt;

&lt;p&gt;The XM138F-IDK-V3.0 is a development board designed based on the Sienovo XM138-SP6-SOM core module, fabricated using a 4-layer lead-free ENIG process. It provides a testing platform for the XM138-SP6-SOM core module, enabling rapid evaluation of its overall performance.&lt;/p&gt;

&lt;p&gt;The XM138-SP6-SOM exposes all CPU resource signal pins, making secondary development extremely easy. Customers can focus on upper-layer applications, significantly reducing development difficulty and time-to-market, allowing products to quickly enter the market and gain competitive advantage. In addition to rich demo programs, comprehensive development tutorials and full technical support are provided to assist customers with carrier board design, debugging, and software development.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Typical Application Areas
Data acquisition, processing, and display systems
Smart power systems
Image processing equipment
High-precision instrumentation
Mid-to-high-end CNC systems
Communication equipment
Audio and video data processing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnz9odj279d8i0smtgw7o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnz9odj279d8i0smtgw7o.png" width="692" height="385"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Figure 2 Typical application areas&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Introduction to OMAP-L138 + FPGA Development Board
The XM138F-IDK-V3, designed by Sienovo, is a high-speed data acquisition and processing development board based on a DSP+ARM+FPGA tri-core architecture, suitable for applications in power systems, communications, industrial control, medical devices, and audio/video processing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This design employs the OMAP-L138 + Spartan-6 platform. The OMAP-L138 is a low-power, high-performance dual-core processor from Texas Instruments (TI), integrating a floating-point DSP C6748 and an ARM9 core. The Spartan-6 is a Xilinx FPGA processor known for flexible platform scalability and excellent cost-performance ratio. The two chips are interconnected via communication interfaces such as uPP and EMIF on the OMAP-L138, while the internal DSP and ARM cores communicate via DSPLINK/SYSLINK, forming a unique, flexible, and powerful DSP+ARM+FPGA tri-core high-speed data acquisition and processing system.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Resource Block Diagram of OMAP-L138 + FPGA Development Board&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs748cxyk4b4w7ghhdmhs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs748cxyk4b4w7ghhdmhs.png" width="800" height="597"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;Figure 1 Block diagram of the OMAP-L138 + FPGA tri-core high-speed data acquisition and processing system&lt;/p&gt;

&lt;p&gt;System Architecture Description:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The front end uses a Xilinx Spartan-6 XC6SLX9/16/25/45 FPGA to synchronously acquire two-channel AD data with a maximum sampling rate of 65 MHz. The AD data is transmitted to the OMAP-L138's DSP via the uPP or EMIF bus.
&lt;/li&gt;
&lt;li&gt;After processing by the DSP, data is transferred to the ARM via DSPLINK or SYSLINK inter-processor communication components for application interface development, network forwarding, SATA disk storage, and other applications.
&lt;/li&gt;
&lt;li&gt;Based on processing results, the OMAP-L138's DSP or ARM sends logic control commands to the FPGA, which drives the onboard DA to generate logic outputs with an update rate of 175 MSPS.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(1) High-speed data acquisition front end: The Xilinx Spartan-6 XC6SLX9/16/25/45 FPGA synchronously acquires two analog input signals and can perform pre-filtering on the AD data, with a maximum sampling rate of 65 MSPS. Another DAC channel outputs parallel DA data with arbitrary amplitude and waveform, updated at 175 MSPS.&lt;/p&gt;

&lt;p&gt;(2) High-speed data transmission: Implemented via uPP, EMIF, SPI, and I2C buses. High-throughput AD and DA data are stably transferred between the DSP and FPGA via the uPP bus. The DSP controls FPGA logic and performs medium-throughput data exchange via the EMIF bus. The ARM configures and initializes the FPGA via SPI and I2C.&lt;/p&gt;

&lt;p&gt;(3) High-speed data processing: Composed of the DSP core and algorithm libraries, enabling real-time signal processing such as FFT and FIR filtering for time-domain, frequency-domain, and amplitude analysis of AD and DA data.&lt;/p&gt;

&lt;p&gt;(4) DSP+ARM dual-core communication: Composed of the DSP core, ARM core, and DSPLINK/SYSLINK inter-processor communication components. Data exchange between the DSP and ARM is achieved via shared memory.&lt;/p&gt;

&lt;p&gt;(5) Data display and storage expansion: Composed of the ARM core, graphics display, network, and SATA disk. The ARM-based application interface enables real-time display of time-domain and frequency-domain waveforms of AD and DA signals, as well as large-scale data storage and remote network communication.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Xilinx Spartan-6 FPGA High-Speed Data Acquisition Front-End Logic Implementation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rs5th73gkp82a1w1l8c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4rs5th73gkp82a1w1l8c.png" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Xilinx Spartan-6 FPGA — XC6SLXx&lt;/p&gt;

&lt;p&gt;The XC6SLXx is a widely used, cost-effective FPGA platform from the Xilinx Spartan-6 series, featuring 324 pins, abundant available I/Os, good platform scalability, reasonable cost differences across variants, and smooth scalability. Depending on application needs, models such as LX9, LX16, LX25, and LX45 can be selected. Additionally, these four CPU models are pin-to-pin compatible.&lt;/p&gt;

&lt;p&gt;(1) XC6SLX9: Interface-level, capable of interface programming and clock control.&lt;br&gt;&lt;br&gt;
(2) XC6SLX16: Algorithm-level, suitable for simple algorithm processing.&lt;br&gt;&lt;br&gt;
(3) XC6SLX25: Algorithm-level, capable of intermediate-level algorithm processing.&lt;br&gt;&lt;br&gt;
(4) XC6SLX45: System-level, meeting requirements for complex algorithms and system logic processing.&lt;/p&gt;

&lt;p&gt;High-Speed ADC — AD9238&lt;/p&gt;

&lt;p&gt;The AD9238 is a 12-bit dual-channel A/D converter from Analog Devices (ADI), offering the industry's fastest sampling rates. It supports selectable input voltage ranges of 1 Vp-p and 2 Vp-p and is widely used in high-speed data acquisition applications in power, communications, industrial control, and medical systems.&lt;/p&gt;

&lt;p&gt;The AD9238 comes in three variants with sampling rates of 20 MS/s, 40 MS/s, and 65 MS/s. It delivers dynamic performance comparable to single-channel ADCs and offers better crosstalk immunity than using two separate single-channel ADCs. The three variants are pin-to-pin compatible, enabling flexible configuration based on requirements.&lt;/p&gt;

&lt;p&gt;The power consumption of the three AD9238 variants is 180 mW, 330 mW, and 600 mW, respectively—about half that of comparable ADCs. Packaged in a 64-pin LQFP (9 mm × 9 mm), it is ideal for space-constrained applications.&lt;/p&gt;

&lt;p&gt;High-Speed DAC — AD9706&lt;/p&gt;

&lt;p&gt;The AD9706 is a 12-bit D/A converter from Analog Devices with a 175 MSPS update rate and output current range of 1 mA to 5 mA, widely used in high-speed data output applications in communications, industrial control, medical, and power systems.&lt;/p&gt;

&lt;p&gt;/// All are 175 MSPS update-rate D/A converters, pin-to-pin compatible, with resolutions of 8, 10, 12, and 14 bits. These devices are optimized for low power while maintaining excellent dynamic performance, support flexible supply voltage ranges (1.7 V to 3.6 V), and are ideal for portable and low-power applications. Power consumption can be reduced to 15 mW by lowering full-scale output current, and standby power in power-down mode can be as low as 2.2 mW.&lt;/p&gt;

&lt;p&gt;The AD9748/AD9740/AD9742/AD9744 series D/A converters are also pin-to-pin compatible (in LFCSP_VQ package) and optimized for transmit signal paths in communication systems. Users can select suitable devices based on performance, resolution, and cost requirements.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implementation of Communication Between Xilinx Spartan-6 FPGA and TI OMAP-L138&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftei2mluz5oymdr4mhrr0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftei2mluz5oymdr4mhrr0.png" width="799" height="217"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Figure 6 FPGA-OMAP-L138 Communication Principle&lt;/p&gt;

&lt;p&gt;High-Speed Communication Bus — uPP&lt;/p&gt;

&lt;p&gt;The uPP (Universal Parallel Port) is a distinctive high-speed parallel data transfer interface on the OMAP-L138 CPU. It supports independent or simultaneous data transmission and reception, commonly used for data exchange with FPGAs and other parallel-interface devices.&lt;/p&gt;

&lt;p&gt;The OMAP-L138 uPP features two channels (Channel A and Channel B), 32 data lines, simple control, flexible configuration, and high data throughput. The uPP clock rate can reach up to half the processor clock rate. For the OMAP-L138 running at 456 MHz, the theoretical single-channel throughput can reach up to 228 MB/s.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implementation of DSP and ARM Dual-Core Communication on TI OMAP-L138&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffe55jabt8da47vw9wx80.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffe55jabt8da47vw9wx80.png" width="552" height="267"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Figure 14 OMAP-L138 DSP+ARM Dual-Core Communication Principle&lt;/p&gt;

&lt;p&gt;Basic Principle&lt;/p&gt;

&lt;p&gt;TI's official DSPLINK/SYSLINK dual-core communication components provide a set of generic APIs that abstract the physical connection characteristics between ARM and DSP at the application layer, reducing development complexity. DSPLINK operates with the DSP/BIOS OS, while SYSLINK uses the SYS/BIOS OS; SYSLINK is the newer version of the DSPLINK dual-core communication component.&lt;/p&gt;

&lt;p&gt;In dual-core development, the ARM runs a high-level operating system (HLOS), typically Linux, while the DSP runs a real-time operating system (RTOS), usually DSP/BIOS or SYS/BIOS. Both cores operate at 456 MHz.&lt;/p&gt;

&lt;p&gt;Advantages&lt;/p&gt;

&lt;p&gt;(1) The on-chip DSP+ARM architecture enables stable dual-core communication, significantly reducing development time.&lt;/p&gt;

&lt;p&gt;(2) The DSPLINK/SYSLINK components overcome dual-core development bottlenecks, reducing R&amp;amp;D costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  (3) The integration of DSP and ARM on a single SOC simplifies hardware design, lowering product power consumption and hardware costs.
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/106422831" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/106422831" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dsparm</category>
      <category>mmwave</category>
      <category>radar</category>
      <category>omapl138</category>
    </item>
    <item>
      <title>Adapter Pattern and Facade Pattern (Highly Recommended)</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Tue, 09 Jun 2026 07:11:13 +0000</pubDate>
      <link>https://dev.to/sienovoleo/adapter-pattern-and-facade-pattern-highly-recommended-47ac</link>
      <guid>https://dev.to/sienovoleo/adapter-pattern-and-facade-pattern-highly-recommended-47ac</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Original Link: &lt;a href="http://www.cnblogs.com/goody9807/archive/2011/09/06/2168729.html" rel="noopener noreferrer"&gt;适配器模式和外观模式&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adapter Pattern (Adapter)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's start with an introduction and a problem. As is well known, the common voltage in China is 220V, while in the United States it is 110V. If an IT professional frequently travels between the US and China, always carrying their laptop, how can the laptop's voltage issue be resolved? (Because the voltage differs between the US and China, general electrical appliances are not interchangeable.) This is where the adapter shines.&lt;/p&gt;

&lt;p&gt;Modern laptops all have a power adapter, and it is precisely this power adapter that solves the aforementioned adaptation problem. For example, a Sony laptop might have an input voltage range of AC 100V~240V, with a unified DC output of 19.5V. AC current is input into one end of the power adapter, which then converts the power to the required voltage. In essence, the adapter's role is to make one thing suitable for another.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here is the definition of the Adapter Pattern&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Adapter Pattern converts the interface of a class into another interface clients expect.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is mainly used in the following situations:&lt;/p&gt;

&lt;p&gt;For example, suppose you have an old software system with an outdated component that needs updating. You acquire a third-party component (the new component), but its interface differs from the old component's. At the same time, you don't want to change the existing code (which might be impossible if the system is large). This is where the Adapter Pattern comes in. You can use the Adapter Pattern to convert some interfaces of the new component into the interfaces you expect (i.e., compatible with the old component). This way, you can easily update from the old component to the new one without altering the original code.&lt;/p&gt;

&lt;p&gt;Another good application is when many things that run on Windows cannot run on Linux. For instance, a tool like WINE allows users to run Windows programs in a Linux environment; this is also a form of adapter.&lt;/p&gt;

&lt;p&gt;In essence, the Adapter Pattern can be understood as wrapping some objects and making their interfaces appear as different interfaces.&lt;/p&gt;

&lt;p&gt;It's also worth mentioning: The Adapter Pattern is traditionally divided into Class Adapter Pattern and Object Adapter Pattern. However, since the Class Adapter Pattern relies on multiple inheritance, and C# does not support multiple inheritance, only the Object Adapter is introduced here. If you are interested in the Class Adapter Pattern, you can implement it using C++.  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, let's look at the Target class (the Target class represents the interface that clients can use)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Adapter&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Target&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="c1"&gt;// Temperature &lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
        &lt;span class="c1"&gt;/// The interfaces below are the ones recognizable by the client, i.e., the target interfaces. &lt;/span&gt;
        &lt;span class="c1"&gt;/// The Chinese methods in the Adaptee class, however, are not recognizable by the client and need to be adapted. &lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetTemperature&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Pressure &lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetPressure&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Humidity &lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetHumidity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// UV Intensity &lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetUltraviolet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Next, let's look at the Adaptee class (the interfaces in Adaptee are not recognizable by the client, so they need to be adapted)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Adapter&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Adaptee&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
        &lt;span class="c1"&gt;/// The interfaces in the Adaptee class are not the ones the client needs. &lt;/span&gt;
        &lt;span class="c1"&gt;/// For example, here Chinese is used, but the client requires English. &lt;/span&gt;
        &lt;span class="c1"&gt;/// Therefore, an adapter must be used here. &lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="err"&gt;得到温度&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You got today's temperature"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="err"&gt;得到气压&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You got today's pressure"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="err"&gt;得到湿度&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You got today's humidity"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="err"&gt;得到紫外线强度&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"You got today's UV intensity"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Then, let's look at the code for the Adapter (the Adapter indirectly converts interfaces not recognizable by the client into recognizable ones)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Adapter&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="c1"&gt;// An Adaptee object must be maintained within the adapter &lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Adaptee&lt;/span&gt; &lt;span class="n"&gt;adaptee&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Adaptee&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
        &lt;span class="c1"&gt;/// Adapt interfaces not originally recognized by the client through the adapter &lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetTemperature&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;adaptee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;得到温度&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetPressure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;adaptee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;得到气压&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetHumidity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;adaptee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;得到湿度&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GetUltraviolet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;adaptee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="err"&gt;得到紫外线强度&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Finally, let's look at the client code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;AdapterTest&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="c1"&gt;// Instantiate an adapter for the target interface &lt;/span&gt;
            &lt;span class="n"&gt;Adapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Target&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Adapter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Adapter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// The following are the interfaces recognizable by the client &lt;/span&gt;

            &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetTemperature&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetPressure&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetHumidity&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUltraviolet&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;===================================================================  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Facade Pattern (Facade)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's start with an example from 'Head First Design Patterns' (I've reorganized that example). The example describes how many people in the US set up home theaters (I'll consider the simplest approach, which is just turning everything on and off). In a home theater, you typically need basic tools like lights, a screen, a projector, an amplifier, and a DVD player. Lights can be turned on or off. The projector can be turned on or off. The screen can also be opened or closed. The amplifier can have its volume turned up or down. The DVD player can be turned on or off.&lt;/p&gt;

&lt;p&gt;Then, to watch a movie, I would have to perform the following operations on the client side: first turn on the projector, then turn on the amplifier, then open the screen, then turn on the DVD player, and finally turn on the lights. After all these operations, you can finally watch a movie (how inconvenient, so many steps, too complicated!). And when you're done, you still have to turn off the projector, then the amplifier, then close the screen, then turn off the DVD player, and finally turn off the lights. Oh, this is too complicated!!!&lt;/p&gt;

&lt;p&gt;There are so many operations on the client side (and the problem is that some users might not know how to use one of these tools, meaning they can't watch a movie). Users would be utterly annoyed!!!&lt;/p&gt;

&lt;p&gt;This actually reflects a common phenomenon in modern software development systems: client programs often have direct contact with the internal subsystems of a complex system, causing the client program to change as the subsystems change. In the example above, the client program is the user's operation, and the internal subsystems of the complex system represent the usage interfaces of these tools.&lt;/p&gt;

&lt;p&gt;In the example above, I only used the simplest tool operation interfaces, namely simple on/off functions. What if there are new features in the subsystems, i.e., in each tool? This would inevitably lead to changes in the client code.&lt;/p&gt;

&lt;p&gt;To solve this series of problems, you must simplify the interaction interface between the client program and the subsystems (to enable users who don't know how to use all the tools to still watch a movie). Then, you need to decouple the client program from the subsystems, and the Facade Pattern can solve this problem perfectly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Definition of Facade Pattern (Facade)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Facade Pattern provides a unified interface to a set of interfaces in a subsystem. This pattern defines a higher-level interface that makes the subsystem easier to use.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Simply put, the Facade Pattern hides the complex operations of one or more classes, exposing only a consistent interface for the client to use.&lt;/p&gt;

&lt;p&gt;It's also important to note that the Facade Pattern merely provides you with a more direct and easier way to operate; it does not isolate the original subsystems. Therefore, if you still need higher-level functionality from the subsystem classes, you can still use the original subsystems. This is a major advantage of the Facade Pattern.&lt;/p&gt;

&lt;p&gt;At the same time, the Facade Pattern allows for the creation of a high-level interface over multiple subsystem interfaces, providing this high-level interface to the client. This decouples the client from the complex subsystems.&lt;/p&gt;

&lt;p&gt;Furthermore, the Facade Pattern also enables us to adhere to the Law of Demeter (also known as the Principle of Least Knowledge).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now let's look at the code for a few playback tools&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Facade&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
    &lt;span class="c1"&gt;/// Projector &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Projector&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OpenProjector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Open projector"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CloseProjector&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Close projector"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetWideScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Projector is in widescreen mode"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SetStandardScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Projector is in standard mode"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Facade&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
    &lt;span class="c1"&gt;/// Amplifier &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Amplifier&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OpenAmplifier&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Open amplifier"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CloseAmplifier&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Close amplifier"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Facade&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
    &lt;span class="c1"&gt;/// Screen &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Screen&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OpenScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Open screen"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CloseScreen&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Close screen"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Facade&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
    &lt;span class="c1"&gt;/// DVD Player &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DVDPlayer&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OpenDVDPlayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Open DVD player"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CloseDVDPlayer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Close DVD player"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Facade&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
    &lt;span class="c1"&gt;/// Light &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Light&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OpenLight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Turn on light"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CloseLight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Turn off light"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Next, here's the code for the Facade class&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;Facade&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
    &lt;span class="c1"&gt;/// Define a facade &lt;/span&gt;
    &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MovieFacade&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
        &lt;span class="c1"&gt;/// The facade class must hold references to various objects in the subsystem &lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Projector&lt;/span&gt; &lt;span class="n"&gt;projector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Amplifier&lt;/span&gt; &lt;span class="n"&gt;amplifier&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Screen&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;DVDPlayer&lt;/span&gt; &lt;span class="n"&gt;dvdPlayer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
        &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="n"&gt;Light&lt;/span&gt; &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;MovieFacade&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;projector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Projector&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;amplifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Amplifier&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;dvdPlayer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DVDPlayer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;light&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Light&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
        &lt;span class="c1"&gt;/// Open movie &lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OpenMovie&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="c1"&gt;// First, turn on the projector &lt;/span&gt;
            &lt;span class="n"&gt;projector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenProjector&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Then, turn on the amplifier &lt;/span&gt;
            &lt;span class="n"&gt;amplifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenAmplifier&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Then, open the screen &lt;/span&gt;
            &lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenScreen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Then, turn on the DVD &lt;/span&gt;
            &lt;span class="n"&gt;dvdPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenDVDPlayer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Then, turn on the lights &lt;/span&gt;
            &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenLight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;/// &amp;lt;summary&amp;gt; &lt;/span&gt;
        &lt;span class="c1"&gt;/// Close movie &lt;/span&gt;
        &lt;span class="c1"&gt;/// &amp;lt;/summary&amp;gt; &lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;CloseMovie&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="c1"&gt;// Turn off the projector &lt;/span&gt;
            &lt;span class="n"&gt;projector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CloseProjector&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Turn off the amplifier &lt;/span&gt;
            &lt;span class="n"&gt;amplifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CloseAmplifier&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Close the screen &lt;/span&gt;
            &lt;span class="n"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CloseScreen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Turn off the DVD &lt;/span&gt;
            &lt;span class="n"&gt;dvdPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CloseDVDPlayer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Turn off the lights &lt;/span&gt;
            &lt;span class="n"&gt;light&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CloseLight&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Finally, here's the client code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;System&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;FacadeTest&lt;/span&gt; 
&lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Program&lt;/span&gt; 
    &lt;span class="p"&gt;{&lt;/span&gt; 
        &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
        &lt;span class="p"&gt;{&lt;/span&gt; 
            &lt;span class="n"&gt;Facade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MovieFacade&lt;/span&gt; &lt;span class="n"&gt;movie&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Facade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MovieFacade&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;Facade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Projector&lt;/span&gt; &lt;span class="n"&gt;projector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Facade&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Projector&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// First, watch the movie &lt;/span&gt;
            &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;OpenMovie&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Then, set the projector to widescreen mode &lt;/span&gt;
            &lt;span class="n"&gt;projector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetWideScreen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="c1"&gt;// Then, set the projector back to standard mode &lt;/span&gt;
            &lt;span class="n"&gt;projector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SetStandardScreen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="c1"&gt;// Finally, close the movie &lt;/span&gt;
            &lt;span class="n"&gt;movie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CloseMovie&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadKey&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
        &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  As can be seen from the screenshots above, I can still use the content from the subsystem in the client. That is, the Facade Pattern does not isolate the subsystem from the client; it merely provides a clean interface to the client. However, if the client wants to access interfaces within the complex subsystem, it can still do so, as demonstrated in the Demo above where the projector within the subsystem was accessed and
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/8452320" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/8452320" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>embedded</category>
      <category>programming</category>
      <category>ai</category>
    </item>
    <item>
      <title>Microcontroller Communication Protocol Design</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Mon, 08 Jun 2026 07:37:31 +0000</pubDate>
      <link>https://dev.to/sienovoleo/microcontroller-communication-protocol-design-1e29</link>
      <guid>https://dev.to/sienovoleo/microcontroller-communication-protocol-design-1e29</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Most modern instruments require operation through host computer software for convenient debugging and ease of use. This inherently involves communication. Based on several devices he has developed, the author has summarized a general approach to writing communication programs, covering both host and slave ends.&lt;/p&gt;

&lt;p&gt;　　1. Custom Data Communication Protocol  &lt;/p&gt;

&lt;p&gt;　　The data protocol discussed here refers to the format of communication data packets built upon the physical layer. The physical layer of communication refers to common methods such as RS232, RS485, infrared, fiber optic, wireless, and so on. At this layer, the low-level software provides two basic operations: sending a byte of data and receiving a byte of data. All data protocols are built upon these two operations.&lt;br&gt;&lt;br&gt;
Data in communication is often transmitted in the form of data packets, which we refer to as a data frame. Similar to TCP/IP protocols in network communication, a relatively reliable communication protocol typically includes the following components: frame header, address information, data type, data length, data block, checksum, and frame tail.  &lt;/p&gt;

&lt;p&gt;　 　The frame header and frame tail are used to determine the completeness of the data packet. They usually consist of a fixed length of specific bytes. The requirement is that the bit error rate for identifying data packets in the entire data link should be as low as possible. This means minimizing the chance of matching the characteristic bytes of the frame header and frame tail within the entire data stream. There are generally two approaches: first, reduce the probability of matching characteristic bytes; second, increase the length of characteristic bytes. The first method is usually chosen when the data in the entire data link is not random and is predictable, allowing the characteristic words for the frame header and frame tail to be artificially selected to avoid matches, thereby reducing the probability of matching characteristic bytes. The second method is more general and suitable for situations where data is random. Increasing the length of characteristic bytes reduces the matching probability, and although it cannot completely avoid matching, it significantly lowers the chances. If a match does occur, it can also be detected by the checksum. Therefore, this approach is quite reliable in most cases.&lt;/p&gt;

&lt;p&gt;　　Address information is primarily used in multi-device communication to identify different communication terminals based on their unique addresses. In a one-to-many communication system, it may only include destination address information. Including both source and destination addresses is suitable for many-to-many communication systems.  &lt;/p&gt;

&lt;p&gt;　　Data type, data length, and data block constitute the main data part. The data type identifies whether the subsequent data is a command or actual data. The data length indicates the number of valid data bytes.  &lt;/p&gt;

&lt;p&gt;　　The checksum is used to verify the integrity and correctness of the data. It is typically derived from calculations involving the data type, data length, and data block. The simplest approach is to perform a cumulative sum on the data segment, while more complex methods might involve CRC calculations, etc., chosen based on requirements for calculation speed, fault tolerance, and other factors.  &lt;/p&gt;

&lt;p&gt;　　2. Data Transmission in Host and Slave  &lt;/p&gt;

&lt;p&gt;　　The physical communication layer provides two basic operational functions, and sending a single byte of data forms the basis of data transmission. Sending a data packet simply involves transmitting all bytes within the packet one by one in sequence. Of course, there are different methods for transmission.  &lt;/p&gt;

&lt;p&gt;　 　In microcontroller systems, a common method is to directly call a serial function that sends a single byte of data. The disadvantage of this method is that the processor needs to be fully involved throughout the transmission process. The advantage is that the data to be sent appears immediately on the communication line and can be received by the receiving end without delay. Another method is to use interrupt-driven transmission, where all data to be sent is placed into a buffer, and a transmit interrupt sends the data from the buffer. The advantage of this method is that it consumes fewer processor resources, but there might be a slight delay before the data is actually sent, though this delay is usually very small. For 51-series microcontrollers, direct transmission is often preferred, as interrupt-driven transmission consumes more RAM resources and doesn't offer significant advantages over direct transmission. Below is a function for sending a single byte in a 51-series microcontroller.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SendByte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;SBUF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TI&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;TI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;　 　There are also various methods for serial communication on the host computer. This refers not to whether data is buffered, but to different ways of operating the serial port, as data transmission on a PC is almost always buffered before sending. For programming, there are three ways to operate a serial port: First, use the serial communication controls built into the Windows system. This method is relatively simple, but attention must be paid to blocking handling during reception and threading mechanisms. Second, use system APIs directly to read serial data. In Windows and Linux systems, devices are virtualized as files, and serial data can be sent and received using the API functions provided by the system. Third, use a serial port class for serial operations. Here, we will only introduce programming with a serial port class in a Windows environment.  &lt;/p&gt;

&lt;p&gt;　　&lt;code&gt;CSerialPort&lt;/code&gt; is a commonly used serial port class. It provides the following serial port operation method:  &lt;/p&gt;

&lt;p&gt;　　&lt;code&gt;void WriteToPort(char* string, int len);&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;　　After the serial port is successfully initialized, this function can be called to send data to the serial port. To avoid delays caused by serial port buffering, the serial port's flush mechanism can be enabled.&lt;/p&gt;

&lt;p&gt;　　3. Data Reception and Protocol Parsing in Slave  &lt;/p&gt;

&lt;p&gt;　　There are also two ways for the slave to receive data: first, wait for reception, where the processor continuously queries the serial port status to determine if data has been received; second, interrupt-driven reception. The advantages and disadvantages of these two methods were discussed in detail in a previous article on serial communication. The conclusion was that interrupt-driven reception is generally better.  &lt;/p&gt;

&lt;p&gt;　 　The data packet parsing process can be placed in different locations. If the protocol is simple and the entire system only handles simple commands, the data packet parsing process can be directly integrated into the interrupt service routine. When a correct data packet is received, the corresponding flag is set, and the main program then processes the command. If the protocol is slightly more complex, a better approach is to store the received data in a buffer, and the main program reads and parses the data later. There are also hybrid approaches, for example, in a one-to-many system, the "connect" command is first parsed in the receive interrupt. After the connect command is received, the main program enters a setup state and uses polling to parse the remaining protocols.  &lt;/p&gt;

&lt;p&gt;　　A specific example is provided below. In this system, the serial port commands are very simple. All protocols are handled within the serial interrupt. The data packet format is as follows:  &lt;/p&gt;

&lt;p&gt;　　&lt;code&gt;0x55, 0xAA, 0x7E, 0x12, 0xF0, 0x02, 0x23, 0x45, SUM, XOR, 0x0D&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;　　Here, &lt;code&gt;0x55, 0xAA, 0x7E&lt;/code&gt; are the frame header, &lt;code&gt;0x0D&lt;/code&gt; is the frame tail, &lt;code&gt;0x12&lt;/code&gt; is the device's destination address, &lt;code&gt;0xF0&lt;/code&gt; is the source address, &lt;code&gt;0x02&lt;/code&gt; is the data length, followed by two data bytes &lt;code&gt;0x23, 0x45&lt;/code&gt;. The cumulative sum and XOR checksum are calculated starting from the destination address and ending at the last data byte.&lt;br&gt;&lt;br&gt;
　　The purpose of protocol parsing is first to determine the completeness and correctness of the data packet, then to extract data type, data, and other information, and store it for processing by the main program. The code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;// Protocol parsing state machine  &lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcvdat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mh"&gt;0x55&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Received first byte of frame header  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Reset state machine  &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcvdat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mh"&gt;0xAA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Received second byte of frame header  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Reset state machine  &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcvdat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mh"&gt;0x7E&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Received third byte of frame header  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Reset state machine  &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;sumchkm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Start calculating cumulative sum and XOR checksum  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;xorchkm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcvdat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;m_SrcAdr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Check if destination address is correct  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;sumchkm&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;xorchkm&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcvdat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;m_DstAdr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Check if source address is correct  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;lencnt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c1"&gt;// Data reception counter  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;rcvcount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// Received data length  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;sumchkm&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;xorchkm&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;m_ucData&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;lencnt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// Save data  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;sumchkm&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;xorchkm&lt;/span&gt; &lt;span class="o"&gt;^=&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lencnt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rcvcount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Check if data reception is complete  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sumchkm&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Check if cumulative sum matches  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xorchkm&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Check if XOR checksum matches  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0x0D&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;// Check if frame tail terminator is received  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;retval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0xaa&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Set flag, indicating a data packet is received  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;        &lt;span class="c1"&gt;// Reset state machine  &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;　 　During this process, a variable &lt;code&gt;state_machine&lt;/code&gt; is used as the protocol state machine's transition state to determine which part of a data frame the current byte belongs to. Simultaneously, checksum calculation and processing are performed automatically during reception. When the data packet reception is complete, the checksums are compared. Therefore, when the frame tail terminator is received, it indicates that a data frame has been fully received and validated, and the key data has been saved to the buffer. The main program can then use the &lt;code&gt;retval&lt;/code&gt; flag to proceed with protocol parsing.  &lt;/p&gt;

&lt;p&gt;　　During reception, if the data received at any step is not the expected value, the state machine is immediately reset to prepare for the next data frame. This significantly reduces the occurrence of state deadlocks, making the system relatively stable. If data packets are lost, the host can re-send commands, though the author has not encountered such a situation.  &lt;/p&gt;

&lt;p&gt;　　The process for protocol handling in the main program is similar. The main program continuously reads data from the serial buffer in its loop, and this data is then fed into the protocol processing routine within the main loop. The code is exactly the same as described above.  &lt;/p&gt;

&lt;p&gt;　　4. Data Reception and Command Processing in Host  &lt;/p&gt;

&lt;p&gt;　　The data reception process on the host can be entirely consistent with that on the slave, though there are differences depending on the serial port operation method. For blocking serial read functions, such as direct API operations or calling Windows serial communication controls, it is best to start a dedicated thread to monitor serial data reception. Each time a data byte is received, a message can be sent to the system. The &lt;code&gt;CSerialPort&lt;/code&gt; class, commonly used by the author, handles this process in a similar way. After &lt;code&gt;CSerialPort&lt;/code&gt; opens the serial port, it starts a thread to monitor serial data reception, saves the received data to a buffer, and sends a data reception message to the parent process. The data is sent along with the message to the parent process. The parent process enables the message handling function, and after obtaining serial data from it, the code described above can be copied and used.  &lt;/p&gt;

&lt;p&gt;　　The message ID sent by &lt;code&gt;CSerialPort&lt;/code&gt; to the parent class is as follows:  &lt;/p&gt;

&lt;p&gt;　　&lt;code&gt;#define WM_COMM_RXCHAR WM_USER+7 // A character was received and placed in the input buffer.&lt;/code&gt;&lt;br&gt;&lt;br&gt;
　　Therefore, a response function for this message needs to be manually added:  &lt;/p&gt;

&lt;p&gt;　　&lt;code&gt;afx_msg LONG OnCommunication(WPARAM ch, LPARAM port);&lt;/code&gt;&lt;br&gt;&lt;br&gt;
　　&lt;code&gt;ON_MESSAGE(WM_COMM_RXCHAR, OnCommunication)&lt;/code&gt;  &lt;/p&gt;

&lt;p&gt;　　The specific code for the response function is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;LONG&lt;/span&gt; &lt;span class="n"&gt;CWellInfoView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;OnCommunication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;WPARAM&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LPARAM&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;&amp;nbsp;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;retval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;rcvdat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BYTE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Protocol parsing state machine  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcvdat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mh"&gt;0x55&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Received first byte of frame header  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Reset state machine  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rcvdat&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mh"&gt;0xAA&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// Received second byte of frame header  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="n"&gt;state_machine&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// Reset state machine  &lt;/span&gt;
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="err"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/span&gt; &lt;span class="p"&gt;......&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;　&lt;/p&gt;

&lt;p&gt;　　5. Conclusion  &lt;/p&gt;

&lt;h2&gt;
  
  
  　　The above provides a basic prototype for how a communication system operates. Although simple, it is feasible. Actual communication systems have more complex protocols and involve a series of issues such as data packet responses, command errors, and delays. Building upon this foundation, these difficulties can be overcome to achieve a more stable and reliable system.
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/6782630" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/6782630" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>windows</category>
      <category>character</category>
      <category>api</category>
      <category>programming</category>
    </item>
    <item>
      <title>ARM+FPGA-based Industrial Medical Endoscope Solution</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Sun, 07 Jun 2026 07:21:21 +0000</pubDate>
      <link>https://dev.to/sienovoleo/armfpga-based-industrial-medical-endoscope-solution-2k6a</link>
      <guid>https://dev.to/sienovoleo/armfpga-based-industrial-medical-endoscope-solution-2k6a</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This article describes the first installment of a multi-part series on an FPGA-based monocular endoscope positioning system for cardiac surgery simulation. The system uses a camera, SDRAM frame buffering, and real-time image processing on an FPGA to track and localize the tip of a surgical catheter inside a cardiac simulator — giving trainee surgeons coordinate feedback without requiring direct visual inspection of the heart interior. This post covers the project background, FPGA fundamentals, system design rationale, algorithm selection, and hardware module overview.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Background and Motivation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Medical Device Context
&lt;/h3&gt;

&lt;p&gt;Cardiovascular disease treatment is among the most commercially and clinically significant segments of the medical device industry. Cardiac assist devices — whether implantable or external, electronic or mechanical — have steadily decreased in size, making them easier to implant. At the same time, cardiac electrophysiology has emerged as a rapidly evolving subspecialty: it studies the electrical phenomena generated by excitable tissue (action potentials in nerves, cardiac muscle, etc.) and uses that information to guide interventional procedures.&lt;/p&gt;

&lt;p&gt;A critical bottleneck in expanding surgical capacity is the shortage of experienced surgeons. Current estimates suggest that only about 6% of cardiac patients who could benefit from surgery actually receive it; many others die waiting, or survive with unmanaged risk. The root cause is that training a competent cardiac surgeon through traditional apprenticeship — operating on real patients under supervision — is slow, expensive, and carries ethical and medico-legal risk, particularly in the current climate of heightened doctor-patient tension. A cardiac surgery simulator that lets trainees practice catheter navigation in a realistic phantom heart, while receiving real-time position feedback, directly addresses this bottleneck.&lt;/p&gt;

&lt;p&gt;Image processing is the enabling technology here. The system described in this series captures live endoscope video from inside a cardiac simulator, detects the catheter tip in each frame, outputs its 2-D coordinates for display, and forwards contact-point signals to an external instrument for 3-D cardiac modeling.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 FPGA in Medical Imaging
&lt;/h3&gt;

&lt;p&gt;FPGAs (Field-Programmable Gate Arrays) have long been the preferred platform for real-time video processing because their massively parallel datapath architecture maps naturally to pipeline-oriented image algorithms. In a typical FPGA image pipeline, each processing stage operates on a pixel as it arrives from the sensor, so the end-to-end latency is a handful of clock cycles rather than a full frame period. This makes sustained frame rates of 25 fps straightforward to achieve, with 60 fps attainable on mid-range devices. Early FPGA adoption was concentrated in communications (baseband coding, modulation/demodulation), but the technology has migrated into medical imaging, ECG processing, endoscopy, and other clinical signal-processing domains. The design described here targets catheter-tip localization, but the same FPGA platform can be extended to multi-camera configurations for 3-D coordinate recovery.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. FPGA Architecture and Design Flow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  2.1 How FPGAs Implement Logic
&lt;/h3&gt;

&lt;p&gt;An FPGA achieves programmability through three cooperating mechanisms:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Programmable logic blocks.&lt;/strong&gt; The fundamental element is a Look-Up Table (LUT) — typically 4-input or 6-input, 1-output — implemented in SRAM or Flash. A 4-input LUT can store the truth table of any 4-input/1-output combinational function; changing the stored bits reconfigures the function. Paired with a D flip-flop, any sequential logic can be expressed as combinational logic plus register, making the LUT+FF pair a universal building block.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Programmable interconnect.&lt;/strong&gt; Individual LUT+FF cells are too small to be useful alone. A mesh of programmable routing resources — segments, switch matrices, and connection boxes controlled by configuration bits — joins thousands of cells into large, arbitrary logic networks. Asserting or de-asserting configuration bits connects or disconnects individual wire segments, giving the designer full control over the netlisting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Programmable I/O.&lt;/strong&gt; Every FPGA pin (except dedicated power, ground, and programming pins) can be configured as input, output, or bidirectional. Voltage standard, drive strength, slew rate, and on-chip termination are all software-settable, allowing the device to interface to a wide range of external components without external level-shifting hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 Design Flow
&lt;/h3&gt;

&lt;p&gt;A typical FPGA design moves through the following stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;RTL entry&lt;/strong&gt; — HDL source (VHDL or Verilog) is written or generated from IP cores, or captured schematically for small blocks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functional simulation&lt;/strong&gt; — logic behavior is verified in a simulator before synthesis. ModelSim is the most widely used tool for this stage, valued for its simulation speed and accuracy. Cadence Verilog-XL and Synopsys VCS are also common in larger teams; ActiveHDL adds a state-machine debugger that is useful for control-path verification.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synthesis&lt;/strong&gt; — the HDL is compiled to a gate-level netlist optimized for area and timing. Synopsys Synplify/Synplify Pro is widely used for its timing-driven synthesis engine and fast runtimes. Xilinx XST (now Vivado Synthesis) and the integrated synthesizer in Altera/Intel Quartus II are the primary vendor alternatives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Implementation (map + place + route)&lt;/strong&gt; — the synthesized netlist is mapped to device primitives, placed within the FPGA fabric, and routed. This stage is performed by vendor tools: Xilinx ISE/Vivado or Intel Quartus II.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Static timing analysis&lt;/strong&gt; — the place-and-route tools report critical paths and maximum achievable clock frequency. The design constraint is that logic utilization must remain below 80% of available macrocells to preserve routability margin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-route simulation&lt;/strong&gt; — gate-level simulation with extracted back-annotation verifies that timing-dependent behavior matches intent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bitstream generation and download&lt;/strong&gt; — a configuration bitstream is generated and programmed into the FPGA (or companion Flash/EEPROM for non-volatile retention).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3. System Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Design Task
&lt;/h3&gt;

&lt;p&gt;The goal is to detect the 2-D position of a catheter tip as it moves within the field of view of an endoscope camera. The catheter enters the camera's field of view from any edge of the frame; only the tip coordinate is of interest — the rest of the catheter body is ignored. The FPGA implementation must keep logic utilization below 80% of available macrocells.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Algorithm Selection
&lt;/h3&gt;

&lt;p&gt;Three candidate detection strategies were evaluated:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A — Color-based segmentation.&lt;/strong&gt; The catheter tip is painted a distinctive color; the FPGA isolates the tip by thresholding a specific color component or HSV range. This approach is simple but requires pre-processing the catheter (painting it) and is sensitive to background clutter with similar colors. Rejected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B — Frame difference method.&lt;/strong&gt; Consecutive frames are subtracted; the absolute-value difference image is thresholded to yield a binary motion mask, highlighting the moving catheter while suppressing the static background. The method is insensitive to lighting and background complexity because it only responds to temporal change. Its weaknesses are sensitivity to high-frequency noise (requiring a denoising pre-stage) and parameter sensitivity: if the inter-frame interval is too short relative to catheter speed, two separated tip blobs appear; if too long relative to a slow-moving catheter, no motion is detected. The resulting binary image shows edges rather than a solid region, requiring frame buffering for correct processing. Selected for this design.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option C — Background subtraction.&lt;/strong&gt; A clean background frame is stored and subtracted from each live frame; the residual is thresholded to isolate the catheter. The method produces a solid binary region (better fill than frame difference), but the stored background becomes stale as lighting or scene conditions change. Compensating for this requires a background-update algorithm (mean, median, Kalman filter, or Gaussian mixture), which adds considerable FPGA complexity. Implementing a robust background update in hardware is the primary difficulty; this option would have been viable but was judged harder to implement reliably within the logic budget.&lt;/p&gt;

&lt;p&gt;The decision matrix is summarized in Table 3.1 (see original figures). &lt;strong&gt;Option B (frame difference) was selected&lt;/strong&gt; based on its combination of acceptable fill quality, low background-complexity requirement, and manageable FPGA implementation effort.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.3 Key Technical Challenges
&lt;/h3&gt;

&lt;p&gt;Seven problems were identified as technically critical:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Image frame buffering in SDRAM&lt;/li&gt;
&lt;li&gt;Image pre-processing (noise removal)&lt;/li&gt;
&lt;li&gt;Frame-difference computation&lt;/li&gt;
&lt;li&gt;Ping-pong SDRAM buffer management&lt;/li&gt;
&lt;li&gt;Pixel format conversion chain&lt;/li&gt;
&lt;li&gt;Binary image projection&lt;/li&gt;
&lt;li&gt;Correct catheter-tip coordinate extraction from projection data&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  3.4 Solutions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Frame buffering.&lt;/strong&gt; The design requires three simultaneous SDRAM accesses: the camera writes incoming pixels to one of two alternating regions, while two read ports simultaneously deliver both buffered frames to the subtraction stage. A dual-buffered SDRAM interface is used, with two FIFOs on the write side and two on the read side, satisfying the three-port concurrency requirement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Image pre-processing.&lt;/strong&gt; Morphological operations are applied before differencing: erosion removes isolated noise pixels, and dilation expands the remaining foreground regions to fill gaps and strengthen connectivity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frame differencing.&lt;/strong&gt; The two buffered frames are subtracted pixel-by-pixel; the absolute value of the difference is taken (|Frame_N − Frame_{N−1}|), eliminating dependence on subtraction order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ping-pong buffer switching.&lt;/strong&gt; The camera write alternates between buffer 0 and buffer 1 on successive frames; the read side mirrors this assignment one frame behind. A common mistake is using the capture or output module's "done" signal to trigger the buffer switch — this can cause data accumulated in the FIFOs to be flushed by a reset before it is read, resulting in partial frame loss. The correct approach is to use the SDRAM controller's internal completion signal to drive the switch, as implemented here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Format conversion.&lt;/strong&gt; The OV7670 outputs YUV 4:2:2 (YCbCr422). The pipeline converts this to YUV444, then to RGB888 (using a lookup table for the YCbCr→RGB matrix), and finally to RGB565 for VGA output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Binary image projection.&lt;/strong&gt; After thresholding the difference image to binary, horizontal and vertical projections are computed: the horizontal projection sums pixel values along each row (Y axis), and the vertical projection sums along each column (X axis). The resulting 1-D profiles are stored and used to find the bounding box of the motion region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip coordinate extraction.&lt;/strong&gt; The four boundary lines derived from the projection profiles define the bounding rectangle of the detected motion region. Within that rectangle, the algorithm computes the midpoint of the horizontal boundary span (endpoint 7 in Figure 3.1), then compares the distance from endpoint 7 to the right boundary against the distance from endpoint 7 to the left boundary to determine catheter orientation. Points lying on the boundaries are classified as non-target; the outlier point not on any boundary is identified as the catheter tip, and its coordinates are output.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Hardware Module Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Power Module
&lt;/h3&gt;

&lt;p&gt;The system requires multiple supply rails: 5 V (VCC), 3.3 V, 2.8 V, 2.5 V, and 1.2 V. All are generated from the AMS1117 family of low-dropout linear regulators. AMS1117 devices are available in fixed-output variants (1.2 V, 1.5 V, 1.8 V, 2.5 V, 2.8 V, 3.0 V, 3.3 V) and an adjustable variant. Each regulator includes built-in thermal shutdown and current-limit protection. A 22 µF output capacitor is placed on each regulator output to ensure stability and suppress high-frequency transients.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Acquisition Module (OV7670 Camera)
&lt;/h3&gt;

&lt;p&gt;The OV7670 CMOS image sensor is selected for its wide availability, well-documented register interface, and straightforward FPGA integration. Key interface signals are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SCCB / I²C bus&lt;/strong&gt; — used to configure internal registers (resolution, format, exposure, white balance, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XCLK&lt;/strong&gt; — master clock input to the sensor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PCLK&lt;/strong&gt; — pixel clock output; runs at twice the pixel rate to allow 8-bit parallel data capture&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FRAME_VALID / LINE_VALID&lt;/strong&gt; — frame and line synchronization signals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOUT[7:0]&lt;/strong&gt; — 8-bit parallel pixel data output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additional signals include STROBE (flash sync), STANDBY/PWDN (power management), and reset. The sensor output is configured for YUV 4:2:2 format.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Buffer Module (SDRAM)
&lt;/h3&gt;

&lt;p&gt;The selected SDRAM is the Hynix H57V2562GTR: 256 Mb (16 M × 16 bit), stable operation to 200 MHz, 4 banks, 13-bit row address, 16-bit data width. It stores the YCbCr 4:2:2 data stream from the camera. In the video capture context, the SDRAM is configured for page-write burst mode with burst-interrupt support, enabling arbitrary burst lengths and maximizing sustained bandwidth — essential for maintaining real-time frame rates in the processing pipeline.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 Display Module (VGA)
&lt;/h3&gt;

&lt;p&gt;VGA is chosen as the display output for its universality: virtually all monitors and projectors support VGA, and the interface is straightforward to implement in FPGA logic (R/G/B analog signals driven by simple resistor-ladder DACs, plus HSYNC and VSYNC). VGA supports high resolutions, fast refresh rates, and full color depth. Limitations — no hot-plug detection, no embedded audio — are not relevant for this application.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;h2&gt;
  
  
  This first installment has covered the project motivation, FPGA architecture fundamentals, algorithm trade-offs, key implementation challenges, and the top-level hardware block diagram. The next installment will present the detailed hardware design: power supply schematics, FPGA peripheral circuits, acquisition circuit, SDRAM buffer circuit, and VGA output circuit. The following installment will then cover the full software (RTL) implementation of each module — acquisition, buffering, frame-difference processing, format decoding, and display — along with simulation waveforms and board-level test results.
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/138665690" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/138665690" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fpgadev</category>
    </item>
    <item>
      <title>Virtual Functions in C++</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Sat, 06 Jun 2026 07:02:09 +0000</pubDate>
      <link>https://dev.to/sienovoleo/virtual-functions-in-c-1a8e</link>
      <guid>https://dev.to/sienovoleo/virtual-functions-in-c-1a8e</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Virtual Functions in C++ (1)
&lt;/h2&gt;

&lt;p&gt;Virtual functions are a mechanism in C++ used to achieve polymorphism. The core idea is to access functions defined in derived classes through a base class.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I. Introduction&lt;/strong&gt;&lt;br&gt;
Virtual functions are a mechanism in C++ used to achieve polymorphism. The core idea is to access functions defined in derived classes through a base class. Suppose we have the following class hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"A::foo() is called"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"B::foo() is called"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when using it, we can:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;B&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Here, although 'a' is a pointer to A, the function called (foo) is B's!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example is a typical application of virtual functions, and through it, you might already have some concept of what they are. Their "virtual" nature lies in what's called "deferred binding" or "dynamic binding," where the call to a class function is not determined at compile time but at runtime. Because it's not possible to determine at the time of writing the code whether the base class's function or a derived class's function will be called, they are referred to as "virtual" functions.&lt;/p&gt;

&lt;p&gt;Virtual functions can only achieve polymorphism through pointers or references. If the code is as follows, even though it's a virtual function, it won't exhibit polymorphism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// A::foo() is called&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1.1 Polymorphism&lt;/p&gt;

&lt;p&gt;After understanding what virtual functions mean, it's easy to grasp polymorphism. Still considering the class hierarchy above, but with a slightly more complex usage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Is A::foo() or B::foo() called?&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;foo()&lt;/code&gt; is a virtual function, within the &lt;code&gt;bar&lt;/code&gt; function, based solely on this code, it's impossible to determine whether &lt;code&gt;A::foo()&lt;/code&gt; or &lt;code&gt;B::foo()&lt;/code&gt; will be called. However, it can be stated with certainty: if &lt;code&gt;a&lt;/code&gt; points to an instance of class A, then &lt;code&gt;A::foo()&lt;/code&gt; is called; if &lt;code&gt;a&lt;/code&gt; points to an instance of class B, then &lt;code&gt;B::foo()&lt;/code&gt; is called.&lt;br&gt;
This characteristic, where the same code can produce different effects, is called "polymorphism."&lt;br&gt;
1.2 What is polymorphism used for?&lt;/p&gt;

&lt;p&gt;Polymorphism is amazing, but what can it be used for? This is a question I find hard to summarize in a sentence or two. Most C++ tutorials (or tutorials for other object-oriented languages) use a drawing example to demonstrate the use of polymorphism, so I won't repeat that example. If you don't know it, any book should cover it. I'll try to describe it from an abstract perspective, and then, by revisiting the drawing example, it might be easier for you to understand.&lt;br&gt;
In object-oriented programming, abstraction (determining base classes) and inheritance (determining derived classes) are first performed on data to form a class hierarchy. If users of this class hierarchy still write code specific to the base class when a base class is needed, and code specific to a derived class when a derived class is needed, it means the class hierarchy is completely exposed to the user. If there are any changes to this class hierarchy (e.g., adding a new class), the user needs to "know" about it (write code for the new class). This increases the coupling between the class hierarchy and its users, which some consider one of the "bad smells" in programming.&lt;br&gt;
Polymorphism can relieve programmers from this predicament. Looking back at the example in 1.1, &lt;code&gt;bar()&lt;/code&gt; as a user of the A-B class hierarchy doesn't know how many classes are in this hierarchy or what each class is called, yet it can still work well. When a class C is derived from class A, &lt;code&gt;bar()&lt;/code&gt; doesn't need to "know" (be modified). This is entirely due to polymorphism—the compiler generates code for virtual functions that can determine the called function at runtime.&lt;br&gt;
1.3 How "Dynamic Binding" Works&lt;/p&gt;

&lt;p&gt;How does the compiler generate code for virtual functions that can determine the called function at runtime? In other words, how are virtual functions actually processed by the compiler? Lippman discusses several methods in different chapters of "Inside the C++ Object Model" [1]. Here, I'll briefly introduce the "standard" method.&lt;br&gt;
What I refer to as the "standard" method is the so-called "VTABLE" mechanism. When the compiler finds a function declared as &lt;code&gt;virtual&lt;/code&gt; in a class, it creates a virtual function table for it, known as a VTABLE. A VTABLE is essentially an array of function pointers, with each virtual function occupying a slot in this array. A class has only one VTABLE, regardless of how many instances it has. Derived classes have their own VTABLEs, but the derived class's VTABLE has the same function arrangement order as the base class's VTABLE; virtual functions with the same name are placed in the same position in both arrays. When creating a class instance, the compiler also adds a &lt;code&gt;vptr&lt;/code&gt; field to the memory layout of each instance, which points to the class's VTABLE. Through these means, when the compiler sees a virtual function call, it rewrites this call. For the example in 1.1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;would be rewritten as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;vptr&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the &lt;code&gt;foo()&lt;/code&gt; functions of derived and base classes have the same VTABLE index, and their &lt;code&gt;vptr&lt;/code&gt;s point to different VTABLEs, this method allows determining which &lt;code&gt;foo()&lt;/code&gt; function to call at runtime.&lt;br&gt;
Although the actual situation is far more complex, the basic principle is roughly as described.&lt;br&gt;
1.4 Overload and Override&lt;/p&gt;

&lt;p&gt;Virtual functions are always rewritten in derived classes; this rewriting is called "override." I often confuse the words "overload" and "override." However, with the increasing number of C++ books, later programmers may no longer make the mistakes I did. But I intend to clarify:&lt;br&gt;
&lt;code&gt;override&lt;/code&gt; refers to a derived class rewriting a virtual function of its base class, just as our class B rewrote the &lt;code&gt;foo()&lt;/code&gt; function in class A. The rewritten function must have a consistent parameter list and return type (the C++ standard allows for different return types in some cases, which I will briefly introduce in the "Syntax" section, but few compilers support this feature). This word doesn't seem to have a suitable Chinese equivalent; some translate it as "覆盖" (cover), which is somewhat apt.&lt;br&gt;
&lt;code&gt;overload&lt;/code&gt; is conventionally translated as "重载." It refers to writing a function with the same name as an existing function but with a different parameter list. For example, a function that can accept an integer as a parameter and also a floating-point number as a parameter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;II. Virtual Function Syntax&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The hallmark of a virtual function is the "virtual" keyword.&lt;br&gt;
2.1 Using the &lt;code&gt;virtual&lt;/code&gt; keyword&lt;/p&gt;

&lt;p&gt;Consider the following class hierarchy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// No virtual keyword!&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;C&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="c1"&gt;// Inherits from B, not A!&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Also no virtual keyword!&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, &lt;code&gt;B::foo()&lt;/code&gt; is a virtual function, and &lt;code&gt;C::foo()&lt;/code&gt; is also a virtual function. Therefore, it can be said that a virtual function declared in a base class remains virtual in derived classes, even if the &lt;code&gt;virtual&lt;/code&gt; keyword is no longer used.&lt;br&gt;
2.2 Pure Virtual Functions&lt;/p&gt;

&lt;p&gt;The following declaration indicates a function is a pure virtual function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// =0 marks a virtual function as pure virtual&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a function is declared pure virtual, it means: "I am an abstract class! Do not instantiate me!" Pure virtual functions are used to standardize the behavior of derived classes, essentially serving as an "interface." They tell the user that all my derived classes will have this function.&lt;br&gt;
2.3 Virtual Destructors&lt;/p&gt;

&lt;p&gt;Destructors can also be virtual, or even pure virtual. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Pure virtual destructor&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a class is intended to be used as a base class for other classes, its destructor must be virtual. Consider the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ptra_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;];}&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ptra_&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt; &lt;span class="c1"&gt;// Non-virtual destructor&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ptra_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ptrb_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;];}&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;ptrb_&lt;/span&gt;&lt;span class="p"&gt;;}&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;ptrb_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, the program might not run as you expect. When &lt;code&gt;delete a&lt;/code&gt; is executed, only &lt;code&gt;A::~A()&lt;/code&gt; is actually called, and the destructor of class B is not called! Isn't that a bit scary?&lt;br&gt;
If &lt;code&gt;A::~A()&lt;/code&gt; above is changed to &lt;code&gt;virtual&lt;/code&gt;, it can guarantee that &lt;code&gt;B::~B()&lt;/code&gt; is also called when &lt;code&gt;delete a&lt;/code&gt;. Therefore, base class destructors must be &lt;code&gt;virtual&lt;/code&gt;.&lt;br&gt;
A pure virtual destructor serves no particular purpose; being virtual is sufficient. Usually, a pure virtual destructor is used only when you want to make a class abstract (a class that cannot be instantiated) and there is no other suitable function to make pure virtual.&lt;br&gt;
2.4 Virtual Constructors?&lt;br&gt;
Constructors cannot be virtual.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;III. Virtual Function Usage Tips&lt;/strong&gt;&lt;br&gt;
3.1 Private Virtual Functions&lt;/p&gt;

&lt;p&gt;Consider the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;();}&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;private:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, even though &lt;code&gt;bar()&lt;/code&gt; is private in class A, it can still appear in derived classes and still produce polymorphic effects just like public or protected virtual functions. It won't happen that &lt;code&gt;A::foo()&lt;/code&gt; cannot access &lt;code&gt;B::bar()&lt;/code&gt; because it's private, nor will &lt;code&gt;B::bar()&lt;/code&gt;'s override of &lt;code&gt;A::bar()&lt;/code&gt; be ineffective.&lt;br&gt;
The semantic meaning of this writing style is: A tells B, "You'd better override my &lt;code&gt;bar()&lt;/code&gt; function, but don't worry about how it's used, and don't call this function yourself."&lt;br&gt;
3.2 Virtual Function Calls in Constructors and Destructors&lt;/p&gt;

&lt;p&gt;When a class's virtual function is called within its own constructor or destructor, they become ordinary functions and are no longer "virtual." This means you cannot achieve polymorphism within constructors and destructors. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();}&lt;/span&gt; &lt;span class="c1"&gt;// Here, A::foo() is always called, no matter what!&lt;/span&gt;
&lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;A&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();}&lt;/span&gt; &lt;span class="c1"&gt;// Same as above&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;B&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;A&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;A&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;B&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you expect &lt;code&gt;delete a&lt;/code&gt; to cause &lt;code&gt;B::foo()&lt;/code&gt; to be called, then you are mistaken. Similarly, when &lt;code&gt;new B&lt;/code&gt; is executed, A's constructor is called, but within A's constructor, &lt;code&gt;A::foo()&lt;/code&gt; is called, not &lt;code&gt;B::foo()&lt;/code&gt;.&lt;br&gt;
3.3 Virtual Functions in Multiple Inheritance 3.4 When to use virtual functions&lt;/p&gt;

&lt;p&gt;When designing a base class, if you find that a function needs to behave differently in derived classes, then it should be virtual. From a design perspective, a virtual function in a base class is an interface, and a virtual function in a derived class is a concrete implementation of that interface. Through this method, the behavior of objects can be abstracted.&lt;br&gt;
Taking the Factory Method pattern [2] in design patterns as an example, the &lt;code&gt;factoryMethod()&lt;/code&gt; of the Creator is a virtual function. After derived classes override this function, they produce different Product classes, and the generated Product classes are used by the base class's &lt;code&gt;AnOperation()&lt;/code&gt; function. The base class's &lt;code&gt;AnOperation()&lt;/code&gt; function operates on the Product class, and naturally, the Product class must also have polymorphism (virtual functions).&lt;br&gt;
Another example is collection operations. Suppose you have a class hierarchy with class A as the base class, and you use a &lt;code&gt;std::vector&lt;/code&gt; to store pointers to instances of different classes in this hierarchy. You would certainly want to operate on the classes in this collection without having to cast each pointer back to its original type (derived class), but rather perform the same operation on all of them. In this case, that "same operation" should be declared &lt;code&gt;virtual&lt;/code&gt;.&lt;br&gt;
In reality, there are far more examples than the two I've given, but the general principle is what I stated earlier: "if you find that a function needs to behave differently in derived classes, then it should be virtual." This statement can also be reversed: "if you find that a base class provides a virtual function, then you'd better override it."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Appendix: Usage of Virtual Functions and Pure Virtual Functions in C++&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Virtual functions and pure virtual functions can be defined in the same class. A class containing pure virtual functions is called an abstract class, while a class containing only virtual functions cannot be called an abstract class.&lt;/li&gt;
&lt;li&gt;A virtual function can be used directly or called polymorphically after being overridden by a subclass. A pure virtual function must be implemented in a subclass before it can be used, because a pure virtual function only has a declaration but no definition in the base class.&lt;/li&gt;
&lt;li&gt;Both virtual functions and pure virtual functions can be overridden in subclasses and called polymorphically.&lt;/li&gt;
&lt;li&gt;Virtual functions and pure virtual functions typically exist in abstract base classes (ABC) and are overridden by inherited subclasses to provide a unified interface.&lt;/li&gt;
&lt;li&gt;The definition form of a virtual function is: &lt;code&gt;virtual {method body}&lt;/code&gt;; the definition form of a pure virtual function is: &lt;code&gt;virtual { } = 0;&lt;/code&gt;. The &lt;code&gt;static&lt;/code&gt; identifier cannot be used in the definition of virtual functions and pure virtual functions. The reason is simple: functions modified by &lt;code&gt;static&lt;/code&gt; require early binding at compile time, whereas virtual functions are dynamically bound (run-time bind), and the lifetime of functions modified by both is also different.&lt;/li&gt;
&lt;li&gt;If a class contains pure virtual functions, any attempt to instantiate that class will result in an error, because abstract base classes (ABC) cannot be called directly. They must be inherited and overridden by subclasses, and their subclass methods called as required.
The following is a simple demonstration of virtual and pure virtual function usage, intended to spark further thought!
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include
&lt;/span&gt;&lt;span class="c1"&gt;//father class&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Virtualbase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Demon&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//prue virtual function&lt;/span&gt;
&lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"this is farther class"&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;//sub class&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SubVirtual&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Virtualbase&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;public:&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Demon&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;" this is SubVirtual!"&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;cout&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="s"&gt;"this is subclass Base"&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="cm"&gt;/* instance class and sample */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="n"&gt;Virtualbase&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;inst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;SubVirtual&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;//multstate pointer&lt;/span&gt;
&lt;span class="n"&gt;inst&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Demon&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;inst&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// inst = new Virtualbase();&lt;/span&gt;
&lt;span class="c1"&gt;// inst-&amp;gt;Base()&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/7233742" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/7233742" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>function</category>
      <category>c</category>
      <category>class</category>
      <category>compiler</category>
    </item>
    <item>
      <title>VXWORKS Kernel Analysis</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Fri, 05 Jun 2026 07:26:22 +0000</pubDate>
      <link>https://dev.to/sienovoleo/vxworks-kernel-analysis-35o</link>
      <guid>https://dev.to/sienovoleo/vxworks-kernel-analysis-35o</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;VXWORKS Kernel Analysis&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Structure of Real-time Operating Systems&lt;/p&gt;

&lt;p&gt;The most primitive structural form of operating systems developed in the early days of computing was a monolithic entity. In such systems, modules providing different functionalities, such as processor management, memory management, and input/output, were typically independent. However, they did not consider other modules in use during their execution, and each module ran with the same time granularity.&lt;/p&gt;

&lt;p&gt;As modern real-time environments require many different functionalities, and due to the asynchrony and non-determinism caused by concurrent activities in such environments, operating systems have become more complex. Therefore, the monolithic structure of early operating systems has been superseded by more precise internal structures.&lt;/p&gt;

&lt;p&gt;The Starting Point of Hierarchical Structure - The Kernel&lt;/p&gt;

&lt;p&gt;The best internal structural model for an operating system is a hierarchical one, with the kernel at the lowest layer. These layers can be seen as an inverted pyramid, with each layer building upon the functionalities of the lower layers. The kernel contains only the most important low-level functions executed by an operating system. Like a monolithic operating system, the kernel provides an abstraction layer between high-level software and low-level hardware. However, the kernel only provides a minimal set of operations required to construct the other parts of the operating system.&lt;/p&gt;

&lt;p&gt;Requirements for a Real-time Kernel&lt;/p&gt;

&lt;p&gt;A real-time operating system kernel must satisfy several fundamental requirements imposed by specific real-time environments. These include:&lt;/p&gt;

&lt;p&gt;Multitasking: Due to the asynchronous nature of real-world events, it is crucial to be able to run many concurrent processes or tasks. Multitasking provides a better match to the real world because it allows multithreaded execution corresponding to many external events. The system kernel allocates the CPU to these tasks to achieve concurrency.&lt;/p&gt;

&lt;p&gt;Preemptive Scheduling: Real-world events have inherent priorities, and these priorities must be considered when allocating the CPU. With priority-based preemptive scheduling, tasks are assigned priorities, and among the tasks that are ready to execute (not suspended or waiting for resources), the highest-priority task is allocated CPU resources. In other words, when a higher-priority task becomes executable, it immediately preempts the currently running lower-priority task.&lt;/p&gt;

&lt;p&gt;Fast and Flexible Inter-task Communication and Synchronization: In a real-time system, many tasks may execute as part of an application. The system must provide fast and powerful communication mechanisms between these tasks. The kernel must also provide synchronization mechanisms required for efficiently sharing non-preemptable resources or critical sections.&lt;/p&gt;

&lt;p&gt;Convenient Communication Between Tasks and Interrupts: Although real-world events often arrive as interrupts, to provide effective queuing, prioritization, and reduced interrupt latency, it is often desirable to handle the corresponding work at the task level. Therefore, communication between the task level and the interrupt level is necessary.&lt;/p&gt;

&lt;p&gt;Performance Bounds: A real-time kernel must provide worst-case performance optimization, rather than throughput optimization. We prefer a system that can consistently execute a function in 50 microseconds over one that executes it in 10 microseconds on average but occasionally takes 75 microseconds.&lt;/p&gt;

&lt;p&gt;Special Considerations: As the requirements for real-time kernels increase, the need for the kernel to support increasingly complex functionalities must be considered. This includes multiprocessing, Ada, and support for newer, more powerful processor architectures like RISC.&lt;/p&gt;

&lt;p&gt;Kernels with Other Names&lt;/p&gt;

&lt;p&gt;Many commercial kernels support far more functionality than the requirements listed above. In this respect, they are not true kernels but more like small monolithic operating systems, as they include simple memory allocation, clock management, and even some input/output system calls.&lt;/p&gt;

&lt;p&gt;This classification is not merely a semantic argument; later sections of this article will illustrate the importance of limiting kernel functionality and optimizing these functions.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;VxWorks Kernel: Wind&lt;/p&gt;

&lt;p&gt;The VxWorks operating system is one of the most fully-featured, processor-independent real-time systems available today. However, VxWorks is a hierarchical structure with a relatively small, true microkernel. The kernel only provides a multitasking environment, inter-process communication, and synchronization functions. These functional modules are sufficient to support the rich performance requirements provided by VxWorks at higher levels. Kernel operations are typically invisible to the user. Applications use system calls to implement task management and synchronization that require kernel involvement, but the processing of these calls is invisible to the calling task. Applications simply link the appropriate VxWorks routines (often using VxWorks' dynamic linking capability) and issue system calls as if calling subroutines. This interface is not like some systems that require a clumsy jump table interface where the user specifies a kernel function call via an integer.&lt;/p&gt;

&lt;p&gt;Multitasking&lt;/p&gt;

&lt;p&gt;The fundamental function of the kernel is to provide a multitasking environment. Multitasking makes many programs appear to execute concurrently, while in fact, the kernel executes them in segments according to basic scheduling algorithms. Each seemingly independent program is called a task. Each task has its own context, which includes the CPU environment and system resources it sees when the kernel schedules it for execution.&lt;/p&gt;

&lt;p&gt;Task States&lt;/p&gt;

&lt;p&gt;The kernel maintains the current state of each task in the system. State transitions occur when an application calls a kernel function service. The Wind kernel states are defined as follows:&lt;/p&gt;

&lt;p&gt;Ready state – A task is not waiting for any resources other than the CPU.&lt;br&gt;
Blocked state – A task is blocked because some resource is unavailable.&lt;br&gt;
Delayed state – A task is sleeping for a specified period.&lt;br&gt;
Suspended state – An auxiliary state primarily used for debugging; suspension prevents task execution.&lt;/p&gt;

&lt;p&gt;A task enters the suspended state after creation. Specific operations are required to move the created task into the ready state. This operation is very fast, allowing applications to create tasks in advance and activate them quickly.&lt;/p&gt;

&lt;p&gt;Scheduling Control&lt;/p&gt;

&lt;p&gt;Multitasking requires a scheduling algorithm to allocate the CPU to ready tasks. The default scheduling algorithm in VxWorks is priority-based preemptive scheduling, but applications can also choose to use round-robin scheduling.&lt;/p&gt;

&lt;p&gt;Priority-based Preemptive Scheduling: With priority-based preemptive scheduling, each task is assigned a priority, and the kernel allocates the CPU to the highest-priority task in the ready state. Scheduling is preemptive because when a task with a higher priority than the current task becomes ready, the kernel immediately saves the current task's context and switches to the higher-priority task's context. VxWorks has 256 priorities, from 0 to 255. Tasks are assigned a priority at creation, and this priority can be dynamically modified during task execution to track real-world event priorities. External interrupts are assigned a priority higher than any task, allowing them to preempt a task at any time.&lt;/p&gt;

&lt;p&gt;Round-Robin: Priority-based preemptive scheduling can be extended with round-robin scheduling. Round-robin scheduling allows ready tasks of the same priority to share the CPU fairly. Without round-robin scheduling, when multiple tasks share the processor at the same priority, one task might monopolize the CPU, not being blocked until preempted by a higher-priority task, and not giving other tasks of the same priority a chance to run. If round-robin is enabled, the execution time counter for the running task increments with each clock tick. When the specified time slice expires, the counter is reset, and the task is placed at the end of the queue for tasks of the same priority. New tasks joining a specific priority group are placed at the end of that group's queue, and their run counter is initialized to zero.&lt;/p&gt;

&lt;p&gt;Basic Task Functions&lt;/p&gt;

&lt;p&gt;Basic task functions for state control include task creation, deletion, suspension, and resumption. A task can also put itself to sleep for a specific interval.&lt;br&gt;
Many other task routines provide state information obtained from the task context. These routines include access to a task's current processor register control.&lt;/p&gt;

&lt;p&gt;Task Deletion Issues&lt;/p&gt;

&lt;p&gt;The Wind kernel provides mechanisms to prevent tasks from being accidentally deleted. Typically, a task executing in a critical section or accessing a critical resource needs special protection. Consider the following scenario: a task obtains exclusive access to some data structure, and while it is executing within the critical section, it is deleted by another task. Since the task cannot complete its operation on the critical section, the data structure may be left in a corrupted or inconsistent state. Moreover, if the task has no opportunity to release the resource, then no other task can now acquire that resource; the resource is frozen.&lt;/p&gt;

&lt;p&gt;Any task attempting to delete or terminate a task that has set deletion protection will be blocked. When the protected task completes its critical section operation, it will cancel deletion protection to allow itself to be deleted, thereby unblocking the deleting task.&lt;/p&gt;

&lt;p&gt;As shown above, task deletion protection is often accompanied by mutual exclusion operations.&lt;br&gt;
Thus, for convenience and efficiency, mutex semaphores include a deletion protection option. (See "Mutex Semaphores")&lt;/p&gt;

&lt;p&gt;Inter-task Communication&lt;/p&gt;

&lt;p&gt;To provide full multitasking system functionality, the Wind kernel offers a rich set of inter-task communication and synchronization mechanisms. These communication functions enable individual tasks within an application to coordinate their activities.&lt;/p&gt;

&lt;p&gt;Shared Address Space&lt;/p&gt;

&lt;p&gt;The basis of the Wind kernel's inter-task communication mechanism is the shared address space where all tasks reside. Through a shared address space, tasks can communicate freely using pointers to shared data structures. Pipes do not require mapping a memory region into the address spaces of two communicating tasks.&lt;/p&gt;

&lt;p&gt;Unfortunately, while shared address space offers the aforementioned advantages, it introduces the risk of unprotected reentrant access to memory. UNIX operating systems provide such protection by isolating processes, but this comes with a significant performance penalty for real-time operating systems.&lt;/p&gt;

&lt;p&gt;Mutual Exclusion Operations&lt;/p&gt;

&lt;p&gt;While a shared address space simplifies data exchange, it becomes necessary to avoid resource contention through mutually exclusive access. Many mechanisms used to achieve mutually exclusive access to a resource differ only in the scope of their mutual exclusion. Methods for achieving mutual exclusion include interrupt disabling, task preemption disabling, and resource locking via semaphores.&lt;/p&gt;

&lt;p&gt;Interrupt Disabling: The strongest mutual exclusion method is masking interrupts. Such a lock guarantees exclusive access to the CPU. While this method can certainly solve the mutual exclusion problem, it is inappropriate for real-time systems because it prevents the system from responding to external events during the lock. Long interrupt latencies are unacceptable for applications requiring deterministic response times.&lt;/p&gt;

&lt;p&gt;Preemption Disabling: Disabling preemption provides a weaker form of mutual exclusion. Other tasks are not allowed to preempt the current task while it is running, but interrupt service routines (ISRs) can execute. This can also lead to poor real-time responsiveness, similar to interrupt disabling, as blocked tasks may experience significant preemption latency, and ready high-priority tasks may be forced to wait an unacceptable amount of time before they can execute. To avoid this, semaphores should be used for mutual exclusion whenever possible.&lt;/p&gt;

&lt;p&gt;Mutex Semaphores: Semaphores are the fundamental way to lock access to shared resources. Unlike disabling interrupts or preemption, semaphores limit mutual exclusion operations to only the relevant resources. A semaphore is created to protect a resource. VxWorks semaphores follow Dijkstra's P() and V() operation model.&lt;/p&gt;

&lt;p&gt;When a task requests a semaphore, P(), two things can happen, depending on the set or cleared state of the semaphore at the time of the call. If the semaphore is in the set state, it is cleared, and the task immediately continues execution. If the semaphore is in the cleared state, the task is blocked to wait for the semaphore.&lt;/p&gt;

&lt;p&gt;When a task releases a semaphore, V(), several things can happen. If the semaphore is already in the set state, releasing it has no effect. If the semaphore is in the cleared state and no tasks are waiting for it, the semaphore is simply set. If the semaphore is in the cleared state and one or more tasks are waiting for it, the highest-priority task is unblocked, and the semaphore remains in the cleared state.&lt;/p&gt;

&lt;p&gt;By associating resources with semaphores, mutual exclusion operations can be achieved. When a task wants to operate on a resource, it must first acquire the semaphore. As long as the task owns the semaphore, all other tasks requesting that semaphore are blocked. When a task finishes using the resource, it releases the semaphore, allowing another task waiting for the semaphore to access the resource.&lt;/p&gt;

&lt;p&gt;The Wind kernel provides binary semaphores to address problems caused by mutual exclusion operations. These problems include deletion protection for resource owners and priority inversion caused by resource contention.&lt;/p&gt;

&lt;p&gt;Deletion Protection – One problem caused by mutual exclusion involves task deletion. In a critical section protected by a semaphore, it is necessary to prevent the executing task from being accidentally deleted. Deleting a task executing in a critical section is catastrophic. The resource will be corrupted, and the semaphore protecting the resource will become unavailable, making the resource inaccessible. Deletion protection is usually provided in conjunction with mutual exclusion operations. For this reason, mutex semaphores often provide options to implicitly offer the task deletion protection mechanisms mentioned earlier.&lt;/p&gt;

&lt;p&gt;Priority Inversion / Priority Inheritance – Priority inversion occurs when a high-priority task is forced to wait for an indefinite period for a lower-priority task to complete execution. Consider the following hypothesis:&lt;/p&gt;
&lt;h2&gt;
  
  
  T1, T2, and T3 are high, medium, and low priority tasks, respectively. T3 acquires a related resource by owning a semaphore. When T1 preempts T3 and requests the same
&lt;/h2&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/7272065" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/7272065" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>task</category>
      <category>datastructure</category>
      <category>algorithms</category>
      <category>performanceoptimization</category>
    </item>
    <item>
      <title>Detailed Explanation of ANSI C Standard File I/O Functions</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Thu, 04 Jun 2026 07:34:43 +0000</pubDate>
      <link>https://dev.to/sienovoleo/detailed-explanation-of-ansi-c-standard-file-io-functions-gkp</link>
      <guid>https://dev.to/sienovoleo/detailed-explanation-of-ansi-c-standard-file-io-functions-gkp</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  I. Overview
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The ANSI C file system is built upon the buffered file system of earlier C versions (also known as formatted or high-level file systems).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Difference between Streams and Files:&lt;/strong&gt; The level of abstraction provided by C's I/O system between the programmer and the device being used is called a stream, while the physical device is called a file. The C file system can operate on various devices such as terminals, disk drives, and tape drives. Regardless of how different these devices are, the ANSI file system converts them into logical devices called "streams," providing great device independence. In C, the logical concept of a file refers to anything from a disk file to a terminal printer. A stream is associated with a file by performing an open operation. Once a file is opened, information can be exchanged between the program and that file. Not all files have the same capabilities; for example, disk files support random access, but terminals do not. This illustrates an important characteristic of the C I/O system: all streams are similar, but files are different.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There are two types of streams: text streams and binary streams. A text stream is a sequence of characters. In a text stream, specific character conversions are required by the host environment, so there is no one-to-one correspondence between the characters written (or read) and the characters stored in the device. Similarly, due to possible conversions, the number of characters written (or read) may not match the number of characters stored in the device. A binary stream, on the other hand, is a sequence of bytes where the storage in the device is one-to-one, meaning there are no character conversions. Therefore, the number of bytes written (or read) matches the number of bytes stored in the device.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Files are linked to streams through a close operation. For output streams, when a file is closed, the associated stream is written to the device, a process commonly referred to as flushing. This ensures that no information is left in the disk buffer. When a program ends, all files are automatically closed; however, if a program crashes, files may not be closed properly, meaning information might not be written to disk. Each stream associated with a file has a control structure of type &lt;code&gt;FILE&lt;/code&gt;, which is defined in &lt;code&gt;stdio.h&lt;/code&gt;, and cannot be modified directly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The following functions and types are defined in &lt;code&gt;stdio.h&lt;/code&gt;:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- `fopen()`
- `fclose()`
- `putc()`
- `fputc()`
- `getc()`
- `fgetc()`
- `fseek()`
- `fprintf()`
- `fscanf()`
- `feof()` — returns true if the end of the file is reached
- `ferror()`
- `rewind()`
- `remove()` — deletes a file
- `fflush()` — flushes a file
- `fread()` — reads from a file
- `fwrite()` — writes to a file

Additionally, types such as `size_t` (large enough to hold the result of subtracting two pointers, a variant of `unsigned int`), `fpos_t` (used to describe a specific position in a file, also a variant of `unsigned int`), and the `FILE` type are defined. Several macros are also defined: `EOF` (typically defined as -1), `SEEK_SET`, `SEEK_CUR`, and `SEEK_END`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt; When operating on files in read/write mode, two points must be noted: First, if a write operation is followed by a read operation, the &lt;code&gt;fflush()&lt;/code&gt; function or file positioning functions such as &lt;code&gt;fseek()&lt;/code&gt; or &lt;code&gt;rewind()&lt;/code&gt; must be called. Second, if a read operation is followed by a write operation, it must be at the end of the file or a file positioning function must be called between the two operations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  II. Function Details
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Opening and Closing Files&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(1) &lt;strong&gt;Opening Files:&lt;/strong&gt; The function to open files in the ANSI C library is declared as follows:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;fopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kr"&gt;restrict&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="kr"&gt;restrict&lt;/span&gt; &lt;span class="n"&gt;modes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If successful, it returns a pointer to the opened file. If it fails, it returns &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The first parameter is a pointer to the string of the filename to be opened (e.g., &lt;code&gt;/etc/service&lt;/code&gt;). The second parameter specifies the mode for opening the file. The opening modes are as follows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;r&lt;/code&gt; (or &lt;code&gt;rb&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Opens the file in read-only mode; the file must exist.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;r+&lt;/code&gt; (or &lt;code&gt;rb+&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Opens the file in read/write mode; the file must exist.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;w&lt;/code&gt; (or &lt;code&gt;wb&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Opens the file in write-only mode; if the file exists, it is cleared; if it does not exist, it is created.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;w+&lt;/code&gt; (or &lt;code&gt;wb+&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Opens the file in read/write mode; if the file exists, it is cleared; if it does not exist, it is created.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a&lt;/code&gt; (or &lt;code&gt;ab&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Opens the file in write-only mode for appending; if the file exists, data is appended to the end; if it does not exist, it is created.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;a+&lt;/code&gt; (or &lt;code&gt;ab+&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Opens the file in read/write mode for appending; if the file exists, data can be read/written at the end; if it does not exist, it is created.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;(2) &lt;strong&gt;Closing Files:&lt;/strong&gt; The function to close a file is declared as:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fclose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;To close all opened stream objects, the &lt;code&gt;fcloseall&lt;/code&gt; function can be used:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fcloseall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;To update the contents of the buffer, even if the buffer is not full, the &lt;code&gt;fflush&lt;/code&gt; function can be used:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fflush&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reading and Writing File Streams&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(1) &lt;strong&gt;Character Read/Write Operations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(a) &lt;strong&gt;Character Read Operation:&lt;/strong&gt; This operation reads one character at a time from the stream. The related function declarations are:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fgetc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Reads one character from the stream&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Reads one character from standard input&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;getc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Equivalent to fgetc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;(b) &lt;strong&gt;Character Write Operation:&lt;/strong&gt; This operation writes one character at a time to the stream. The relevant function declarations are:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fputc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Writes a character to an output file stream&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;putc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Similar to fputc&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;putchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Equivalent to putc(c, stdout)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;(2) &lt;strong&gt;Line Read/Write Operations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(a) &lt;strong&gt;Line Read Operation:&lt;/strong&gt; This operation reads one line of characters from the stream. The &lt;code&gt;fgets&lt;/code&gt; function reads a string from the input file stream and writes it to the string pointed to by &lt;code&gt;s&lt;/code&gt;, stopping at a newline or end-of-file marker, and appends a null byte (&lt;code&gt;\0&lt;/code&gt;) at the end.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;fgets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The &lt;code&gt;gets&lt;/code&gt; function is similar to &lt;code&gt;fgets&lt;/code&gt;, but it reads from standard input and discards the newline character.&lt;/p&gt;

&lt;p&gt;(b) &lt;strong&gt;Line Write Operation:&lt;/strong&gt; This operation writes one line of characters to the standard output. The &lt;code&gt;puts&lt;/code&gt; function writes the string pointed to by &lt;code&gt;s&lt;/code&gt; (terminated with a null character) to standard output, followed by a newline. The &lt;code&gt;fputs&lt;/code&gt; function writes the string pointed to by &lt;code&gt;s&lt;/code&gt; to the specified output stream but does not append a newline.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fputs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;(3) &lt;strong&gt;Block Read/Write Operations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(a) &lt;strong&gt;Block Read Operation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fread&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;code&gt;fread()&lt;/code&gt; reads &lt;code&gt;size&lt;/code&gt; bytes from the file pointed to by &lt;code&gt;fp&lt;/code&gt; starting from the current position, repeating this &lt;code&gt;count&lt;/code&gt; times, and stores the data in the memory starting at &lt;code&gt;buffer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(b) &lt;strong&gt;Block Write Operation:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;code&gt;fwrite()&lt;/code&gt; outputs &lt;code&gt;size&lt;/code&gt; bytes from &lt;code&gt;buffer&lt;/code&gt; to the file pointed to by &lt;code&gt;fp&lt;/code&gt;, repeating this &lt;code&gt;count&lt;/code&gt; times. This is generally used for handling binary files.&lt;/p&gt;

&lt;p&gt;(c) &lt;strong&gt;Example of Writing a String to a File:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hello, I am a test program!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;To write a character array to a file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sc"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'b'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'c'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'d'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sc"&gt;'e'&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;To write an array of integers to a file:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;nmemb&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="n"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;nmemb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fp&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Note: Since the generated file is a binary file rather than a text file, the representation of integers may vary across machines, so it cannot be opened directly. You can use the &lt;code&gt;fread&lt;/code&gt; function to verify if the data has been written to the file.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;File Stream Positioning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(1) &lt;strong&gt;Returning Current Read/Write Position:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;ftell&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If successful, it returns the current position of the pointer in bytes from the beginning of the file; if it fails, it returns -1.&lt;/p&gt;

&lt;p&gt;(2) &lt;strong&gt;Modifying Current Read/Write Position:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The usage of &lt;code&gt;fseek&lt;/code&gt; is as follows:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fseek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fromwhere&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The first parameter is the file pointer, the second parameter is the offset to move, and the third parameter indicates where to move from, using three macros:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- `SEEK_SET` (0) — beginning of the file
- `SEEK_CUR` (1) — current position in the file
- `SEEK_END` (2) — end of the file

It is recommended to use macros instead of numbers. In summary:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```c
fseek(fp, 100L, SEEK_SET); // Move the pointer 100 bytes from the start of the file
fseek(fp, 100L, SEEK_CUR); // Move the pointer 100 bytes from the current position
fseek(fp, 100L, SEEK_END); // Move the pointer back 100 bytes from the end of the file
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This function is commonly used to calculate the length of a stream:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```c
int filesize = fseek(fp, 0, SEEK_END);
fseek(fp, 0, SEEK_SET);
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(3) **Resetting Current Read/Write Position:** After completing one operation, to prepare for the next operation, the `rewind` function should be called to reset the read/write position to the beginning of the file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```c
void rewind(FILE *stream);
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;File Stream Error Detection:&lt;/strong&gt; To identify an error, many &lt;code&gt;stdio&lt;/code&gt; library functions return an out-of-bounds value, such as a null pointer or the constant &lt;code&gt;EOF&lt;/code&gt;. In these cases, the errors are indicated by the external variable &lt;code&gt;errno&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;errno.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;errno&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;It is important to note that many functions may change the value of &lt;code&gt;errno&lt;/code&gt;. The value is only valid when a function call fails. It is advisable to check the value of &lt;code&gt;errno&lt;/code&gt; immediately after a function indicates failure. Before using it, copy its value to another variable, as some printing functions, like &lt;code&gt;fprintf&lt;/code&gt;, may modify its value.&lt;/p&gt;

&lt;p&gt;You can also determine whether an error has occurred or if the end of the file has been reached by checking the status of the file stream.&lt;/p&gt;

&lt;p&gt;To check if the end of the file has been reached:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;feof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If the end of the file is reached, it returns 1; otherwise, it returns 0.&lt;/p&gt;

&lt;p&gt;To check for errors in a given stream:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;ferror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If no error has occurred, it returns 0; otherwise, it returns an error, which is stored in &lt;code&gt;errno&lt;/code&gt;. When using the above two functions for file stream detection, the error flag will be set. After error handling, the error flag should be cleared:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;clearerr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;The &lt;code&gt;clearerr&lt;/code&gt; function clears the end-of-file or error indicator for the file stream pointed to by &lt;code&gt;stream&lt;/code&gt;. This function does not return a value or define an error. You can use this function to recover from error conditions on the stream, such as when the disk is full and data needs to be rewritten to the file stream.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Streams and File Descriptors:&lt;/strong&gt; Each file stream corresponds to a lower-level file descriptor. While it is possible to mix low-level input/output with high-level file stream operations, it is generally unwise due to unpredictable buffer effects.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;fileno&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Returns the file descriptor for a given file stream&lt;/span&gt;
&lt;span class="kt"&gt;FILE&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;fdopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fildes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Creates a new file stream based on an existing file descriptor&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;By calling the &lt;code&gt;fileno&lt;/code&gt; function, you can determine which lower-level file descriptor is being used by a file stream. It returns a file descriptor for the specified file stream; if it fails, it returns -1. If you need low-level access to an open stream, you can use this function, such as with &lt;code&gt;fstat&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fdopen&lt;/code&gt; function allows you to create a new file stream based on an already opened file descriptor. Essentially, this function provides a &lt;code&gt;stdio&lt;/code&gt; buffer for an already opened file descriptor, which can be useful for clarity. The &lt;code&gt;fdopen&lt;/code&gt; function operates similarly to &lt;code&gt;fopen&lt;/code&gt;, except that it uses a lower-level file descriptor. If you want to use &lt;code&gt;open&lt;/code&gt; to create a file, perhaps for better permission control, but wish to perform write operations using file streams, this function becomes particularly useful. The &lt;code&gt;mode&lt;/code&gt; parameter is the same as that for &lt;code&gt;fopen&lt;/code&gt;, and it must be compatible with the file access mode established when the file was initially opened. &lt;code&gt;fdopen&lt;/code&gt; returns a new file stream; if it fails, it returns &lt;code&gt;NULL&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File and Directory Maintenance:&lt;/strong&gt; The standard library and system calls provide complete control over file creation and maintenance.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- **`chmod`:** You can use the `chmod` system call to change the permissions of a file or directory. The syntax is as follows:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```c
#include &amp;lt;sys/stat.h&amp;gt;
int chmod(const char *path, mode_t mode);
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The file specified by `path` will have the permissions specified by `mode`. The `mode` specified is similar to that in the `open` system call, being a bitwise OR of the desired permissions. Unless the program is granted appropriate permissions, only the file's owner or a superuser can change its permissions.

- **`chown`:** A superuser can use the `chown` system call to change the owner of a file. The syntax is as follows:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```c
#include &amp;lt;unistd.h&amp;gt;
int chown(const char *path, uid_t owner, gid_t group);
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This call uses the numeric user ID or group ID (which can be obtained via `getuid` and `getgid`) and a constant to determine who can change the file's owner. With appropriate permissions set, we can change both the owner and the group of a file.

- **`unlink`, `link`, `symlink`:** You can use `unlink` to remove a file. `unlink` removes the directory entry for a file and decreases its link count. If the function call is successful, it returns 0; if it fails, it returns -1. You must have write and execute permissions in the directory where the command is executed, as files have their own directory entries.

The syntax is as follows:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```c
int unlink(const char *path);
int link(const char *path1, const char *path2);
int symlink(const char *path1, const char *path2);
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If the link count reaches 0 and no processes are using the file, the file will be deleted. In fact, a directory entry is always deleted, but the file's space will not be reclaimed until the last associated process is closed. The `rm` program uses this call. Typically, we can use the `ln` program to create a link for a file. We can use the `link` system call to create a planned link for a file. The `link` system call creates a new link for an existing file specified by `path1`, with the new directory entry specified by `path2`. Similarly, we can use `symlink` to create a symbolic link. It is important to note that a file's symbolic link will not prevent the file from being deleted, unlike a hard link.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/6830811" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/6830811" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>language</category>
      <category>c</category>
      <category>io</category>
      <category>stream</category>
    </item>
    <item>
      <title>Power Data Acquisition and Transmission Design Based on OMAPL138 + Xilinx Spartan-6</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Wed, 03 Jun 2026 07:37:59 +0000</pubDate>
      <link>https://dev.to/sienovoleo/power-data-acquisition-and-transmission-design-based-on-omapl138-xilinx-spartan-6-4oh9</link>
      <guid>https://dev.to/sienovoleo/power-data-acquisition-and-transmission-design-based-on-omapl138-xilinx-spartan-6-4oh9</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ma Shixi and Yang Han, researchers from Wuhan Fiberhome Furi Electric Co., Ltd., presented in the 10th issue of "Electrical Engineering" magazine in 2015 a power data acquisition and transmission design based on OMAPL138. This solution is built upon Sienovo's OMAPL138+SPARTAN6 development board, with the OMAPL138 microprocessor at its core. The microprocessor analyzes and processes acquired data such as voltage, current, and digital inputs, then transmits the processed data to upper-level host systems via interfaces like RS485/RS232 and Ethernet using various communication protocols. The article details the functions of each module, hardware design, and software design flow. This design is flexible, convenient, and offers significant practical value.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1 Evaluation Board Overview&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Based on TI OMAP-L138 (fixed/floating-point DSP C674x + ARM9) + Xilinx Spartan-6 FPGA processor;&lt;/li&gt;
&lt;li&gt;  OMAP-L138 and FPGA are connected via uPP, EMIFA, and I2C buses, supporting communication speeds up to 228 MByte/s; OMAP-L138 operates at a main frequency of 456 MHz, delivering up to 3648 MIPS and 2746 MFLOPS computing performance;&lt;/li&gt;
&lt;li&gt;  FPGA compatible with Xilinx Spartan-6 XC6SLX9/16/25/45, offering strong platform scalability;&lt;/li&gt;
&lt;li&gt;  The development board exposes rich peripherals, including Gigabit Ethernet, SATA, EMIFA, uPP, USB 2.0, and other high-speed data transmission interfaces, as well as common interfaces such as GPIO, I2C, RS232, PWM, and McBSP;&lt;/li&gt;
&lt;li&gt;  Passed high and low-temperature testing certification, suitable for harsh operating environments;&lt;/li&gt;
&lt;li&gt;  Triple-core (DSP+ARM+FPGA) core board measuring 66mm × 38.6mm, using industrial-grade B2B connectors to ensure signal integrity;&lt;/li&gt;
&lt;li&gt;  Supports bare-metal, SYS/BIOS, and Linux operating systems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib7zvd942o8f2zq3hsqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib7zvd942o8f2zq3hsqe.png" width="799" height="454"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In power system operations, large volumes of data from electrical equipment—such as three-phase voltages, three-phase currents, and switch status signals—must be collected and transmitted over long distances. As power systems continue to evolve, increasingly higher demands are placed on the accuracy of data acquisition and the timeliness of data transmission[1].&lt;/p&gt;

&lt;p&gt;Traditional low-end measurement instruments based on microcontrollers suffer from poor data processing capabilities and limited module functionality. Mid-to-high-end instruments based on DSPs take advantage of DSPs' strong data processing performance but often lack sufficient storage capacity and communication capabilities.&lt;/p&gt;

&lt;p&gt;Since the International Electrotechnical Commission (IEC) adopted IEC61850 as the standard protocol for seamless communication in power systems in 2000, IEC61850 has gradually been applied in power systems and substation automation both domestically and internationally[2]. However, many existing meters and automated monitoring devices still fail to meet IEC61850 requirements. In practice, both Modbus and IEC61850 protocols often coexist, and devices supporting only a single protocol face significant limitations in application.&lt;/p&gt;

&lt;p&gt;To address this issue, this paper proposes a data acquisition and transmission device design based on OMAPL138 that supports multiple communication protocols. The proposed solution can simultaneously support two different protocols, offering flexibility, convenience, and high practical value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1 Overall Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The hardware design is based on the OMAPL138 processor, a dual-core embedded microprocessor combining ARM and DSP, developed by Texas Instruments. It features rich peripheral interfaces, including DDR2 controller, Ethernet controller, UARTs, SPI, I2C, LCD controller, USB, and others[3].&lt;/p&gt;

&lt;p&gt;The FPGA is compatible with Xilinx Spartan-6 XC6SLX9/16/25/45, enabling strong platform upgrade capability. The use of an OMAPL138+SPARTAN6 core board facilitates secondary development.&lt;/p&gt;

&lt;p&gt;In this design, the OMAPL138 microprocessor connects to Nand-Flash via the EMIFA bus. The Nand-Flash stores UBOOT, the operating system kernel, file system, application programs, and related data. OMAPL138 connects to a DDR2SDRAM chip via the DDR2 bus, which is used for running the operating system and applications and storing temporary files.&lt;/p&gt;

&lt;p&gt;As shown in Figure 1, OMAPL138 connects to an external analog-to-digital converter (ADC) via the SPI interface to acquire analog signals. It uses universal asynchronous serial ports (UARTs) to multiplex between RS232 and RS485 modes, transmitting acquired data using the Modbus protocol from the acquisition terminal to the backend system.&lt;/p&gt;

&lt;p&gt;OMAPL138 connects its internal Ethernet controller to an external Ethernet PHY chip via the MII interface, enabling Ethernet communication. Collected data is sent to the backend using the IEC61850 protocol over the Ethernet interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggfe6g9vqc1ut41goh1p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggfe6g9vqc1ut41goh1p.png" width="662" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 1 Hardware System Block Diagram&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2 Hardware Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.1 Acquisition Module Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) Analog Input Acquisition Module&lt;/p&gt;

&lt;p&gt;The design uses ADI's AD7689 as the analog-to-digital converter, which features 8 analog input channels, 16-bit resolution, and a sampling rate of 250 kSPS[4]. The AD7689 converts analog signals into digital form and sends the digitized data to the processor via the SPI bus for further processing.&lt;/p&gt;

&lt;p&gt;Configuration in this design is as follows: (1) The ADC is powered by 5V, with a reference voltage of 4.096V provided by an internal voltage reference; (2) The ADC's digital output voltage is 3.3V, matching the processor's IO voltage level; (3) The analog input signals are unipolar, converted from differential signals from current/voltage transformers via a signal conditioning circuit; (4) The data output rate is controlled by the processor.&lt;/p&gt;

&lt;p&gt;2) Digital Input Acquisition Module&lt;/p&gt;

&lt;p&gt;Digital input signals (switch status) are optically isolated and then directly fed into the IO pins of the OMAPL138 processor. Software configures these IO pins as inputs to capture external digital signals, which are then stored in shared memory and subsequently transmitted using the appropriate communication protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.2 Communication Module Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The design supports both Modbus and IEC61850 protocols, enabling flexible adaptation to different communication requirements. Modbus can be implemented via either RS232 or RS485, with software-configurable multiplexing and automatic switching between the two. IEC61850 communication uses either optical or electrical Ethernet interfaces.&lt;/p&gt;

&lt;p&gt;1) RS232/RS485 Multiplexed Interface&lt;/p&gt;

&lt;p&gt;The circuit diagram for the RS232/RS485 multiplexing scheme is shown in Figure 2. The OMAPL138 UART's transmit pin connects to both the transmit input of the RS232 module (MAX3232) and the transmit input of the RS485 module (MAX485). Similarly, the UART's receive pin connects to both the receive output of MAX3232 and MAX485.&lt;/p&gt;

&lt;p&gt;Two IO pins of OMAPL138 drive the set and reset coils of a dual-coil latching miniature relay. Pulse signals on these IO pins switch the relay contacts between set and reset states.&lt;/p&gt;

&lt;p&gt;To enhance the driving capability of the OMAPL138 IO pins, this design uses the IO pins to drive transistors, which in turn drive the relays. This allows the processor to control which interface—RS232 or RS485—is connected to the external communication lines by changing the state of its IO pins. Thus, the two communication methods can be easily multiplexed through software configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazj21mjqf5z6ozqakae7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fazj21mjqf5z6ozqakae7.png" width="682" height="713"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 2 RS232/RS485 Multiplexed Communication Module&lt;/p&gt;

&lt;p&gt;2) Ethernet Communication Interface Design&lt;/p&gt;

&lt;p&gt;The OMAPL138 processor includes an Ethernet controller supporting 10/100 Mbps Ethernet. By connecting its MII or RMII interface to an external Ethernet PHY chip, Ethernet communication is achieved. This design uses Micrel's KSZ8001 as the Ethernet PHY chip, supporting both optical and electrical 10/100 Mbps Ethernet. The circuit diagram for the OMAPL138 and KSZ8001 Ethernet interface is shown in Figure 3.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsk23by35sgtpi1785ee.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwsk23by35sgtpi1785ee.png" width="703" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 3 Ethernet Communication Interface Circuit Diagram&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2.3 Power Supply Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The design requires three power supply voltages: 1.2V, 1.8V, and 3.3V, which are used for the processor core, DDR2 controller, processor IO, and other chip operations, respectively. These voltages are generated from an external 5V supply using dedicated power management ICs: Intersil's ISL6410A and ISL65426. ISL6410A provides 1.2V output, while ISL65426 provides 1.8V and 3.3V outputs.&lt;/p&gt;

&lt;p&gt;ISL6410A and ISL65426 are high-performance PWM-modulated DC/DC converters from Intersil, capable of delivering stable voltage and current output across an industrial temperature range of -40°C to 85°C with high conversion efficiency[5][6].&lt;/p&gt;

&lt;p&gt;Both chips integrate internal MOSFETs and include properly designed power-good and enable pins, greatly simplifying user design.&lt;/p&gt;

&lt;p&gt;When designing the power supply with these ICs, the following points should be noted:&lt;/p&gt;

&lt;p&gt;(1) Since the OMAPL138 processor has specific power-up sequencing requirements, it is essential that 1.2V is established before 1.8V, and 1.8V before 3.3V. The enable pin (EN) of ISL6410A is pulled up to 5V. Its power-good pin (PG) is connected to the enable pin (EN1) of the 1.8V output channel of ISL65426. The power-good pin (PG1) of the 1.8V output channel is connected to the enable pin (EN2) of the 3.3V output channel. This ensures proper power-up sequencing.&lt;/p&gt;

&lt;p&gt;(2) The output configuration pin (VSET) of ISL6410A is pulled up to 5V, ensuring that ISL6410A correctly outputs 1.2V when supplied with 5V input.&lt;/p&gt;

&lt;p&gt;(3) ISL65426 has two output channels and six adjustable power output modules, allowing independent configuration. Output voltages can be set via programming pins; for non-standard voltages, feedback via resistive dividers can be used. Since 1.8V and 3.3V are supported by programming pins in this design, pins V1SET1, V1SET2, V2SET1, and V2SET2 are all pulled up to 5V. Given that the 3.3V supply draws more current than the 1.8V supply in this design, the current configuration pins ISET1 and ISET2 are set to 1 and 0, respectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3 Software Design&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The software consists of five components: ubl, uboot, Linux kernel, file system, and application programs. The ubl and uboot handle initialization of OMAPL138 and loading/launching the kernel.&lt;/p&gt;

&lt;p&gt;The Linux kernel version used is 2.6.32, extended with custom serial port drivers. The application layer supports multiple communication protocols, including ModbusRTU and IEC61850 for data reporting. After processing the acquired signals using algorithms, the CPU writes the results into shared memory for use by various protocol modules in packet encapsulation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.1 Low-Level Serial Port Driver&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The Modbus protocol is supported via serial communication with upper-level devices. Serial data transmission and reception are handled by kernel-level serial drivers. The Linux serial driver architecture comprises three layers: low-level driver, line discipline, and tty device driver core, as illustrated in Figure 4.&lt;/p&gt;

&lt;p&gt;As shown in Figure 4, user-space commands and data are passed to the low-level driver, processed and scheduled by the line discipline, and then passed to the tty core layer, which performs hardware operations according to the command. In some cases, data may bypass the line discipline and go directly from the low-level driver to the core driver.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.2 Data Acquisition and Processing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1) Data Accuracy Algorithm&lt;/p&gt;

&lt;p&gt;To minimize anomalies in A/D acquisition data caused by environmental interference or hardware issues, the design applies algorithmic processing before storing data in shared memory. The algorithm discards the highest and lowest values from ten consecutive samples and computes the average of the remaining eight values as the valid signal for that acquisition cycle. While this reduces data acquisition efficiency, it improves data reliability and is suitable for applications where high sampling rates are not critical.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0old17yzgajafenqyshi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0old17yzgajafenqyshi.png" width="579" height="728"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 4 Serial Port Driver Architecture&lt;/p&gt;

&lt;p&gt;2) Data Transfer&lt;/p&gt;

&lt;p&gt;To ensure efficient data transfer between protocols, this design uses shared memory. The data structures for different protocols map to the same physical memory block. During main process startup, a memory block is allocated. The configuration file is then read to define the mapping of data structure members across protocols, enabling seamless inter-protocol data sharing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.3 Communication Protocols&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The design supports the ModbusRTU protocol, acting as a slave device that responds to various operations initiated by upper-level devices via ModbusRTU. Communication with upper-level devices occurs via either RS485 or RS232, with the interface type selectable via configuration file. Considering the half-duplex nature of RS485, appropriate delays are introduced between sending and receiving to ensure data integrity.&lt;/p&gt;

&lt;p&gt;The system also supports the IEC61850 protocol, transmitting data to the backend via Ethernet. Data for both protocols originates from shared memory. The system module block diagram is shown in Figure 5. As seen in Figure 5, both protocol modules operate as independent task processes, allowing the system to simultaneously support both protocols and serve multiple types of upper-level devices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3.4 Data Logging&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The design includes comprehensive fault logging, saving abnormal operational data and fault timestamps to log files stored in flash memory for easy troubleshooting by maintenance personnel. Additionally, historical data logging is supported, recording each data change along with its timestamp into log files stored in flash. The system supports up to 128MB of log file storage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7yl4sa913cuxtjwnw5l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg7yl4sa913cuxtjwnw5l.png" width="640" height="602"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 5 Data Acquisition and Shared Memory Module Diagram&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4 Testing and Verification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To verify the data acquisition functionality and the Modbus and IEC61850 communication capabilities, the following test setup was used: A relay protection tester outputs three-phase current, three-phase voltage, and digital signals to the device under test. The device acquires and processes the data, then transmits it to a PC using both Modbus and IEC61850 protocols.&lt;/p&gt;

&lt;p&gt;For Modbus communication verification, the PC sends Modbus polling messages to the device via a serial debugging tool and checks the response data.&lt;/p&gt;

&lt;p&gt;IEC61850 communication is verified using the Ethernet packet capture tool MMS Ethereal to capture MMS messages and inspect data transmission. Screenshots of the transmitted protocol messages are shown in Figures 6 and 7. Comparison of the protocol messages with the data output from the relay protection tester confirms that the design meets the original requirements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftka9f0evpnd4g5uj1hvx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftka9f0evpnd4g5uj1hvx.png" width="492" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 6 Modbus Polling and Response&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmy3e1gbe4i5muir4nceb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmy3e1gbe4i5muir4nceb.png" width="505" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 7 MMS Message&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5 Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  This paper presents a power data acquisition and multi-protocol transmission solution based on OMAPL138. The modular design supports multiple communication protocols for data transmission, greatly enhancing user convenience. It also provides a robust hardware and software platform for future expansion of additional functionalities and protocols, demonstrating high practical value.
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/118175736" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/118175736" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>omapl138</category>
      <category>c6748</category>
      <category>dsp</category>
      <category>spartan6</category>
    </item>
    <item>
      <title>ZYNQ Lidar-Visual SLAM Algorithm Porting and Design</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Tue, 02 Jun 2026 07:35:04 +0000</pubDate>
      <link>https://dev.to/sienovoleo/zynq-lidar-visual-slam-algorithm-porting-and-design-56cc</link>
      <guid>https://dev.to/sienovoleo/zynq-lidar-visual-slam-algorithm-porting-and-design-56cc</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This article details the development and implementation of a ZYNQ-based system for real-time 3D reconstruction, leveraging both FPGA-accelerated stereo vision and a LiDAR-based mobile platform. Readers will learn about the system's architecture, key algorithms like BM, SGBM, and ICP, hardware components including a Mecanum wheel robot and R-Fans-16 LiDAR, and its performance characteristics for environmental perception in applications like robotics and autonomous systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  FPGA-Accelerated Stereo Vision on ZYNQ
&lt;/h2&gt;

&lt;p&gt;The initial phase of this project focused on developing an FPGA-accelerated stereo vision system on a Xilinx ZYNQ platform for real-time 3D reconstruction. This involved a tight integration of the ZYNQ's Processing System (PS) and Programmable Logic (PL) for collaborative processing, enabling high-performance image processing tasks.&lt;/p&gt;

&lt;p&gt;Key achievements in this stereo vision component include:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Stereo Image Acquisition and PS+PL Processing&lt;/strong&gt;: Successfully implemented the capture of stereo images, utilizing the ZYNQ's PS for control and higher-level tasks, and the PL (FPGA fabric) for accelerating computationally intensive image processing operations.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Camera Calibration and Lens Correction&lt;/strong&gt;: Developed and integrated routines for precise camera calibration and lens distortion correction directly on the ZYNQ platform. This is crucial for accurate 3D reconstruction, as it eliminates geometric distortions introduced by the camera lenses.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Stereo Matching Algorithms&lt;/strong&gt;: Implemented and optimized several stereo matching algorithms on ZYNQ. This currently includes both local stereo matching (Block Matching - BM) and semi-global stereo matching (Semi-Global Block Matching - SGBM), allowing for the computation of disparity maps from stereo image pairs.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Depth Map and Rainbow Map Generation&lt;/strong&gt;: After stereo matching, the system converts the generated disparity maps into real-time depth maps. These depth maps are further processed into "rainbow maps," which are colorized depth visualizations that provide an intuitive representation of scene depth.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Ranging Algorithm&lt;/strong&gt;: A ranging algorithm has been developed on ZYNQ to calculate distances. However, it currently lacks a recognition algorithm, meaning it can only compute the distance to a single specified point in the image. Despite this limitation, the system achieves an accuracy within 10mm, with an effective testing range of up to 5m.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Algorithm Optimization&lt;/strong&gt;: All implemented algorithms have undergone significant optimization. The system currently achieves near 720p image quality at a frame rate of approximately 30 frames per second, demonstrating its capability for real-time applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A dedicated series of articles is planned to detail the implementation process of this real-time 3D reconstruction project on ZYNQ, covering its development from scratch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2swe7d7117xd02295jje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2swe7d7117xd02295jje.png" width="799" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Context of 3D Reconstruction
&lt;/h2&gt;

&lt;p&gt;In recent years, fields such as machine learning and autonomous driving have become prominent research areas, where environmental perception by computers is paramount. Consequently, 3D environment reconstruction is an essential trend, facilitating interaction between virtual and real worlds.&lt;/p&gt;

&lt;p&gt;The two primary methods for 3D reconstruction are vision-based and LiDAR-based technologies. Vision-based ranging relies on triangulation, typically offering a maximum range of 5-8m, making it less suitable for larger spaces. It is also highly susceptible to lighting conditions. In contrast, LiDAR (Light Detection and Ranging) can be applied in a much wider array of scenarios due offering superior range and robustness to ambient light.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sienovo's ZYNQ-based LiDAR 3D Modeling System
&lt;/h2&gt;

&lt;p&gt;This article's primary focus is on a ZYNQ-based LiDAR 3D modeling system designed to address the challenges of environmental perception. This system aims to deeply explore target environments, collect point cloud data, reconstruct 3D spatial models, and support various machine vision applications such as measurement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4f8svh2wlyebn80m3o4p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4f8svh2wlyebn80m3o4p.png" width="687" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Application Areas
&lt;/h3&gt;

&lt;p&gt;3D modeling has extensive applications across various domains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Drones and Autonomous Vehicles&lt;/strong&gt;: Essential for real-time obstacle avoidance and path planning.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Machine Vision and 3D Printing&lt;/strong&gt;: Can be integrated with these technologies for advanced perception and manufacturing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cultural Heritage Preservation&lt;/strong&gt;: Significant for the replication and reconstruction of cultural relics.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Environmental Survey&lt;/strong&gt;: The system, mounted on a remote-controlled mobile platform, can survey environments inaccessible to humans.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Technical Features
&lt;/h3&gt;

&lt;p&gt;The ZYNQ-based LiDAR 3D modeling system incorporates several distinguishing technical features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;PCL ICP Algorithm&lt;/strong&gt;: The system utilizes the Iterative Closest Point (ICP) algorithm from the Point Cloud Library (PCL) to perform multiple iterative calculations on LiDAR data, achieving precise point cloud registration.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Integrated Mobile Platform&lt;/strong&gt;: The LiDAR is mounted on a Mecanum wheel robot. The robot's gyroscope and motor encoders provide real-time position and velocity information. Through coordinate system transformations, this enables precise, real-time localization of the LiDAR sensor.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Robust Indoor Reconstruction&lt;/strong&gt;: This design enables real-time 3D reconstruction of indoor objects within a 1-5m range, with minimal blind spots. A significant advantage is its low susceptibility to lighting conditions, making it reliable in varied indoor environments.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Remote Exploration&lt;/strong&gt;: By deploying the LiDAR on a remotely controllable robot, the system can be used to survey environments that are hazardous or inaccessible to human operators.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key Innovations
&lt;/h3&gt;

&lt;p&gt;The system introduces several innovative aspects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Mobile App Remote Control&lt;/strong&gt;: The Mecanum wheel robot can be controlled remotely via a mobile application, enhancing operational flexibility.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PCL-based Algorithm Foundation&lt;/strong&gt;: The core algorithms are built upon the robust and widely-used PCL point cloud library.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Real-time Data Registration&lt;/strong&gt;: Achieves real-time data registration through the efficient implementation of the ICP algorithm.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;HLS Acceleration on PYNQ&lt;/strong&gt;: The ICP algorithm benefits from High-Level Synthesis (HLS) acceleration on the Programmable Logic (PL) module of the PYNQ-Z2 development board, significantly boosting computational speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  System Composition and Functionality
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overall Introduction
&lt;/h3&gt;

&lt;p&gt;The system comprises three main components: the LiDAR sensor, a Mecanum wheel robot based on an STM32 microcontroller, and a Xilinx PYNQ-Z2 development board.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;LiDAR Data Transmission&lt;/strong&gt;: The LiDAR transmits collected point cloud data to the PYNQ-Z2 via an Ethernet interface.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Robot Data Acquisition&lt;/strong&gt;: The Mecanum wheel robot is equipped with motor encoders, a gyroscope, and a Bluetooth module. Its movement and steering are controlled remotely via a mobile phone Bluetooth application. During motion, displacement and attitude information are sent to the STM32 microcontroller.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;STM32 to PYNQ-Z2 Communication&lt;/strong&gt;: The STM32 microcontroller then transmits this displacement and attitude information to the PYNQ-Z2 via a UART protocol at a baud rate of 115200, cycling in real-time.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;ZYNQ Processing&lt;/strong&gt;: The ZYNQ board calculates the LiDAR's displacement and attitude offset based on the received data. It then uses the ICP algorithm to stitch together successive point cloud data, compensating for the LiDAR's movement and orientation. The stitched point cloud data is then output via Ethernet.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Scanning Strategy&lt;/strong&gt;: In this design, the LiDAR-equipped robot performs mobile scanning, collecting information from the left, right, and top surfaces to reconstruct the 3D environment.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Module Details
&lt;/h3&gt;

&lt;h4&gt;
  
  
  R-Fans-16 LiDAR
&lt;/h4&gt;

&lt;p&gt;The system employs the R-Fans-16 navigation-grade LiDAR sensor for data acquisition. This sensor performs 360° scanning with 16 lines to achieve 3D detection and imaging. Based on high-precision laser echo signal measurement technology, the R-Fans-16 offers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Extended Range&lt;/strong&gt;: Detection capability up to 200m.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;High Measurement Accuracy&lt;/strong&gt;: Ranging accuracy better than 2cm.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Accurate Echo Intensity&lt;/strong&gt;: Target reflection echo intensity is captured with 8-bit precision.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Angular Coverage&lt;/strong&gt;: Balances pitch direction angular coverage with angular resolution.
The LiDAR transmits real-time point cloud data to the PYNQ-Z2 via an Ethernet port during operation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  STM32-based Mecanum Wheel Robot
&lt;/h4&gt;

&lt;p&gt;This robot platform integrates an STM32 microcontroller, which is central to its operation. For this experiment, the robot utilizes its onboard gyroscope, motor encoders, and Bluetooth module.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Sensor Data to STM32&lt;/strong&gt;: The gyroscope and motor encoders transmit data to the STM32 microcontroller via SPI protocol.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;STM32 Processing and Transmission&lt;/strong&gt;: The microcontroller processes this data to calculate the robot's attitude and wheel speed. It then continuously sends this information to the ZYNQ via UART protocol at a baud rate of 115200.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Remote Control&lt;/strong&gt;: The robot's movement and steering are controlled remotely using a Bluetooth-enabled mobile device.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Coordinate System Transformation
&lt;/h4&gt;

&lt;p&gt;The R-Fans-16 LiDAR collects data within its own local coordinate system. The essence of 3D reconstruction in this context is to transform this data into an absolute world coordinate system, effectively converting from spherical coordinates to Cartesian coordinates.&lt;/p&gt;

&lt;p&gt;A spherical coordinate system uses spherical coordinates (r, θ, φ) to define a point P in 3D space. Here, 'r' is the radial distance from the origin to point P, 'θ' is the polar angle between the line from the origin to P and the positive z-axis, and 'φ' is the azimuthal angle between the projection of the line onto the xy-plane and the x-axis.&lt;/p&gt;

&lt;p&gt;The conversion formulas between spherical (r, θ, φ) and Cartesian (x, y, z) coordinates are:&lt;br&gt;
x = r * sin(θ) * cos(φ)&lt;br&gt;
y = r * sin(θ) * sin(φ)&lt;br&gt;
z = r * cos(θ)&lt;/p&gt;

&lt;p&gt;In this design, the robot's starting position defines the origin of the absolute world coordinate system. Subsequently, for each cycle of LiDAR data acquisition, a sub-coordinate system is established with the LiDAR as its origin. The displacement and rotation of this LiDAR-centric sub-coordinate system relative to the initial absolute coordinate system are continuously recorded.&lt;/p&gt;

&lt;p&gt;The x, y, and z axes of the sub-coordinate system align with the LiDAR's internal axes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Translational Information&lt;/strong&gt;: Motor encoders measure the LiDAR's horizontal planar movement speed and direction, providing the translation offset between the LiDAR's coordinate system and the absolute coordinate system.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Rotational Information&lt;/strong&gt;: The gyroscope measures the LiDAR's attitude angles, providing the rotational offset between the LiDAR's coordinate system and the absolute coordinate system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By combining these measured translational and rotational values with the spherical-to-Cartesian conversion formulas, points from the LiDAR's local coordinate system can be accurately mapped into the absolute world coordinate system.&lt;/p&gt;

&lt;h4&gt;
  
  
  Point Cloud Registration (ICP Algorithm)
&lt;/h4&gt;

&lt;p&gt;The ICP (Iterative Closest Point) algorithm is fundamental for point cloud registration, which involves aligning two point sets originating from different coordinate systems based on their geometric properties. The objective is to determine the rigid body transformation matrix (R) and translation matrix (T) that, when applied to the target point set, cause it to best overlap with the reference point set. For a target point set P and a reference point set Q, the ideal transformation is:&lt;/p&gt;

&lt;p&gt;P' = R * P + T&lt;/p&gt;

&lt;p&gt;Since this ideal transformation is not always perfectly achievable, the algorithm aims to minimize an objective function, typically the sum of squared distances between corresponding points:&lt;/p&gt;

&lt;p&gt;Minimize Σ ||(R * P_i + T) - Q_i||^2&lt;/p&gt;

&lt;p&gt;Common methods for solving for R and T include Singular Value Decomposition (SVD) and non-linear optimization. This design utilizes the SVD method.&lt;/p&gt;

&lt;p&gt;The ICP algorithm problem is often broken down into two parts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Coarse Registration (Global Registration)&lt;/strong&gt;: This initial step calculates the pose between two point sets to achieve a rough overlap. It provides a suitable initial value for the subsequent precise registration.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Precise Registration (Local Registration)&lt;/strong&gt;: This step applies an iterative optimization strategy to two sufficiently close point sets to achieve the final, highly accurate registration result.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Completion Status and Performance Parameters
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overview
&lt;/h3&gt;

&lt;p&gt;This system successfully implements LiDAR point cloud acquisition and the collection of attitude information from the gyroscope and encoders. The PYNQ-Z2 development board's ZYNQ control chip, with its PS-PL (Processing System - Programmable Logic) design, significantly enhances the system's design convenience, feasibility, and reduces overall design complexity. The PS-PL master-slave architecture maintains design simplicity while boosting the system's operating speed and processing capabilities. The design of IP cores on the PL side dramatically accelerates algorithm computation. Specifically, the point cloud stitching component of this design benefits from PL-side IP core acceleration, improving the stitching effect and successfully achieving real-time 3D reconstruction functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Completion Status
&lt;/h3&gt;

&lt;p&gt;An indoor corridor environment was used for testing, featuring a row of tables, a fire extinguisher, and walls and windows on either side.&lt;/p&gt;

&lt;p&gt;Data was collected in groups of 200 frames, stored as PCD (Point Cloud Data) files. The raw images generated from each data group are illustrated in Figure 3.2.2 (referring to the original Chinese source's image). The right side of the image clearly shows details of the table surface and legs, while the lower left side depicts the fire extinguisher. Discrete small point clouds on the right side of the image represent laser reflections from the windows projecting outdoors. If both sides of the corridor were solid walls, a more complete 3D model would be obtained.&lt;/p&gt;

&lt;p&gt;Adjacent point cloud groups were registered using the ICP algorithm to construct a complete 3D model of the corridor as the robot traversed it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Parameters
&lt;/h3&gt;

&lt;p&gt;The resolution of LiDAR point cloud acquisition decreases with increasing distance. Within the effective detection range, the measurement error for object width and depth is influenced by the stability of the equipment (i.e., the degree of LiDAR jitter).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Width Measurement Error&lt;/strong&gt;: Within 2cm.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Inclined Object Measurement Error&lt;/strong&gt;: 6cm.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Inclined Angle Error&lt;/strong&gt;: 4°.
It is noted that slight vibrations from the robot itself during measurement contribute to these observed errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary and Future Extensions
&lt;/h2&gt;

&lt;p&gt;This ZYNQ-based LiDAR 3D modeling system demonstrates robust real-time 3D reconstruction capabilities. To further enhance its utility and user experience, several areas for expansion have been identified:&lt;/p&gt;

&lt;h3&gt;
  
  
  Expandability
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Python Programming Integration&lt;/strong&gt;: The current system operates on Ubuntu 18.04, with programming primarily in C++. While effective, it primarily relies on basic C language libraries and the PCL library, which can make display and interaction less convenient for users. Porting PCL to a Jupyter platform and leveraging Python would offer a more user-friendly and faster development environment.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2.  &lt;strong&gt;Omnidirectional Stereo Scanning&lt;/strong&gt;: The current car-mounted LiDAR setup primarily scans the left, right, and top surfaces of the environment. To obtain comprehensive volumetric information of specific objects, especially for detailed modeling, a more flexible mounting platform, such as a drone, could be employed to enable omnidirectional scanning.
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/124908639" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/124908639" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>robotics</category>
      <category>slam</category>
      <category>laservision</category>
    </item>
    <item>
      <title>【Domestic Virtual Instrument】DSP+FPGA+ADS1282-based 32-Bit High-Precision Data Acquisition Solution (II) Analog Circuit Design</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Mon, 01 Jun 2026 07:41:36 +0000</pubDate>
      <link>https://dev.to/sienovoleo/domestic-virtual-instrument-dspfpgaads1282-based-32-bit-high-precision-data-acquisition-solution-4ik5</link>
      <guid>https://dev.to/sienovoleo/domestic-virtual-instrument-dspfpgaads1282-based-32-bit-high-precision-data-acquisition-solution-4ik5</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This article walks through the analog front-end and ADC circuit design for a 32-bit high-precision data acquisition system built around the TI ADS1282 delta-sigma ADC, an FPGA, and a DSP. Part II focuses on the signal-conditioning chain from sensor current output all the way to the FPGA's SPI interface, covering component selection rationale, filter design, ADC operating-mode selection, pin-level interfacing, SPI timing constraints, multi-channel synchronization, and register-level control.&lt;/p&gt;

&lt;h2&gt;
  
  
  System Architecture Overview
&lt;/h2&gt;

&lt;p&gt;Figure 4.1 shows the hardware signal block diagram. The digital signal processing board centers on two processing chips: an FPGA and a DSP. The FPGA acts as the bridge between the DSP and all peripheral interfaces and handles data pre-processing, while the DSP is the computation core.&lt;/p&gt;

&lt;p&gt;The FPGA receives data from external devices over various data buses, performs packetization and other pre-processing, and stores results in designated on-chip memory. The DSP reads this data via its EMIF bus interface and executes the data-solving algorithms. Intermediate parameters and final results are written by the DSP back into FPGA-internal memory via EMIF, after which the FPGA transmits them externally over the CAN bus interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feupnh9qg3685i7n25fvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feupnh9qg3685i7n25fvd.png" width="799" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zsm2m9fa3tz5cri0edr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zsm2m9fa3tz5cri0edr.png" width="799" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4.2 High-Precision Data Acquisition Circuit Design
&lt;/h2&gt;

&lt;p&gt;Figure 4.2 shows the structural block diagram of the data acquisition module. Because the sensors output current signals while the ADS1282 requires a differential voltage input, the first task is current-to-voltage conversion. A high-precision, low-temperature-coefficient sampling resistor performs this conversion. The resulting voltage then passes through a second-order low-pass filter built from a fully-differential op-amp, which removes high-frequency noise. The filtered signal goes through clamp protection diodes before entering the ADS1282's differential inputs. The clamp diodes protect the ADC against instantaneous input overload: when the input signal level exceeds the rating of the internal ESD diodes, the external clamp diodes engage and limit the voltage, preventing damage. The ADC's digital communication interface connects directly to the FPGA, which implements an SPI bus in fabric to read the converted data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh418hw9na7w382qmd3d1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh418hw9na7w382qmd3d1.png" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4.2.1 Analog Signal Conditioning Circuit Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Current Sampling
&lt;/h3&gt;

&lt;p&gt;Accelerometer signals demand very high precision, so front-end circuit topology and component selection are critical. The op-amp must exhibit high common-mode rejection ratio (CMRR), high precision, low drift, and low noise. The low-pass filter must have excellent linearity and a well-defined cutoff frequency, and the analog front end must accept differential input.&lt;/p&gt;

&lt;p&gt;As shown in Figure 4.3, the input current signal passes through a sampling resistor to produce a proportional voltage. The stability and accuracy of that voltage depend directly on the resistor's performance. The design uses a VISHAY VSMP-series ultra-precision metal-foil resistor, chosen for its excellent load-life stability, near-zero temperature coefficient of resistance, extremely low noise, and tight resistance tolerance.&lt;/p&gt;

&lt;p&gt;A filter capacitor C provides additional high-frequency attenuation at the resistor output. The resulting voltage is buffered by a voltage-follower op-amp before entering the conditioning stage. The buffer is an OPA227, selected for its combination of very low noise (3 nV/√Hz), wide bandwidth (8 MHz), very high CMRR (138 dB), high open-loop gain (160 dB), low output offset voltage (75 µV max), extremely low temperature drift (0.1 µV/°C), and wide supply voltage range (±2.5 V to ±18 V). It operates across −55 °C to +125 °C.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Conditioning Circuit
&lt;/h3&gt;

&lt;p&gt;The ADS1282 uses a differential voltage input. Differential signaling's primary advantage is strong noise immunity: external interference couples onto both signal lines nearly simultaneously and is cancelled when the difference is taken. The two signal lines also cancel each other's radiated electromagnetic fields. Differential switching transitions occur at the signal crossing point, making timing less sensitive to process and temperature variation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxupdyraoj8889inev4fm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxupdyraoj8889inev4fm.png" width="694" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the ADC input is differential while the signal source is single-ended, a low-noise analog conditioning circuit is needed to interface them. There are two common single-ended-to-differential conversion approaches: (1) a multi-op-amp discrete circuit, and (2) a single fully-differential op-amp IC. The discrete approach inevitably introduces more noise sources. The single-chip approach is simpler and uses fewer components, so this design uses a single fully-differential op-amp — the OPA1632 — for the front-end analog signal conditioning and filtering function.&lt;/p&gt;

&lt;p&gt;The OPA1632 is a high-performance, low-noise fully-differential op-amp with THD as low as 0.000022% and noise density as low as 1.3 nV/√Hz. The voltage from the sampling resistor, after capacitor filtering, feeds into the OPA1632.&lt;/p&gt;

&lt;p&gt;As shown in Figure 4.4, the OPA1632 is configured as a second-order Butterworth low-pass filter. The passband width is set by selecting appropriate resistor and capacitor values to meet design requirements. The Butterworth topology is chosen because it provides maximally flat frequency response within the passband — no ripple — while attenuating monotonically outside the passband.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frif6lphwi2mmlyu0w5v8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frif6lphwi2mmlyu0w5v8.png" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzv4ommxtbrazq16jh8jl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzv4ommxtbrazq16jh8jl.png" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 4.5 shows the Multisim-simulated Bode plot for this low-pass filter. The gain curve is very flat within the passband with no ripple, and attenuation outside the passband is rapid, confirming the filter performs as designed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwcych64978u8g4b52850.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwcych64978u8g4b52850.png" width="690" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4.2.2 ADC Circuit Design
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Operating Mode and SNR Selection
&lt;/h3&gt;

&lt;p&gt;The ADS1282 supports two primary operating modes selectable via register: low-power mode and high-performance mode. SNR varies with mode, output data rate, and on-chip PGA gain setting. Under equal conditions, high-performance mode always yields better SNR than low-power mode. Lower data rates and lower PGA gain also improve SNR.&lt;/p&gt;

&lt;p&gt;This design selects high-performance mode at 500 SPS. The SNR at this operating point is 127 dB. Using the standard ENOB formula, this corresponds to an effective number of bits of 20.8, which meets and exceeds the system's precision requirement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjvykdk2j7779s9jyy30.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjvykdk2j7779s9jyy30.png" width="799" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pin Description
&lt;/h3&gt;

&lt;p&gt;The ADS1282 is housed in a 28-pin TSOP SMD package. Key pin functions are as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLK&lt;/strong&gt;: Master clock input&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SCLK&lt;/strong&gt;: Serial clock input&lt;/li&gt;
&lt;li&gt;*&lt;em&gt;DRDY*&lt;/em&gt;: Data-ready output; data is read on its falling edge&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOUT / DIN&lt;/strong&gt;: Serial data output / input&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCLK, M1, M0&lt;/strong&gt;: In modulator-output mode, MCLK is the modulator clock output and M1/M0 are modulator data outputs; otherwise these pins must be tied to fixed levels&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SYNC&lt;/strong&gt;: Synchronization input for multi-chip synchronized operation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MFLAG&lt;/strong&gt;: Over-range flag; logic 0 = normal, logic 1 = over-range&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DGND&lt;/strong&gt;: Digital ground reference&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAPP / CAPN&lt;/strong&gt;: Internal PGA output pins; a 10 nF capacitor must be connected between them&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AINP2 / AINN2, AINP1 / AINN1&lt;/strong&gt;: Positive/negative analog inputs for channels 2 and 1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AVDD / AVSS&lt;/strong&gt;: Analog supply; single-ended +5 V or bipolar ±2.5 V&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VREFP / VREFN&lt;/strong&gt;: Positive/negative reference voltage inputs&lt;/li&gt;
&lt;li&gt;*&lt;em&gt;PWDN*&lt;/em&gt;: Power-down input, active low&lt;/li&gt;
&lt;li&gt;*&lt;em&gt;RESET*&lt;/em&gt;: Reset input, active low&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DVDD&lt;/strong&gt;: Digital supply, +1.8 V to +3.3 V&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BYPAS&lt;/strong&gt;: Calibration output; requires a 1 µF capacitor to DGND&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft985z9qjq9yxhgyii364.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft985z9qjq9yxhgyii364.png" width="408" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. ADC Internal Architecture and Operating Principle
&lt;/h3&gt;

&lt;p&gt;The ADS1282's internal structure (Figure 4.7) consists of a multiplexer (MUX), a programmable gain amplifier (PGA), a fourth-order delta-sigma (Δ-Σ) modulator, a programmable digital filter, an over-range detection circuit, gain and offset calibration modules, a controller, and an SPI serial bus interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo92m6burzb6czh32squ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo92m6burzb6czh32squ.png" width="799" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The MUX supports five configurations: channel 1 input, channel 2 input, short-circuit between the two channel inputs of the same polarity, an internal 400 Ω short-circuit test, and a common-mode test. Following the MUX is the differential-input, differential-output PGA with a gain range of 1 to 64, configured via the PGA[2:0] bits of the CONFIG1 register. The PGA drives the modulator differentially through internal 300 Ω resistors. A COG-type ceramic capacitor connected between CAPP and CAPN serves as an anti-aliasing filter, eliminating instantaneous interference generated during modulator sampling. Its corner frequency is determined by this capacitor value and the internal resistance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkqb43y3y0znrx2f7xal.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhkqb43y3y0znrx2f7xal.png" width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ADS1282 modulator is a high-performance, inherently stable fourth-order Σ-Δ modulator. It pushes quantization noise to frequencies well above the signal band, where the digital filter can remove it effectively. The modulator differential input signal VIN = (AINP − AINN) × PGA has a range equal to VREF = VREFP − VREFN, i.e., ±2.5 V. When the modulator input exceeds full-scale, the modulator enters a stable saturation state, the digital output code locks to the positive or negative full-scale value, and the MFLAG pin transitions from low to high, providing a hardware over-range indicator. The modulator sampling frequency is CLK/4 = 1.024 MHz in high-performance mode and CLK/8 = 512 kHz in low-power mode, where the master clock CLK is 4.096 MHz.&lt;/p&gt;

&lt;p&gt;The modulator's bitstream output can be accessed directly or routed through the on-chip digital filter. Setting FILTR[1:0] = 00 in the CONFIG0 register puts the modulator into output mode, where M0 and M1 become modulator data outputs and MCLK becomes the modulator clock output. When not in output mode, these pins are inputs and must be tied to fixed levels.&lt;/p&gt;

&lt;p&gt;As shown in Figure 4.8, the digital filter receives and decimates the modulator bitstream. It consists of three cascaded stages: a five-stage sinc filter with adjustable decimation ratio, a 32× decimation finite impulse response (FIR) low-pass filter with programmable phase, and a programmable first-order high-pass filter (HPF). Data can be tapped after any stage. If no on-chip filtering is desired, the raw modulator output is available. This design uses the sinc filter plus the FIR low-pass filter path with the 500 SPS filter configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiak9ge3gk0992kyb9kfb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiak9ge3gk0992kyb9kfb.png" width="800" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The digital filter output path is selected via the FILTR[1:0] bits as summarized in Table 4.2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvagmy0h88hfm0bl5y2k7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvagmy0h88hfm0bl5y2k7.png" width="768" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the on-chip filter stages, a digital correction module consisting of an adder and a multiplier (Figure 4.9) applies offset and gain correction to the raw data according to the values stored in the OFC (offset calibration) and FSC (full-scale calibration) registers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhnybranrrx0dr4g3wk1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhnybranrrx0dr4g3wk1.png" width="751" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhyuvas4nyl3dtdf4xhc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhyuvas4nyl3dtdf4xhc.png" width="702" height="272"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. ADC Data Output Interface Circuit
&lt;/h3&gt;

&lt;p&gt;The ADS1282 communicates via SPI. Conversion data is output as 32-bit two's-complement values, MSB first, with the MSB serving as the sign bit (0 = positive, 1 = negative). The SPI interface uses three core signals: SCLK, DIN, and DOUT. Data is clocked into DIN on the rising edge of SCLK and shifted out of DOUT on the falling edge. When DRDY\ goes low, a new conversion result is ready and the FPGA can initiate a read. If SCLK remains low for 64 consecutive DRDY\ periods, any ongoing data transfer or command is aborted and the SPI interface resets; the next SCLK pulse starts a new communication cycle. SCLK must be held low when idle.&lt;/p&gt;

&lt;p&gt;Figure 4.10 shows the SPI timing requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;t_SCLK&lt;/strong&gt;: SCLK period — minimum 2/f_sclk, maximum 16/f_sclk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t_SPWHL&lt;/strong&gt;: SCLK pulse width — minimum 0.8/f_sclk, maximum 10/f_sclk&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t_DIST&lt;/strong&gt;: DIN setup time — minimum 50 ns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t_DIHD&lt;/strong&gt;: DIN hold time — minimum 50 ns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t_DOPD&lt;/strong&gt;: Delay from SCLK falling edge to new DOUT valid — maximum 100 ns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t_DOHD&lt;/strong&gt;: Hold time from SCLK falling edge to DOUT change — minimum 0 ns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;t_SCD_L&lt;/strong&gt;: Interval from last rising edge of a command to first rising edge of a register read/write — minimum 24/f_sclk&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxva7tqnetua0ueusmndk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxva7tqnetua0ueusmndk.png" width="800" height="255"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Figure 4.11 shows the SPI read-data timing. Each data packet is four 8-bit bytes (32 bits total), transmitted MSB first out of the DOUT pin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gr65ztkpznxgwa6bni8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6gr65ztkpznxgwa6bni8.png" width="799" height="298"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Channel Synchronization
&lt;/h3&gt;

&lt;p&gt;This system uses three ADS1282 channels that must operate synchronously to avoid inter-channel timing errors. Synchronization can be achieved either through the SYNC pin or by issuing a sync command over SPI. When a sync event occurs, the ADC resets its internal memory and begins a new conversion. The ADS1282 supports two sync modes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(1) Single-pulse sync mode.&lt;/strong&gt; The ADC synchronizes only once during operation. Synchronization occurs at the first CLK rising edge after the SYNC rising edge, or at the eighth SCLK rising edge after a sync command. After this single sync event, the ADC continues converting at the selected rate without further synchronization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(2) Continuous sync mode.&lt;/strong&gt; The ADC re-synchronizes on every SYNC rising edge, performing one conversion per sync pulse.&lt;/p&gt;

&lt;p&gt;This design uses SYNC-pin single-pulse synchronization. After all three ADC channels synchronize via the shared SYNC pin, they continue converting at the same rate continuously. Figure 4.12 shows the wiring of the three-channel ADC array. Each ADC's interface carries CLK, SYNC, SCLK, DIN, DOUT, and DRDY. Importantly, CLK and SYNC originate from the same internal FPGA signal, ensuring tight, deterministic synchronization across all three channels.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxl8vn6ugoin9ulwpdo5a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxl8vn6ugoin9ulwpdo5a.png" width="770" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Register Access Protocol
&lt;/h3&gt;

&lt;p&gt;All ADS1282 control is exercised by sending commands over SPI, including register read and write operations. Figures 4.13 and 4.14 show the read and write register timing diagrams. A command word must be sent first; it encodes the starting register address and the number of registers to access (Table 4.3). The register data transfer follows immediately after the command word is clocked in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40tmk45qcr29ynojmfgx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F40tmk45qcr29ynojmfgx.png" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdi79wonrw4rbgr020efw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdi79wonrw4rbgr020efw.png" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4.2.3 Voltage Reference Design
&lt;/h2&gt;

&lt;h2&gt;
  
  
  A stable, low-noise voltage reference is essential for achieving the full dynamic range of a 32-bit ADC. The reference voltage VREF = VREFP − VREFN defines the modulator's input span of ±2.5 V, so any noise or drift on the reference directly adds to the ADC's output noise floor and degrades accuracy. The reference circuit design — covered in the next section — must match the precision of the sampling resistor, the OPA1632 conditioning stage, and the ADS1282 itself to avoid becoming the system bottleneck.
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/130419477" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/130419477" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fpgadev</category>
      <category>ads1282</category>
      <category>highprecisionadc</category>
      <category>domesticvirtualinstrument</category>
    </item>
    <item>
      <title>Design and Implementation of an MPSoC-Based Hardware Platform for Autonomous Driving Multi-Sensor Fusion Localization</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Sun, 31 May 2026 07:16:19 +0000</pubDate>
      <link>https://dev.to/sienovoleo/design-and-implementation-of-an-mpsoc-based-hardware-platform-for-autonomous-driving-multi-sensor-4mpd</link>
      <guid>https://dev.to/sienovoleo/design-and-implementation-of-an-mpsoc-based-hardware-platform-for-autonomous-driving-multi-sensor-4mpd</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  1. Introduction
&lt;/h3&gt;

&lt;p&gt;High-precision localization is a core prerequisite for environmental perception, path planning, and vehicle motion control in autonomous driving. The accuracy and timing synchronization performance of localization directly determine the reliability and safety of autonomous driving systems. Currently, mainstream localization solutions mostly adopt a &lt;strong&gt;discrete multi-module architecture&lt;/strong&gt;: multi-sensor data acquisition relies on independent hardware boards, while positioning computation and algorithm acceleration depend on industrial PCs, GPUs, or high-end CPUs.&lt;/p&gt;

&lt;p&gt;This separated architecture has several engineering drawbacks: hardware interconnection among multiple modules results in low system integration and bulky structure; inter-board communication introduces transmission latency, making it difficult to achieve microsecond-level time synchronization across sensors; general-purpose computing platforms tend to have high power consumption and heat dissipation, failing to meet automotive-grade requirements for wide temperature range, low power, and reliability.&lt;/p&gt;

&lt;p&gt;MPSoC integrates ARM processors and programmable FPGA logic on a single chip, enabling &lt;strong&gt;hardware-software cooperative parallel computing&lt;/strong&gt;. The FPGA excels at high-precision timing control, synchronized multi-sensor data acquisition, and hardware acceleration of algorithms, while the ARM is adept at system scheduling, data fusion, and task management. Building an integrated localization hardware platform based on MPSoC enables tight integration of synchronized sensor acquisition, time reference establishment, and positioning algorithm acceleration—offering an effective technical solution to current hardware bottlenecks in autonomous driving localization.&lt;/p&gt;

&lt;p&gt;Based on multi-sensor localization theory and the characteristics of MPSoC heterogeneous architecture, this article presents the complete design of a high-precision localization hardware platform, including system architecture, modular circuit development, embedded software porting, and functional performance testing. Combined with industrial engineering practices, it also discusses the customization design methodology and key implementation considerations for MPSoC-based autonomous driving localization motherboards, providing valuable reference for similar navigation and positioning hardware development. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyb2q9xa0hsdh1inkhrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyb2q9xa0hsdh1inkhrg.png" width="649" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Overall Scheme of Multi-Sensor Localization for Autonomous Driving
&lt;/h3&gt;

&lt;h4&gt;
  
  
  2.1 System Sensor Composition
&lt;/h4&gt;

&lt;p&gt;The localization system adopts a &lt;strong&gt;GNSS + IMU + 3D LiDAR + wheel encoder&lt;/strong&gt; multi-source fusion architecture, with complementary functions among sensors:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;GNSS&lt;/strong&gt;: Provides global absolute position information and outputs PPS (pulse-per-second) and NMEA timing data, serving as the primary time reference for the system;&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;IMU (Inertial Measurement Unit)&lt;/strong&gt;: Outputs high-frequency tri-axis acceleration and angular velocity data, compensating for positioning outages during satellite signal blockage;&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;3D LiDAR&lt;/strong&gt;: Captures real-time environmental point cloud data, enabling high-precision relative localization through map matching;&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Wheel Encoder&lt;/strong&gt;: Outputs wheel rotation pulse signals for dead reckoning and vehicle motion state verification.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Time synchronization&lt;/strong&gt; and &lt;strong&gt;spatial registration&lt;/strong&gt; across sensors are prerequisites for fusion localization. Only with temporally aligned sensor data can the accuracy and robustness of integrated positioning be effectively improved.&lt;/p&gt;

&lt;h4&gt;
  
  
  2.2 MPSoC Heterogeneous Task Allocation
&lt;/h4&gt;

&lt;p&gt;The system adopts a collaborative architecture between PS (Processing System) and PL (Programmable Logic), following the design principle of &lt;strong&gt;"real-time tasks in hardware, complex tasks in software"&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;PL (FPGA)&lt;/strong&gt;: Establishes a high-precision system time reference, parses GNSS PPS pulses, and calibrates clock signals using a high-stability oscillator; performs hardware-synchronized acquisition of IMU and encoder data; handles serial timing control, data buffering, and AXI bus data exchange; implements pipeline-based hardware acceleration of positioning algorithms.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;PS (ARM)&lt;/strong&gt;: Runs an embedded Linux system to manage task scheduling and peripherals; receives and parses LiDAR point cloud data; performs multi-sensor data fusion and positioning computation; handles host communication, parameter configuration, and data logging.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  2.3 Overall Platform Architecture Diagram
&lt;/h4&gt;

&lt;p&gt;The system adopts a layered architecture: &lt;strong&gt;Sensor Layer → Hardware Interface Layer → MPSoC Heterogeneous Processing Layer → Application Computation Layer&lt;/strong&gt;. All sensor signals are conditioned and fed into the MPSoC, where the PL handles synchronized timing and raw data acquisition, and the PS performs algorithm computation and outputs positioning results. This compact, streamlined architecture avoids timing delays and electromagnetic interference issues inherent in discrete designs.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Overall Hardware Platform Design
&lt;/h3&gt;

&lt;h4&gt;
  
  
  3.1 Main Control Chip Selection
&lt;/h4&gt;

&lt;p&gt;The platform uses the Xilinx Zynq UltraScale+ series MPSoC as the main controller, while remaining compatible with industrial-grade heterogeneous processors such as Zynq7000 and TI AM5728/AM62x. The chip integrates multi-core ARM A53 and R5 real-time processing units, along with abundant programmable logic resources. It natively supports DDR4, Gigabit Ethernet, USB 3.0, mSATA, and high-speed serial interfaces, operating across a temperature range of -40°C to +85°C, meeting long-term stability requirements for automotive and industrial unmanned equipment.&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2 Modular Hardware Design
&lt;/h4&gt;

&lt;p&gt;The hardware platform employs a modular, layered design, divided into five functional units:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Minimal System Module&lt;/strong&gt;: Includes the MPSoC controller, DDR4 high-speed memory, eMMC solid-state storage, configuration circuitry, high-stability oscillator, and real-time clock unit, providing a stable operating foundation and timing reference.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Sensor Interface Module&lt;/strong&gt;: Features dedicated signal conditioning circuits for IMU, GNSS, LiDAR, and encoder, performing differential level conversion, signal shaping, and isolation protection to comply with each sensor's electrical interface standards.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Power Management Module&lt;/strong&gt;: Supports 20–36V wide-range automotive input, integrates overvoltage, undervoltage, and reverse-polarity protection, and implements multi-channel DC/DC voltage conversion following the MPSoC power-up sequence to deliver stable 1.2V, 1.8V, 3.3V, and 5V outputs for reliable module startup.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;High-Speed Communication and Storage Module&lt;/strong&gt;: Equipped with dual Gigabit Ethernet ports, USB 3.0, mSATA, and SD card interfaces to meet demands for high-bandwidth LiDAR data transmission, system image storage, and positioning log recording.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Debug and Expansion Module&lt;/strong&gt;: Exposes JTAG and serial debug interfaces, status indicator LEDs, and reserved GPIO expansion pins for convenient hardware debugging, firmware updates, and functional extensions.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  3.3 High-Precision Time Reference Design
&lt;/h4&gt;

&lt;p&gt;The system employs a &lt;strong&gt;combined GNSS PPS pulse and high-stability oscillator&lt;/strong&gt; timing scheme: leveraging the long-term stability of GNSS timing and the short-term low-drift characteristics of the oscillator, a microsecond-precision timer is implemented in the PL. Under normal conditions, the local clock is continuously calibrated by the PPS pulse; during GNSS signal outages, the high-stability oscillator maintains the system time reference.&lt;/p&gt;

&lt;p&gt;For different sensor types, three synchronization modes are applied: &lt;strong&gt;active synchronization, passive synchronization, and timing synchronization&lt;/strong&gt;. The IMU uses trigger-based active synchronization, the wheel encoder uses pulse-based passive synchronization, and the LiDAR uses PPS-based timing synchronization—achieving unified timing across all sensors with microsecond-level synchronization accuracy. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffn9q5t2s7tng37kal1s1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffn9q5t2s7tng37kal1s1.png" width="600" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Core Circuit Module Design
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.1 Power Circuit Design
&lt;/h4&gt;

&lt;p&gt;The power circuit employs staged protection and sequenced power delivery. EMI filtering and reverse-polarity protection are implemented at the front end, while multi-channel power regulators generate core and peripheral voltages at the back end. The MPSoC power-up sequence is strictly followed to prevent chip lockup due to improper sequencing. Load current limiting and thermal protection are also added to adapt to complex automotive electrical environments.&lt;/p&gt;

&lt;h4&gt;
  
  
  4.2 Sensor Interface Circuit
&lt;/h4&gt;

&lt;p&gt;The IMU uses an RS422 differential interface, adapted to FPGA I/O levels via level-shifting chips to ensure noise immunity over long-distance transmission. The GNSS interface reserves independent serial and PPS lines with electrical isolation to prevent timing signal distortion. Differential encoder pulses are shaped and filtered using Schmitt triggers to suppress high-frequency noise. The LiDAR connects directly to a Gigabit PHY chip to meet real-time, high-bandwidth point cloud transmission requirements. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjjqbhmku4ukmne6il9k0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjjqbhmku4ukmne6il9k0.png" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  4.3 High-Speed Interface Circuit
&lt;/h4&gt;

&lt;p&gt;Industrial-grade PHY devices are used for Gigabit Ethernet, with RGMII routing rules followed, including impedance matching and length-matched traces to ensure high-speed communication stability. DDR4 memory is designed strictly according to high-speed PCB guidelines, controlling trace impedance, length, and spacing for reliable large-data read/write operations. ESD protection components are added to mSATA and USB 3.0 interfaces to enhance robustness in industrial environments.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Embedded Software Design and Porting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  5.1 Linux System Customization and Porting
&lt;/h4&gt;

&lt;p&gt;Using the Petalinux development environment, the embedded Linux system is customized and trimmed. Device trees are adapted to hardware peripherals, kernel drivers, file systems, and boot methods are configured, supporting dual boot modes from SD card and QSPI flash. PS-PL communication protocols are set up, and a multi-core task scheduling framework is established to meet real-time algorithm execution requirements.&lt;/p&gt;

&lt;h4&gt;
  
  
  5.2 FPGA PL Logic Design
&lt;/h4&gt;

&lt;p&gt;Verilog HDL is used for logic development, primarily implementing: high-precision time counter and PPS pulse calibration logic; state-machine-based timing control for multi-sensor data acquisition; serial command parsing and FIFO data buffering; AXI bus protocol adaptation for high-speed data transfer from PL to PS. The design follows a modular state-machine approach with efficient resource utilization and good timing convergence. &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2zl9u76w9y56e4ii3l7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn2zl9u76w9y56e4ii3l7.png" width="629" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  5.3 ARM PS Application Software Design
&lt;/h4&gt;

&lt;p&gt;The PS software adopts a layered architecture: the lower layer includes drivers for serial, Ethernet, and sensors; the middle layer handles data parsing and temporal alignment; the upper layer implements multi-sensor fusion algorithms, positioning computation, host interaction, and data storage. The software uses time-sliced multitasking to ensure real-time performance of positioning tasks, while supporting online parameter configuration and offline log storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. System Function and Performance Testing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  6.1 Basic Function Testing
&lt;/h4&gt;

&lt;p&gt;A static lab test platform was built, integrating LiDAR, IMU, GNSS, and wheel encoder for full-system integration testing. Results show all hardware interfaces function correctly, PS-PL command interaction is reliable, and all sensors can power up synchronously and stably acquire raw data without packet loss or timing anomalies. The system demonstrates stable hardware and software operation.&lt;/p&gt;

&lt;h4&gt;
  
  
  6.2 Time Synchronization Accuracy Testing
&lt;/h4&gt;

&lt;p&gt;Using an oscilloscope to compare the edge deviation between the GNSS standard PPS and the system's local clock signal, the time synchronization error remains within microsecond level under normal satellite lock conditions. During continuous 4-hour operation without GNSS signals, the cumulative time error is less than 140μs, maintaining high relative timing precision—meeting the requirements for multi-sensor fusion localization.&lt;/p&gt;

&lt;h4&gt;
  
  
  6.3 Algorithm Acceleration Performance Testing
&lt;/h4&gt;

&lt;p&gt;Using LiDAR point cloud feature extraction as the test algorithm, performance was compared across the MPSoC platform, an embedded GPU platform, and a desktop CPU platform. Results show that the MPSoC, leveraging FPGA pipeline acceleration, achieves computational efficiency close to that of a desktop CPU, while consuming significantly less power than general-purpose computing platforms. This demonstrates dual advantages of low power and high computing performance, making it ideal for embedded deployment in unmanned systems.&lt;/p&gt;

&lt;h4&gt;
  
  
  6.4 Platform Advantages
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;High Integration&lt;/strong&gt;: A single MPSoC chip integrates data acquisition, timing, computation, and acceleration, eliminating the need for multiple discrete boards and reducing device size;&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;High Timing Precision&lt;/strong&gt;: Microsecond-level multi-sensor time synchronization significantly improves fusion localization accuracy;&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Low Power and Automotive-Grade Compatibility&lt;/strong&gt;: Heterogeneous architecture enables on-demand compute allocation, with wide-temperature and noise-resistant design suitable for automotive and outdoor unmanned applications;&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Customizable Hardware and Software&lt;/strong&gt;: PL logic, PS software, and peripheral interfaces can be tailored to project needs;&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Easy Algorithm Iteration&lt;/strong&gt;: Positioning algorithms run on the ARM side, allowing upgrades and optimization without modifying hardware logic.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  7. Engineering Practice of Custom MPSoC Autonomous Driving Localization Motherboard
&lt;/h3&gt;

&lt;p&gt;From an industrialization perspective, devices such as autonomous vehicles, AGV inspection robots, agricultural machinery, and power line inspection robots all require customized MPSoC hardware platforms. Drawing on years of industrial motherboard development experience, Sienovo offers end-to-end customization services—from solution evaluation and schematic/PCB design to software/hardware development and mass production—based on full-series MPSoC chips from Xilinx, TI, and Intel.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Custom Chip Solutions&lt;/strong&gt;: Core and full-board designs can be tailored to Zynq 7000/UltraScale+, TI AM5728/AM62x, and other heterogeneous platforms based on positioning accuracy, compute needs, and cost constraints.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Customizable Interfaces&lt;/strong&gt;: Dedicated peripherals such as Gigabit LiDAR ports, differential serial interfaces for IMU/GNSS, encoder interfaces, CAN/RS485, 5G expansion, and multiple Ethernet ports can be customized to match diverse sensing and networking requirements.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Industrial-Grade Reliability&lt;/strong&gt;: Designs support -40°C to +85°C wide temperature range, shock resistance, triple protection (moisture, dust, corrosion), dual power redundancy, and EMC compliance, meeting automotive and industrial safety standards.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Turnkey Hardware and Software Delivery&lt;/strong&gt;: Services include Linux/Petalinux system porting, low-level driver development, PL logic design, and multi-sensor synchronization framework integration—enabling customers to focus on high-level positioning algorithms and application development, significantly shortening R&amp;amp;D cycles.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  8. Conclusion
&lt;/h3&gt;

&lt;p&gt;This article presents the design, circuit development, software porting, and performance testing of an MPSoC-based multi-sensor fusion localization hardware platform for autonomous driving. It addresses engineering challenges of traditional discrete platforms—low integration, poor timing synchronization, and high power consumption. By leveraging FPGA's real-time timing control and ARM's flexible algorithm scheduling, the platform achieves microsecond-level synchronized sensor acquisition and hardware-accelerated positioning algorithms, meeting stability and accuracy requirements for autonomous driving and intelligent navigation systems.&lt;/p&gt;

&lt;p&gt;As autonomous driving and outdoor unmanned equipment move toward industrialization, customized MPSoC industrial motherboards have become the mainstream choice in hardware development. The standardized core board + custom baseboard development model enables rapid adaptation to multi-sensor localization needs across diverse scenarios, providing reliable support for cost reduction, efficiency improvement, and shortened time-to-market for unmanned systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sienovo provides MPSoC customization and manufacturing services.
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/161366456" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/szxinmai/article/details/161366456" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>armdev</category>
      <category>embeddedhardware</category>
      <category>fpgadev</category>
    </item>
    <item>
      <title>RK3399+FPGA-based Ground Test Bench Multi-parameter Data Logger Solution (Part 1) Software Design and Testing</title>
      <dc:creator>Leo Liu</dc:creator>
      <pubDate>Sat, 30 May 2026 06:58:19 +0000</pubDate>
      <link>https://dev.to/sienovoleo/rk3399fpga-based-ground-test-bench-multi-parameter-data-logger-solution-part-1-software-design-5a97</link>
      <guid>https://dev.to/sienovoleo/rk3399fpga-based-ground-test-bench-multi-parameter-data-logger-solution-part-1-software-design-5a97</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://intl.sienovo.cn/blog" rel="noopener noreferrer"&gt;Sienovo Engineering Blog&lt;/a&gt;. Sienovo is the overseas brand of 深圳信迈 (Shenzhen Xinmai), building edge AI computing solutions for industrial video analytics.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This blog post delves into the software design and testing aspects of an RK3399+FPGA-based ground test bench, specifically focusing on a multi-parameter data logger solution. We will explore the software's architecture, its core functionalities, the underlying Qt communication mechanisms, and the practical implementation and testing of its user interface, providing a comprehensive overview for engineers working with similar embedded systems and data acquisition challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Software Design Principles for the Ground Test Bench
&lt;/h2&gt;

&lt;p&gt;The development of the test bench software began with a thorough analysis of the operational requirements for a specific type of data logger. The primary goal was to create a fully functional, user-friendly, and intuitive test bench application using Linux-Qt as the main development framework.&lt;/p&gt;

&lt;p&gt;The software's architecture is structured around three distinct operating modes, each managed by its own functional window:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Analog Signal Source:&lt;/strong&gt; Responsible for generating simulated data.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Memory Detection and Readback:&lt;/strong&gt; Manages the retrieval of stored data.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Data Processing:&lt;/strong&gt; Handles the analysis and separation of raw data.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These windows are designed to function independently, allowing for focused testing of specific modules, yet they are also closely integrated to support comprehensive, end-to-end testing workflows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F17rtad6eqoe3roxai4yd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F17rtad6eqoe3roxai4yd.png" width="800" height="510"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Analog Signal Source Window
&lt;/h3&gt;

&lt;p&gt;The Analog Signal Source window is crucial for pre-flight testing, simulating various ground data sources. The host computer sends commands to the main CPU within the test bench, which then generates a diverse set of signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  5 channels of switching quantities (digital on/off signals)&lt;/li&gt;
&lt;li&gt;  2 channels of analog quantities&lt;/li&gt;
&lt;li&gt;  1 channel of 422 data (RS-422 serial communication)&lt;/li&gt;
&lt;li&gt;  5 channels of network data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This simulation allows for a complete system test before actual flight experiments. Pre-configured analog data is sent and stored in Flash memory, then read back. This data undergoes initial comparison and further processing to verify the entire system's operational status.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Detection and Readback Window
&lt;/h3&gt;

&lt;p&gt;This window is dedicated to managing and retrieving data stored in the data logger's Flash memory. Its functionalities include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Network configuration&lt;/li&gt;
&lt;li&gt;  Memory selection (e.g., primary or backup storage)&lt;/li&gt;
&lt;li&gt;  Querying current storage status&lt;/li&gt;
&lt;li&gt;  Setting read counts and read volumes&lt;/li&gt;
&lt;li&gt;  Stopping read operations&lt;/li&gt;
&lt;li&gt;  Erasing the data logger's memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core task here is to efficiently read back existing data from the Flash memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Processing Window
&lt;/h3&gt;

&lt;p&gt;The Data Processing window's main function is to separate raw data files that have been read back and saved to the desktop. It allows for the segregation of data according to different channels, facilitating independent analysis and research.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communication Modes
&lt;/h3&gt;

&lt;p&gt;The system employs two primary communication modes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Serial Command Transmission and Reception:&lt;/strong&gt; This mode uses a send-and-feedback mechanism to ensure the accuracy of each command. Real-time feedback is displayed on the software interface, and a &lt;code&gt;.log&lt;/code&gt; file is generated upon exit for easy review. The lower-level device provides immediate feedback upon receiving a command.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;UDP Data Readback:&lt;/strong&gt; When the host computer sends a data readback command, the data logger transmits the data stored in Flash memory back to the host via UDP over the network. This data is then saved as a local file for subsequent processing. The data packet format is structured as: &lt;code&gt;Frame Header + Frame Count + Data + Checksum&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Detailed control command tables are used to define the specific instructions for interaction between the host and the data logger.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0t8rdnpktlj37kqat49d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0t8rdnpktlj37kqat49d.png" width="800" height="714"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpub-048dcb961257f476697b113fcb5939cb9.r2.dev%2Fblog%2F131095204%2F03_f2601ca61e910483261c2365c4c67532.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpub-048dcb961257f476697b113fcb5939cb9.r2.dev%2Fblog%2F131095204%2F03_f2601ca61e910483261c2365c4c67532.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Software Function Overview
&lt;/h3&gt;

&lt;p&gt;The software incorporates the following key functionalities:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Work Mode Selection:&lt;/strong&gt; Users can choose between "Analog Source Mode," "Data Readback Mode," and "Data Separation Mode" directly from the software's startup interface.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Analog Source Control:&lt;/strong&gt; In Analog Source Mode, the software sends serial commands to enable relays on the test bench and control the CPU to generate simulated data. Operations can be performed in either automatic or manual modes. During experiments, any switching quantity can be enabled or disabled at any time, which is useful for verifying the proper functioning of specific channels within the system.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Data Readback Management:&lt;/strong&gt; In Data Readback Mode, the host computer queries storage information from a specified memory unit to select data for reading. No other operations are permitted during the read process. Upon completion, users can choose to erase the data logging device (a full erase), which requires a re-confirmation step.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Data Separation:&lt;/strong&gt; The Data Separation Mode allows users to further process read-back and stored data files. Users can separate one or multiple data channels as needed. Each data separation operation functions as an independent module, ensuring no interference between different channel separations.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Qt Signal Communication Mechanism
&lt;/h2&gt;

&lt;p&gt;As a host computer control software, its primary function is to communicate with lower-level devices through command transmission and reception. When developing with Qt, communication is a fundamental consideration. Qt's unique "Signal &amp;amp; Slot" mechanism is a powerful communication paradigm. Conceptually, a signal is like a switch, and a slot function is like a light bulb. When the switch is triggered, the light bulb activates. Similarly, when an event associated with a signal function occurs (e.g., clicking a button), a signal is emitted. The purpose is for a corresponding slot function to execute. This mechanism abstracts away complex low-level implementations; once a signal and slot are connected, the signal emitter doesn't need to know how Qt finds and executes the slot function. Compared to callback functions in some other development frameworks, Qt's Signal &amp;amp; Slot mechanism is more flexible and makes programming goals for various UI components clearer.&lt;/p&gt;

&lt;p&gt;Signals and slot functions are connected using the &lt;code&gt;QObject::connect()&lt;/code&gt; function, typically written as:&lt;br&gt;
&lt;code&gt;QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));&lt;/code&gt;&lt;br&gt;
or&lt;br&gt;
&lt;code&gt;connect(sender, SIGNAL(signal()), receiver, SLOT(slot()));&lt;/code&gt; (where &lt;code&gt;QObject&lt;/code&gt; can be omitted).&lt;/p&gt;

&lt;p&gt;In this function:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;sender&lt;/code&gt; refers to the name of the control that emits the signal.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;signal()&lt;/code&gt; is the name of the signal itself (e.g., a button click, text input).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;receiver&lt;/code&gt; refers to the name of the class that receives the signal (e.g., a window, a dialog).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;slot()&lt;/code&gt; is the corresponding slot function, defining the operation the receiving class should perform (e.g., popping up a window, displaying text).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When programming with the Signal &amp;amp; Slot mechanism, several key points should be understood:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;One-to-Many and Many-to-One Connections:&lt;/strong&gt; A single signal can be connected to multiple slot functions, and multiple signals can also be associated with the same slot function. There isn't a fixed numerical match between signals and slots. When a signal is connected to several slot functions, their execution order depends on the specific program logic and their connection sequence. This one-to-many and many-to-one relationship can be visualized as shown in Figure 3.2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1qox011hlq1p8gwwuk0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw1qox011hlq1p8gwwuk0.png" width="669" height="597"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Signal-to-Signal Connections:&lt;/strong&gt; Signals can also be connected to other signals. A signal doesn't strictly have to correspond to a slot function; it can trigger another signal, which then responds to one or more slot functions. This offers great flexibility depending on the specific application requirements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Immediate and Synchronous Execution:&lt;/strong&gt; The association between signals and slot functions is immediate and synchronous. Similar to an interrupt, when a signal triggers a slot function, it executes immediately. The main program flow will only resume after the slot function has completed its execution.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Beyond the fundamental Signal &amp;amp; Slot mechanism, Qt also provides functions to handle other event triggers, such as mouse operations and keyboard input. These operations have corresponding functions in Qt to respond to them. Figure 3.3 illustrates some common event handling functions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vmiypf7dli4vb5bf3lx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9vmiypf7dli4vb5bf3lx.png" width="767" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Software Program Interface Design and Implementation
&lt;/h2&gt;

&lt;p&gt;Based on Qt's communication mechanisms and project requirements, the software's user interface is designed with a startup screen and three main functional windows: the Analog Source window, the Data Readback window, and the Data Separation window.&lt;/p&gt;

&lt;h3&gt;
  
  
  Boot Interface
&lt;/h3&gt;

&lt;p&gt;The boot interface displays the software name, version number, and provides entry points to the three main functional windows. Each window is functionally independent and does not interfere with the others. Users can select the appropriate function button based on their specific needs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofr2is2ye4jt9ow8gx2f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fofr2is2ye4jt9ow8gx2f.png" width="573" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Ground Data Simulation Window
&lt;/h3&gt;

&lt;p&gt;Ground data simulation is an essential part of the overall system, necessary for verifying the data logger's operation before flight experiments. The simulation source offers both manual and automatic modes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftdzox5i8927xez04sdvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftdzox5i8927xez04sdvf.png" width="746" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon clicking "System Power On," the software sends a command to the CPU, activating the relays and powering up the system. In &lt;strong&gt;manual mode&lt;/strong&gt;, users can freely select any switching or analog quantity for testing, which is convenient for diagnosing faults in specific system channels. In &lt;strong&gt;automatic mode&lt;/strong&gt;, the software sends a single command, and the CPU automatically powers up, sequentially activating various switching and analog data channels, recording them in memory, thereby automating the operation. The test bench features corresponding indicator lights to show whether each switch is open. The right-hand panel of the window displays real-time operation and feedback information.&lt;/p&gt;

&lt;p&gt;The working duration for the analog source mode can be controlled by the user, typically set for 3-5 minutes. The on/off times for each switching quantity are stored as data in memory, allowing for a direct visual representation of each switch's activation time and sequence upon readback. Buttons like "System Power On" and "Automatic Mode" are designed to be toggle buttons; a single click changes their label to "System Power Off" and "Stop Test," respectively. The main CPU on the test bench is responsible for overall data framing and storage of received data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Readback Window
&lt;/h3&gt;

&lt;p&gt;The Data Readback window is a core component of the software's functionality, responsible for retrieving framed data stored in Flash memory back to the host computer. The hardware interface utilizes a 100-megabit Ethernet chip (W5300) and employs the UDP protocol for data readback. The retrieved data is saved to the desktop in &lt;code&gt;.hex&lt;/code&gt; file format.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpub-048dcb96257f476697b113fcb5939cb9.r2.dev%2Fblog%2F131095204%2F08_f0d7db396cdec509.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpub-048dcb96257f476697b113fcb5939cb9.r2.dev%2Fblog%2F131095204%2F08_f0d7db396cdec509.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The readback process involves several steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Click the "System Power On" button.&lt;/li&gt;
&lt;li&gt; Configure the network IP address and port number.&lt;/li&gt;
&lt;li&gt; Click the "Connect" button; the interface will display the current network connection status.&lt;/li&gt;
&lt;li&gt; Select the memory unit. The system is equipped with two memory chips, both with backups, ensuring data integrity.&lt;/li&gt;
&lt;li&gt; Click the "Read Recorder Information" button to display the current storage status on the interface, including power-on count and data size. Users can then select the number of reads and the read volume based on this information.&lt;/li&gt;
&lt;li&gt; Click "Read Main Memory" or "Read Backup Memory" to begin reading. The data will be saved to the desktop, named according to the read time. A prompt will appear in the text box upon completion.&lt;/li&gt;
&lt;li&gt; Finally, click the "Stop Reading" button to free up the FPGA. Users can also choose to erase the memory as needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The right-hand text box of the interface provides operational steps, and every operation and feedback is displayed in real-time in the lower text box. To prevent user errors, each button is grayed out and unusable until the previous operation is completed. After reading is finished, clicking "Exit" returns to the main interface for subsequent operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Separation Window
&lt;/h3&gt;

&lt;p&gt;The Data Separation window's primary function is to process and separate the raw data files that have been read back.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpub-048dcb96257f476697b113fcb5939cb9.r2.dev%2Fblog%2F131095204%2F09_dd5a0269c42c9414a1671023e163f1d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpub-048dcb96257f476697b113fcb5939cb9.r2.dev%2Fblog%2F131095204%2F09_dd5a0269c42c9414a1671023e163f1d.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each button in the interface represents the separation of a single data channel. Clicking a button automatically opens a file index window, allowing the user to select any previously read raw data file for separation. Upon completion, an information prompt indicates that the separation is finished. The output sub-data files are saved to the desktop. By separating the data, users can further observe and analyze each channel's data, compare it with pre-set simulated data, and draw conclusions about the overall system's operation.&lt;/p&gt;

&lt;p&gt;The fundamental idea behind data separation is to segregate data based on the frame headers of different channels. Since data from each channel is independent and unaffected by others, and the data format is fixed (Frame Header + Frame Count + Data + Checksum), using the frame header for judgment allows for accurate separation of the raw data. The program design flowchart for the separation process is illustrated in Figure 3.8.&lt;/p&gt;

&lt;p&gt;Sienovo (Sienovo) provides the RK3399+FPGA hardware and software solution for this system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Software Function Testing
&lt;/h2&gt;

&lt;p&gt;To validate the software, the RK3399 development board was connected to the test bench. This included providing 12V power from the test bench to the development board, and connecting the serial and network ports to the test bench. Upon powering on the test bench, the voltage display showed a stable 27.7V, and the indicator lights for power, serial, and network connections were all green, confirming proper hardware connectivity.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3vp9x6jyknerzp3k8zc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp3vp9x6jyknerzp3k8zc.png" width="515" height="636"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Analog Source Mode
&lt;/h3&gt;

&lt;p&gt;After launching the software, the "Analog Source" work mode was selected. In "Manual Mode," various switching and analog quantity buttons were clicked sequentially, with the working time set to 5 minutes. The software interface displayed real-time operation information, confirming the commands were being sent and processed correctly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wrzj6mqwvmttfmthmds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6wrzj6mqwvmttfmthmds.png" width="722" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Data Readback Mode
&lt;/h3&gt;

&lt;p&gt;Once the system's simulation work concluded, the data readback process began. In the software's "Data Readback" interface, the read configuration was performed according to the prompts. This involved setting up network parameters and initiating the read operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm31dr4mkttydraf70f4n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm31dr4mkttydraf70f4n.png" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon successful readback, the data was automatically saved to the desktop as a source file, named according to the storage time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Results
&lt;/h2&gt;

&lt;p&gt;The successfully read-back data was transferred from the RK3399's USB port to a PC. Using data analysis software, including Hexedit and Matlab, the raw data was processed and separated based on the data frame headers for each channel. This resulted in the output of 7 distinct data channels. By viewing and processing these channels, corresponding data source code and waveform diagrams were obtained. It was observed that within the first 8 bytes of each data channel, 4 bytes represented the data frame header, and the subsequent 4 bytes represented the frame count, both clearly visible in the raw data.&lt;/p&gt;

&lt;p&gt;The test results for switching and analog quantities are shown in Figure 5.4. For this specific test, the data frame header was &lt;code&gt;F1F20000&lt;/code&gt;, the frequency was &lt;code&gt;100Hz&lt;/code&gt;, and the frame count interval was &lt;code&gt;2&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhl72kpywlm6wsllq24mx.png" width="691" height="549"&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;This article was translated from Chinese to English with AI assistance and a light human review. The original is published at &lt;a href="https://intl.sienovo.cn/blog/131095204" rel="noopener noreferrer"&gt;Sienovo Blog&lt;/a&gt;. The original Chinese source is at &lt;a href="https://blog.csdn.net/yeyuangen/article/details/131095204" rel="noopener noreferrer"&gt;CSDN&lt;/a&gt;. Learn more about &lt;a href="https://intl.sienovo.cn" rel="noopener noreferrer"&gt;Sienovo edge AI computing&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>fpgadev</category>
    </item>
  </channel>
</rss>
