Advanced lighting of 51 Single Chip Microcomputer -- running lamp (realized by blocking delay)

In the last section, we learned to light up the LED with 51 single chip microcomputer. In this section, we continue to study further around lighting - to achieve the effect of running light.
The core of the running light is LED flashing. To realize LED flashing, you need to periodically control the on and off of the LED, and how to realize the interval between on and off? This needs to use the delay. The delay in this experiment adopts blocking delay (let the program execute empty statements continuously, wasting the processor's time).

  • First, briefly introduce the hardware and software to be used in this paper:
    Hardware platform: Puzhong 51 development board - Single core A2
    Software: Keil5 (C51), STC-ISP (or other ISP software), Proteus8.9 (for simulation, not required)

1, Single LED flashing

Before introducing the code, let's discuss the implementation of delay. Before learning the timer, we can only realize delay through empty statements. The next content is very boring. It is recommended to skip and look at the experimental code directly.

Let's first look at the running time of single line C code (one line of empty statement) in 51 single chip microcomputer:

_ nop delay is not discussed here, only empty statements are discussed

Firstly, the software is used for debugging. Before the empty statement, the program running time is 0.00019550 (0.1955ms) [the single chip microcomputer runs from 0s];

After running a line of empty statements, the running time of the program is: 0.00020550 (0.2055ms), which is 0.01ms different from the previous state, that is, 10us. Therefore, in theory (because this is software simulation, we can only get the theoretical value), my 51 single chip microcomputer takes 10us for a line of empty statements.


One line of empty statements takes 10 microseconds. If I want to delay one second, don't I only need to execute 100000 line of empty statements?

The answer is yes, but the time may not be accurate. The answer given on the Internet is that C code (in MCU) can not achieve accurate delay. Only assembly (such as nop) and timer can. Since the latter two items are not considered in this paper, the empty statements of C language are still used to achieve delay. After all, accurate delay is not required now, At the same time, we can also understand how much error can be caused by empty statement delay [this is what I am most interested in].

It should be noted here that the int type of 51 single-chip microcomputer only has 2 bytes, and the maximum value is 32767. If you use the unsigned int, you can use 65535 at most. Although you can use the long type (4 bytes), the efficiency will be reduced on the Internet. As a novice, we are still a safe point and use the commonly used types as much as possible. I will do the experiment in 0.5 seconds (50000 empty sentences)

The general sample is checked by software simulation. Since the running time before the empty statement is the same as before, I directly give the running time after executing 50000 lines of empty statements, which is 0.22529850 (225.29850ms), and the difference between the running time before the empty statement and that before the empty statement is about 225ms.

This is too much different from the 0.5 seconds we imagined. In fact, I made a mistake here - careful friends will find that I did not calculate the running time of empty statements, but a while(1 -); the running time, that is, a while statement plus an empty statement.


However, we find that the delay time of an empty statement loop is about 225/50000=0.0045ms, that is, 4.5us.
So let's try whether other values also meet this law. For example, if I want to delay 100ms, do I use 100 * 1000 / 4.5 = 22222 (keep integer) empty statements to cycle? Try it.

After the 22222 line empty statement cycle is executed, the running time is 0.10024300 (100.243ms), and the difference between the running time before the empty statement and that before the empty statement is about 100ms. The accuracy is OK, and there is no problem with the delay of at least ms level.

The above debugging is carried out using Keil software, and the software simulation can only be used as a reference, so we also need to measure the development board

Let's test the 250ms delay. Use the result just obtained ---- while(i -); the running time of each cycle is 4.5us (i cannot be too small or more than 65535).

The number of loop empty statements required for 0.25s is about 250 * 1000 / 4.5 = 55555 (integer reserved)

The test code is shown below, which is actually the experimental code of LED flashing.

Experimental code

Before writing code, we need to create a Keil project. The steps of creating the project have been described in the previous section. Portal: 🚀 (point me) . I won't repeat it here.

#Include < reg52. H > / / this file defines some special function registers of MCU


sbit led_out = P2^0; //The P2.0 port of MCU is defined as led


/******************************************************************************
 * @ Function name: main
 * @ Function: main function
 * @ Parameter: None
 * @ Return value: None
 ******************************************************************************/
 
void main()
{
	unsigned int t = 55555;  //250ms, when t is small, the delay unit is 10us; when t is large, the delay unit is 4.5us
	unsigned int i = 0;
	while(1)
	{
		i = t;       //250ms
		led_out = 0; //P2.0 port set to low level (LED lit)
		while(i--){} //Empty statement loop
		led_out = 1; //Lights out		
		i = t;       //250ms
		while(i--){} //Empty statement loop			
	}		
}

Compile + burn

After the program is written, the next operation is compiling + burning. These steps have been described in the first article in this series. Portal: 🚀 (point me) . however, the operation process will be briefly described here:

  • Click the compile button. If you are afraid of accidents, you can click the third (recompile). After compilation, the Build Output window will display error messages. If there are no errors and warnings (if there are warnings in these lines, it indicates a problem), and the hex file is generated, it indicates that the compilation is successful.

  • Burn the hex file just generated into the MCU, and you can see that the LED is flashing.

Experimental effect

  • Actual effect: it can be seen that LED1 on the right is flashing, and the flashing interval is about 250ms (250ms is the software simulation value, and the measured value below is 540~660ms).

  • Simulation effect:

  • Unfortunately, the actual test time is quite different from the software simulation. The first figure below shows the delay time from light on to light off, 542ms, which is more than twice that of 250ms.

  • The second picture shows the delay time from the light off to the light on, which is even more different. Therefore, it is best to debug the delay of empty sentences by yourself.

2, LED running light

From the LED flashing experiment, we can know that the delay realized by empty statements in C language is very inaccurate, but these errors can not be recognized by human eyes, so this experiment continues to use the while loop to realize the delay. This time, I will not conduct software simulation to adjust the delay time, because the difference between the simulation time and the real time is too large.

According to the measured delay time above, when the number of empty statement cycles is 55555, the delay time is about 540ms, so we can calculate that running while(1 --) {} takes about 10us (which is the same as the result of the first software simulation, which is strange).
At the same time, in order to improve the simplicity of the code, I encapsulated the delay code into a function, which is more convenient for repeated calls:

/******************************************************************************
 * @ Function name: Delay_10us
 * @ Function: 10us coarse delay
 * @ Parameter: delay time -- Unit: 10us
 * @ Return value: None
 ******************************************************************************/
 void Delay_10us(unsigned int time)
 {
	while(time--);
 }

The next step is to realize the running Lantern:

You should have heard that some people like to call running lights. I don't distinguish them here. What I want to achieve is that 8 LED s turn on and off in turn. This is the core principle. Go directly to the code (below).

Experimental code

The following is the complete code of the running lantern

#Include < reg52. H > / / this file defines some special function registers of MCU

#define led_ Out P2 / / define the P2 port of the MCU as LED and 8 IO S


/******************************************************************************
 * @ Function name: Delay_10us
 * @ Function: 10us coarse delay
 * @ Parameter: delay time -- Unit: 10us
 * @ Return value: None
 ******************************************************************************/
 void Delay_10us(unsigned int time)
 {
	while(time--);
 }


/******************************************************************************
 * @ Function name: main
 * @ Function: main function
 * @ Parameter: None
 * @ Return value: None
 ******************************************************************************/
 
void main()
{
	unsigned char i = 0;
	unsigned int t = 0;
	led_out = 0xff;    //All 8 lights are off
	while(1)
	{
		for(i = 0; i < 8; i++)
		{
			//One of them is on, and it is on in the order of P2.0 to P2.7
			led_out = ~(0x1 << i); 
			Delay_10us(50000); //Rough delay 500ms
		}
	}		
}

If you don't understand the following line of code, you can go to my last article, which introduced the meaning of P2, portal: 🚀 (point me).

#define led_ Out P2 / / define the P2 port of the MCU as LED and 8 IO S

Experimental effect

  • Actual effect: (no nixie tube, and gif pictures are accelerated)

  • Simulation effect:

3, Other pattern lamps

With the delay function, you can achieve any LED effect you want.

The following shows the code of water lamp and breathing lamp.

Water lamp

The running water lamp I'm talking about here means that the LED lights up like water flow, that is, the LED lights up in turn but does not go out immediately. After all 8 LEDs are on, all the lights will go out at one time.

#Include < reg52. H > / / this file defines some special function registers of MCU

#define led_ Out P2 / / define the P2 port of the MCU as LED and 8 IO S


/******************************************************************************
 * @ Function name: Delay_10us
 * @ Function: 10us coarse delay
 * @ Parameter: delay time -- Unit: 10us
 * @ Return value: None
 ******************************************************************************/
 void Delay_10us(unsigned int time)
 {
	while(time--);
 }


/******************************************************************************
 * @ Function name: main
 * @ Function: main function
 * @ Parameter: None
 * @ Return value: None
 ******************************************************************************/
 
void main()
{
	unsigned char i = 0;
	unsigned int t = 0;
	led_out = 0xff;    //All 8 lights are off
	while(1)
	{
		for(i = 0; i < 8; i++)
		{
			//Turn on in the order from P2.0 to P2.7, but do not affect other lamps
			led_out &= ~(0x1 << i); 
			Delay_10us(50000); //Rough delay 500ms
		}
		led_out = 0xff;    //All 8 lights are off
	}		
}
  • Actual effect: (no nixie tube, and gif pictures are accelerated)

  • Simulation effect:

Breathing lamp

Breathing lamp is realized by analog PWM, adjust_ The brightness function is used to set the brightness of 8 LED s, and then dynamically modify the brightness to achieve the effect of breathing lamp. The code is as follows:

#Include < reg52. H > / / this file defines some special function registers of MCU

#define led_ Out P2 / / define the P2 port of the MCU as LED and 8 IO S


/******************************************************************************
 * @ Function name: Delay_10us
 * @ Function: 10us coarse delay
 * @ Parameter: delay time -- Unit: 10us
 * @ Return value: None
 ******************************************************************************/
void Delay_10us(unsigned int time)
{
	while(time--);
}
 
 /******************************************************************************
 * @ Function name: Adjust_Brightness
 * @ Function: adjust the brightness of 8 LED s (analog PWN duty cycle control)
 * @ Parameter: brightness 0 -- 100
 * @ Return value: None
 * @ Note: an analog PWM cycle is 10*100us (1ms)
 ******************************************************************************/
void Adjust_Brightness(unsigned char brightness)
{
	unsigned char low = brightness; //Lighting time
	unsigned char high = 100 - brightness;  //Extinction time
	
	led_out = 0x00;   //Light up 8 LED s
	Delay_10us(low);  //Lighting delay
	
	led_out = 0xff;   //Turn off 8 LED s
	Delay_10us(high); //Extinction delay
		
}

/******************************************************************************
 * @ Function name: main
 * @ Function: main function
 * @ Parameter: None
 * @ Return value: None
 ******************************************************************************/
void main()
{
	unsigned int i = 0, cnt = 1000;  
	led_out = 0xff;    //All 8 lights are off
	
	while(1)
	{
		/* The breathing lamp gradually turns on for 1000*1ms */
		for(i = 0; i < cnt; i++)
		{
			Adjust_Brightness(i / 10);
		}
		
		/* The breathing lamp gradually darkens for 1000*1ms */
		for(i = cnt; i > 0; i--)
		{
			Adjust_Brightness(i / 10);
		}
	}		
}
  • Actual effect: (no nixie tube)

  • Simulation effect: it's terrible. The simulation effect of breathing lamp is too poor, even if I enlarge the delay unit twice (but the delay in software simulation is already twice as slow as the actual situation).

If the article is helpful to you, please leave your praise 👍, This is my biggest motivation to continue to update 🚀.

Posted on Sun, 05 Dec 2021 11:34:38 -0500 by jworisek