Section 68: Multi-file programming skills of single-chip C language

Publisher:创意火舞Latest update time:2016-03-16 Source: eefocusKeywords:MCU Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
Opening remarks:
Many people also call multi-file programming modular programming. In fact, I think it is more practical to call it multi-file programming. There are two biggest advantages of multi-file programming. One is that it adds a directory to our program, which is convenient for us to find. Another advantage is that it is convenient to transplant functional program modules that others have already made. Using this feature, it is particularly suitable for teams to work on large projects together. When many beginners first start learning multi-file programming, they often encounter problems such as duplicate definitions. Do you want to know how to solve these problems? As long as you follow the rules taught by Hongge below, these problems will not exist.
First: Keep each file in pairs. Every .c source file must have a .h header file corresponding to it, and every .h header file must have a .c source file corresponding to it. For example: main.c and main.h, delay.c and delay.h.
 
Second: The .c source file is only responsible for the definition of functions and variables, but not for the declaration of functions and variables. For example:
unsigned char ucLedStep=0; //This is the definition of global variables
void led_flicker() //This is the function definition
{
   //…The specific code content is inside
  }
 
Third: The .h header file is only responsible for function declarations and variable declarations, as well as constant and IO port macro definitions, but not for function definitions and variable definitions. For example:
#define const_time_level 200 //This is a constant macro definition
sbit led_dr=P3^5; //This is the macro definition of the IO port
void led_flicker(); //This is the function declaration
extern unsigned char ucLedStep; //This is a global variable declaration, and cannot be assigned an initial value
 
Fourth: Each .h header file must be fixed with #ifndef, #define, #endif statements as templates. This template is used to avoid errors caused by repeated inclusion of the contents in the header file during compilation. For the flag variable _XXX_, Hongge recommends using its own file name with leading and trailing underscores_.
for example:
#ifndef _LED_ //The flag variable _LED_ is named after its own file name
#define _LED_ //The flag variable _LED_ is named after its own file name
#define const_time_level 200 //This is a constant macro definition
sbit led_dr=P3^5; //This is the macro definition of the IO port
void led_flicker(); //This is the function declaration
extern unsigned char ucLedStep; //This is a global variable declaration, and cannot be assigned an initial value
#endif  
 
Fifth: Each .h header file must declare all defined functions and global variables in its corresponding .c source file. Note: all global variables in the .c source file must be declared once in its corresponding .h header file, not just functions. This place is easily overlooked.
For example: in the led.h header file:
void led_flicker(); //This is the function declaration, because this function is defined in the led.c file.
   extern unsigned char ucLedStep; //This is a global variable declaration, no initial value is allowed
 
Sixth: Each .c source file must contain two files, one is the system header file REG52.H of the microcontroller, and the other is its own header file such as initial.h. The remaining header files are called based on actual conditions. If we use the functions, global variables or macro definitions of certain files, we need to call the corresponding header files.
For example: in the initial.c source file:
#include"REG52.H" //MCU system header file that must be included
#include"initial.h" //Must include its own header file
/* Comments:
   Since the led_dr statement is used in this source file, and led_dr is a macro definition in the led.h file, led.h must also be included.
*/  
#include"led.h" //Because the led_dr statement is used in this source file, led.h must also be included
void initial_myself() //This is the function definition
{
  led_dr=0; //led_dr is defined and declared in the led file
}
 
Seventh: When declaring a global variable, you must add the extern keyword, and you must not assign an initial value when declaring a global variable, for example:
extern unsigned char ucLedStep=0; //This is absolutely wrong.
extern unsigned char ucLedStep; //This is the declaration of a global variable, this is correct
 
Eighth: The compiler does not allocate memory space for function and global variable declarations. The compiler allocates memory space for function and global variable definitions. Function and global variable definitions can only appear once in a .c source file, while function and global variable declarations can appear in multiple .h files.

For details, please refer to the source code explanation. This program example directly changes the source file in Section 4 to the multi-file programming mode.

