In the past few days, I have been organizing and producing some macros for EMC microcontroller programs. I found that this thing is really easy to use beyond imagination. It greatly simplifies the repetitive work when writing programs. The following is mainly based on EM78P260, in fact, other models of Maxus microcontrollers can be used.
If it is different, just modify the register.
(1) The most commonly used PAGE and BANK
EMC ICs are divided into several pages and banks. Low-end EM78P156 and others only have one bank and one page, so there is no need to switch. Newer ICs basically need to switch. This frequently used Dongdong is most suitable as a macro.
code show as below:
/*****************************************************
* BANK SELECTION *
*****************************************************/
BANK macro num
if num == 0
bc R4,6
bc R4,7
elseif num == 1
bs R4,6
bc R4,7
elseif num == 2
bc R4,6
bs R4,7
elseif num == 3
bs R4,6
bs R4,7
else
message "warring!"
endif
endm
/*****************************************************
* PAGE SELECTION *
*****************************************************/
PAGE macro no
if num == 0
bc psw,5
bc psw,6
elseif num == 1
bs psw,5
bc psw,6
elseif num == 2
bc psw,5
bs psw,6
elseif num == 3
bs psw,5
bs psw,6
else
message "warring!"
endif
endm
The calling format is
BANK num (num is 0 to 3, representing 4 banks)
PAGE num (num is 0 to 3, representing 4 PAGEs)
This is much more convenient and will not go wrong.
(2) Macros with parameters
As an example, we assume that a macro "FUNC" is defined with two parameters. Its function is to simply transfer the incoming data to PORT5 and PORT6 to demonstrate its usage.
First, look at the definition:
FUNC MACRO ARG1,@ARG2
MOV A,@ARG1
MOV PORT5,A
MOV A,ARG2
MOV PORT6,A
ENDM
Notice why there is an @ sign in front of ARG1? This means that the first parameter received by the macro is an immediate value, while ARG2 does not have that sign, which means that the second parameter received by the macro is the address of a register.
For the uniformity of the program, when making macros, all immediate variables are prefixed with the @ symbol, while ordinary register variables are not prefixed with the @ symbol, thus forming a unified style.
Okay, let's see how to use it in the main program:
FUNC 0X10, @0X20
This is OK. When the editor is compiled, it will automatically perform macro replacement, pass the immediate value 0X10 as the first parameter, and pass the content of the 0X20 register as the second parameter.
As a result, it is equivalent to:
MOV A,@0x10
MOV PORT5,A
MOV A,0x20
MOV PORT6,A
That's the basic usage. It's not difficult, you will get it working after trying it.
(3) Talk about a good style of C language
There is a better programming style in C language. Here is an example of C51:
We want to set TIMER0 in mode 1 and TIMER1 in mode 2
The general tutorial thinking and code are:
Look up the definition of TMOD bits in the data, then slowly calculate what values should be given for mode 1 and mode 2, and finally write the instruction: TMOD = 0x21; Done…
In fact, we can also have another way, which is to write like this:
TMOD = CT0_MODE1 | CT1_MODE2 ;
Some macros are used in it, the specific definitions are:
#define CT0_MODE0 0x00 // Timer0/Counter0 Mode
#define CT0_MODE1 0x01
#define CT0_MODE2 0x02
#define CT0_MODE3 0x03
#define CT1_MODE0 0x00 // Timer1/Counter1 Mode
#define CT1_MODE1 0x10
#define CT1_MODE2 0x20
#define CT1_MODE3 0x30
TMOD = CT0_MODE1 | CT1_MODE2 ;
This should be easy to understand, right? The "|" in the middle is the OR operation, which is the operation performed by the compiler when compiling. Specifically, CT0_MODE1 represents 0X01 and CT1_MODE2 represents 0x20. Then the result after the "OR operation" is
0X21, the same as above. But honestly, which method would you prefer? I would choose the second one without hesitation. Meaningful symbols are more useful than meaningless data.
(4) Use our EMC assembly compiler to imitate this style
Our EMC assembly compiler also supports this kind of calculation during compilation. Let the compiler help us handle some basic calculations first. Although this small function is really common in C compilers, it is a bit surprising that the assembly compiler can also do it.
The small one is a bit unexpected. The function register allocation of EMC chips is really a bit messy. It's a bit annoying to see it. It's okay to use only one type of IC. If you use several types of IC, it's very frustrating. An example is EM78P447 and
EM78P156, the former is an upgraded version, but why are some controls so different? I have to check the DATASHEET frantically every time. In order to slow down the death of brain cells, I decided to use macros...
For example: We need to enable the TCCA counter of EM78P260. We use a macro with parameters to initialize the counter.
1 First define a macro, which can be used to initialize later
TCCA_SETUP MACRO TCCACNT
clr 0x04; 0x04 is used as a temporary register
ior 0x08; 0x08 is the register that controls TCCA
and a,@0xf8 ; shield TCCA related
mov 0x04,a
mov a,@TCCACNT ; read the passed parameter
or a,0x04
iow 0x08
mov a,@TCCACNT ; If TCCA is enabled, enable TCCA interrupt
and a,@0x04 ; otherwise jump out directly
jbc 0x03,2
jmp $+4
ior 0x0f
or a,@0x08
iow 0x0f
ENDM
(Because this program is in the initialization stage, it doesn't matter if you change the 0x04 register, but don't mess with it when it's running normally, because it will switch banks. Of course, you can open up a space in RAM here.
registers to use, then it's fine. You can modify it yourself if you like)
2 The second step is to define the specific values of some macros (similar to C)
TCCA_ENABLE == 0X04
TCCA_DISABLE == 0X00
TCCA_SRC_INT == 0X00
TCCA_SRC_EXT == 0X02
TCCA_EDGE_RISE == 0X00
TCCA_EDGE_FALL == 0X01
3 The third step is to start using it beautifully. In the main program,
/*****************************************************************************
TCCA_SETUP setup MACRO
argument: TCCA_ENABLE / TCCA_DISABLE whether to allow
TCCA_SRC_INT / TCCA_SRC_EXT counting source selection
TCCA_EDGE_RISE / TCCA_EDGE_FALL departure chord selection
****************************************************************************/
TCCA_SETUP TCCA_ENABLE|TCCA_SRC_INT|TCCA_EDGE_RISE
See it?
(TCCA_DISABLE|TCCA_SRC_INT|TCCA_EDGE_RISE) A bunch of meaningful parameters, or operation after passing as a parameter to the macro TCCA_SETUP, when we modify it, we can easily get it done, even absolutely
No need to look up information, for example, if we want to change to external TCCA pulse counting, we only need to make a simple modification
TCCA_SETUP TCCA_ENABLE|TCCA_SRC_EXT|TCCA_EDGE_RISE
Done. If you want to disable TCCA, just change it to TCCA_DISABLE. Isn't it simple? Very convenient? Of course, the price of convenience is to increase the program code, but it's only 10 lines more, which is not a big problem. The important thing is not to
It takes too many brain cells~hoho~ Sustainable development~
(5) Automatic register allocation
Finally, it's the end, the most BT part, and also the most rewarding thing. How to make registers automatically allocate space? A big difference between assembly and C is that C variables are automatically allocated. I'm jealous of how many good things there are.
Ah, I was tortured by assembly language for a few days, and suddenly found that our EMC assembly compiler also has this function. I am so happy! Maybe there are seniors who know how to use it, so I will show off my skills and give you some advice.
The usual habit of writing programs is to define a meaningful and easy-to-remember name to replace the abstract register name, such as defining a register for a temporary variable
TEMP EQU 0X10
In this way, we have defined TEMP, and will use TEMP to replace the 0X10 register in the future. This is the most common method. However, the problem is that we must redefine TEMP EQU 0X10 every time before writing a program. Of course, it is not annoying, but we all have some subroutines with common functions. If registers are used in the subroutines, they also need to be defined. Then, when doing a project, copy a subroutine here and a subroutine there. OK, there are a lot of conflicting register definitions that must be checked carefully and slowly. If you are unlucky, two names are defined on the same register. Well, it’s terrible, a very hidden logical error will appear, which is a nightmare. However, macros can be used to automatically assign variable macros. The WICE manual also says that the usage is
TEST EVERY 1
MOV A,@TEST
TEST WAS TEST+1
MOV A,@TEST
Comparing the values of A twice, we find that the first value of A is 1, and the second value of A is 2! ! This is the basic principle of variable macros. The compiler treats it as a variable and can change it. However, this change only occurs during compilation and is useless after the code is generated.
Okay, let’s talk about our core and how to distribute it specifically.
First, define a macro to allocate variables. The code is as follows
ADDR_ASSIGN MACRO REGISTER
REGISTER EQU ADDRESS
ADDRESS VAR ADDRESS+1
ENDM
A parameter is used, the name of the variable passed in. For example, we wrote in the main program
ADDRESS WAS 0X10
(First define the address to start allocating, we start from 0X10)
ADDR_ASSIGN Temp0
Temp0 is passed in as a parameter, which actually executes
Temp EQU 0X10
ADDRESS = ADDRESS+1 (Now ADDRESS is 0X11! Because it is a variable macro!)
Next time if we continue to define
ADDR_ASSIGN Temp1
Now Temp1 has been automatically defined as 0X11, and then ADDRESS rolls over to 0X12 for the next register definition.
This is convenient, for example, we define a bunch of registers
ADDR_ASSIGN Temp0
ADDR_ASSIGN Temp1
ADDR_ASSIGN Temp2
ADDR_ASSIGN Temp3
Oh my god, this is so useful!!! We don't need to worry about which register it is allocated to. Anyway, it is allocated and can be used. Ha~~TEST it and you will know.
Issues involved 1
Out of bounds problem. When the address is assigned to 0X3F, a page ends, but ADDRESS continues to be added. Are you afraid? No, the compiler has reported an error and cannot compile. In this way, you don't have to worry about out of bounds and can define it with confidence.
Issue 2
How to allocate multiple banks? Actually, you can add an extra parameter when defining the macro, and use the conditional macro to jump to the definition. However, I am afraid of trouble, so I use the following method:
/*---------------------------BANK 0 entry address-------------------------------------*/
ADDRESS VAR 0X10 ; can be allocated 0x10 ~ 0x3f
/*--------------------------- BANK 0 ----------------------------------------*/
Here are the registers we need to define
/*---------------------------BANK0 debug information output----------------------------------*/
MESSAGE "Bank0 maximum allocated RAM:"
ADDR_DISP ADDRESS-1
/*-------------------------------------------------------------------------------*/
/*---------------------------BANK 1 entry address-------------------------------------*/
ADDRESS VAR 0X20 ; can be allocated 0x20 ~ 0x3f
/*--------------------------- BANK 1 ----------------------------------------*/
Here we need to define the registers of bank 1
/*---------------------------BANK1 debug information output----------------------------------*/
MESSAGE "Bank 1 Maximum Allocated RAM:"
ADDR_DISP ADDRESS-1
/*-------------------------------------------------------------------------------*/
How is it? Harmony, right? Strictly separate the variables. If you need to put it in bank0, fill it in the area of bank0. If you need to put it in bank1, fill it in bank1. Because at the beginning of bank1, ADDRESS is redefined as 0X20, so you can continue to allocate from 0X20. If there are multiple pages, follow the same method. At the end of each bank, I also put two macros, they are
MESSAGE "Bank0 maximum allocated RAM:"
ADDR_DISP ADDRESS-1
The first one simply displays text, and the second one ADDR_DISP is used to display which register is the maximum allocated. The prototype of this macro is:
ADDR_DISP macro reg
IF reg==0x10
MESSAGE "0x10"
ELSEIF reg==0x11
MESSAGE "0x11"
ELSEIF reg==0x12
MESSAGE "0x12"
ELSEIF reg==0x13
……
……
(I wrote the following myself...)
ENDM
It's very simple. Just pass the last address of ADDRESS in and show it. Because ADDRESS executes an extra self-increment instruction, we subtract it back, and it's OK.
It should be noted that this method allocates all global variables. When registers are extremely scarce in a small project and you need to reuse a register, this method is useless. Just be careful.
Previous article:Clear RAM area when the microcontroller is powered on
Next article:Code portability issues
- Popular Resources
- Popular amplifiers
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- Huawei's Strategic Department Director Gai Gang: The cumulative installed base of open source Euler operating system exceeds 10 million sets
- Download from the Internet--ARM Getting Started Notes
- Learn ARM development(22)
- Learn ARM development(21)
- Learn ARM development(20)
- Learn ARM development(19)
- Learn ARM development(14)
- Learn ARM development(15)
- Analysis of the application of several common contact parts in high-voltage connectors of new energy vehicles
- Wiring harness durability test and contact voltage drop test method
- [Factory Visit Live Replay] Arrived at the destination - TE Qingdao Factory
- Useful tutorial | How to quickly splice PCB data at unconventional angles?
- Class AB Audio Amplifier
- Max30102 heart rate blood oxygen sensor measures heart rate problem 2
- Xunwei IMX6 development board non-device tree source code compilation environment construction (I)
- Execution process before keil main
- I need help from a great God to look at this circuit diagram, thank you very much
- FPGA Global Clock Constraints (Xilinx)
- The 2020-2021 ON Semiconductor and Avnet IoT Innovation Design Competition awards have been announced!
- How to implement this email sending program? How to write program analysis? Please help me answer