⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 c516.txt

📁 dsp&c51的编程,从小百合上down的
💻 TXT
📖 第 1 页 / 共 2 页
字号:
发信人: reflection (似水流年), 信区: EEtechnology 

标  题: C51 Primer (5) C Language Extensions for 8051 

发信站: 南京大学小百合站 (Wed Nov 24 11:58:24 1999), 转信 

  

  

5 C Language Extensions For 8051 

Programming 

8051 programming is mainly concerned with accessing real devices at specific 

 locations, plus coping with interrupt servicing. C51 has made many extensio 

ns to the C language to allow near-assembler code efficiency. The main point 

s are now covered. 

5.1 Accessing 8051 On-Chip Peripherals 

In the typical embedded control application, reading and writing port data, 

setting timer registers and reading input captures etc. are commonplace. To 

cope with this without recourse to assembler, C51 has the special data types 

 sfr and sbit. 

Typical declarations are: 

    sfr P0 0x80 

    sfr P1 0x81 

    sfr  ADCON; 0xDE 

    sbit EA  0x9F 

and so on. 

These declarations reside in header files such as reg51.h for the basic 8051 

 or reg552.h for the 80C552 and so on. It is the definition of sfrs in these 

 header files that customises the compiler to the target processor. Accessin 

g the sfr data is then a simple matter: 

   { 

   ADCON = 0x08 ;   /* Write data to register */ 

   P1 = 0xFF    ;   /* Write data to Port */ 

   io_status = P0 ; /* Read data from Port */ 

   EA = 1       ;   /* Set a bit (enable all interrupts) */ 

   } 

It is worth noting that control bits in registers which are not part of Inte 

l's original 8051 design generally cannot be bit-addressed. 

The rule is usually that addresses that are divisible by 8 are bit addressab 

le. Thus for example, the serial Port 1 control bits in an 80C537 must be ad 

dressed via byte instructions and masking. 

Always check the processor's user manual to verify which sfr register bits c 

an be bit addressed. 

5.2 Interrupts 

Interrupts play an important part in most 8051 applications. There are sever 

al factors to be taken into account when servicing an interrupt: 

The correct vector must be generated so that the routine may be called. C51 

does this automatically. 

The local variables in the service routine must not be shared with locals in 

 the background loop code: the L51 linker will try to re-use locations so th 

at the same byte of RAM will have different significance depending on which 

function is currently being executed. This is essential to make best use of 

the limited internal memory. Obviously this relies on functions being execut 

ed only sequentially. Unexpected interrupts cannot therefore use the same RA 

M. 

5.2.1 The Interrupt Function Type 

To allow C coding of interrupts a special function type is used thus; 

    timer0_int() interrupt 1 using 2 

    { 

    unsigned char temp1 ; 

    unsigned char temp2 ; 

    executable C statements ; 

    } 

Firstly, the argument of the "interrupt" statement, "1" causes a vector to b 

e generated at (8*n+3), where n is the argument of the "interrupt" declarati 

on. Here a "LJMP timer0_int" will be placed at location 0BH in the code memo 

ry. Any local variables declared in the routine are not overlaid by the link 

er to prevent the overwriting of background variables. 

Logically, with an interrupt routine, parameters cannot be passed to it or r 

eturned. When the interrupt occurs, compiler-inserted code is run which push 

es the accumulator, B,DPTR and the PSW (program status word) onto the stack. 

 Finally, on exiting the interrupt routine, the items previously stored on t 

he stack are restored and the closing "}" causes a RETI to be used rather th 

an a normal RET. 

5.2.2 Using C51 With Target Monitor Debuggers 

Many simple 8032 target debuggers place the monitor's EPROM code at 0, with 

a RAM mapped into both CODE and XDATA spaces at 0x8000. The user's program i 

s then loaded into the RAM at 0x8000 and, as the PSEN is ANDed with the RD p 

in, the program is executed. This poses something of a problem as regards in 

terrupt vectors. C51/L51 assume that the vectors can be placed at 0. Most mo 

nitors for the 8032 foresee this problem by redirecting all the interrupt ve 

