MCU C language modular programming

Publisher:tetsikaLatest update time:2016-12-26 Source: eefocusKeywords:MCU Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

  A good beginning is half the battle

  Through the study of the previous chapter, I think you have mastered how to release the CPU in the program. I hope you can continue to stick to it. A good start is half the battle. Everything we do today is to do better in microcontroller programming.

Before talking about today's topic, let me talk about some of my previous experiences. When I first came into contact with C language programs, due to the limitations of learning content, the programs I wrote were not very large, generally only a few hundred lines. So all the programs were completed in one source file. I remember that when I participated in an electronic design competition in my freshman year, I debugged for more than a week. All the programs added up to about 1,000 lines. It took a long time to browse through a long file. Simple syntax errors were easy to locate, but other errors often took a long time to find. At that time, I began to know about modular programming and tried to write programs in modules. At first, some functions with the same function (such as the driver of 1602 LCD) were all written in a header file (.h) file, and then included in the place where they needed to be called, but I soon found that this method had its limitations and it was easy to make duplicate inclusion errors.

And it is also inconvenient to call. Soon the summer electronic design competition came, and the school conducted some training on our single-chip microcomputer software programming. Because the school has participated in national and provincial competitions over the years, it has accumulated a certain number of driver modules. In those days, the teacher would assign a certain amount of tasks every day, asking us to combine these modules to complete certain functions. And it was the training of modular programming in those days that gave me a deeper understanding of modular programming. And the program specifications began to pay attention slowly. In the days that followed, regardless of the size of the program, it was written in a modular programming way. For a long time, there have been single-chip microcomputer enthusiasts communicating with me on QQ. Sometimes, they would send me some problematic program source files and ask me to help modify them. It is also a long file, and the naming is extremely irregular. It is really painful to read it from the beginning. To be honest, it is really faster for me to rewrite one for them. This is true, because a certain amount of modules have been accumulated on hand. When completing a new system, it only needs to be completed quickly and conveniently according to the upper-level functional requirements and with the support of the underlying modules. There is no need to rewrite it brick by brick from beginning to end. From this, we can also see that one of the benefits of modular programming is its high reusability.

  Let us now unveil the mystery of modularity and take a glimpse of its true face.

  C language source file *.c

  When it comes to C language source files, everyone is familiar with them. Because the program codes we usually write are almost all in this XX.C file. The compiler also compiles and generates the corresponding target file based on this file. As the basis of modular programming, the source code of all the functions we want to implement is in this file. Ideal modularization should be regarded as a black box. That is, we only care about the functions provided by the module, regardless of the implementation details inside the module. It's like we bought a mobile phone, we only need to know how to use the functions provided by the mobile phone, and we don't need to know how it sends text messages or how to respond to our key input. These processes are a black box for us users.

  In large-scale program development, a program is composed of many modules. It is very likely that the writing tasks of these modules are assigned to different people. When you write this module, you may need to use the interface of the module written by others. At this time, what we care about is what kind of interface its module implements and how I can call it. As for how the module is organized internally, for me, there is no need to pay too much attention. What we need to pay attention to is to pursue the unity of the interface and shield unnecessary details from the outside as much as possible.

  C language header file *.h

  When it comes to modular programming, it is bound to involve multi-file compilation, that is, project compilation. In such a system, there are often multiple C files, and the functions of each C file are different. In our C file, since we need to provide an interface to the outside world, there must be some functions or variables provided to other external files for calling.

  Suppose we have an LCD.C file, which provides the most basic LCD driving function

    LcdPutChar(char cNewValue); //Output a character at the current position

But we need to call this function in another file, so how do we do it?

  This is the role of the header file. It can be called an interface description file. The file should not contain any substantial function code. We can understand this header file as a manual, which describes the interface functions or interface variables provided by our module. At the same time, the file also contains some very important macro definitions and some structure information. Without this information, it is likely that the interface function or interface variable cannot be used normally. But the general principle is: information that should not be known to the outside world should not appear in the header file, and the information required for the outside world to call the interface function or interface variable in the module must appear in the header file, otherwise, the outside world cannot correctly call the interface function we provide. Therefore, in order for external functions or files to call the interface function we provide, it is necessary to include the interface description file we provide, that is, the header file. At the same time, our own module also needs to include this module header file (because it contains the macro definitions or structures required in the module source file). Just like the files we usually use are in triplicate, the module itself also needs to include this header file.

