Hello people…its been quite some time since i ve posted anything here. Well let me fix that…. this is a post on the programmable counter array functionality in 8051. To be clear, the 8051 core as such does not have this functionality but if you have an 8051 board which is not too old, you should have it. Referring the data sheet might be helpful. For the description below i ll be using AT89c51ed2. But you can extend the ideas to other boards too.
A few words on programmable counter array…its there to expand the timing capabilities of 8051. It requires comparatively low software overhead and is more accurate. It has an array of 5 counter/capture modules. They can be programmed in any of the following modes:
- Rising or falling edge timer
- Software timer
- High speed output
If you ve read my previous post on controlling the servo motor using 8051..i wrote about one way PWM can be achieved. Here i ll demonstrate a much simpler approach. I ll be using the module 0 here to generate a PWM wave. We ll set the frequency as 100kHz and the duty cycle to 20%. The output will be available on Pin1.3/CEX0. I ll be using the Keil simulator to see the output.
The modules that i mentioned above require a timer of their own to function properly, this is achieved by what is called a PCA timer. Its a common base timer for all the modules. This timer is itself sourced by :
- Freq (clk-peripheral) / 2
- Freq(clk-peripheral) / 6
- Timer 0 overflow
- Input on ECI pin (Pin 1.2)
A better way to say this is….lets assume that we have programmed it to use Timer 0 overflow as a source. This means every time timer0 overflows the PCA timer increments by 1.
Okay then lets get started with it.
Since the output frequency is 100kHz, Freq(clk-peripheral)/ 6 seems to be a good enough choice .To program that we need the CPS1 and the CPS0 bits in the CMOD register.
MOV 0D9H, #00H
Next we ll fix the contents of CCAPM0 register. This is where we tell the microcontroller that we are going to use it in the PWM and enable the comparison.
MOV 0DAH, #42H
The next step is loading the CH & CL registers with appropriate values such that CH/CL overflows at the rate of 100kHz.
The oscillator frequency here is 33MHz. The F(clk-peripheral) is (Oscillator / 2) in this case..i.e 16.5MHz. Further more the clock to PCA is F(clk-peripheral)/6….i.e. 2.75MHz. As a result the number of increments the CH/CL register should go through to get a frequency of 100kHz is 27.5 ~ 28. So CH can be initialized with 0xFF and CL with 0xE4. Everytime the timer overflows an interrupt is raised. Thus in the interrupt service routine reload the CH/CL registers with the above values.
Once we have taken care of the frequency, the next step is setting the duty cycle. How this works is, we store a value in the CCAPL0 SFR. This value is compared with the value in CL. As long as the count in CL is less than the value in CCAPLo, the output remains LOW, it then toggles to HIGH when the CL count exceeds the value in CCAPL0.
Finally start the timer and enable the PCA interrupt. After debugging and running the program, the plot the output.
Here is the final code for 100kHz and 20 % duty cycle. I ve loaded the CH/CL with values other than what I mentioned above since 28 increments was giving me a frequency of around 80kHz. After experimenting a bit the value of 235 in CL worked out perfectly.