Module division
The "planning" of module division refers to planning, which means how to reasonably divide a large software into a series of functional independent parts to fulfill the system requirements. C language, as a structured programming language, is mainly based on functions in module division (Division by function becomes an error in object-oriented design, and Newton's Law encounters> relativity ), C language modular program design should understand the following concepts:
(1) A module is the combination of a. c file and A. H file. The header file (. h) declares the interface of this module;
(2) external functions and data provided by a module to other modules must be declared with the extern keyword in the. h file;
(3) Functions and global variables in the module must be declared with the static keyword at the beginning of the. c file;
(4) never define variables in. H files! The difference between defining variables and declaring variables is that defining operations that will generate memory allocation is the concept of the assembly phase; the declaration only tells the module that contains the Declaration to find external functions and variables from other modules during the connection phase. For example:
/* Module1.h */ Int A = 5;/* define int A */In the. h file of Module 1 *//* Module1. C */ # Include "module1.h"/* contains the. h file of Module 1 in Module 1 */ /* Module2. C */ # Include "module1.h"/* contains the. h file of Module 1 in Module 2 */ /* Module3. C */ # Include "module1.h"/* contains the. h file of Module 1 in Module 3 */ |
The result of the above program is that the integer variable A is defined in Modules 1, 2, and 3. A corresponds to different address units in different modules. In this world, such a program is never needed. The correct method is:
/* Module1.h */ Extern int A;/* declare int A */In the. h file of Module 1 *//* Module1. C */ # Include "module1.h"/* contains the. h file of Module 1 in Module 1 */ Int A = 5;/* define int A */In the. c file of Module 1 */ /* Module2. C */ # Include "module1.h"/* contains the. h file of Module 1 in Module 2 */ /* Module3. C */ # Include "module1.h"/* contains the. h file of Module 1 in Module 3 */ |
In this way, if modules 1, 2, and 3 operate on a, the corresponding memory unit is the same.
An embedded system usually includes two types of modules:
(1) hardware driver module. A specific hardware corresponds to a module;
(2) The division of software functional modules should meet the requirements of low coupling and high cohesion.
Multi-task or single task
The so-called "single-task system" means that the system cannot support multi-task concurrent operations and execute a task in a macro-serial manner. Multi-task systems can execute multiple tasks in parallel (micro-level serial) at the same time.
Concurrent execution of multiple tasks usually depends on a multi-task operating system (OS). The core of the multi-task operating system is the system scheduler, which uses the task control block (TCB) to manage the task scheduling function. TCB includes the current status, priority, events or resources to wait, start address of the task code, and initial Stack pointer of the task. The scheduler uses this information when the task is activated. In addition, TCB is used to store the context of the task ). The context of a task is all information to be saved when a task is stopped. Generally, the context is the current state of the computer, that is, the content of each register. When a task switchover occurs, the context of the currently running task is stored in TCB, and the context of the task to be executed is retrieved from its TCB and put into various registers.
Typical examples of embedded Multitasking OS include VxWorks and uClinux. Embedded OS is not an unattainable thing. We can use less than 1000 lines of code to implement an OS kernel with the simplest functionality for the 80186 processor. The author is preparing to do this, I hope to share my experiences with you.
Whether to choose multi-task or single-task mode depends on whether the software system is large. For example, the vast majority of Mobile Phone programs are multitasking, but some PHS protocol stacks are single-task and have no operating system. Their main programs call the processing programs of each software module in turn, simulate a multi-task environment.
Typical single-task program architecture
(1) starting from the specified address when the CPU is reset;
(2) Jump to the assembly code startup for execution;
(3) Jump to the main program of the user owner and execute it in main:
A. testing various hardware devices;
B. initialize each software module;
C. Enter an infinite loop and call the processing functions of each module.
The user's main program and the processing functions of each module are completed in C language. The user's main program finally enters an endless loop. The preferred solution is:
Some programmers write as follows:
This syntax does not exactly express the meaning of the code. We can't see anything from for (;). We can only understand what for (;) means unconditional loops in C.
Below are several "Famous" Endless loops:
(1) The operating system is an endless loop;
(2) Win32 programs are endless loops;
(3) embedded system software is an endless loop;
(4) The thread processing function of a multi-threaded Program is an endless loop.
You may argue, and say out loud: "Everything is not absolute, 2, 3, 4 can not be an endless loop ". Yes, you are right, but you cannot get flowers or applause. In fact, this is not very meaningful, because the world never needs a Win32 program to kill the OS after processing a few messages, you don't need an embedded system that just broke itself at the beginning of the run, and you don't need to start a thread for some reason. Sometimes, being too rigorous is not convenient but troublesome. The five-layer TCP/IP protocol stack surpassed the rigorous ISO/OSI Layer-7 protocol stack and became the de facto standard?
Some netizens often discuss:
Printf ("% d, % d", ++ I, I ++);/* What is the output? */ C = A ++ B;/* c =? */ |
. In the face of these problems, we can only express our sincere feelings: there are still many meaningful things in the world waiting for us to digest our food.
In fact, the embedded system will run to the end of the world.
Service Interruption
Interruption is an important part of embedded systems, but does not include interruption in Standard C. Many compilation developers have added support for interruptions on Standard C and provided new keywords for marking the interrupt service program (ISR), such as _ interrupt and # program interrupt. When a function is defined as ISR, the compiler automatically adds the on-site and out-of-stack Code required to interrupt the service program for the function.
The interrupted service program must meet the following requirements:
(1) the return value cannot be returned;
(2) parameters cannot be passed to ISR;
(3) ISR should be as short and precise as possible;
(4) printf (char * lpformatstring ,...) Functions may cause re-entry and performance problems and cannot be used in ISR.
In the development of a project, we designed a queue. In the interrupt service program, we only added the interrupt type to the queue, in the endless loop of the main program, the interruption queue is constantly scanned for interruptions. If yes, the first interruption type in the queue is taken out for corresponding processing.
/* Store the interrupted queue */ Typedef struct tagintqueue { Int inttype;/* interrupt type */ Struct tagintqueue * next; } Intqueue;Intqueue lpintqueuehead; _ Interrupt isrexample () { Int inttype; Inttype = getsystemtype (); Queueaddtail (lpintqueuehead, inttype);/* Add a new interrupt to the end of the queue */ } |
Judge whether there is any interruption in the main program loop:
While (1) { If (! Isintqueueempty ()) { Inttype = getfirstint (); Switch (inttype)/* Is it like the message parsing function of Win32 program? */ { /* Yes. Our interrupt type resolution is similar to message-driven */ Case XXX:/* do we call it "interrupt driver? */ ... Break; Case XXX: ... Break; ... } } } |
The interrupted service program designed according to the above method is very small, and the actual work is handed over to the main program.
Hardware Driver Module
A hardware driver module usually includes the following functions:
(1) interrupt service program ISR
(2) hardware initialization
A. Modify the registers and set hardware parameters (for example, set the baud rate for UART, and set the sampling rate for AD/DA devices );
B. Write the interrupt service program entry address to the interrupt vector table:
/* Set the interrupt vector table */ M_myptr = make_far_pointer (0l);/* return void far pointer void far **/ M_myptr + = itype_uart;/* itype_uart: UART interrupt service program */ /* Offset from the first address of the interrupt vector table */ * M_myptr = & UART _ ISR;/* UART _ ISR: UART interrupt service program */ |
(3) set the CPU control line for the hardware
A. If the control line can be used as the PIO (programmable I/O) and control signal, set the internal registers of the CPU to use it as the control signal;
B. Set the interrupt shielding bit inside the CPU for the device and the interrupt mode (Level Trigger or edge trigger ).
(4) provides a series of operation interface functions for the device. For example, for LCD, the driver module should provide functions such as pixel, line drawing, matrix drawing, and character dot matrix display. For real-time clock, the driver module must provide functions such as obtaining time and setting time.
Object-oriented nature of C
In the object-oriented language, the concept of class appears. Class is a collection of specific operations on specific data. The class contains two categories: Data and operations. In C language, struct is only a collection of data. We can use a function pointer to simulate struct as a "class" that contains data and operations ". The following C program simulates the simplest "class ":
# Ifndef c_class # Define c_class struct # Endif C_class { C_class A * a_this;/* This pointer */ Void (* Foo) (c_class A * a_this);/* behavior: function pointer */ Int A;/* Data */ Int B; }; |
We can use the C language to simulate three features of the forward Object: encapsulation, inheritance, and polymorphism, but more often, we just need to encapsulate the data and behavior to solve the software structure disorder. The purpose of C's Object-oriented thinking is not to simulate the behavior itself, but to solve the problem that the overall framework structure of the program is dispersed, data and functions are out of touch in some cases when C is used for programming. We will see this example in subsequent chapters.
Summary
This article introduces the knowledge of embedded system programming software architecture, including module division, multi-task or single-task selection, typical single-task program architecture, interrupted service program, and hardware driver module design, the main elements of an embedded system software are given from a macro perspective.
Remember: software structure is the soul of software! The structure of the program is messy, and debugging, testing, maintenance, and upgrade are extremely difficult.