Code portability issues

Publisher:hylh2008Latest update time:2016-09-25 Source: eefocus Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
The portability of code is a big challenge for programmers, and this issue should be considered at all times. Even from the moment you plan a project, you should consider that the code may be modified many times, or functional modules may be deleted. The peripheral hardware of the project often changes according to requirements. What kind of programming skills can minimize the amount of modification while completing the project well? This is the topic of today's research. Here are some small examples and how to use them in practice.

(1) Use macro definitions instead of direct IO operations

I think the most typical example is a buzzer, a 4KHZ driven AC buzzer. If the buzzer is connected to the P50 port, then the driver code should be:

mov a,@0x01

xor 0x05,a

The code is very simple. The result of XORing 0x01 with P5 is the inversion of P50. If the inversion is continued, the output square wave of P50 is obtained. It can be described in C language as follows:

PORT5 ^= 0x01

Well, the portability of this code is extremely poor. If my buzzer needs to be modified to port P62, the code must also be modified accordingly:

mov a,@0x04

xor 0x06,a

Of course, we only need one place here, so it is not necessarily troublesome. If there are 50 or even 100 places in your code that need to be modified, I think you will definitely go crazy. So this non-portable code must be thrown away.

Modification 1:

By defining the IO port of the buzzer

buz_port EQU 0x05

mov a,@0x01

xor buz_port,a

Here, the name buz_port is used to replace the output port of the buzzer. Now the portability is improved. If the port is modified, we only need to redefine the buz_port macro to complete all the modifications to the code.

According to the above description, the port problem can be solved, but we found that the problem has not been completely solved. For mov a,@0x01, we still have to manually calculate which pin the buzzer is on, and we have to modify them one by one, so it still needs improvement.

Modification 2:

buz_port EQU 0x05

buz_pin EQU 0

mov a,@1<

xor buz_port,a

This code truly achieves the so-called portability. Explain it in C language

buz_port ^= 1<

Students who are familiar with C language should be familiar with this expression. 1<

For example

1<<0 The result is 1 (00000001b)

1<<3 The result is 8 (00001000b)

This is the truth, but it is not responsible at all to use, it can be said to be very easy to use. It can be directly matched with the name of the IO, for example, if it is P62 mentioned above, then the definition should be:

buz_port EQU 0x06

buz_pin EQU 2

You only need to modify the definition, and all parts of the program that use this IO port do not need to be modified, so the portability is very good. Although this technique is simple, it must be learned.

 

(2) Dynamically bind IO ports

Consider a situation where we use three IO ports to send PWMs with different duty cycles to drive three LEDs of different colors to create a colorful light effect. The requirement is that the three IO ports are independently controlled, so the natural idea is that each of the three programs generates PWM, which means that the situation of one channel is expanded to three channels and controlled separately. However, the consumption of registers and ROM has also become three times correspondingly, which is easy to understand. However, the three programs are surprisingly similar, and it is very likely that only the IO port output part has been modified. Let's think about it, can we write them together? In other words, use one PWM program to generate PWM and output it to three different IO ports. I suggest that you think about it yourself before reading it, and type the program, and you will deeply understand the difficulty of this problem.

Similar situations are not uncommon, such as RC temperature measurement. If you measure one channel, it is easy to do, but what if you measure many channels? You don't really need a corresponding program for each channel, right? That's absolutely impossible. The solution is similar, that is, use one program to process the input data of multiple IOs and switch. This is a bit like the data selector in the digital circuit.

The answer is: dynamically bind IO port input and output, the way is indirect addressing with R0 and R4. How to achieve it? In fact, it is very simple, because indirect addressing itself can access addresses 0x05 and 0x06, that is, it can directly access PORT5 and PORT6, so it is easy to think of, for example, the output status of PORT6, and then the output status of PORT6 is changed to 0X0F (P60~P63 high level, P64~P67 low level). The general practice is:

mov a,@0x0f

mov PORT6,a

If indirect addressing is used, then

mov a,@0x06

mov R4,a

mov a,@0x0f

mov R0,a

First, put the register address 0x06 to be accessed into the address register R4, and then send the data 0x0f to R0. The actual effect is to send 0xf to PORT6. Just run it with the emulator and it will be OK. It is very simple.

After understanding this principle, the so-called "dynamic binding" is easy to understand. The original instructions for accessing the IO port using PORT5 PORT6 are changed to indirect addressing using R0 R4, so that the program can become very flexible.

For example, let’s go back to the previous buzzer example.

The system has two buzzers, P50 and P62. Let's see how to output square waves to these two buzzers through dynamic binding.

Allocate two registers to store the port information that currently needs to be operated.

REG_PORT == 0x10

REG_PIN == 0x11

If you need to output P50

Mov a,@0x05

Mov REG_PORT,a

Mov a,@1<<0

Mov REG_PIN,a

Then the buzzer driver function should be modified accordingly. As mentioned earlier, it used to operate directly on the PORT port, but now it has become indirect addressing.

Move a,REG_PORT

Mov R4,a

Move a,REG_PIN

Xor R0,a

Do you understand? Actually, it is very simple. First, determine the port that needs to be operated. The value of REG_PORT is set to 0x05, which means to operate PORT5. Then, XOR the pin 1<<0, which is 0x01, with R0 (now pointing to PORT5). The result is that P50 changes the level. Multiple calls will turn it into an output square wave.

If you need to output P62 now, then things become very simple, just need to:

Mov a,@0x06

Mov REG_PORT,a

Move a,@1<<2

Mov REG_PIN,a

Then the square wave generation part in the middle is universal. We can directly send the value to the port we need to operate.

See? This is the so-called dynamic binding. If you consider portability, that is, if the port may be modified in the future, then define a macro as mentioned in the first point of the set. Anyway, this thing needs to be used flexibly.

 

Conclusion:

These are two very simple things. I have spent a lot of time describing them just to illustrate the problem. If you understand them, they are really simple, even not worth mentioning. However, how should I put it? Programs are accumulated little by little with these little tricks. I hope the two little tricks I talked about today can be helpful to you.

Reference address:Code portability issues

Previous article:Microcontroller macro definition study notes
Next article:MCU Multiplication and Division Examples

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号