ctors up to 0x8000 and above, i.e. they add a fixed offset of 0x8000. Thus t 

he timer 0 overflow interrupt is redirected by a vector at C:0x000B to C:0x8 

00B. 

Before C51 v3.40 the interrupt vector generation had to be disabled and asse 

mbler jumps had to be inserted. However now the INTVECTOR control has been i 

ntroduced to allow the interrupt vector area to be based at any address. 

In most cases the vector area will start at 0x8000 so that the familar "8 * 

n + 3" formula outlined in section 5.2.1 effectively becomes: 

8 * n + 3 + INTVECTOR 

To use this: 

#pragma INTVECTOR(0x8000)   /* Set vector area start to 0x8000 */ 

void timer0_int(void) interrupt 1 { 

   /* CODE...*/ 

   } 

This produces an LJMP timer0_int at address C:0x800B. The redirection by the 

 monitor from C:0x000B will now work correctly. 

5.2.3 Coping Interrupt Spacings Other Than 8 

Some 8051's do not follow the normal interrupt spacing of 8 bytes - the '8' 

in the 8 * n + 3 formula. Fortunately the "INTERVAL #pragma" copes with this 

. 

The interrupt formula is, in reality: 

INTERVAL * n + INTVECTOR and so: 

#pragma INTERVAL(6)   /* Change spacing */ 

will allow a 6 byte spacing. 

Please note that for convenience INTERVAL defaults to 8 and INTVECTOR to 0x8 

0000! 

5.2.4 The Using Control 

The "using" control tells the compiler to switch register banks. This is an 

area where the 8051 architecture works for the compiler rather than against 

it; the registers R0 to R7 are used extensively for the temporary storage of 

 library routines and for locals. Ordinarily Bank 1 is used. However, to be 

able to use this standard code in an interrupt the register bank must be swi 

tched to 2 in the above example. Thus the variables of the interrupted routi 

nes are preserved. 

As a rule interrupts of the same priority can share a register bank, since t 

here is no risk that they will interrupt each other. 

If interrupt runtime is not important the USING can be omitted, in which cas 

e C51 examines the registers which are actually used within the routine and 

pushes only these onto the stack. This obviously increases the effective int 

errupt latency. 

5.3 Interrupts, USING, Registerbanks, NOAREGS In C51 

Everything You Need To Know 

Interrupts play an important part in most 8051 applications and fortunately, 

 C51 allows interrupt service routines to be written entirely in C. Whilst y 

ou can write perfectly workable (and safe) programs by using just straight A 

NSI C, you can significantly improve the efficiency of your code by gaining 

an understanding of the following special C51 controls: 

INTERRUPT 

USING 

NOAREGS 

RE-ENTRANT 

REGISTERBANK 

5.3.1 The Basic Interrupt Service Function Attribute 

The correct vector must be generated so that the routine may be called. C51 

does this based on the argument to the interrupt keyword. The linker thereaf 

ter does not allow local data from interrupt routines to be overlaid with th 

at from the background by creating special sections in RAM. C51 special "int 

errupt" function attribute example: 

/*Timer 0 Overflow Interrupt Service Routine */ 

timer0_int() interrupt1 

{ 

unsigned char temp1 ; 

unsigned char temp2 ; 

/* executable C statements ; */ 

} 

The "interrupt 1" causes a vector to be generated at (8*n+3), where n is the 

 argument of the "interrupt" declaration. An "LJMP timer0_int" will be place 

d at location 0BH in the code memory. 

Local variables declared in the routine are not overlaid by the linker to pr 

event the overwriting of background variables. 

When the interrupt occurs, compiler-inserted code is run which pushes the ac 

cumulator, B,DPTR and the PSW (program status word) onto the stack if used i 

n function, along with any registers R0-R7 used in the function. 

A RETI is inserted at the end of the function rather than RET. Taking an emp 

ty interrupt service function for the timer 0 overflow interrupt, this is ho 

w C51 starts off an interrupt routine that uses no registers at all: 

timer0_int Entry Code 

void timer0_int(void) interrupt1 

