* Example 68HC12 programs. There are several distinct programs * embedded in this file - each with distinct starting addresses. * * (1) Marius Grueuel's "Hello World and 1 Hz pulse PPO" starts $d00 * * (2) PWM demo starts at location $d5b * * (3) Terminal I/O (polled) starts at $dde * * (4) A/D test starts at $e3c * * (5) Timer Interrupts - both input capture and intervals at $e79-> $ee9 * * Port A is configured for output in the A/D test (one * channel's binary output is sent as 8-bit parallel output * to Port A * * Port B is configured as 8-bit inputs for the PWM demo * * * example.asm : displays Hello World and pulses PP0 with 1Hz * * Copyright (c) Marius Greuel 1998 (miniide@tripod.com) * .nolist #include hc12.inc .list * org EE_START ; for an EEPROM version * * This program segment does not use any RAM variables * bra Main ; To skip around constants/message Hello dc.b "Hello world!", $0d, $0a, 0 Main: clr COPCTL ;disable watchdog lds #USER_STACKTOP ;load stackpointer ;initialize sci movw #$0034,SC0BDH ;baudrate 9600 bits/second movb #$00,SC0CR1 ;loop mode disabled, 8,1,n movb #$0c,SC0CR2 ;transmitter & receiver enabled, sci irq disabled ldaa SC0SR1 ;clean up ;write string to sci ldx #Hello bra L2 L1: brclr SC0SR1,$80,L1 ;wait for TDRE staa SC0DRL ;write byte to data transmit register L2: ldaa 1,X+ ;get next byte bne L1 * ;pulse PP0 with 1Hz movb #%01111111,PWCLK ;CON01, clk A = PCLK/128, clk B = PCLK/128 movb #%00000001,PWPOL ;source is clk A clr PWCTL clr PWTST movw #$f424,PWPER0 ;period movw #$7a12,PWDTY0 ;duty movb #%00000011,PWEN ;enable PWEN0/1 bra * * ******************************************************************** * * PWM test program written by Roy Czernikowski * * Test of 68HC12 Pulse-Width Modulation on Port P * PWM0 - PWM3 on Port P bits 0 - 3 respectively * It reads a duty cycle count (< 200 decimal) from Port B * *USER_STACKTOP equ $0a00 ; just above RAM *PORTA equ $0000 *PORTB equ $0001 *DDRA equ $0002 ; Port A data direction register *DDRB equ $0003 ; Port B " " " * *PWCLK equ $0040 ; no concatenate & clock prescalars *PWPOL equ $0041 ; clock selects & output polarities *PWEN equ $0042 ; PWM Enable *PWPRES equ $0043 ; PWM Prescale Counter - free running *PWSCAL0 equ $0044 ; Channels 0 & 1 prescale value *PWSCAL1 equ $0046 ; Channels 2 & 3 prescale value *PWSCNT0 equ $0045 ; Channels 0 & 1 prescale count-down *PWSCNT1 equ $0047 ; Channels 2 & 3 prescale count-down *PWCNT0 equ $0048 ; Channel 0 counter *PWCNT1 equ $0049 ; Channel 1 counter *PWCNT2 equ $004a ; Channel 2 counter *PWCNT3 equ $004b ; Channel 3 counter *PWPER0 equ $004c ; Channel 0 Period Register *PWPER1 equ $004d ; Channel 1 Period Register *PWPER2 equ $004e ; Channel 2 Period Register *PWPER3 equ $004f ; Channel 3 Period Register *PWDTY0 equ $0050 ; Channel 0 Duty Register *PWDTY1 equ $0051 ; Channel 1 Duty Register *PWDTY2 equ $0052 ; Channel 2 Duty Register *PWDTY3 equ $0053 ; Channel 3 Duty Register *PWCTL equ $0054 ; PWM Control Register *PWTST equ $0055 ; PWM Special Mode Register "Test" *PORTP equ $0056 ; Port P Data Register *DDRP equ $0057 ; Port P Data Direction Register *COPCTL equ $0016 ; Watchdog Comp Operating Properly * * We're aiming for approximately 400 Hz waves with * duty cycles setting in 0.5% increm (0-200 decimal) * * org $0800 PWMstart lds #USER_STACKTOP clr COPCTL ; disable watchdog timer movb #%00011011,PWCLK ; E/8 = 1.0 MHz movb #$FF,PWPOL ; S1 clks, + polarity to start * * Take this 1.0 MHz Eclk/8 and further scale it by 12 * to produce ~ 80 KHz "ticks" * ldab #5 ; (5 + 1)*2 = 12 => ~ 80 KHz tick stab PWSCAL0 ; for faster execution & fewer stab PWSCAL1 ; code bytes vs movb #5,PWSCAL1 ldab #200 ; 200 decimal for periods => 400Hz * * If we now count 200 of these ~80KHz ticks/period, * the basic PWM clock "frequency" is ~ 400 Hz = 2.5 msec * stab PWPER0 stab PWPER1 stab PWPER2 stab PWPER3 * movb #%00000000,PWCTL ; ? bit 1 for no pull-up R movb #$0f,DDRP ; PORTP DDR may be unnec. with PWM clr PWCTL clr PWTST ldab #30 ; we'll start with dummy duty cycles stab PWDTY0 addb #50 stab PWDTY1 addb #50 stab PWDTY2 addb #50 stab PWDTY3 * clr DDRB ; allow switch inputs to Port B ldab #20 stab PWCNT0 ; just intialize 'em all stab PWCNT1 stab PWCNT2 stab PWCNT3 ldx #0000 clra movb #$0f,PWEN ; start 'em running loop ldab PORTB jsr limiter ; make sure Duty Cycle <= 200 stab PWDTY0 ; send the input to PWM0 addd #50 ; just make a different value jsr limiter stab PWDTY2 delay dex bne delay ldab PWDTY1 incb jsr limiter stab PWDTY1 ldab PWDTY3 incb jsr limiter stab PWDTY3 bra loop * limiter cpd #200 bls limitend subd #200 limitend rts * * ****************************************************************** * * * Terminal I/O : Reads in a character string from terminal keyboard (echoing * characters to the screen) until a is entered, at * which point it will send the characters entered in reverse * order on a new line of the screen and then go to a new line * to begin again. It uses RAM locations beginning at $800 * * Roy Czernikowski - December 27, 1998 * * .nolist * #include hc12.inc .list StringBuffer equ $800 * * org EE_START TermIOStart clr COPCTL ;disable watchdog timer lds #USER_STACKTOP ;load stackpointer * * Initialize SCI movw #0052,SC0BDH ;baudrate 9600 movb #$00,SC0CR1 ;loop mode disabled, 8 bits data,1 stop,no parity movb #$0c,SC0CR2 ;transmitter & receiver enabled, SCI IRQ disabled ldaa SC0SR1 ;clean up movb #$00,StringBuffer ; put a zero for possible terminator * Back ldx #StringBuffer+1 WaitChr ldab SC0SR1 andb #$20 ; check for RDRF, beq WaitChr ; if no new input char, go to WaitChr ldaa SC0DRL ; else get the new input character cmpa #$0d ; look for , if so go to send routines beq SendString bsr SendChar ; else echo input to screen staa 1,X+ ; else put input char into StringBuffer bra WaitChr * SendString bsr SendChar ; send the to screen ldaa #$0a ; send a to screen bsr SendChar dex ; readjust X to point at last char NextChar ldaa 1,X- ; get next character <<<<<<< check this beq SendCR ; if we encountere $00 (end_of_string) * ; go to clean-up 'WaitCR' bsr SendChar ; else output character to screen bra NextChar * SendCR ldaa #$0d ; send bsr SendChar ldaa #$0a ; send bsr SendChar bra Back ; go Back * * dummy alternate code from Marius Greuel's example * not used in this demo program * bra LL2 LL1: brclr SC0SR1,$80,LL1 ;wait for TDRE staa SC0DRL ;write byte to data transmit register LL2: ldaa 1,X+ ;get next byte bne LL1 * ********************************************************* * * Subroutine SendChar: it sends the character * passed in Reg A to terminal output screen * - note it does a busy wait for TDRE * this routine trashes Reg B * SendChar ldab SC0SR1 ; check for TDRE bpl SendChar ; if not go to WaitLF staa SC0DRL rts * ***************************************************** * * * org $8fe * fdb $0021 ; safety $00 and '!' character * *StringBuffer fcb $00 ; end_of_string delimiter * fcb 'abcdefghijklmnopqrstuvwxyz' ; just to get some indicators * ***************************************************** * * AtoD Converter Test - Scan Mode - 8 channels * * This program deposits the 8 channels' digitized inputs * into 8 locations beginning at location $800 * ValueTable equ $800 ; * rmb 8 ; space for results * USER_STACKTOP equ $0a00 ; just above RAM * PORTA equ $0000 ; we'll display an A2D result at PortA * DDRA equ $0002 ; *ATDCTL2 equ $0062 ; set bit 7 = 1 to power-up A2D *ATDCTL3 equ $0063 ; cleared at reset - leave it alone *ATDCTL4 equ $0064 ; reset to $01 - leave it alone *ATDCTL5 equ $0065 ; 01110000 for 8 channel scan convers. * ATDSTATH equ $0067 ; bit 7 Scan Complete Flag * ATDSTATL equ $0068 ; 8 channel conversion complete flags *PORTAD equ $006f ; digital values of bits input *ADR0H equ $0070 ; ATD Converter Result Register 0 *ADR1H equ $0072 ; ATD Converter Result Register 1 *ADR2H equ $0074 ; ATD Converter Result Register 2 *ADR3H equ $0076 ; ATD Converter Result Register 3 *ADR4H equ $0078 ; ATD Converter Result Register 4 *ADR5H equ $007a ; ATD Converter Result Register 5 *ADR6H equ $007c ; ATD Converter Result Register 6 *ADR7H equ $007e ; ATD Converter Result Register 7 * * org $0800 AtoDstart lds #USER_STACKTOP ; just above RAM ldab #$80 ; power-up AtoD Converter stab ATDCTL2 ldab #$70 ; standard 8 Channel scan conversions stab ATDCTL5 * ldab #$ff ; enable Port A to be all outputs for stab DDRA ; sending one channel's results * AtoDloop ldx #ValueTable ldab ATDSTATH ; wait for Scan Complete Flag in bge AtoDloop ; bit 7 ldaa ADR0H ; read channel results and store staa 1,x+ ; them in ValueTable ldaa ADR1H staa 1,x+ ldaa ADR2H staa 1,x+ ldaa ADR3H staa 1,x+ ldaa ADR4H staa 1,x+ ldaa ADR5H staa 1,x+ ldaa ADR6H staa 1,x+ ldaa ADR7H staa 1,x+ ldab ValueTable+6 stab PORTA bra AtoDloop * ********************************************************* * * Timer interrupt demo Timer 5 does regular timer * interval interrupts 5 times per second and * toggles output bit PT5. Timer 1 counts until * a one-to-zero transition is detected on PT1 * at which point the most significant half of * the TCNT register is output on Port A * flags equ $800 ; Debug RAM copy of Timer IRQ Status Timer1vec equ $0b2c ; Ram IRQ vector locations Timer5vec equ $0b24 ; was $ffe4 for ROM & $f7e4 Fast_Timer clr COPCTL ; disable watchdog lds #USER_STACKTOP ; load stackpointer * sei ; disable interrupts for moment * movw #Timer1IRQ,Timer1vec ; initialize interrupt movw #Timer5IRQ,Timer5vec ; vector locations clr flags ; a RAM debug location * clr INTCR ; see page 53 section 9.3 ldab #$ff ; set Port A all outputs for stab DDRA ; displaying TCNT (msb) when an * ; an external falling edge pulse * ; arrives on bit PT5 * clr TMSK1 ; disable all interrupts for now ldab #%00000101 ; 8MHz / 32 = 250KHz stab TMSK2 ldab #%00001000 ; enable timer 1 capture on stab TCTL4 ; falling edge ldab #$20 ; enable output compare stab TIOS ; on Timer 5 output (bit 5) * ldab #$04 ; enable toggle output on OC5 stab TCTL1 ldab #$00 ; no timer outputs on OC3-OC0 stab TCTL2 ; ldab #$00 ; No timer input captures on 7-4 stab TCTL3 ; ldab #%00100010 ; enable two interrupts stab TMSK1 ; OC5 and IC1 ldaa TFLG1 ; just to read it before ldab #$ff ; clearing any possible spurious stab TFLG1 ; timer interrupts ldd TCNTH ; schedule first interrupt in addd #50000 ; future std TC1H std TC5H ; may not be necessary ldab #$80 stab TSCR ; turn on timers cli ; enable interrupts backgnd nop bra backgnd * Timer1IRQ ldab TFLG1 ; just to read it ldab #$02 ; clear C2F in TFLG1 stab TFLG1 ldd TC1H ; get entire TCNT at time of * ; external hi-to-low pulse staa PORTA ; display hi-order TCNT * * maybe do something "useful" * rti * Timer5IRQ ldab TFLG1 ; copy timer status stab flags * ldab #$20 ; clear Timer 5 OCFlag stab TFLG1 ; * ldd TC5H ; schedule next interrupt in addd #50000 ; future std TC5H * * maybe do something useful * rti