How do I change the PWM frequency? I tried the PWM library without success
Arduino has never managed PWM frequency change and does not manage it anymore with the Every.
Registry of 4809 are different from those of the 328p or 2560 you have to wait until the authors of PWM libraries meet their libraries up to date.
You can also read the 4809 datasheet to modify yourself the microcontroller registry value.
Rick_Shoop:
How do I change the PWM frequency? I tried the PWM library without success
I´ve upgraded the "old" Nano with 328p to the nano Every board and have the same problem like you. I tried to increase the PWM-Frequency but without success so far.
At the moment I´am struggling with the Datasheet so i tried to find help here :-). I will let you know when I succeed.
I read a lot of application-notes yesterday. I hope this Timer-Config will help you. PWM-Freq. is 62.5kHz.
// Set PB0 as output (waveform output, pg. 204)
PORTB.DIRSET = PIN0_bm;
// Frequency: Fpwm_ss = Fclk_per/(N(PER+1))
// Max resolution: Rpwm_ss = (log(PER+1))/(log(2))
TCA0.SINGLE.PER = 256;
// CMP sets the duty cycle of the PWM signal -> CT = CMP0 / PER
// DUTY CYCLE is approximately 50% when CMP0 is PER / 2
TCA0.SINGLE.CMP0 = 200;
// Counter starts at 0
TCA0.SINGLE.CNT = 0x00;
// Configuring CTRLB register
// Compare 0 Enabled: Output WO0 (PB0) is enabled
// Single slope PWM mode is selected
TCA0.SINGLE.CTRLB = TCA_SINGLE_CMP0EN_bm | TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
// Using system clock (no frequency division, the timer clock frequency is Fclk_per)
// Enable the timer peripheral
TCA0.SINGLE.CTRLA = TCA_SINGLE_CLKSEL_DIV1_gc | TCA_SINGLE_ENABLE_bm;
I found the better solution for Arduino. I think Arduino needs TCA for the system-control. If TCA is manipulated, the internal functions like millis() etc. wont work correctly.
Here is the better solution which use TCB:
pinMode(3, OUTPUT); //Port F, Pin 5 = Arduino ~D3
/* set the alternate pin mux */
PORTMUX.TCBROUTEA |= PORTMUX_TCB1_bm;
TCB1.CCMPL = 255; /* PWM Period*/
TCB1.CCMPH = 128; /* PWM Compare*/
TCB1.CTRLB = 0 << TCB_ASYNC_bp /* Asynchronous Enable: disabled */
| 1 << TCB_CCMPEN_bp /* Pin Output Enable: enabled */
| 0 << TCB_CCMPINIT_bp /* Pin Initial State: disabled */
| TCB_CNTMODE_PWM8_gc; /* 8-bit PWM */
// TCB1.DBGCTRL = 0 << TCB_DBGRUN_bp; /* Debug Run: disabled */
// TCB1.EVCTRL = 0 << TCB_CAPTEI_bp /* Event Input Enable: disabled */
// | 0 << TCB_EDGE_bp /* Event Edge: disabled */
// | 0 << TCB_FILTER_bp; /* Input Capture Noise Cancellation Filter: disabled */
// TCB1.INTCTRL = 0 << TCB_CAPT_bp; /* Setting: disabled */
TCB1.CTRLA = TCB_CLKSEL_CLKDIV1_gc /* CLK_PER (No Prescaling) */
| 1 << TCB_ENABLE_bp /* Enable: enabled */
| 0 << TCB_RUNSTDBY_bp /* Run Standby: disabled */
| 0 << TCB_SYNCUPD_bp; /* Synchronize Update: disabled */
TCB1.CTRLA |= TCB_ENABLE_bm;
This code will provide a 62,5 kHz PWM with 50% DC (TCB1.CCMPH = 128;) on Pin D3 (which is PortF Pin 5). Further informations can be found in the datasheet 20.3.3.1.8 8-Bit PWM Mode and Atmel Start.
Thanks for posting this. I am looking for a PWM solution driving two switch controllers in parallel. They can run on the same clock (31.25 kHZ or 62.5 kHz will be fine), but I want to be able to set different PWM percentages for the two channels. As there are three compare units for each of the two base counters, I believe this should be possible.
I have read the 4809 data sheets and believe I know roughly what I should do. Port F pin 4 should be ~D6, right?
How should I set up this second channel to have a PWM of e.g. 25% while port F pin 5 is at 50%?
Where do I find the definitions of TCB1. CCMPH etc? Is there a header file where they are defined? Sorry if this is a stupid question.
olivertrepte:
Thanks for posting this. I am looking for a PWM solution driving two switch controllers in parallel. They can run on the same clock (31.25 kHZ or 62.5 kHz will be fine), but I want to be able to set different PWM percentages for the two channels. As there are three compare units for each of the two base counters, I believe this should be possible.I have read the 4809 data sheets and believe I know roughly what I should do. Port F pin 4 should be ~D6, right?
How should I set up this second channel to have a PWM of e.g. 25% while port F pin 5 is at 50%?
I've been playing with a Nano Every for the past couple of days as well, trying to move some code from a Nano (328P) to an Every. Although my goal was to make periodic interrupts work, I've also been monitoring the PWM outputs of pins 3, 5, 6, 9, and 10 with a scope so the following may be useful to you.
PWM on pins 5, 9, and 10 is controlled by compare channels CMP2, CMP0, and CMP1 of TCA0, recpectively. The clock prescaler for TCA0 is set to 64 by default. It is true that if you change this, you "break" millis() and delay(), but you break them in a controlled fashion. The length of a second, as measured by millis() and delay(), is inversely proportional to the prescaler setting. So if you change the prescaler setting from 64 to 256, one second becomes 4 seconds. Knowing that, you could compensate for it in your sketch. It's not elegant, but it works.
The following program runs pins 5 and 9 at 25% and 50% duty cycle, respectively, while counting seconds. Every 10 seconds it switches the PWM frequency from 976 Hz to 31.25 kHz and back while continuing to count seconds using millis(). The duty cycles do not change. At the faster PWM rate, one "second" is 32000 millis().
void setup() {
Serial.begin(115200);
pinMode(5, OUTPUT);
pinMode(9, OUTPUT);
analogWrite(5, 64); // 25% duty cycle
analogWrite(9, 128); // 50% duty cycle
}
bool prescaler = false; // false - 64 (default), true - 2 (fast)
// Going from 64 to 2 is an increase in
// tick speed of 32x, so at the fast speed
// one second takes 32000 "milliseconds".
bool prescaler_toggled = true; // Start with true so that the prescaler
// is not switched immediately at second 0.
int second_count = 0;
unsigned long t = millis();
void loop() {
if (millis() - t == (prescaler ? 32000 : 1000)) {
second_count++;
t = millis();
Serial.print("Seconds: ");
Serial.println(second_count);
}
if ((second_count % 10) == 0) {
if (!prescaler_toggled) { // Toggle prescaler every 10 seconds.
prescaler = !prescaler;
prescaler_toggled = true;
if (prescaler) { // Switch prescaler to 2.
TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_ENABLE_bm;
// Turn off timer while we change parameters.
TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_CLKSEL_gm;
// Clear all CLKSEL bits.
TCA0.SINGLE.CTRLA |= TCA_SINGLE_CLKSEL_DIV2_gc;
// Set prescaler to 2.
TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm;
// Re-enable timer. Pins 5 and 9 now run at
// 31.25 kHz.
} else { // Switch prescaler back to 64.
TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_ENABLE_bm;
// Turn off timer while we change parameters.
TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_CLKSEL_gm;
// Clear all CLKSEL bits.
TCA0.SINGLE.CTRLA |= TCA_SINGLE_CLKSEL_DIV64_gc;
// Set prescaler to 64.
TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm;
// Re-enable timer. Pins 5 and 9 now run
// at 976 Hz.
}
Serial.print("PWM frequency: ");
Serial.println(prescaler ? "31.25 kHz" : "976 Hz");
} else {
// Do nothing if already toggled for current second.
}
} else {
prescaler_toggled = false;
}
}
If you intend to use pins 3 or 6 (TCB1 and TCB0), be aware that these two counters take their clock from TCA0 by default (TCBn.CTRLA register CLKTCA configuration). You can switch that to either use the system clock or the system clock divided by 2: there doesn't seem to be an option to use dividers of 1024, 256, 64 etc like for TCA0.
Also, while I know that changing the TCA0 prescaler breaks millis() and delay(), I don't know what else it might break. Use at your own risk!
The above simplified, using delay():
void setup() {
Serial.begin(115200);
pinMode(5, OUTPUT);
pinMode(9, OUTPUT);
analogWrite(5, 64); // 25% duty cycle
analogWrite(9, 128); // 50% duty cycle
}
bool prescaler = true ; // false - 64 (default), true - 2 (fast)
// Going from 64 to 2 is an increase in
// tick speed of 32x, so at the fast speed
// one second takes "32000" milliseconds.
// Initialised to true because of how the
// first if block below is laid out. We
// actually start with the default (64)
// prescaler.
int second_count = 0;
unsigned long t;
void loop() {
t = millis();
if ((second_count % 10) == 0) {
prescaler = !prescaler;
Serial.print("PWM frequency: ");
Serial.println(prescaler ? "31.25 kHz" : "976.6 Hz");
if (prescaler) { // Switch prescaler to 2.
TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_ENABLE_bm;
// Turn off timer while we change parameters.
TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_CLKSEL_gm;
// Clear all CLKSEL bits.
TCA0.SINGLE.CTRLA |= TCA_SINGLE_CLKSEL_DIV2_gc;
// Set prescaler to 2.
TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm;
// Re-enable timer. Pins 5 and 9 now run at
// 31.25 kHz.
} else { // Switch prescaler back to 64.
TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_ENABLE_bm;
// Turn off timer while we change parameters.
TCA0.SINGLE.CTRLA &= ~TCA_SINGLE_CLKSEL_gm;
// Clear all CLKSEL bits.
TCA0.SINGLE.CTRLA |= TCA_SINGLE_CLKSEL_DIV64_gc;
// Set prescaler to 64.
TCA0.SINGLE.CTRLA |= TCA_SINGLE_ENABLE_bm;
// Re-enable timer. Pins 5 and 9 now run
// at 976.6 Hz.
}
}
Serial.print("Seconds: ");
Serial.println(second_count);
second_count++;
delay(prescaler ? 32000 : 1000);
}
If you install MegaCoreX you can use the built-in analogWriteFrequency() function to set the PWM frequency.
Hello. Does anybody know how to / experience doing variable PWM and Duty Cycle?
Dear Mohdrais,
What would you like to do with the PWM functionality ?
The following pins of the every support PWM:
pin 3 (by timer B1)
pin 5 (by timer A0)
pin 6 (by timer B0)
pin 9 (by timer A0)
pin 10 (by timer A0)
The default arduino platform configures these timers for PWM on these pins.
If you uses analogWrite(pin,value) the PWM generation is started with the duty cycle specified by 'value': between 0 and 255. E.G. writing a 128 will give a 50% duty cycle, a value of 64 will give 25% duty cycle.
WR,
Kees
TQVM Mr Kees-van-der-Oord for the information. I would like to make a vibration plate by using vibration of electromagnet. By controlling the duty cycle and frequency of PWM, maybe I can get an optimum separation of meterials
Ben84:
I found the better solution for Arduino. I think Arduino needs TCA for the system-control. If TCA is manipulated, the internal functions like millis() etc. wont work correctly.
Here is the better solution which use TCB:
So am I.
Have you check the PWM frequency on D3(PF05)?
Maybe my USBee(logic analyzer) did not work well,
The output is absolutlly chaos.
I want to get 40KHZ PWM on D3, my set for CCMP is:
(CPU: 20M, clock for TCB1: 20/6M)
CCMPL : 83
CCMPH: 40 (50% duty cycle)
But,the output keeps at nearly 1K hz, no matter how I adjust the values of CCMP.
I hope it is because of my measure device.
I have bought new oscilloscope ,waiting for it.
Dear Xiaomc, Mohdrais,
it would be useful if you would show the codes of your attempts.
We are in a similar situation: I also don't have a oscilloscope to monitor the output ...
On the Atmega4809 data sheet:
you can find the commands to set the TCA in PWM mode.
On startup, the arduino frameworks configures the TCA in the source file:
%LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5\variants\nona4809\variant.c
with this configuration:
// PORTMUX setting for TCA -> all outputs [0:2] point to PORTB pins [0:2]
PORTMUX.TCAROUTEA = PORTMUX_TCA0_PORTB_gc;
// Setup timers for single slope PWM, but do not enable, will do in analogWrite()
TCA0.SINGLE.CTRLB = TCA_SINGLE_WGMODE_SINGLESLOPE_gc;
// Period setting, 16 bit register but val resolution is 8 bit
TCA0.SINGLE.PER = PWM_TIMER_PERIOD;
// Default duty 50%, will re-assign in analogWrite()
TCA0.SINGLE.CMP0BUF = PWM_TIMER_COMPARE;
TCA0.SINGLE.CMP1BUF = PWM_TIMER_COMPARE;
TCA0.SINGLE.CMP2BUF = PWM_TIMER_COMPARE;
// Use DIV64 prescaler (giving 250kHz clock), enable TCA timer
TCA0.SINGLE.CTRLA = (TCA_SINGLE_CLKSEL_DIV64_gc) | (TCA_SINGLE_ENABLE_bm);
in timers.h the constants are defined:
#define PWM_TIMER_PERIOD 0xFF // For frequency
#define PWM_TIMER_COMPARE 0x80 // For duty cycle
So the clock is 250kHz and the SINGLE.PER is set to 256.
This results in the PWM frequency of 250kHz / 256 ~= the 1kHz that you observe.
To get a higher frequency, I think you should select a different clock divider.
Can you try if this code makes the frequency 64x faster ?
TCA0.SINGLE.CTRLA = (TCA_SINGLE_CLKSEL_DIV1_gc) | (TCA_SINGLE_ENABLE_bm);
I expect the frequency will go up to ~ 63kHz:
clock will be 16Mzh, PER register is 256, => 16000/256 = 62.5 kHz.
You can tune the frequency with the PER register.
The arduino core initializes this with the value 255, but it is 16-bits register, so it can go up to 65536.
To get your 40kHz, the period must be 62.5/40 = 1.56x slower.
So I expect that if you write ((255 + 1) * 1.56) - 1 = 399 to this register, the PWM frequency is 40 kHz.
TCA0.SINGLE.PER = 399;
The duty cycle is controlled by the CMPnBUF register.
Which register to use depends on which pin you use:
pin 5 (D5): CMP2BUF
pin 8 (D8): CMP0BUF
pin 9 (D9): CMP1BUF
To get 50% duty cycle, write 399 * 0.5 = 200 to this register.
E.G. for pin 8:
TCA0.SINGLE.CMP0BUF = 200;
Next you have to enable the PWM output to the pin.
The value to enable the PWM also depends on the channel.
pin 5 (D5): 0x40
pin 8 (D8): 0x10
pin 9 (D9): 0x20
So for pin 8:
TCA0.SINGLE.CTRLB |= 0x10;
Finally, you have to set the pin to output mode:
E.G. for pin 8:
pinMode(8, OUTPUT);
I hope this helps,
WR,
Kees
Hi all,
I have an arduino Nano every, and I also wanted higher PWM speed. I also have a scope, and I can confirm:
TCA0.SINGLE.CTRLA = (TCA_SINGLE_CLKSEL_DIV1_gc) | (TCA_SINGLE_ENABLE_bm); -> Makes the clock 64 times faster (about 62.5kHz)
TCA0.SINGLE.PER = 399; -> Indeed increases resolution, lowering the speed to about 40khz
AnalogWrite still works after these calls for values <255. I have checked the analogWrite function on my PC, and it contains an 'if <255' statement. For values >255, the pin will be set to digital output with value 1. So if you want to set the value of the PWM, simply re-use the code from the analogWrite function for your pin to your own sketch. In my case (I use pin 9, should also work for pin 5 and pin 8 ) this code is:
pinMode(9, OUTPUT);
uint8_t bit_pos = digitalPinToBitPosition(9);
/* Calculate correct compare buffer register /
uint16_t timer_cmp_out = ((uint16_t*) (&TCA0.SINGLE.CMP0BUF)) + bit_pos;
/* Configure duty cycle for correct compare channel */
(timer_cmp_out) = (1000);
/ Enable output on pin */
TCA0.SINGLE.CTRLB |= (1 << (TCA_SINGLE_CMP0EN_bp + bit_pos));
Using the simple code of Kees above does not work because bit numbers for the digital outputs are not correct (D8 is not a PWM output at all on arduino nano every). See also below in this message for the correct numbers.
Only remark is that the system timer also scales up 64 times, so if you use the delay function,you should also give 64 times larger numbers.
edit:
I did some more experiments, and these are the results; PWM ports with their corresponding timers and their bit position:
Pin | Bitpos | Timer
pin D5 | 2 | A0
pin D9 | 0 | A0
pin D10| 1 | A0
pin D3 | 5 | B1
pin D6 | 4 | B0
So the (tested and confirmed) code to set the duty cycle for pin 9 is:
pinMode(9, OUTPUT) // -> Only required on setup or if you did a digital read or write in between
TCA0.SINGLE.CMP0BUF = val;
TCA0.SINGLE.CTRLB |= 0x10;
For the other pins I am not able to test due to the hardware connected. But it should be at least for pin 5 and 10:
pinMode(5, OUTPUT) // -> Only required on setup or if you did a digital read or write in between
TCA0.SINGLE.CMP2BUF = val;
TCA0.SINGLE.CTRLB |= 0x40;
pinMode(10, OUTPUT) // -> Only required on setup or if you did a digital read or write in between
TCA0.SINGLE.CMP1BUF = val;
TCA0.SINGLE.CTRLB |= 0x20;