Let's define this header file. Generally speaking, the name of the header file should be consistent with the name of the source file, so that we can clearly know which header file describes which source file.

  So we got the header file LCD.h of LCD.C, and its content is as follows.

#ifndef _LCD_H_#define _LCD_H_ extern LcdPutChar(char cNewValue);#endif

 

  This is similar to defining a function in a source file. The difference is that the extern modifier is added in front of it to indicate that it is an external function that can be called by other external modules.

#ifndef _LCD_H_#define _LCD_H_#endif

 

  These conditional compilation and macro definitions are to prevent duplicate inclusion. If there are two different source files that need to call the LcdPutChar(char cNewValue) function, they both include this header file through #include "Lcd.h". When the first source file is compiled, since _LCD_H_ has not been defined, the #ifndef _LCD_H_ condition is established, so _LCD_H_ is defined and the following statement is included. When the second file is compiled, since _LCD_H_ has been defined when the first file is included, #ifndef _LCD_H_ is not established, and the entire header file content is not included. Assuming there is no such conditional compilation statement, then both files include extern LcdPutChar(char cNewValue);, which will cause a duplicate inclusion error.

  Typedef must be mentioned

  Many friends seem to be accustomed to using the following statements in the program to define data types

#define uint unsigned int #define uchar unsigned char

 

    Then use it directly when defining variables

uint g_nTimeCounter = 0;

 

  It is undeniable that this is indeed very convenient, and it is also convenient for transplantation. But do you still think so considering the following situation?

#define PINT unsigned int * //define unsigned int pointer type PINT g_npTimeCounter, g_npTimeState;

 

  So did you define two unsigned int pointer variables, or one pointer variable and one integer variable? And what was your original intention? Did you want to define two unsigned int pointer variables? If so, you will probably be looking for errors everywhere soon. Fortunately, C language has taken this into consideration for us. Typedef was born for this purpose. To give a variable an alias, we can use the following statement

typedef unsigned int uint16 ; //Give an alias to the pointer to an unsigned integer variable uint16typedef unsigned int * puint16 ; //Give an alias to the pointer to an unsigned integer variable puint16

 

  When we define variables, we can define them like this:

uint16 g_nTimeCounter = 0; //define an unsigned integer variable puint16 g_npTimeCounter; //define a pointer to an unsigned integer variable

 

  When we use C language to program in 51 MCU, the range of integer variables is 16 bits, while the range of integer variables in 32-bit microprocessors is 32 bits. If we want to port some code written in 8-bit MCU to 32-bit processor, then we may need to modify the type definition of variables everywhere in the source file. This is a huge task. In order to consider the portability of the program, we should develop a good habit of defining variables with aliases at the beginning.

For example, on an 8-bit microcontroller platform, there is a variable definition as follows

uint16 g_nTimeCounter = 0;

 

  If porting to a 32-bit MCU platform, the range is still expected to be 16 bits.

  You can directly modify the definition of uint16, that is

typedef unsigned short int uint16;

 

  This is all you need, without having to search and modify the source files everywhere.

 

Define all commonly used data types in this way to form a header file, which is convenient for us to call directly in future programming.

File name MacroAndConst.h

Its contents are as follows:

Copy code

#ifndef _MACRO_AND_CONST_H_#define _MACRO_AND_CONST_H_typedef unsigned int uint16;
typedef unsigned int UINT;
typedef unsigned int uint;
typedef unsigned int UINT16;
typedef unsigned int WORD;
typedef unsigned int word;
typedef int int16;
typedef int INT16;
typedef unsigned long uint32;

typedef unsigned long UINT32;
typedef unsigned long DWORD;
typedef unsigned long dword;
typedef long int32;
typedef long INT32;
typedef signed char int8;
typedef signed char INT8;
typedef unsigned char byte;
typedef unsigned char BYTE;
typedef unsigned char uchar;
typedef unsigned char UINT8;
typedef unsigned char uint8;
typedef unsigned char BOOL;#endif

Copy code

 

So far, it seems that we have a little idea about the division of labor between source files and header files and modular programming. So let's strike while the iron is hot and divide the LED flashing function we wrote in the previous chapter into modules and reorganize and compile it.

 

In the previous chapter, we mainly completed the function of making the LED driven by port P0 flash at a frequency of 1Hz. The timer and LED driver module were used. Therefore, we can simply divide the entire project into three modules: timer module, LED module, and main function.