{ 

RSEG ?PR?timer0_int?TIMER0 

USING 0 

timer0_int: 

; SOURCE LINE # 2 

If a function, here called "sys_interp" is now called from the timer0 servic 

e function, this is how the entry code to the interrupt changes. 

timer0_int Entry Code Now With Called Function 

; void timer0_int(void) interrupt 1 

{ 

RSEG ?PR?timer0_int?TIMER0 

USING 0 

timer0_int: 

PUSH ACC 

PUSH B 

PUSH DPH 

PUSH DPL 

PUSH PSW 

PUSH AR0 

PUSH AR1 

PUSH AR2 

PUSH AR3 

PUSH AR4 

PUSH AR5 

PUSH AR6 

PUSH AR7 

Note that the entire current registerbank is pushed onto the stack when ente 

ring timer0_int() as C51 assumes that all will be used by sys_interp. Sys_in 

terp receives parameters in registers; if the entry to sys_interp is examine 

d, an important compiler trick is revealed: 

sys_interp() Entry Code 

; unsigned char sys_interp(unsigned char x_value, 

RSEG ?PR?_sys_interp?INTERP 

 USING 0 

_sys_interp: 

MOV y_value?10,R5 

MOV map_base?10,R2 

MOV map_base?10+01H,R3 

;--Variable 'x_value?10' assigned to Register 'R1' -- 

MOV R1,AR7 

The efficient MOV of R7 to R1 by using AR7 allows a MOV direct, direct on en 

try to sys_interp(). This is absolute register addressing and is a useful do 

dge for speeding up code. 

5.3.2 The absolute register addressing trick in detail 

The situation often arises that the contents of one Ri register needs to be 

moved directly into another general purpose register. This usually occurs du 

ring a function's entry code when a pointer is passed. Unfortunately, Intel 

did not provide a MOV Reg,Reg instruction and so Keil use the trick of treat 

ing a register as an absolute D: segment address: 

Simulating A MOV Reg,Reg Instruction: 

In registerbank 0 - MOV R0,AR7, is identical to - MOV R0,07H. 

Implementing a "MOV Reg,Reg" instruction the long way: 

XCH A,R1 

MOV A,R1 

The use of this trick means however, that you must make sure that the compil 

er knows which is the current registerbank in use so that it can get the abs 

olute addresses right. If you use the USING control, problems can arise! See 

 the next few sections... 

5.3.3 The USING Control 

"using" tells the compiler to switch register banks on entry to an interrupt 

 routine. This "context" switch is the fastest way of providing a fresh regi 

sterbank for an interrupt routine's local data and is to be preferred to sta 

cking registers for very time-critical routines. Note that interrupts of the 

 same priority can share a register bank, since there is no risk that they w 

ill interrupt each other. 

8051 Register Bank Base Addresses 

R0 AR0 Absolute Addr.0x00 REGISTERBANK 0 

R1 AR1 

R2 AR2 

R3 AR3 

R4 AR4 

R5 AR5 

R6 AR6 

R7 AR7 

R0 Absolute Addr. 0x08 REGISTERBANK 1, "USING 1" 

R1 

R2 

R3 

R4 

R5 

R6 

R7 

R0 Absolute Addr. 0x10 REGISTERBANK 2, "USING 2" 

R1 

R2 

R3 

R4 

R5 

R6 

R7 

R0 Absolute Addr. 0x18 REGISTERBANK 3, "USING 3" 

R1 

R2 

R3 

R4 

R5 

R6 

R7 

If a USING 1 is added to the timer1 interrupt function prototype, the pushin 

g of registers is replaced by a simple MOV to PSW to switch registerbanks. U 

nfortunately, while the interrupt entry is speeded up, the direct register a 

ddressing used on entry to sys_interp fails. This is because C51 has not yet 

 been told that the registerbank has been changed. If no working registers a 

re used and no other function is called, the optimizer eliminiates teh code 

to switch register banks. 

timer0_int Entry Code With USING 

With USING 1 

; void timer0_int(void) interrupt 1 using 1 { 

RSEG ?PR?timer0_int?TIMER0 

USING 1 <--- New register bank now 

timer0_int: 

PUSH ACC 

PUSH B 

PUSH DPH 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -