blink an LED using T1 and AD interrupts
These exercises give practice in configuring and using the 16-bit T1 counter and the 10-bit AD converter, by blinking an LED at a rate determined by a potentiometer. First, we blink the LED at a fairly accurate frequency of 1 Hz. Next, we use a potentiometer to control the frequency by digitizing its output voltage, polling the AD converter to determine when it is done. This will show that the T1 interrupt does not disturb program execution because of context saving. Finally, we operate both T1 and the AD converter with interrupt service routines while the program idles in a tight loop.
The 16-bit timer T1 can count with the cycle clock f0/4, an external clock at the OSC1 pin, or its own LP crystal oscillator at the OSC1 and OSC2 pins, if these are not in use for the system clock oscillator. It can be started and stopped by a signal at the OSC2 pin if this is not used for the LP oscillator, allowing it to measure pulse widths very easily. There is a prescaler with ratios 1:1, 1:2, 1:4 and 1:8. The counter registers are TMR1H and TMR1L, and the counter is configured by the T1CON register.
In T1CON, bit 0 is TMR1ON; when it is set, the timer runs. Bit 6 is TMR1GE; when it is set, the /T1G gate signal turns on the timer when it is low. This bit is normally clear. Bit 1 selects the clock source. When clear, the cycle clock f0/4 is used, while if it is set, an external clock on OSC1 is used. If bit 3, T1OSCEN is set in addition, the LP oscillator using OSC1 and OSC2 is selected. The internal oscillator must be used for the system clock for this to be available. Bit 2 selects whether the external oscillator is synchronized with the system clock; normally this bit is clear to select synchronization. Finally, bit 4 and 5 select the prescaler ratio. 00 selects 1:1, while 11 selects 1:8. Bit 7 is not used and reads as 0. When configuring the timer, load T1CON with TMR1ON = 0, and then start the timer with bsf T1CON,TMR1ON. Loading T1CON with 0x30 will use the cycle clock f0/4 with 1:8 prescaler.
The T1 counter registers can be read or loaded at any time, but care must be exercised to get a valid result. If the counter is stopped, there is no difficulty. If the counter is running, the possibility that the low order byte rolls over between the reads or writes to the two registers must be recognized. To load the counter, first clear TMR1L, then write TMR1H, and follow by writing TMR1L. In an interrupt service routine, the counter will just have rolled over, so it is not necessary to clear TMR1L. To read the counter, first read TMR1H and TMR1L, then read TMR1H a second time and compare it with the first read by subtracting the two values. If there is no difference, then the read is valid. If there is a difference, a subsequent read of TMR1H and TMR1L will give a valid result.
The LP oscillator is intended for a 32.768 KHz crystal when the T1 counter is used for maintaining a real-time clock. With this oscillator frequency, the T1 counter will roll over every 2 seconds with a 1:1 prescaler. In an interrupt service routine, if the counter is loaded with 0x8000, the interrupts will occur at 1s intervals, making a real-time clock very convenient.
The T1 timer is considered a peripheral, so its interrupt flag is located in PIR1, and its interrupt enable bit is located in PIE1. To enable an interrupt when T1 rolls over, T1IE in PIE1, and PEIE and GIE in INTCON must be set.
Connect an LED to GP0 in series with a 330Ω resistor. Remember that this is an analog input AN0 and a comparator analog input, so the ANS0 bit in ANSEL must be cleared, and the comparator mode must be set to 7 in CMCON. The ISR can toggle this bit by xorwf GPIO with W = 0x01. Configure the 12F675 to use the internal 4 MHz oscillator, and T1 to use the cycle clock with a 1:8 prescaler, it will count at a rate of 125 kHz, so for a timeout at 0.5 sec, it must make 62500 counts, and so must be started with 65535 - 62500 = 3035 or 0x0BDC.
Write a program that initializes the 12F675 then enters a tight loop to wait for interrupts. The ISR should save and restore the context, W and STATUS, although it is not necessary here. It should toggle the LED, clear the TMR1IF, and return. When you run the program, the LED should blink a 1 Hz rate.
Connect a 10kΩ potentiometer between +5 and GND, with its wiper connected to AN1. This pin should be configured as an analog input, as it is by default. The result of an 8-bit AD conversion, which will be between 0000 and FFFF, should be loaded into TMR1H in the ISR. This can be done by saving AD results in a location that is read during the ISR.
Configure the AD converter to use an f0/64 clock rate, left justified result, with input on AN1, and turn it on. In the main program, trigger a conversion by setting GO and waiting until this bit returns to 0. Then store the conversion result, and trigger another conversion. Alter the ISR to store the AD result in TMR1H instead of the value for 1 Hz. TMR1L can be cleared. Test the program and observe that the blinking rate can be varied from less than 1 Hz to rates too high for the blinking to be detected by the eye.
The AD converter interrupt enable and flag are located in the same registers as for T1. In the ISR, test the flags to determine the source of the interrupt. Move servicing the AD converter from the main program to the ISR, clearing the interrupt flag and triggering a conversion. In the main program, after initialization trigger a conversion and jump into a tight loop. This program should work just as in the preceding section.
Connect a 32.768 crystal between OSC1 and OSC2, and 100 pF ceramic capacitors between each of these pins and ground. Initialize T1CON with 0x3A instead of 0x30 to select the LP oscillator. When the program is run, note the much lower blinking frequency because of the reduction of the counter frequency by a factor of 30.5.
Refer to the Mid-Range User's Manual for information on interrupts, the T1 counter and the AD converter.
Composed by J. B. Calvert
Created 29 September 2010