Here is the .asm file:
Basic_PWM... This tutorial is an extention of the "Timers and Interrupts" tutorial. It expands upon the code by adding two more blinking LED's to the circuit. All three LED's will be pulsed ON at the same time but will blink at different rates based upon the value of the OFF time. This "time on" verses "time off" is known as the Duty Cycle.
For instance, if the LED is on for 25% of the time and off for 75% of the time, it has a Duty Cycle of 25%. Maintaining the same pulse rate while varying the on/off time is known as Pulse Width Modulation (PWM).
How This Program Works:
During setup, three port pins are set high (which turns on three LED's simultaneously), and three count variables (DC0, DC1, DC2) are filled with values that represent the time each of the LED's will stay on.
An interrupt count variable (int_count) will be filled with a value that is decremented each time TMR1 causes an interrupt. After being decremented, the int_count variable is then compared to the DCx variables.
The "x" in the DCx variable stands for the corrosponding PORT pin that it represents.
DC0 corresponds to PORTB, 0 while DC1 corresponds to PORTB, 1 and DC2 corresponds to PORTB, 2.
Upon a match between the int_count variable and any of the DCx variables, the corrosponding PORT pin will be cleared (turning off the LED). This will continue until the int_count variable reaches zero at which point the PORT pins will be reset, the int_count variable will be refilled with it's respective value and the process will start over.
if the value in any of the DCx variables is larger than the value in the int_count variable, that PORT pin will never be cleared and the LED will stay on the entire time.
To show how to use a software interrupt to implement Pulse Width Modulation. The pulse rate chosen for this demonstration is such that it can be seen using blinking LED's and not have the need for expensive test equipment (I love LED's).
What you will need:
The same as in Brad's tutorial 2 (a simple LED flasher) with the addition of two LED's and resistors.
Follow the instructions for connecting your LED and resistor exactly as you would in Brad's tutorial 2 (a simple LED flasher) and add two more resistors to PORTB pin 1 and PORTB pin 2.
At this point it is expected that you have completed and understand both Brad's tutorial 2 – "a simple LED flasher" as well at the "Timers and Interrupts" tutorial I previously posted here:
I will only be discussing the changes that were made to the "Timers and Interrupts" tutorial since all I did was expand on the code. The only change made to the circuit is the original LED was moved from PORTA pin 1 to PORTB pin 0 (to coincide with Brad's original circuit) and the addition of two LED's.
Lines 16-19 show that four more variables are added. The code is commented quite well to explain their purpose as they are also described above under the section "How This Program Works" so I don't feel the need to re-explain them.
Before getting involved with the code in the interrupt routine, lets jump down to the changes made in the setup and main program.
Take a look below at lines 127-128. They have been changed to turn off the prescaler (it was originally set to divide by 8 ). The timing values of the interrupt intervals have changed so that we need the prescaler to divide by 1 (no prescaler needed).
Below you will notice code was also added in the setup to load the count value for the int_count variable, set the PORT pins high, and load the Duty Cycle variables with values that represent a 25% Duty Cycle, a 50% Duty Cycle, and a 75% Duty Cycle.
The main program still does nothing as before but the subroutine to turn on and off the LED has been removed. We are no longer going to use that for this demonstration.
Now it's time to go back to the interrupt routine and see what changes were made:
Below you will see that there are new timer values. These values were chosen because they will cause TMR1 to time out every 10mS (setting the prescaler for 1:1).
Below that actually starts the new code and the beginning of the PWM cycle.
Lines 59-60 are the two lines of code that decrement the interrupt counter and determine whether or not it needs to be reloaded and the PORT pins need to be reset. If the int_count has reached zero it will skip to lines 63-69 and then leave the interrupt routine.
If the int_count has NOT reached zero then it will jump to PWM_0 (line 70) where it will begin to compare itself to the values stored in the Ducty Cycle registers (DC0, DC1, DC2).
Well, that is about the best I can do to explain it! I hope everyone can understand it.
The code is well commented and should explain all that is necessary to understand what is happening line by line. Below are some photos and a link to a video to give a little "visual" as to what is going on at each PORT pin.
PS> Play around with the values stored in the DC0, DC1, & DC2 registers (variables) and watch how the LED's blink at different rates. This is an excellent way to control motors and dim lights (among other useful applications).