4 Generate Two Variable-Frequency Signals in NCO Fixed Duty Cycle Waveform Generation
Use case description: Configure TCF to generate an overflow event on the compare registers on a range of frequencies, from 10 Hz to 100 kHz.
Result: TCF will generate an output on Channel 0 and Channel 1 on a range of frequencies, which toggles every time the accumulator overflows.
- Configuring the Output
Signals
Enable Waveform Output 0 and Waveform Output 1.
Figure 4-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 4-2. CTRLB Register Figure 4-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; }
- Selecting the Prescaler
Figure 4-4. CTRLA Register The CTRLA register is double buffered. It is recommended to wait in a loop for bit CTRLABUSY in the Status register to be cleared every time the CTRLA register is written. Create a function that returns void and takes an argumnet of type TCF_PRESC_t. void TCF0_PrescalerSet(TCF_PRESC_t config) { while((TCF0.STATUS & TCF_CTRLABUSY_bm) != 0){}; TCF0.CTRLA |= config; }
- Configuring the Waveform
Generation ModeThe mode in which the timer works is selected using the CTRLB register. NCOFDC will be selected.
Figure 4-5. CTRLB Register Figure 4-6. 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 CMP
Register
Using Equation 2, the Increment can be calculated as follows:
Using the desired values results in:
The result is hexadecimal, 0x00000011.
The above formula cand be translated into the formula: #define TCF0_NCOFD_HZ_TO_INCREMENT(HZ, F_CLOCK, TCF0_PRESCALER) (uint32_t)(((float)(HZ) * 33554432.0 * (TCF0_PRESCALER)) / ((float)(F_CLOCK)) + 0.5)
Before the timer starts, the CMP is written with the value of 11.
Figure 4-7. CMP Register 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. Create a function that returns void and takes an argument of type uint32_t. void TCF0_CompareSet(uint32_t value) { while((TCF0.STATUS & TCF_CMP0BUSY_bm) != 0){}; TCF0.CMP = value; }
The CNT register is double buffered. It is recommended to wait in a loop for bit CNTBUSY in the Status register to be cleared every time the CNT register is written. Create a function that returns void and takes an argument of type uint32_t. void TCF0_CounterSet(uint32_t value) { while((TCF0.STATUS & TCF_CNTBUSY_bm) != 0){}; TCF0.CNT0 = (uint8_t)value; }
- Start and Stop the
Timer
Figure 4-8. CTRLA Register The CTRLA register is double buffered. It is recommended to wait in a loop for bit CTRLABUSY in the Status register to be cleared every time the CTRLA register is written. Create a function that returns void and takes an argument of type void. void TCF0_Start(void) { while((TCF0.STATUS & TCF_CTRLABUSY_bm) != 0){}; TCF0.CTRLA |= TCF_ENABLE_bm; } Create a function that returns void and takes an argument of type void. void TCF0_Stop(void) { while((TCF0.STATUS & TCF_CTRLABUSY_bm) != 0){}; TCF0.CTRLA &= ~TCF_ENABLE_bm; }
The TCF will be initialized with the following settings:
- Both outputs enabled
- Peripheral clock selected
- NCO Fixed Duty Cycle mode set
- Set a waveform generation pulse length of 1 clock cycle
- Set a frequency of 10 Hz
- 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_PrescalerSet(TCF_PRESC_DIV1_gc); TCF0_ModeSet(TCF_WGMODE_NCOFDC_gc); TCF0_CounterSet(0); TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(10, 20000000, 1)); }
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 uint_32t. Use the TCF0_CompareSet function to change the frequency. void NCO_Fixed_DutyCycle_Demo(void) { /* Configure the TCF to start counting from 0 */ TCF0_CounterSet(0); /* Enable the TCF */ TCF0_Start(); /* Delay for 600 ms */ _delay_ms(600); /* Load the CMP register with a frequency of 100 Hz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(100, 20000000, 1)); /* Delay for 60 ms */ _delay_ms(60); /* Load the CMP register with a frequency of 1 KHz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(1000, 20000000, 1)); /* Delay for 6 ms */ _delay_ms(6); /* Load the CMP register with a frequency of 10 KHz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(10000, 20000000, 1)); /* Delay for 600 us */ _delay_us(600); /* Load the CMP register with a frequency of 100 KHz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(100000, 20000000, 1)); /* Delay for 60 us */ _delay_us(60); /* Stop the TCF */ TCF0_Stop(); /* Load the CMP register with a frequency of 10 Hz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(10, 20000000, 1)); }
The main function looks this:void main(void) { CLOCK_Initialize(); TCF0_Initialize(); while(1) { NCO_Fixed_DutyCycle_Demo(); _delay_ms(1000); } }
MCC Melody Implementation
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 should be 1 - System clock
– Waveform Generation Mode: NCO Fixed Duty-Cycle mode
– Requested Period[s]: 0.1
– 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 from 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_Fixed_DutyCycle_Demo. Use the TCF0_CompareSet function to change the frequency.
The code is as follows:Create a function that retuns void and takes an argument of type void. void NCO_Fixed_DutyCycle_Demo(void) { /* Configure the TCF to start counting from 0 */ TCF0_CounterSet(0); /* Enable the TCF */ TCF0_Start(); /* Delay for 600 ms */ _delay_ms(600); /* Load the CMP register with a frequency of 100 Hz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(100, 20000000, 1)); /* Delay for 60 ms */ _delay_ms(60); /* Load the CMP register with a frequency of 1 KHz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(1000, 20000000, 1)); /* Delay for 6 ms */ _delay_ms(6); /* Load the CMP register with a frequency of 10 KHz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(10000, 20000000, 1)); /* Delay for 600 us */ _delay_us(600); /* Load the CMP register with a frequency of 100 KHz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(100000, 20000000, 1)); /* Delay for 60 us */ _delay_us(60); /* Stop the TCF */ TCF0_Stop(); /* Load the CMP register with a frequency of 10 Hz */ TCF0_CompareSet(TCF0_NCOFD_HZ_TO_INCREMENT(10, 20000000, 1)); }
- The main.c file looks like
this:
int main(void) { SYSTEM_Initialize(); while(1) { NCO_Fixed_DutyCycle_Demo(); _delay_ms(1000); } } }
- Flash the project.
Result 1: two identical signals are generated with a frequency of 10 Hz and duty cycle of 50%.
Result 2: two identical signals are generated with a frequency of 100 Hz and duty cycle of 50%.
Result 3: two identical signals are generated with a frequency of 1 kHz and duty cycle of 50%.
Result 5: two identical signals are generated with a frequency of 100 kHz and duty cycle of 50%.