The corresponding file relationship is as follows

main.c 

Timer.c --?Timer.h

Led.c --?Led.h

Before we start to rewrite our program, let me first tell you how to create a project template in KEIL. I have been using this template until now. I hope it can give you some inspiration.

The following content is mainly based on pictures, supplemented by a small amount of text description.

Let's take the chip AT89S52 as an example.  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  OK, now a simple project template has been established. When we create new source files and header files in the future, we can save them directly to the src file directory.

 

Next we start writing each module file.

First, write Timer.c. The main content of this file is timer initialization and timer interrupt service function. Its content is as follows.

Copy code

#include bit g_bSystemTime1Ms = 0 ; // 1MS system time stamp void Timer0Init(void)
{
    TMOD &= 0xf0 ;
    TMOD |= 0x01 ; //Timer 0 working mode 1
    TH0 = 0xfc; //Timer initial value
    TL0 = 0x66 ;
    TR0 = 1 ;
    ET0 = 1 ;
}void Time0Isr(void) interrupt 1{
    TH0 = 0xfc; //Re-initialize the timer
    TL0 = 0x66 ;
    g_bSystemTime1Ms = 1; //1MS time stamp flag is set}

Copy code

  Since we need to call our g_bSystemTime1Ms variable in the Led.c file, and the main function needs to call the Timer0Init() initialization function, we should make an external declaration for this variable and function in the header file to facilitate other function calls. 

The contents of Timer.h are as follows.

Copy code

#ifndef _TIMER_H_#define _TIMER_H_extern void Timer0Init(void) ;extern bit g_bSystemTime1Ms ;#endif

Copy code

After completing the timer module, we start writing the LED driver module.

The content of Led.c is as follows:

Copy code

#include #include "MacroAndConst.h"#include "Led.h"#include "Timer.h"static uint16 g_u16LedTimeCount = 0 ; //LED counterstatic uint8 g_u8LedState = 0 ; //LED status flag, 0 means on, 1 means off#define LED P0 //Define LED interface#define LED_ON() LED = 0x00 ; //All LEDs are on#define LED_OFF() LED = 0xff ; //All LEDs are offvoid LedProcess(void)
{ if(0 == g_u8LedState) //If the LED state is on, turn on the LED {
        LED_ON() ;
    } else //Otherwise turn off the LED {
        LED_OFF();
    }
}void LedStateChange(void)
{ if(g_bSystemTime1Ms) //System 1MS time stamp reaches {
        g_bSystemTime1Ms = 0;
        g_u16LedTimeCount++; //LED counter plus one
        if(g_u16LedTimeCount >= 500) //The count reaches 500, which means 500MS has passed, and the LED status is changed. {
            g_u16LedTimeCount = 0;
            g_u8LedState = ! g_u8LedState ;
        }
    }
}

Copy code

This module has only two external interfaces, so corresponding declarations need to be made in the corresponding Led.h.

Led.h content:

Copy code

#ifndef _LED_H_#define _LED_H_extern void LedProcess(void) ;extern void LedStateChange(void) ;#endif

Copy code

After these two modules are completed, we add their C files to the project and then start writing the code in the main function.

As follows:

Copy code

#include #include "MacroAndConst.h"#include "Timer.h"#include "Led.h"sbit LED_SEG = P1^4; //digital tube segment selection sbit LED_DIG = P1^5; //digital tube position selection sbit LED_CS11 = P1^6; //led control bit void main(void)
{
    LED_CS11 = 1; //74HC595 output enable
    LED_SEG = 0; //Disable the segment selection and bit selection of the digital tube (because they share the P0 port with the LED)
    LED_DIG = 0 ;
    Timer0Init() ;
    EA = 1 ; while(1)
    {
        LedProcess() ;
        LedStateChange();
    }
}

Copy code

 

The screenshot of the entire project is as follows

 

 

  This is the end of Chapter 3.

Let's summarize what we need to pay attention to.
1. What is the role of C language source files (*.c)?
2. What is the role of C language header files (*.h)?
3. The role of typedef
4. How to organize project templates
5. How to create a multi-module (multi-file) project


Keywords:MCU Reference address:MCU C language modular programming

Previous article:Keil program debugging window
Next article:Modular Programming in KEIL

Recommended ReadingLatest update time:2024-11-16 15:47