(1) Hardware platform:
    Based on Zhu Zhaoqi 51 single-chip microcomputer learning board. Change the source file in Section 4 to the multi-file programming mode.

(2) Function: Same as the function in Section 4 above, make an LED flash.
   
(3) Screenshot preview of Keil multi-file programming:
(4) Download the entire source code explanation project file:
 more_file_4.rar  (23.69 KB, Downloads: 214)
(5) The source code is explained as follows (note that the following code cannot be directly put into a source file for compilation):
  1. /*The following is the content of main.h*/
  2.  
  3. /* Note 1:
  4.   Each header file is fixed with #ifndef, #define, #endif
  5.   For the template, I suggest using the file name itself plus leading and trailing underscores for the identifier variable _XXX_.
  6.   This flag variable name is used to prevent multiple inclusion errors. Please see Note 2 for detailed explanation.
  7.   Each header file only declares functions and variables, as well as macro definitions for constants and IO ports.
  8.   Function definition and variable definition.
  9. */  
  10. #ifndef _MAIN_ //The flag variable _MAIN_ is named after its own file name
  11. #define _MAIN_ //The flag variable _MAIN_ is named after its own file name 
  12.  
  13. void main(); //This is the function declaration
  14.  
  15. #endif  
  16.  
  17. /* Note 2:
  18. The above statement
  19. #ifndef  
  20. #define
  21. Insert more content...
  22. #endif
  23.  
  24. Similar to treating _MAIN_ as a flag variable
  25. if(_MAIN_==0) // equivalent to #ifndef _MAIN_
  26. {
  27.     _MAIN_=1; // equivalent to #define _MAIN_ 
  28.      Insert more content...
  29.  
  30. } //Equivalent to #endif
  31.  
  32. The purpose is to assign a flag variable so that the compiler only includes this header file once during compilation to avoid multiple inclusion errors.
  33. */  
  34.  
  35. /*------Dividing line--------------------------------------------------*/
  36.  
  37. /*The following is the content of main.c*/
  38.  
  39. /* Note 1:
  40.   Each source file must contain two files, one is the system header file REG52.H of the microcontroller,
  41.   The other one is its own header file main.h. The rest of the header files depend on the actual situation.
  42.   To decide whether to call, which files' functions, global variables or macro definitions we use, we need to call the corresponding header files.
  43.   Each source file only defines functions and variables, but does not declare functions and variables.
  44. */  
  45.  
  46. #include "REG52.H" //Must include the microcontroller system header file
  47. #include "main.h" //Must include its own header file
  48.  
  49. /* Note 2:
  50.    (1) Since the initial_myself() and initial_peripheral() functions are called in this source file, and these two functions
  51.       They are all defined and declared in the initial file, so initial.h must also be included.
  52.    (2) Since the delay_long(100) function is called in this source file, and this function
  53.       It is defined and declared in the delay file, so delay.h must also be included.
  54.    (2) Since the led_flicker() function is called in this source file, and this function
  55.       It is defined and declared in the led file, so led.h must also be included.
  56. */  
  57.  
  58.  
  59. #include "initial.h" //Since the initial_myself() and initial_peripheral() functions are used in this source file, initial.h must also be included
  60. #include "delay.h" //Because the delay_long(100) function is used in this source file, delay.h must also be included
  61. #include "led.h" //Since the led_flicker() function is used in this source file, led.h must also be included
  62.  
  63. void main() //This is the function definition
  64.   {
  65.    initial_myself();  
  66.    delay_long(100);   
  67.    initial_peripheral(); 
  68.    while(1)   
  69.    {
  70.       led_flicker();   
  71.    }
  72.  
  73. }
  74.  
  75. /*------Dividing line--------------------------------------------------*/
  76.  
  77. /*The following is the content of delay.h*/
  78.  
  79.  
  80. #ifndef _DELAY_ //The flag variable _DELAY_ is named after its own file name
  81. #define _DELAY_ //The flag variable _DELAY_ is named after its own file name 
  82.  
  83. void delay_long(unsigned int uiDelaylong); //This is the function declaration. Each function in a source file must be declared in its header file.
  84.  
  85.  
  86. #endif  
  87.  
  88. /*------Dividing line--------------------------------------------------*/
  89.  
  90. /*The following is the content of delay.c*/
  91.  
  92.  
  93. #include "REG52.H" //Must include the microcontroller system header file
  94. #include "delay.h" //Must include its own header file
  95.  
  96.  
  97. void delay_long(unsigned int uiDelayLong) //This is the function definition
  98. {
  99.    unsigned int i; //This is the definition of a local variable
  100.    unsigned int j; //This is the definition of a local variable
  101.    for(i=0;i
  102.    {
  103.       for(j=0;j<500;j++) 
  104.           {
  105.              ;
  106.           }
  107.    }
  108. }
  109. /*------Dividing line--------------------------------------------------*/
  110. /*The following is the content of initial.h*/
  111.  
  112. #ifndef _INITIAL_ //The flag variable _INITIAL_ is named after its own file name
  113. #define _INITIAL_ //The flag variable _INITIAL_ is named after its own file name 
  114.  
  115. void initial_myself(); //This is a function declaration. Each function in a source file must be declared in its header file.
  116. void initial_peripheral(); //This is a function declaration. Each function in a source file must be declared in its header file.
  117.  
  118. #endif  
  119.  
  120. /*------Dividing line--------------------------------------------------*/
  121. /*The following is the content of initial.c*/
  122.  
  123.  
  124. #include "REG52.H" //Must include the microcontroller system header file
  125. #include "initial.h" //Must include its own header file
  126.  
  127. /* Note 1:
  128.    Since the led_dr statement is used in this source file, and led_dr is a macro definition in the led file, led.h must also be included.
  129. */  
  130. #include "led.h" //Since the led_dr statement is used in this source file, led.h must also be included
  131.  
  132. void initial_myself() //This is the function definition
  133. {
  134.  
  135.  
  136.   TMOD=0x01; //The following can directly use the register keywords TMOD, TH0, TL0, EA, ET0, TR0 because the REG52.H header file is included
  137.  
  138.   TH0=0xf8;  
  139.   TL0=0x2f;   
  140.  
  141.   led_dr=0; //led_dr is defined and declared in the led file
  142. }
  143.  
  144.  
  145. void initial_peripheral() //This is the function definition
  146. {
  147.   EA=1;     
  148.   ET0=1;    
  149.   TR0=1;    
  150.  
  151. }
  152.  
  153. /*------Dividing line--------------------------------------------------*/
  154. /*The following is the content of interrupt.h*/
  155.  
  156.  
  157. #ifndef _INTERRUPT_ //The flag variable _INTERRUPT_ is named after its own file name
  158. #define _INTERRUPT_ //The flag variable _INTERRUPT_ is named after its own file name 
  159.  
  160. void T0_time(); //This is a function declaration. Each function in a source file must be declared in its header file.
  161.  
  162. /* Note 1:
  163. To declare an external global variable, you must add the extern keyword. At the same time, you must not assign an initial value when declaring a global variable, for example:
  164. extern unsigned int uiTimeCnt=0; This is absolutely wrong.
  165. */  
  166. extern unsigned int uiTimeCnt; //This is a global variable declaration, and cannot be assigned an initial value
  167.  
  168.  
  169. #endif  
  170.  
  171. /*------Dividing line--------------------------------------------------*/
  172. /*The following is the content of interrupt.c*/
  173.  
  174.  
  175. #include "REG52.H" //Must include the microcontroller system header file
  176. #include "interrupt.h" //Must include its own header file
  177.  
  178. unsigned int uiTimeCnt=0; //This is the definition of a global variable, which can be assigned an initial value
  179.  
  180.  
  181. void T0_time() interrupt 1 //This is the function definition
  182. {
  183.   TF0=0; //The following can directly use the register keywords TF0, TR0, TH0, TL0 because the REG52.H header file is included
  184.   TR0=0;
  185.  
  186.   if(uiTimeCnt<0xffff) 
  187.   {
  188.       uiTimeCnt++; 
  189.   }
  190.  
  191.   TH0=0xf8;  
  192.   TL0=0x2f;
  193.   TR0=1; 
  194. }
  195.  
  196.  
  197. /*------Dividing line--------------------------------------------------*/
  198. /*The following is the content of led.h*/
  199.  
  200.  
  201. #ifndef _LED_ //The flag variable _LED_ is named after its own file name
  202. #define _LED_ //The flag variable _LED_ is named after its own file name 
  203.  
  204.  
  205. #define const_time_level 200 //Macro definitions are placed in header files
  206.  
  207. /* Note 1:
  208.   The macro definition of the IO port is also placed in the header file.
  209.   If it is a PIC microcontroller, the following IO port definition is equivalent to the macro definition #define led_dr LATBbits.LATB4 and other statements 
  210. */  
  211. sbit led_dr=P3^5; //If it is a PIC microcontroller, it is equivalent to the macro definition #define led_dr LATBbits.LATB4 and other statements 
  212.  
  213. void led_flicker(); //This is the function declaration. Each function in a source file must be declared in its header file.
  214.  
  215. /* Comment 3:
  216. When declaring a global variable, you must add the extern keyword. At the same time, you must not assign an initial value when declaring a global variable, for example:
  217. extern unsigned char ucLedStep=0; This is absolutely wrong.
  218. */  
  219. extern unsigned char ucLedStep; //This is the declaration of a global variable
  220.  
  221.  
  222.  
  223. #endif  
  224.  
  225. /*------Dividing line--------------------------------------------------*/
  226. /*The following is the content of led.c*/
  227.  
  228. #include "REG52.H" //Must include the microcontroller system header file
  229. #include "led.h" //Must include its own header file
  230.  
  231.  
  232. /* Note 1:
  233.    Since the uiTimeCnt global variable is used in this source file, and uiTimeCnt is declared and defined in the interrupt file,
  234.    So interrupt.h must also be included
  235. */  
  236. #include "interrupt.h" //Must include its own header file
  237.  
  238. unsigned char ucLedStep=0; //This is the definition of a global variable, which can be assigned an initial value
  239.  
  240. void led_flicker() //This is the function definition
  241. {
  242.   switch(ucLedStep)
  243.   {
  244.      case 0:
  245.  
  246.          if(uiTimeCnt>=const_time_level) 
  247.          {
  248.  
  249.              ET0=0; //The following can directly use the ET0 register keyword because the REG52.H header file is included
  250.              uiTimeCnt=0; //The uiTimeCnt variable is declared and defined in the interrupt file, so interrupt.h must also be included
  251.              ET0=1; 
  252.              led_dr=1; //This IO port definition has been defined in the led.h header file
  253.              ucLedStep=1; //Switch to the next step
  254.          }
  255.          break;
  256.      case 1:
  257.          if(uiTimeCnt>=const_time_level) 
  258.          {
  259.              ET0=0; 
  260.              uiTimeCnt=0; 
  261.              ET0=1;  
  262.              led_dr=0;   
  263.              ucLedStep=0; //Return to the previous step
  264.          }
  265.          break;
  266.   
  267.   }
  268.  
  269. }
  270.  
  271.  
  272. /*------Dividing line--------------------------------------------------*/
  273.  
 
Closing remarks:
The next section will start to talk about LCD display. For more details, please listen to the next analysis - commonly used dot matrix font program with 12864 LCD screen font library.
Keywords:MCU Reference address:Section 68: Multi-file programming skills of single-chip C language

Previous article:Section 67: Using external interrupts to simulate serial port data transmission and reception
Next article:Section 69: Using the static keyword can reduce the use of global variables

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号