3 Generate Two Fixed Frequency PWM Signals
with Variable Duty Cycle
Use case description: Configure the TCF to generate an overflow event on the
compare registers at a fixed frequency while increasing the waveform duty cycle to all
available steps.
Result: The TCF will generate an output on the Channel 0 and Channel 1 at a fixed
frequency, while the waveform duty cycle will increase.
Embedded Bare Metal Implementation
Configuring the Output
Signals
Enable Waveform Output 0 and Waveform Output 1.
Figure 3-1. CTRLC
Register
The CTRLC register is double buffered. It is recommended to wait in a loop for bit CTRLCBUSY in the Status register to be cleared every time the CTRLC register is written.
Create a function called TCF0_OutputsSet that returns void and takes an argument of type uint8_t.
void TCF0_OutputsSet(uint8_t value)
{
while((TCF0.STATUS & TCF_CTRLABUSY_bm) != 0){};
TCF0.CTRLC = value;
}
Configuring the TCF
Clock
The AVR16EB32 board runs default with a system clock of 3.33
MHz. For the TCF to run at 20 MHz , the system-clock prescaler must be
disabled.
Create a function take returns void and take an argument of type void.
void CLOCK_Initialize(void)
{
_PROTECTED_WRITE(CLKCTRL.MCLKCTRLB, 0x0);
}
Figure 3-2. CTRLB RegisterFigure 3-3. Clock Select Bit
Mask
The TCF provides a range of clock options to choose from. For this
application select the default option, which is the Peripheral Clock running
at 20 MHz. The CTRLB register controls the clock selection.
Create a function that returns void and takes an argument of type TCF_CLKSEL_t.
void TCF0_ClockSet(TCF_CLKSEL_t config)
{
TCF0.CTRLB |= config;
}
The Prescaler is disabled in this mode.
Configuring the Waveform Generation Mode
The mode in which the timer
works is selected using the CTRLB register. NCOPF will be selected.
Figure 3-4. Waveform
Generation Mode Bit Mask
Create a function that returns void and takes an argument of type TCF_WGMODE_t.
void TCF0_ModeSet(TCF_WGMODE_t mode)
{
TCF0.CTRLB |= mode;
}
Setting the
Frequency
After configuring the TCF clock, choose the frequency at
which the timer will operate - 125 kHz using the CMP register.
Figure 3-5. CMP RegisterUsing Equation 1, the Increment can be calculated as follows:
Using the desired values results in:
The hexadecimal result is 0x0001999A.
The above formula cand be translated into the formula:
#define TCF0_NCOPL_HZ_TO_INCREMENT(HZ, F_CLOCK) (uint32_t)(((float)(HZ) * 16777216.0) / (float)(F_CLOCK) + 0.5)
The CMP register is double buffered. It is recommended to wait in a loop for bit CMP0BUSY in the Status register to be cleared every time the CMP register is written.
void TCF0_CompareSet(uint32_t value)
{
while((TCF0.STATUS & TCF_CMP0BUSY_bm) != 0){};
TCF0.CMP = value;
}
Figure 3-7. Waveform
Generation Pulse Length Bit Mask
Set the pulse length to be a single clock increment, which is 50 ns. 1
clock cycle takes 50 ns beacuse the timer is running at 20 MHz. 1 divided by
20 MHz equals 50
ns.
The TCF will be initialized with the following settings:
Both outputs enabled
Peripheral clock
selected
NCO Pulse-Length mode
set
Set a frequency of 125
kHz
Set a waveform generation
pulse length of 1 clock cycle
Set the timer to start
counting from 0
Create a function that returns void and takes an argument of type void.
void TCF0_Initialize(void)
{
TCF0_OutputsSet(TCF_WO0EN_bm | TCF_WO1EN_bm);
TCF0_ClockSet(TCF_CLKSEL_CLKPER_gc);
TCF0_ModeSet(TCF_WGMODE_NCOPF_gc);
TCF0_CompareSet(TCF0_NCOPL_HZ_TO_INCREMENT(125000, 20000000));
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK1_gc);
TCF0_CounterSet(0);
}
The util/delay.h header file requires to define the F_CPU frequency.
#define F_CPU 20000000UL
Include the util/delay.h header file
#include <util/delay.h>
Create a function that returns void and takes an argument of type void. Use the TCF0_NCO_Pulse_Length_Demo function to change the pulse-length with a delay in-between the changes.
void NCO_Pulse_Length_Demo(void)
{
/* Configure the TCF to start counting from 0 */
TCF0_CounterSet(0);
/* Enable the TCF */
TCF0_Start();
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 2 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK2_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 4 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK4_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 8 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK8_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 6 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK16_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 32 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK32_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 64 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK64_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 128 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK128_gc);
/* Delay for 25 us */
_delay_us(25);
/* Stop the timer */
TCF0_Stop();
/* Configure the pulse-length to 1 clock cycle */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK1_gc);
}
To generate this project using MPLAB Code Configurator, MCC Melody (MCC Classic is not
supported), follow the next steps:
Create a new MPLAB® X IDE project for AVR16EB32.
Open MCC from the toolbar find
more information on installing the MCC plug-in here.
In MCC Content Manager
Wizard, select MCC Melody, then click Finish.
From Device Resources, go
to System, click the CLCKCTRL window and disable the
Prescaler.
From Device Resource, go
to Drivers, click the Timer window, add the TCF module, then do the
following configuration:
– Clock Divider: System clock (by default, the
divider will be 1 - System clock)
– Waveform Generation Mode: NCO
Pulse-Length mode
– Waveform Generation Pulse Length: 1 Clock
Period
– Requested Period[s]: 0.000008
– Waveform Output n:
check the boxes from the Enable column for Waveform Output 0 and Waveform
Output 1
Select the PA0 and PA1 pins in
the Pin Grid View. When the boxes from Enable column and Waveform Output
n are checked, the pins are also locked. To change the PORT, click on a pin from
another PORT in Pin Grid View.
In the Project Resources
window, click the Generate button so that MCC will generate all the
specified drivers and configurations.
Edit the main.c file,as
following:
Include the util/delay.h header
file.
#include <util/delay.h>
Create
a function called NCO_Pulse_Length_Demo. Use the TCF0_NCO_PulseLengthSet
function to change the pulse length.
The code is as follows:
void NCO_Pulse_Length_Demo(void)
{
/* Configure the TCF to start counting from 0 */
TCF0_CounterSet(0);
/* Enable the TCF */
TCF0_Start();
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 2 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK2_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 4 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK4_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 8 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK8_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 6 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK16_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 32 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK32_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 64 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK64_gc);
/* Delay for 20 us */
_delay_us(20);
/* Configure the pulse-length to 128 clock cycles */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK128_gc);
/* Delay for 25 us */
_delay_us(25);
/* Stop the timer */
TCF0_Stop();
/* Configure the pulse-length to 1 clock cycle */
TCF0_NCO_PulseLengthSet(TCF_WGPULSE_CLK1_gc);
}
The main function looks like
this:
int main(void)
{
SYSTEM_Initialize();
while(1)
{
NCO_Pulse_Length_Demo();
_delay_ms(20);
}
}
Flash the project.
Result:
TCF generates two identical signals with a frequency of 125 kHz and PWM with a variable
duration ranging from 1 to 128 clock cycles. In this case, one clock cycle takes 50
ns.