ARM microcontroller (learning) - my first ARM7 microcontroller program
It was not easy. I finally completed my first ARM7 microcontroller program. Although it is very simple, it took me a long time to learn the software IAR for ARM. The most serious problem I encountered was the header file problem. After a long time, I suddenly realized it in the examples they provided. I had to list th
[Microcontroller]
ARM microcontroller (learning) - my first ARM7 microcontroller program
A preliminary study on the early failure problem of MCU circuit for USB key
  1 Problem Statement   The MCU circuit used in the USBkey products produced by our company has been subjected to electrical aging tests since the mass production of USBkey products began in early September 2007. We found that the early failure of this circuit did not meet our requirements, and the failure rate was
[Microcontroller]
A preliminary study on the early failure problem of MCU circuit for USB key
51 MCU cumulative interrupt times make the LED light flash
1. Use proteus to draw a simple circuit diagram for subsequent simulation 2. Programming /******************************************************************************************************************** ---- @Project: LED ---- @File: main.c ---- @Edit: ZHQ ---- @Version: V1.0 ---- @CreationTime: 2020
[Microcontroller]
51 MCU cumulative interrupt times make the LED light flash
LPC1788--I2C settings to drive PCF8574 and special considerations
        Simply record the register operation of LPC1788 learning process---I2C learning         Direct operation of registers can be learned more intuitively and give you a deeper understanding of chip functions!         Special note: If you use P0_27 and P0_28 of I2C0, you must add external pull-up resistors! #in
[Microcontroller]
LPC1788--I2C settings to drive PCF8574 and special considerations
Master-slave multi-machine communication between PC and PIC microcontroller and its application in data detection system
Introduction: Aiming at the data transmission problem of gas alarm detection system, this paper proposes a master-slave serial asynchronous communication method between PC and multiple PIC microcontrollers. It also gives the hardware circuit diagram, communication protocol, software flow chart and the precautions when
[Microcontroller]
Master-slave multi-machine communication between PC and PIC microcontroller and its application in data detection system
Classic design of LED dot matrix handwriting screen using PIC microcontroller
This paper proposes a design scheme of LED dot matrix handwriting screen based on PIC microcontroller. This scheme uses PIC 16F877A microcontroller as the main core control component and designs a 32×32 LED dot matrix module writing display screen. It is mainly composed of light pen module and dot matrix display modul
[Microcontroller]
Classic design of LED dot matrix handwriting screen using PIC microcontroller
Domestic MCUs challenge established manufacturers and enter the automotive field
Although there are many IC products, there are only a handful of ICs with a scale of over 100 billion yuan and sales of over 10 billion units, and MCU (microcontroller) is one of them. After all, from 1971 to the present, the continuously advanced MCU has become a walking "microcomputer" and is widely used in consumer
[Embedded]
Domestic MCUs challenge established manufacturers and enter the automotive field
Design of digital virtual surround sound controller based on PIC microcontroller
introduction With the rapid development of smart entertainment technology, consumers generally want to create the sound effects of a home theater. To configure a home theater sound system, you need a surround sound processor and a high-power AV amplifier, and at least 5 or 6 speakers. However, using 5 or 6
[Microcontroller]
Design of digital virtual surround sound controller based on PIC microcontroller
Latest Microcontroller Articles
  • Download from the Internet--ARM Getting Started Notes
    A brief introduction: From today on, the ARM notebook of the rookie is open, and it can be regarded as a place to store these notes. Why publish it? Maybe you are interested in it. In fact, the reason for these notes is ...
  • Learn ARM development(22)
    Turning off and on interrupts Interrupts are an efficient dialogue mechanism, but sometimes you don't want to interrupt the program while it is running. For example, when you are printing something, the program suddenly interrupts and another ...
  • Learn ARM development(21)
    First, declare the task pointer, because it will be used later. Task pointer volatile TASK_TCB* volatile g_pCurrentTask = NULL;volatile TASK_TCB* vol ...
  • Learn ARM development(20)
    With the previous Tick interrupt, the basic task switching conditions are ready. However, this "easterly" is also difficult to understand. Only through continuous practice can we understand it. ...
  • Learn ARM development(19)
    After many days of hard work, I finally got the interrupt working. But in order to allow RTOS to use timer interrupts, what kind of interrupts can be implemented in S3C44B0? There are two methods in S3C44B0. ...
  • Learn ARM development(14)
  • Learn ARM development(15)
  • Learn ARM development(16)
  • Learn ARM development(17)
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号