Arduino generating two fast phase offset signals
So recently I wanted to generate two fast (40kHz), phase offset signals with Arduino. I have done various modes of PWM and PPM modulation before, but have not yet tried to generate phase offset signals. Actually there is only one timer mode on arduino that allows you to set the frequency and phase offset of a signal. The duty cycle of output signals is 50% (you can’t generate phase offset PWM signals).
For slower signals you may get away with using interrupts and software generation of output waveform, however at this speed and resolution, hardware is the only option that will generate consistent results.
Timer CTC mode is used for signal generation. The timer uses ICR1 as the top value and OCR1A/OCR1B for signal generation. On each compare match the output is toggled, which means that the actual output frequency will be half of that of the timer overflow.
For 40kHz signal generation we can use the 16MHz clock frequency directly to clock the timer and set ICR1 to 199. This way timer will take 200 ticks to overflow and will overflow at 80kHz. The output frequency will be 40kHz. With warying OCR1A versus OCR1B value we can set the phase difference of two output signals. Both registers can have a value between 0 and 199.
The setup for generation of two signals with fixed phase offset is pretty simple. We just load the timer settings and let the hardware do its thing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// code by dejko1, rei-labs.net void setup() { PORTB=0x00; DDRB=0x06; ICR1=199; OCR1A=100; OCR1B=50; TCNT1=0; TCCR1A=0x50; TCCR1B=0x19; } void loop() { } |
If you load the code to your arduino you can observe the two generated signals at digital pins 9 and 10.
However if you want to change the phase offset of the two generated signals dynamically it gets slightly more complicated than that. As the OCR1A and OCR1B registers are not buffered in CTC mode, directly writing to them while the timer is running may generate a compare miss or multiple compare matches, both of which will result in one of the signals being turned around (180 degrees phase jump).
If you have oscilloscope you can try running the following example just to show how it misbehaves.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// code by dejko1, rei-labs.net void setup() { PORTB=0x00; DDRB=0x06; ICR1=199; OCR1A=0; OCR1B=100; TCNT1=0; TCCR1A=0x50; TCCR1B=0x19; OCR1B=100; } void loop() { delay(10); OCR1B = OCR1B + 1; if(OCR1B > 199) OCR1B=0; } |
To avoid missing a compare match you can stop the timer while the OCR register is being updated, however that is not all. When the timer is stopped it can be in any state. TCNT can be any value and there may be 0, 1 or 2 compare matches (output pin states depend on this) that already happened during the current timer run. To “reset” the state of timer counter the TCNT value is reset to zero.
The output pin states can be read by PIN variable, however they can not be changed using a PORT variable. When using timer / pwm output the PORT register is disconnected from the pin and internal register of waveform generator is connected to it. There is no way to directly input the value into waveform generator registers, but you can use force compare match bits in TCCR1C register to change the outputs.
Also after TCNT value is updated, the next timer tick will be blocked from generating a compare match. Therefore to get correct behaviour if one of the OCR registers is set to 0, you have to use the force compare match bits once again.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// code by dejko1, rei-labs.net void setup() { PORTB=0x00; DDRB=0x06; ICR1=199; OCR1A=100; OCR1B=100; TCNT1=0; TCCR1A=0x50; TCCR1B=0x19; } int j=0; int a; void loop() { delay(100); a = analogRead(0); j = (a/3); if(j>199) j=199; //update ocr registers with the value TCCR1B=0x18; if((PINB&0x02)!=00) TCCR1C=0x80; if((PINB&0x04)!=00) TCCR1C=0x40; TCNT1=0; OCR1B = j; if(j==0) TCCR1C=0x40; TCCR1B=0x19; } |
The code takes analog value (eg. potentiometer output connected to it) at analog 0 input on Arduino for the phase difference value.
This code generates quite nice signals with consistent phase offset. However updating the OCR registers like this often (or when not needed) should be avoided as the code may/will generate a glitch every time the timer update procedure is used.
A slightly better way of updating would be to leave the TCNT1 alone and just determine which TCCR1C bits have to be set for a practicular OCR value being written to timer.
An even better way would be to do some calculation and instead of modifying only one register modify both OCR1A and OCR1B. This way both registers could have a value in range 0-100, which would leave 100 clock ticks free of any compare match events. After the last of the two compare match events happens we get a known state – both events already evaluated. The event interrupt could be used to change both OCR registers for the next timer run.
The phase offset between the two signals can be varied from 0-180 degrees, for varying it full 0-360 degrees you would have to split the input into 0-180 degrees and 180-360 degrees. Additional fixed value 180 phase shift can be achieved by simply turning one of the signals around (using TCCR1C on timer init).
PS: 0-360 degree shift could also be obtained by modifying both OCR1A and OCR1B.
I follow how to change the output frequency with the above example. I am wondering how to control the pulse width of the signal once I have the frequency I need.
The above code will only work to generate a 50% duty cycle signal. To control pulse width of the signal you could use any of the PWM modes offered by the microcontroller. In PWM modes however it is not possible to control the phase offset of the signals – you only have a few options – rising / falling edge aligned and center aligned.
i have three pwm signals in 180 conduction mode and i have to apply it on three phase inverter for variable frequency so i need six signals kindly tell me how i make it six by changing in code because gate driver ir 2104 is working si i have to change my code and i need your guidance
This code is no good for 3ph inverter. In arduino/atmega each timer only has 2 pwm chanels. You should synchronise two timers to get up to 4 pwm chanels running synchronously. If no dead time is required you can get away by using only 3 pwm outputs (some arduino powered gimbal controllers do it this way). As atmega8 and similiar chips have no dedicated dead time generator you can synchronize all three timers to get 6 pwm signals. You can check the following document for driving sensored motors http://www.atmel.com/images/doc8010.pdf Note that for sensorless application you will need to adapt the code. You can also check the following video and documents for sensorless aplication: https://www.youtube.com/watch?v=fHoEmjUX8_8
how can I change duty cycle with this code, I need 60 per on and 40 per off
Unfortunately this code is only useful for 50% duty cycle. To get varying duty cycle you should use a simple pwm library (or multiple synchronized timers if you need to achieve phase shifting).