Pulse width Measurement, Square Wave Generation, and External Interrupts (on RB0) Dr. Abdel-Rahman Jaradat In this lab session we will address three topics: 1. We want to measure the ON time in a square wave (or the positive pulse duration). The following CCS program will do the required as follows: read RA0 and wait while it is zero ’OFF’, when it is one (ON) start TMR0, and while it is one (ON) wait till it becomes zero (OFF) and read TMR0. Number of ’ticks’ multiplied by the prescaler times MC (machine cycle) will give the ON time of the signal. In the lab we want to measure the ON time for a 1 KHz square wave (be careful to adjust the square wave to be between 0 and 5 Volts by using the oscilloscope) and connect it to RA0. We took the prescaler value P = 16 and hence every tick will take 8 times MC=16 µs. For an ON time of approximately half a milli-second (500 µs) TMR0 will count 500 16 = 31.25 ticks (≈ 31) ticks. /* PW_TMR0.c Use TMR0 to measure pulse duration. Input pulse applied to RA0. From Start when LOW-to-HIGH (0->1) and Stop when HIGH-to-LOW (1->0) Ref. Smith pg189 */ #include "16f84a.h" #use delay (clock=4000000) // 4MHz crystal #fuses XT, NOWDT, NOPUT, NOPROTECT void main() { int x; set_tris_A(0x1); // pin RA0 as input set_tris_B(0x0); // pins RB7:RB0 output setup_timer_0(RTCC_DIV_16|RTCC_INTERNAL); // presacaler=16, Use internal clock while(1) { while (!input(PIN_A0)); // wait for A0 to go high set_timer0(0); // init timer0 to 0 while (input(PIN_A0)); // wait for A0 to go low x=get_timer0(); output_B(x); // display PW in micro seconds times prescaler (=16) // if prescaler = 256, then every tick approximately = 0.25 millisecond } } When using Oshon simulator, inject a square wave on RA0 (signal generator assigned to pin RA0) and put the period to be 1000 µs (for 1 KHz the period=1ms=1000 µs) which corresponds to 4MHz 1000 = 4000 (crystal) clock cycles and enter the duty cycle as 50. 1 2. Configure portB as o/p and connect it to 8-leds. We want to display the binary representations of the numbers 0, 1, 2, · · ·, 255 on portB with separated in 1 second time interval. Use 16F84A and 4 MHz xtal. fosc = 4M Hz → Tosc = 14 µsec → machine cycle (MC) = 4* Tosc = 1µs. For a prescaler of 1, P = 1, the Time-Out (TO) occurs after 256 × M C = 256µs. 6 µs 1s To number of Time-Outs that we need to reach 1 s = 256µs = 10 256µs = 3906. This number is much more than 255 which is the limit that can fit in 8-bit register. The only way to go around this is to use a prescaler value bigger than 1. Try to use P=256 (which the largest 106 = 15.259 which can easily fit in an possible value for prescaler), then the number of Time-Outs TOs= 256×256 8-bit register. The following CCS program will ’tick’ every (Tick Time) T T = M C × 256 = 256µs and a TO every T T × 256 = 65536µs and hence we need 106 /65536 = 15.259 to reach one second. /* tick_per_second.c fosc=4MHz, RTCC_DIV_256 (max. value), TMR0 increments at a rate=(4000000/4)/256=3906.25 increments per second 10**6/256=3906.25 ==> 256 us per increment. TMR0 will interrupt every 2**8=256 counts (0,1,2,...,255) In 1 second, there will be 3906.25/256=15.25 interrupts. Approx. set this to 15. */ #include "16f84a.h" #use delay (clock=4000000) // 4MHz crystal #fuses XT, NOWDT, NOPUT, NOPROTECT int ctr=0; int sec=0; // interrupt from RTCC (=TMR0) #int_rtcc void timer_rtcc () { ctr++; if (ctr == 15) // every 15 interrupts occur in 1 sec { sec++; ctr=0; output_b(sec); } } void main() { set_tris_B(0x0); // pins RB7:RB0 output setup_counters(RTCC_DIV_256,RTCC_INTERNAL); // presacaler=256, Use internal clock enable_interrupts(INT_RTCC); enable_interrupts(GLOBAL); while(1); // repeat always } 3. Write a program to generate 1kHz square wave on RA0. Display the output on the scope. // generate 1kHz square wave #include "16f84a.h" #use delay (clock=4000000) // 4MHz crystal main(){ set_tris_a(0x00); // pins 0 to 4 of port A are outputs while(TRUE){ output_high(PIN_A0); delay_us(500); output_low(PIN_A0); 2 delay_us(500); } } Modify the above program to use interrupts. 4. Use example in class for interrupt. int ext: Interrupt on RB0 when occurs write 31=b’11111’ show output on simulator scope then on physical. Modify your program to count number of one-to-zero changes on RB0 and display the output on RA4:RA1. // C-Programming - Bates, page 106 // External interrupt test program #include "16F877a.h" #use delay (clock=4000000) // 4MHz crystal #int_ext void isrext() {output_D(255); delay_ms(1000);} main(){ int x=0; enable_interrupts(int_ext); enable_interrupt(global); ext_int_edge(H_TO_L); while(1) { output_D(x); x++; delay_ms(100); } } 5. Simulate the following Assembly language program that uses interrupts from RB0 (connect RB0 to a switch) ; xint-delay-tmr0.asm: delay using tmr0 via interrupt #include "p16f84a.inc" COUNT EQU 0X0C TEMP EQU COUNT+1 LIST P=16F84 ; we are using the 16F84. ORG 0x0 GOTO START ;********************************************************************** ; Configuration Bits __CONFIG _CP_OFF & _WDT_OFF & _PWRTE_ON & _XT_OSC ;H’3FF1’ ;********************************************************* ;RB0 Interrupt Initialization ;In order to initialize the RB0 interrupt, the following operations must take place: ;1. Port-B, line 0, must be initialized for input. ;2. The interrupt source must be set to take place either on the falling or the rising edge of the ; signal by set or reset of INTEDG=OPTION_REG bit 6. ;3. The external interrupt flag (INTF in the INTCON Register) must be initially cleared. ;4. Global interrupts must be enabled by setting the GIE bit in the INTCON Register. ;5. The External Interrupt on RB0 must be enabled by setting the INTE bit in the INTCON Register. ;The following code fragment, from the program RB0Int in Sanchez and Canton, performs these operation ;============================= ; interrupt handler STARTS FROM LOCATION 0X04 ;============================= org 0x04 goto IntServ ;============================= ; main program 3 ;============================= START ; SETUP INTERRUPT REGISTER: INTCON IS IN BOTH BANKS 0 AND 1 BSF INTCON,GIE BSF INTCON,INTE ; ENABLE INT/EXT FROM RB0 BCF INTCON,INTF ; CLR FLAG BIT ... JUST IN CASE ;SETUP PORTA AND PORTB DIRECTIONS BSF STATUS,RP0 ; TO BANK 1 MOVLW 0X01 MOVWF TRISB ; RB0 AS INPUT MOVLW 0X10 MOVWF TRISA ; RA3,RA2,RA1,RA0 AS INPUT ; Set up interrupt on rising edge by setting OPTION register bit INTEDG (BIT 6) BSF OPTION_REG,INTEDG BCF STATUS,RP0 ; BANK 0 LOOP MOVF COUNT,W ; W <- COUNT MOVWF PORTA ; PORTA <- COUNT GOTO LOOP ; REPEAT FOREVER SLEEP ;RB0 Interrupt Service Routine ;The Service Routine for the RB0 interrupt depends on the specific application. Nevertheless, ;the following processing steps should be considered: ;1. Determine if the source of interrupt is an RB0 interrupt. ;2. Clear the RB0 interrupt flag (INTF bit) in the INTCON Register. ;3. Save the context. Which registers and variables need to be saved depends on the specific applicat ;4. Perform the interrupt action. ;5. Restore the context. ;6. Return from the interrupt with the retfie instruction. ;======================================================= ; Interrupt Service Routine ;======================================================= ; Service routine receives control whenever RB0 change state IntServ MOVWF TEMP ;SAVE W TILL WE RETURN FROM INTERRUPT INCF COUNT,F ; COUNT <- COUNT+1 MOVLW 0X0A ; W <- 10 SUBWF COUNT,W ; W <- COUNT-W BTFSS STATUS,C; CHECK CARRY BIT GOTO CARRY_ON ; COUNT < 10 GOTO CLEAR ; COUNT > 9 ... CLEAR IT CARRY_ON BCF INTCON,INTF ; ENABLE MORE INTERRUPTS MOVF TEMP,W ;RESTORE W BEFORE INTERRUPT retfie ; RETURN FROM INTERRUPT ; CLEAR CLRF COUNT ; COUNT <- 0 BCF INTCON,INTF RETFIE ; RETURN FRON INTERRUPT END Modify the pulse width measurement to use external interrupt on RB0 and measure the period of a square wave 4