Many friends are learning MCU development technology, but they will inevitably encounter various problems during development. Some problems may not affect the overall situation, but some problems directly affect the cost, size and performance of the product. Here are a few tips from the author, hoping to help everyone's work.
1. Embedding assembly language in C language
In the development of single-chip microcomputers, we usually use C language to write the main program, so that we can make full use of the calculation library functions and powerful data processing capabilities provided by C language tools. However, the controllability of C language is not as good as that of assembly language. In some processing with strict timing requirements, we still need to use more flexible assembly language to write. Shanghai AVR single-chip microcomputer training This leads to the problem of mixed programming of C language and assembly language, which is generally divided into three ways: 1. Assembly language calls C language function; 2. C language calls assembly language; 3. Assembly language is embedded in C language. Here we mainly introduce the third method, that is, assembly language is embedded in C language.
The following program is the main program calling the precise 205μS delay subroutine and making P1.0 output high and low level square waves alternately.
/*------------Program name test.c------------*/
#include P crystal frequency 12.000MHz《》
/****************/
void delay(void) //delay 205μS
{
#pragma asm
MOV R0, #100
LOOP:
DJNZ R0, LOOP
#pragma endasm
}
/***************/
void main (void) //Main function, which makes P1.0 output high and low level square waves alternately
{
while(1)
{P1_0=!P1_0;
delay();}
}
The specific implementation process is:
1. First, compile a delay program in assembly language, compile it in the Keil development environment, and then perform software simulation. The crystal frequency setting should be consistent with your requirements. During simulation, pay attention to the time display in the register window on the left, and adjust the parameters of the delay program to get the precise delay we need.
2. Use C51 to write the shell of the main program and the delay subroutine (waiting to embed assembly language), assuming that the name of this program is test.c.
3. Put the assembly delay subroutine obtained in step 1 into the delay subroutine shell written in C51. Note that #pragma asm and #pragma endasm statements are added at the beginning and end respectively. This method tells the C51 compiler through asm and endasm that the intermediate lines do not need to be compiled into assembly lines.
4. According to the instructions of Keil, create project files and add source programs.
5. Click the C source program containing the assembler and right-click. Select Options for File 'test.c' in the pop-up drop-down menu (Figure 1). The interface shown in Figure 2 will appear. Check Generate Assembler SRC File and Assembler SRC File to make them effective.
6. Load the package library file according to the compilation mode of the project, usually C51S.LIB in Small mode (the file is in C:\\Keil\\C51\\Lib\\C51S.LIB), as shown in Figure 3.
7. Click Rebuild target (Rebuild all target files) to get the compilation results (Figure 4).
Figure 1
Figure 2
Figure 3
Figure 4
2. Extending external interrupts using software
As we all know, the 51 MCU has only two external interrupts. The book once introduced a method to expand the external interrupt source, but it requires additional hardware overhead (see Figure 5). The external interrupt source input (/INT0 or /INT1) is introduced through the NOR gate and connected to a certain I/0 port. In this way, each "source" may cause an interrupt. In the interrupt service program, the software query can determine which interrupt source is being requested. The query order is determined by the interrupt source priority, which can achieve the expansion of multiple external interrupt sources.
Figure 5[page]
Although this method expands the external interrupt source, it also has some unsatisfactory aspects. For example, to design a circuit with 8 interrupt sources, an 8-input NOR gate (OR gate) is required, which is obviously not conducive to volume and cost. Here we introduce the method designed by the author to expand the external interrupt source, which is implemented by pure software without adding any components (see Figure 6).
Figure 6
#include 《 P》
static unsigned char data m; //m is a global variable
/*-------Delay subroutine-------*/
void delay(unsigned int k)
{
unsigned int i, j;
for(i=0;i
for (j=0;j<121;j++)
{;}}
}
/*---External interrupt INT0 subroutine---*/
void init0()interrupt 0
{
delay(10); //Delay 10mS to resist jitter interference
if (P3_2 == 0)
{
EX0=0; //Disable INT0 interrupt
EA=0; // turn off the general interrupt
P3_2=0; //Set P3.2 to low level
P2=0xff; //Set P2 port to all 1s
m=P2; //Read the status of P2 port to m
P2=0x00; //Restore P2 port to all 0
P3_2=1; //Set P3.2 to high level
IT0=1; //Set INT0 to edge trigger
EX0=1; //Open INT0 interrupt
EA=1;} //Open the general interrupt
}
/********Main program************/
void main(void)
{
P2=0x00; // Set P2 port to all 0
P3_2=1; // Set P3.2 to high level
IT0=1; // Set INT0 to edge trigger
EX0=1; // Enable INT0 interrupt
EA=1; //Open the general interrupt
while(1) // infinite loop
{
P0=m; // Output the content of global variable m to port P0
P3_0=! P3_0; //P3.0 is inverted to indicate the program status
delay(500); //delay 500mS
}
}
Program explanation: When no button is pressed, the light-emitting diode of P3.0 flashes to display the program status. When the main program is initialized, set P2 port to all 0, set P3.2 to high level, set INT0 to edge trigger, and open interrupt. When any of the 8 buttons is pressed, it will cause INT0 interrupt. After entering the interrupt service subroutine, first turn off the interrupt, then set P3.2 to low level, set P2 port to all 1, and then read the status of P2 port to m. By querying the status word of m, you can know the interrupt source being applied. The method we use here is to output m to P0 port to light up the LED for indication. When exiting the interrupt, reopen the interrupt.
3. Generation of library functions
When you provide your own program to others but it is inconvenient to disclose the source code, making the source code into a library function is a feasible way to protect your intellectual property rights and interests. Here we introduce the method of generating library functions and their use.
/*------------Program name test1.c------------*/
void delay(unsigned int k)
{
unsigned int i, j;
for(i=0;i
for (j=0;j<121;j++)
{;}}
}
1. According to the instructions for using Keil, create the project file test1.uv2 and add the source program test1.c above.
2. Click the project, click Options for Target 'Target 1' in the pop-up drop-down menu, and in the Output page, select "Create Library:" and compile, and a "Lib" file with the same name as the project will be generated in the specified path (Figure 1). It should be noted that the storage mode (Large or Small) should be the same as the system setting used.
Figure 1
3. Create another project file test2.uv2.
/*------------Program name test2.c------------*/
#include P crystal frequency 12.000MHz《》
/****************/
extern void delay(void);
void main (void) //Main function, which makes P1.0 output high and low level square waves alternately
{
while(1)
{P1_0=!P1_0;
delay();}
}
4. Add test2.c containing the main program and test1.LIB just generated to the project (Figure 2). In the Output page, check Create hex file.
Figure 2
5. Click Rebuild target to get the compilation result (Figure 3).
Figure 3
[page]
4. Modify the Startup.a51 start code
The microcontroller is inevitably disturbed during operation, which may sometimes cause a crash. We can use the "watchdog" to reset and restart the microcontroller. According to the author's experience, the data in the memory area may not be completely destroyed at this time, mainly because the PC pointer is confused. Shanghai Analog Circuit/Digital Circuit Training However, the program written in C51 will execute a section of Startup.a51 "starting code" after reset, causing all memory to be cleared and all running data to be lost. The solution to this problem is to modify the Startup.a51 "starting code". This January's article "Talking about the Application of C Language in Microcontroller Development" also talked about this issue, but many readers don't know how to do it in the Keil integrated development environment? Here we will explain it in detail through an experimental program. The experiment uses the S2 test board of the lecture "Teaching You to Learn Microcontrollers Step by Step" (the circuit principle of the S2 board can be found in the February 2003 issue of "Electronic Production").
/*------------Program nametest3.c------------*/
#include P crystal frequency 11.0592MHz《》
#define uchar unsigned char
#define uint unsigned int
uchar code DATA_7SEG[10]={0xC0,0xF9,0xA4,0xB0,0x99,//0~9 digital tube font code
0x92, 0x82, 0xF8, 0x80, 0x90};
uchar data counter1, counter2; //define two software counters
void delay(uint k) //delay subroutine
{
uint i, j;
for(i=0;i
for (j=0;j<121;j++)
{;}}
}
void main(void) //Main program
{ delay(1); //delay 1mS
while(1) // infinite loop
{
if (counter1==counter2) // if the two count values are equal
{P0= DATA_7SEG[counter1]; //output to P0 port for display
delay(500); //delay 500mS
counter1++;counter2++;//count value increases
if (counter1 >= 10) { counter1 = 0; counter2 = 0; } // count value loops from 0 to 9
}
else
{ counter1=0xff; counter2=0xff; // otherwise the count value is set to 0xff
//…………Error handling
}
}
}
1. According to the instructions for using Keil, create the project file test3.uv2 and add the source program test3.c above. In the Output page, check Create hex file.
2. Click Rebuild target (Rebuild all target files) to get the compilation results.
3. After the compilation is successful, burn the generated test3.hex file into the single-chip microcomputer 89C51, insert the 89C51 chip into the S2 test board, and after power on, the digital tube on the right starts to display from 0 to 9 in a cycle. When a certain number (such as 5) is displayed, press the RESET key, and the digital tube on the right starts to display from 0 to 9 in a cycle again. This is because when the power is reset (hot start), C51 executes a "starting code" to clear all 128 units of the memory, resulting in the loss of the count value (such as 5).
The steps to solve this are as follows:
4. Click "File", select "Open" in the drop-down menu, select C:KeilC51LibStartup.a51 in the pop-up search path and open it. You can see the following code:
…………………………………………………………………………………………………………
…………………………………………………………………………………………………………
IDATALEN EQU 80H ; the length of IDATA memory in bytes.
;
XDATASTART EQU 0H ; the absolute start-address of XDATA memory
XDATALEN EQU 0H ; the length of XDATA memory in bytes.
;
PDATASTART EQU 0H ; the absolute start-address of PDATA memory
PDATALEN EQU 0H ; the length of PDATA memory in bytes.
…………………………………………………………………………………………………………
…………………………………………………………………………………………………………
We change IDATALEN EQU 80H; the length of IDATA memory in bytes. to IDATALEN EQU 00H; the length of IDATA memory in bytes. Then save and close.
5. Add Startup.a51 to the test3.uv2 project (Figure 4).
Figure 4
6. Click Rebuild target to get the compilation results.
7. Burn the generated test3.hex file into the 89C51 microcontroller, insert the 89C51 chip into the S2 test board, and after power on, the digital tube on the right starts to display cyclically from 0 to 9. When it displays 5, press the RESET key, and the digital tube on the right continues to count and display from 5 (note: this time it does not start from 0), realizing the continuous counting function after hot start.
This technology is very useful. For example, if the "watchdog" is activated (i.e., hot start) due to interference or other factors, the data being processed will not be lost, so it can continue to work. Some readers may ask, once the interference destroys the data, the data that continues to work may be wrong, isn't it a mistake on top of a mistake? For this problem, we can adopt data redundancy, such as the value being counted is stored in two memory units (such as counter1 and counter2 in this example). When using, the data of the two memory units are compared. If they are not equal, it means that the interference has destroyed the data, and error processing can be performed. Otherwise, the data can be considered correct and valid.
[page]
5. Absolute address access
The anti-interference ability of the MCU system is very important during operation. MCUs with strong anti-interference ability can work normally in complex industrial environments. MCUs with poor anti-interference ability will have more abnormal working and low working efficiency at best, and will not be able to run at all and often freeze. Therefore, the quality of a MCU system design is directly related to its anti-interference ability.
In order to improve the reliability of RAM area data, we can create two flags flag1 and flag2 in two RAM units that are far apart (such as 20H, 75H, etc.), write the flag word (such as 88H) during initialization, and compare whether the two flags are equal when accessing RAM data. If they are not equal, it means that the RAM area data may be wrong. At this time, the program jumps to the error handling subroutine, otherwise it executes normally. This method makes the data reliability higher during program execution. Shanghai FPGA/CPLD Training This involves absolute address access in C language. The following are three methods.
1. Use the _at_ keyword
Its usage is relatively simple, just add _at_ and the address constant after the data declaration. However, please note that absolute address variables cannot be initialized, and bit type functions and variables cannot be specified with _at_.
Example 1:
#include 《 P》
static unsigned char data flag1 _at_ 0x0020; // Position the two flags at 20H and 75H
static unsigned char data flag2 _at_ 0x0075;
/******************/
void main()
{
//When entering the main program initialization, set flag1 and flag2 to 0x88
flag1=0x88; flag2=0x88;
while(1)
{
if ((flag1 == 0x88) && (flag2 == 0x88)) // flags are equal
{//Normal working process}
else
{//Error handling}
}
}
2. Method using pointers
Example 2:
#include 《 P》
char data *point1; //define two pointers to the data area
char data *point2;
/******************/
void main()
{point1=0x20;point1=0x75;//points to 20H, 75H units
//When initializing, set the flags *point1 and *point2 to 0x88
*point1=0x88; *point2=0x88;
while(1)
{
if ((*point1 == 0x88) && (*point2 == 0x88)) // flags are equal
{//Normal working process}
else
{//Error handling}
}
}
3. Absolute macro "P" declared using #include
Example 3:
#include 《 P》
#include 《 P》
/******************/
void main()
{ //Set the flags DBYTE[0x20] and DBYTE[0x75] to 0x88 during initialization
DBYTE[0x20]=0x88;DBYTE[0x75]=0x88;
while(1)
{
if ((DBYTE[0x20]==0x88)&& (DBYTE[0x75]==0x88)) // Flags are equal
{//Normal working process}
else
{//Error handling}
}
}
6.C language calls assembly language
In order to enable C language to call assembly language, the assembly program must have clear boundaries, parameters, return values and local variables like the C program. In order to make the assembly program segment compatible with the C program, the segment name should be specified and defined for the assembly program. If you want to pass parameters, you must ensure that the storage area used by the assembly program to pass parameters is consistent with the storage area used by the C program. And declare it in the called C language. The conversion rules of function names are shown in Table 1. The registers for receiving parameters are shown in Table 2. The comparison between the return value type and the register is shown in Table 3.
Function name conversion rules
Declaration assembly symbol name description in the main function
Void func(void) FUNC No parameter passing
Void func(char) _FUNC with register parameter passing
Void func(void) reentrant_? FUNC Reentrant function including stack parameter passing
Table 1
Receive parameter register
Parameter number charintLong, float general pointer
1R7R6, R7R4~R7R1~R3
2R5R4, R5--
3R3R2, R3--
Table 2
Return value type and register comparison
Return value type register description
BitC (flag bit) is returned by the specific flag bit
Char/unsigned char/1_byte pointer R7 single byte returned by R7
Int/ unsigned int/2_byte pointer R6, R7 double byte is returned by R6, R7, the high bit is in R6, the low bit is in R7
Long/ unsigned long R4~R7 Four bytes are returned from R4~R7, the high bit is in R4, the low bit is in R7
FloatR4~R7 32bit IEEE format, exponent and sign are in R7
The general pointer R1~R3 storage type is in R3, the high bit is in R2, and the low bit is in R1
Table 3
The following two examples illustrate this.
Example 4 (no parameter passing):
1. According to the instructions of Keil, create a project file and add the main program test4.c written in C51 (Figure 5).
/*------------Program nametest4.c------------*/
#include P crystal frequency 12.000MHz《》
/****************/
void delay(void); //delay function declaration
/***************/
void main (void) //Main function, which makes P1.0 output high and low level square waves alternately
{
while(1)
{P1_0=!P1_0;
delay();}
}
Figure 5
[page]
2. Use assembly language to compile a 205μS precise delay program ttest4.asm and add it to the project (Figure 6).
UDELAY SEGMENT CODE
RSEG UDELAY
PUBLIC DELAY
DELAY: MOV R0, #100
LOOP:
DJNZ R0, LOOP
RET
END
Figure 6
3. Click Rebuild target (Rebuild all target files) to get the correct compilation results (Figure 7).
Figure 7
Example 5 (with parameter passing):
1. According to the instructions of Keil, create a project file and add the main program test5.c written in C51 (Figure 8).
/*------------Program nametest5.c------------*/
#include P crystal frequency 12.000MHz《》
/****************/
void delay(unsigned int k); //delay function declaration
/***************/
void main (void) //Main function, which makes P1.0 output high and low level square waves alternately
{
while(1)
{P1_0=!P1_0;
delay(500);}
}
Figure 8
2. Use assembly language to compile a delay program ttest5.asm and add it to the project (Figure 9). Because of parameter passing, an underscore "_" must be added before the function name.
UDELAY SEGMENT CODE
RSEG UDELAY
PUBLIC _DELAY
_DELAY:
DJNZ R6, $
DJNZ R7, $
RET
END
Fig. 9
3. Click Rebuild target to get the correct compilation results (Figure 10).
There is another method, which is to use the compiler to automatically complete the segment arrangement, so it is also convenient to implement mixed programming of C language and assembly language. The process is:
1. Use C51 to write the main program test.c and the shell of the delay subroutine delay.c (waiting to embed assembly language). In the main program, the delay subroutine should be declared as an external function: extern void delay (delay).
2. Click the delay.c source code and right-click it. Select Options for File 'test.c' in the pop-up drop-down menu. Check Generate Assembler SRC File and Assembler SRC File to make them effective.
3. Load the package library file according to the compilation mode of the project, usually C51S.LIB in Small mode (the file is in C:\\Keil\\C51\\Lib\\C51S.LIB).
4. Click Rebuild target to get a delay.SRC file.
5. Rename delay.SRC to delay.A51.
6. Load delay.A51 into the project group and remove delay.c and C51S.LIB.
7. Click Rebuild target again to get the body of the delay.A51 assembly statement.
8. Put the precise assembly delay subroutine obtained through other experiments into the body of delay.A51, save it and load it into the Source Group 1 project group, then click Rebuild target to get the correct compilation result.
Previous article:Design of LED display screen for subway train
Next article:Principle and overall design of single chip microcomputer controlled LED outline display
Recommended ReadingLatest update time:2024-11-16 17:32
- Popular Resources
- Popular amplifiers
- Wireless Sensor Network Technology and Applications (Edited by Mou Si, Yin Hong, and Su Xing)
- Modern Electronic Technology Training Course (Edited by Yao Youfeng)
- Modern arc welding power supply and its control
- Small AC Servo Motor Control Circuit Design (by Masaru Ishijima; translated by Xue Liang and Zhu Jianjun, by Masaru Ishijima, Xue Liang, and Zhu Jianjun)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Innolux's intelligent steer-by-wire solution makes cars smarter and safer
- 8051 MCU - Parity Check
- How to efficiently balance the sensitivity of tactile sensing interfaces
- What should I do if the servo motor shakes? What causes the servo motor to shake quickly?
- 【Brushless Motor】Analysis of three-phase BLDC motor and sharing of two popular development boards
- Midea Industrial Technology's subsidiaries Clou Electronics and Hekang New Energy jointly appeared at the Munich Battery Energy Storage Exhibition and Solar Energy Exhibition
- Guoxin Sichen | Application of ferroelectric memory PB85RS2MC in power battery management, with a capacity of 2M
- Analysis of common faults of frequency converter
- In a head-on competition with Qualcomm, what kind of cockpit products has Intel come up with?
- Dalian Rongke's all-vanadium liquid flow battery energy storage equipment industrialization project has entered the sprint stage before production
- Allegro MicroSystems Introduces Advanced Magnetic and Inductive Position Sensing Solutions at Electronica 2024
- Car key in the left hand, liveness detection radar in the right hand, UWB is imperative for cars!
- After a decade of rapid development, domestic CIS has entered the market
- Aegis Dagger Battery + Thor EM-i Super Hybrid, Geely New Energy has thrown out two "king bombs"
- A brief discussion on functional safety - fault, error, and failure
- In the smart car 2.0 cycle, these core industry chains are facing major opportunities!
- The United States and Japan are developing new batteries. CATL faces challenges? How should China's new energy battery industry respond?
- Murata launches high-precision 6-axis inertial sensor for automobiles
- Ford patents pre-charge alarm to help save costs and respond to emergencies
- New real-time microcontroller system from Texas Instruments enables smarter processing in automotive and industrial applications
- pybCN's UF2 bootloader
- USB Type-C and USB Power Delivery Power Path Design Considerations
- xds510 simulator transfer
- How to handle different tasks at the same time in the time slice polling method
- Simple voice-controlled light circuit schematic and PCB wiring method
- LPS22HH air pressure sensor PCB package and code
- Xunwei-IMX6 development board device tree-Linux kernel configuration two-way CAN
- Looking for a chip that can be used with 485 and 232
- Mobile 5G device antenna tuning revealed
- CircuitPython upgrade micropython kernel