e;[]------------------------------------------------------------[] ;| | ;| | ;| Example program for EX2051_2 | ;| | ;| | ;| Steven Murray, AirBorn Electronics, 1995 | ;[]------------------------------------------------------------[] ;This code is copyright (C)1995 ____________________ ;If you modify this code, put your name in the copyright entry above. ;This code was originally downloaded from http://www.webcom.com/airborn ;( This example code, written by AirBorn Electronics in 1995, is released ) ;( into the public domain with no copyright claim or cover, to be copied ) ;( and modified freely, in whole or in part, to form the basis of programs ) ;( written by other parties who may in turn claim copyright on any ) ;( code added or modifications made to this material, as they desire. ) .include "8051.H" ;Without this file, the TASM assembler does not recognise any of the 8051 ; port locations automatically as other 8051 specific assemblers do. ;The file 8051.H contains all the 8051 port definitions, as equates. ;While this code is written so as to assemble on the TASM301 series ; assemblers, it should assemble with minor modification on most other ; 8051 assemblers. Indeed, you might find that other specific 8051 ; assemblers are slightly better for long term multi-project development ; work, as by its very flexible multi-CPU nature TASM is not quite as ; good at picking up syntactical coding errors as some other assemblers. .TITLE "*Demo code, AirBorn Electronics 61-2-9640-0774, V1.00a" ;This 8051 example code is for the AT89C2051 demonstration PCB. ;Revision History -------------------------------------------------------- ;It is common practice to put a version number on each file, and a history ; at the top of the file that lists past version numbers, features added ; and bugs fixed at each step, the date, and the programmer. ;21/7/94 SM V1.00a Original code - Serial to Parallel converter ;[]------------------------[] ;| Constants | ;[]------------------------[] ;By defining constants in your program, and then using the constant's name in ; place of a numeric value, you can make your program more readable and more ; easily modifiable. Care is required, as TASM is case sensitive. ;A classic example might be "LineLen equ 80" used in a program in such a way ; that a microcontroller that happened to do output printing used "LineLen" ; in its program to format text, (e.g. "subb A,#LineLen") rather than just ; "80". At a later date it might be possible to alter the program for a ; different printer by changing LineLen to 72 or 64 - without changing code. Crystal equ 11059200 ;Crystal frequency ;The crystal is entered as an equate so that ; timing loops may be calculated at assembly ; time. By making the value for all timing ; loops dependent on this equate, just one ; number needs to be changed to alter the ; crystal frequency for all timing in the ; program. Baud equ 9600 ;Desired baud rate of serial port ; With an 11.0592Mhz crystal, the baud rate ; can be all common rates between 300 - 57600, ; except 38,400. The baud rates possible at ; other crystal frequencies vary - see the ; 8051 data sheet for calculation details. T1RL equ (Crystal/(Baud*192)) ;Work out correct T1 reload for the baud rate SampCntRL equ (Crystal/172800) ;Sample switch countdown time 500ms approx BusyCntRL equ 127 ;Busy countdown time * 8ms approx ;TASM has the advantage of handling 32 bit "assemble time" arithmetic. ;Not all other assemblers support this. If your assembler does only 16 bit ;arithmetic it might be necessary to alter the equations above to suit, or ;put in hard coded values, eg: T1RL equ 6 for 11.0592Mhz, 9600 Baud. ;[]------------------------[] ;| Input/Output allocation | ;[]------------------------[] ;By defining input/outputs as equates, and then using the I/O's long name in ; place of the I/O pins name, it is easy to alter the program later for new ; hardware, and the program is more readable. ;For instance, on our demo PCB P3.7 is a Red LED. However, if you perhaps ; produced your own hardware, and found it more convenient to place the ; red LED on P3.5, then changing the program to output on the new port bit ; would be simply a matter of changing the equate. ;Refer to schematic diagram AB9505741: AT89C2051 Proto PCB, to match bit names L1 equ P3.7 ;Red LED - send pin low to light LED L2 equ P1.0 ;Pads L2-L9 may be LEDs or wires to other I/O L3 equ P1.1 L4 equ P1.2 L5 equ P1.3 L6 equ P1.4 L7 equ P1.5 L8 equ P1.6 L9 equ P1.7 L10 equ P3.4 ;Green LED - send pin low to light LED L11 equ P3.5 ;Pad L11 Merc equ P3.3 ;Pads S3070 - fit Mercury switch if desired PBut equ P3.2 ;Push button SW1 - goes low when switch pushed ;Input/Output names specifically for this example program DatPort equ P1 ;Parallel input/output data port on L2-L9 RedLED equ P3.7 ;Red LED GrnLED equ P3.4 ;Green LED Strobe equ P3.5 ;Active low strobe for data output (L11) Busy equ P3.3 ;Active high busy input for data port (Merc) Sample equ P3.2 ;Active low strobe for data input (Push sw) SwPort equ P3 ;Port with switches (for debounce) Sampbt equ 4 ;Bit mask for sample switch (P3.2 = bit2 = 4) ;[]------------------------[] ;| Hardware and Operational | ;[]------------------------[] ;It is not common place to put operational and hardware descriptions in code ; - however, we do it. ;This example program is a serial to parallel converter, with buffering. ;The incoming serial stream is placed into a buffer, and as long as the Busy ;input is not asserted, it is clocked out the parallel port with strobe ;pulses. If busy is active, the input is buffered up until such time as the ;buffer overflows and the data is lost. If busy is never seen inactive, ;after a suitable delay it is ignored, and the data is sent out the parallel ;port regardless ;Due to the open collector nature of "DatPort", the parallel port, it can be ;used for both input and output. ;When the sample input is strobed, the parallel port is sampled once and the ;value is output to the serial port. The sample input is debounced, suitable ;for a switch contact input. If the sample input remains continously active, ;after a suitable delay samples are read from the parallel port continuously. ;In the example PCB the sample input is connected to the push button switch. ;The red LED is a "heartbeat" if the buffer is not being used - i.e. Busy ;is low or ignored - and it is an inverted heartbeat if serial input is being ;buffered up. The green LED indicates incoming serial data. ;In operation the parallel device - e.g. Printer - is connected to D0-D7 ;on DatPort, with Strobe and Busy, and the other (printer) signals wired to ;+5v or Gnd as appropriate. In a printer application BUSY might also be ;connected via a 1488 buffer to the CTS input of the PC. Other applications ;might even connect the RTS output of the PC, via a 1489 buffer, to the ;Sample input of the CPU, with appropriate software to strobe the RTS line ;each time a parallel sample was desired. ;[]------------------------[] ;| RAM Memory allocation | ;[]------------------------[] ;Defining memory locations as equates, then using long names in referencing, ;makes a program more readable. RxPointA equ 20h ;Write Pointer to Serial Rx buffer RxRPointA equ 21h ;Read pointer to serial Rx buffer SwHist equ 22h ;Switches history byte - last sample value SwDeboun equ 23h ;Debounced switches byte SampCnt equ 24h ;Sample switch count - counts down "ON" time BusyCnt equ 25h ;Busy counter - counts down "Busy" valid time LEDPres equ 26h ;LED prescaler SeenSamp bit 7Eh ;Flag - when set, sample sw was just proc'ed TxRdy bit 7Fh ;Semaphore bit - when set, it is OK to Tx StackBase equ 30h ;Start of stack in internal RAM RxBufA equ 40h ;Start of the Serial Receive buffer RxBufALen equ 40h ;Receive buffer length RxBufAWrap equ RxBufA+RxBufALen ;Point at which Rx Buffer wraps ;[]------------------------[] ;| Reset/Interrupt Vectors | ;[]------------------------[] ;At reset 8051 type microprocessors start executing at location 0. There ;should be a jump at location zero to the start of the microprocessor code. ;When an 8051 type microprocessor receives an interrupt, it pushes the ;program counter onto the stack and jumps to a location near 0 - the location ;depends on the Interrupt: IE0: 3, TF0: 0Bh, IE1: 13h, TF1: 1Bh, Ser: 23h ;There should be interrupt code and/or jumps to interrupt code at these locs. ORG 0 ;CPU Jumps here at reset ;The .ORG directive starts code at that loc'tn. ;Other assemblers use "CSEG AT 0" ajmp Init ORG 3h ;CPU Calls here on external int 0, if enabled ;The program should jump here to int0 routine ajmp Stub ;INT0 is not wanted, INT0 does not happen ; unless it is enabled, but in case it does ; accidentally get enabled jump to stub ORG 0Bh ;CPU Calls here on timer 0 overflow, if enabled ajmp Timer0 ;The program should jump to Timer 0 routine ORG 13h ;CPU Calls here on external int 1, if enabled ;The program should jump to ext int 1 routine ajmp Stub ;INT1 int is not enabled in the example program ; and so should never happen - if it does then ; jump to "stub" ORG 1Bh ;CPU Calls here on timer 1 overflow, if enabled ;The program should jump to Timer 1 routine ajmp Stub ;TF1 int is not enabled in the example program ; and so should never happen - if it does then ; jump to "stub" ORG 23h ;CPU Calls here on Serial interrupt, if enabled ajmp SerInt ;The program should jump to Serial Int routine ;[]------------------------[] ;| Interrupt Routines | ;[]------------------------[] Stub: clr EA ;Unexpected interrupt - disable all ints ;If it is possible to give some indication of ; error, such as lighting an LED, do it here acall AReti ;Flag the interrupt routine is finished ;A "reti" instruction tells the CPU that its ; interrupt routine has finished and it ; should return to the main program. ; However as we "called" the reti the CPU is ; manipulated into returning here, but in an ; "all interrupts finished" condition. ajmp Init ;Re-initialize: So unexpected Int causes reset AReti: reti ;------------------------------------------------------------------------------ SerInt: ;This is the serial interrupt routine: Tx & Rx jbc TI,SerTx ;Jump if the Tx int flag is set, clearing TxInt push PSW ;Save the PSW register push ACC ;Save the Accumulator jbc RI,SerRx ;Jump if the Rx int flag is set, clearing RxInt ;Unusual - neither int flag set - act like Rx SerRx: ;Serial recieve processing clr GrnLED ;Turn on the Green LED by clearing GrnLED bit mov A,RxPointA ;Get the Rx buffer pointer, from RAM to A xch A,R0 ;Put the buffer pointer to R0, get R0 to A mov @R0,SBUF ;Put the Rx'd serial char into the RAM buffer ; at the location pointed to by RxPointA inc R0 ;Move the buffer pointer ahead one character cjne R0,#RxBufAWrap,NoWrpA ;Test if the buffer pointer has wrapped ;This instruction compares register 0 with ; the numeric value RxBufAWrap (the # symbol ; specifies numeric value vs RAM location) ; and jumps to NoWrpA if they are different mov R0,#RxBufA ;If the pointer is at end of buf, set to start ;This instr loads the numeric val RxBufA to A NoWrpA: xch A,R0 ;Get the write pointer back to A, restore R0 cjne A,RxRPointA,NoOvr ;Test if New Write pointer = current read ptr ;This instr compares A and RAM loc RxRPointA ajmp SerOut ;If pointers equal, we have overrun, abort NoOvr: mov RxPointA,A ;No overrun - save the updated write pointer SerOut: pop ACC ;Restore Acc, previously pushed pop PSW ;Restore PSW, previously pushed reti ;Return SerTx: setb TxRdy ;The serial transmit interrupt just sets a ; semaphore flag, (to indicate to mainline ; that it can now send another char) and ; clears the Tx interrupt. reti ;------------------------------------------------------------------------------ Timer0: ;This is the timer 0 interrupt push PSW ;Save the PSW register push ACC ;Save the Accumulator ;Debounce the switches port - (port 3 in hw) ;Not all of this port is switches - this ; doesn't matter - we only use bits we want mov A,SwPort ;Get the switches port xch A,SwHist ;Swop with the switches history byte xrl A,SwHist ;XOR - 1 bits set for any switch changes ;Correspondingly, any stable switches are 0 anl SwDeboun,A ;Mask all stable switches in final reg to 0 cpl A ;Invert - 1 bits now set for stable switches anl A,SwHist ;Get stable switches in 1 state as set bits orl SwDeboun,A ;Mask in all stable switches to final reg ;SwDeboun now contains debounced switches ; Any switches which have not been the same ; value for the last two successive samples ; have not been overwritten - others have. mov A,SwDeboun ;Get the switches byte anl A,#Sampbt ;Mask to see the sample switch jz SampChk ;Jump if the sample switch is pushed mov SampCnt,#SampCntRL ;Sample switch is not pushed, reload count SampChk: mov A,SampCnt ;Get the sample switch "ON" time jz SampDn ;Jump if the switch has been on a long time ;jz and jnz instructions always work on the ; current value of the Accumulator unlike ; other micros which act on the flag results ; of the last arithmetic operation. dec SampCnt ;Count down the sample switch "ON" time SampDn: jb Busy, BusyTst ;Jump if the busy input is active orl BusyCnt,#0FFh ;Busy input is inactive - kill the counter BusyTst: mov A,BusyCnt ;Get the busy count jb ACC.7,BusyDn ;Exit if the busy counter has been killed jz BusyDn ;Exit if the busy counter has timed out dec BusyCnt ;Count down the busy timer BusyDn: clr c ;If serial data is in buffer, carry stays clr mov A,RxPointA ;Get the serial buffer write pointer cjne A,RxRPointA,BufUse ;Test against the serial buffer read pointer setb c ;If pointers are equal, buffer is empty, set c BufUse: dec LEDPres ;Decrement the LED counter mov A,LEDPres ;Get the prescaler anl A,#0A0h ;Get heartbeat cadence jnz NoBeat ;Jump unless now is the heartbeat time cpl C ;Invert Carry on the heartbeat NoBeat: mov RedLED,C ;Output the heartbeat to the LED setb GrnLED ;Turn off the green LED ;The Green LED is turned on by Serial Rx TimOut: pop ACC ;Restore Acc, previously pushed pop PSW ;Restore PSW, previously pushed reti ;Return ;[]------------------------[] ;| Setup | ;[]------------------------[] Init: ;Here at initialization ;Referring to the 8051 data sheets, regs (e.g. ; Acc) have a known state at reset (usually 0) ;RAM powers up indeterminate, or in the state ; it was in previously if Vcc stayed above ; the minimum power-down voltage. ;If "Init" is jumped to as part of program ; operation, then the the registers could ; have any value... we init both registers ; and RAM in this program. mov SP,#(StackBase-1) ;Initialise the Stack pointer mov PSW,#0 ;Set active register bank (Regbnk 0: Locs 0-7) acall POFlash ;Do a power on LED flash, then return clr A ;Clear all RAM - not all applications would ; want this - if you are battery backing the ; CPU and keeping values in RAM, don't clear! mov R0,#7Fh ;Start at location 7Fh - work down mov R1,#7Eh ;Do 7Eh locations - stop before R0/R1 ;DEBUG - Changed from value 7Dh 22/7/95 Put0: mov @R0,A ;Clear a byte of RAM dec R0 ;Update the RAM pointer djnz R1,Put0 ;Loop until all locations cleared dec R0 ;Clear location 0 - the last location ;Init important things mov TMOD,#20h ;T1: 8 bit Auto R/L for baud, T0: 13 bit mov PCON,#80h ;Enables high baud rate generation divisor mov TH1,#(256-T1RL) ;Initialise Baud rate mov TCON,#50h ;Run both timers mov SCON,#50h ;Standard UART settings setb TxRdy ;Set the serial Tx semaphore flag to "Go" mov RxPointA,#RxBufA ;Initialise the Rx Write pointer mov RxRPointA,#RxBufA ;Initialise the Rx Read pointer setb ES ;Enable the serial port interrupt setb ET0 ;Enable timer 0 interrupt mov BusyCnt,#BusyCntRL ;Set the busy counter to start mov SwHist,#0FFh ;Mask switch history to inactive mov SwDeboun,#0FFh ;Mask debounced switches to inactive setb EA ;Set the global interrupt enable bit ;[]------------------------[] ;| Main Program | ;[]------------------------[] Main: mov A,BusyCnt ;Get the busy counter jz DoOut ;If busy has never been inactive, and also ; has been active too long, ignore it jnb Busy,DoOut ;If busy is not active, do the output ajmp NoOut ;Busy is active and valid - skip output DoOut: mov A,RxRPointA ;Get the Receive read pointer cjne A,RxPointA,PtOut ;Compare pointers - see if there is any data ajmp NoOut ;Jump if there is no data to output PtOut: ;There is data to output mov R0,A ;Put the Receive read pointer to R0 mov DatPort,@R0 ;Get the serial data to the parallel output inc A ;Update the receive read pointer cjne A,#RxBufAWrap,NoRWrp ;Test if the pointer has wrapped mov A,#RxBufA ;Wrap the pointer if required NoRWrp: mov RxRPointA,A ;Save the updated receive read pointer acall Settle ;Do a settling delay clr Strobe ;Activate the strobe bit acall Settle ;Do another settling delay setb Strobe ;Deactivate the strobe bit acall Settle ;Do a third settling delay NoOut: ;------------------------------------------------------------------------------ mov A,SwDeboun ;Get the debounced switches anl A,#Sampbt ;Look at the sample switch jnz NoIn ;If the sample switch is not pushed, skip input mov A,SampCnt ;Get the sample count jz DoIn ;Jump if we are to do continous sample inputs ;We are not at "continous" yet jb SeenSamp,DoneIn ;Jump out if we have just processed sample sw DoIn: jnb TxRdy,DoneIn ;Exit if we are not allowed to Tx yet mov DatPort,#0FFh ;Set the data port to all floating lines acall Settle ;Do a settling delay mov A,DatPort ;Read the data port clr TxRdy ;Reset the semaphore flag, as we are to Tx mov SBUF,A ;Transmit the data down the serial link ;Serial int will set semaphore when Tx done setb SeenSamp ;Flag we have now done a sample ajmp DoneIn ; and jump out NoIn: clr SeenSamp ;Clear the seen sample switch semaphore DoneIn: ajmp Main ;Jump back to start ;[]------------------------[] ;| Subroutines | ;[]------------------------[] POFlash: ;Power On LED flash - display then return ;Destroys R4,R5,R6 mov R6,#20 ;Number of flashes POFLp: clr RedLED ;Red LED on setb GrnLED ;Green LED off acall POFDel1 ;Short delay clr GrnLED ;Green LED on setb RedLED ;Red LED off acall POFDel1 ;Short delay djnz R6,POFLp ;Loop until the flashes are done ;Fall through POFDel1 and return POFDel equ 50 ;Desired delay in milliseconds POFDel1: mov R5,#((Crystal*POFDel)/6180000) ;Work out the djnz delay value POFDel2: ; mov R4,#0 POFDel3: djnz R4,POFDel3 ;This inside loop takes 515 cycles djnz R5,POFDel2 ;This loop happens the correct number of times ; to get closest to the desired delay in ms ret ;Return when delay is complete ;------------------------------------------------------------------------------ SettlDel equ 50 ;Desired delay in microseconds Settle: push ACC ;Save accumulator mov A,#((Crystal*SettlDel)/24000000) ;Work out djnz delay value SettLp: djnz ACC,SettLp ;Loop until delay complete pop ACC ret ;------------------------------------------------------------------------------ END