I have been struggling, really struggling with the task of creating a "4 transistor control with Arduino due" for a few months now. I have made a few prototypes, but when I put the oscilloscope on the outputs, something it's not ok.
Exactly for this code below is that: the output frequency is 13,54 kHz in real, but i need around 200 kHz.
At the begging in i have used (simple) Arduino Uno and i have achieved 16.5 kHz, and with Arduino due i have achieved only 13,54 khz.
Also when i put 10 khz, the real frequency on the oscilloscope is 10 Hz.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Инициализация на LCD дисплея с адрес 0x27 и размер 16x2
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int potPin = A0;
const int freqPins[] = {9, 10, 11, 7}; // Пинове за транзисторите
int currentPWM = 0;
int freq_kHz = 10; // Начална честота в kHz
unsigned long interval = 1000000 / (freq_kHz * 4); // Интервал между импулсите за всеки транзистор в микросекунди
unsigned long previousMicros = 0;
bool usePot = true;
int dutyCycle = 50; // Запълване по подразбиране - 50%
void setup() {
Serial.begin(9600);
// Инициализация на LCD дисплея
lcd.init();
lcd.backlight(); // Включване на подсветката на LCD
delay(1000); // Кратко закъснение след инициализация
// Показване на тестово съобщение на LCD дисплея
lcd.setCursor(0, 0);
lcd.print("Initializing...");
delay(2000);
lcd.clear();
for (int i = 0; i < 4; i++) {
pinMode(freqPins[i], OUTPUT);
digitalWrite(freqPins[i], LOW);
}
Serial.println("Enter frequency in kHz to set frequency digitally.");
Serial.println("Enter 'P' to switch to potentiometer input.");
Serial.println("Enter 'D' followed by duty cycle in percent (0 - 100) to set duty cycle.");
updateDisplay();
}
void loop() {
if (Serial.available() > 0) {
String input = Serial.readStringUntil('\n');
input.trim();
Serial.print("Received input: ");
Serial.println(input);
if (input.equalsIgnoreCase("P")) {
usePot = true;
Serial.println("Switched to potentiometer input.");
} else if (input.startsWith("D")) {
int newDutyCycle = input.substring(1).toInt();
Serial.print("Converted duty cycle to int: ");
Serial.println(newDutyCycle);
if (newDutyCycle >= 0 && newDutyCycle <= 100) {
dutyCycle = newDutyCycle;
Serial.print("Duty cycle set to: ");
Serial.print(dutyCycle);
Serial.println("%");
updateDisplay();
} else {
Serial.println("Error: Duty cycle out of range (0 - 100%)");
}
} else {
int newFreq_kHz = input.toInt();
Serial.print("Converted input to int: ");
Serial.println(newFreq_kHz);
if (newFreq_kHz >= 1) {
freq_kHz = newFreq_kHz;
interval = 1000000 / (freq_kHz * 4); // Update interval calculation for higher frequencies
usePot = false;
Serial.print("Frequency set to: ");
Serial.print(freq_kHz);
Serial.println(" kHz");
updateDisplay();
} else {
Serial.println("Error: Frequency must be greater than 0 kHz");
}
}
}
if (usePot) {
int potValue = analogRead(potPin);
int newFreq_kHz = map(potValue, 0, 1023, 1, 1000); // Update potentiometer mapping to higher frequency range
if (newFreq_kHz != freq_kHz) {
freq_kHz = newFreq_kHz;
interval = 1000000 / (freq_kHz * 4); // Update interval calculation for higher frequencies
updateDisplay();
}
}
unsigned long currentMicros = micros();
if (currentMicros - previousMicros >= interval) {
previousMicros = currentMicros;
pwmCycle();
}
}
void pwmCycle() {
for (int i = 0; i < 4; i++) {
digitalWrite(freqPins[i], LOW);
}
unsigned long pulseWidth = (interval * dutyCycle) / 100;
digitalWrite(freqPins[currentPWM], HIGH);
delayMicroseconds(pulseWidth);
digitalWrite(freqPins[currentPWM], LOW);
currentPWM = (currentPWM + 1) % 4;
}
void updateDisplay() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Freq: ");
lcd.print(freq_kHz);
lcd.print(" kHz ");
lcd.setCursor(0, 1);
lcd.print("Duty: ");
lcd.print(dutyCycle);
lcd.print("% ");
}
So let's have a look at the hardware first. Can you post a schematic? Also, controlling transistors is probably not the ultimate goal of the setup; what are the transistors in turn switching/driving?
Having said that, I see you're trying to make a PWM output using digitalWrite and millis() timings. This is inefficient and unreliable (due to interrupts). It's a better idea to use counter/timers for this. There are plenty of examples out there; keep in mind that they are platform-specific. For the Arduino UNO, you'd be looking at the ATmega328P timers.
Trust me my project is working fine. I need support for the controling of the transistor with arduino. I have achieved some results, but i need more frequency.
What kind of duty cycle do you need? Especially if a fixed 50% is OK, you can go pretty high up in frequency - essentially to half the core clock speed. If you need a variable duty cycle, the maximum frequency you can output PWM with depends on the resolution you need.
OK, 50% duty cycle is a convenient starting point; makes things a lot easier. Start reading on timers; it's a little technical, but once you get the hang of it, the principles turn out to be not too difficult to wrap your head around. At that point it becomes a matter of setting up a test that outputs a single PWM signal to a single pin, and then working your way upwards in complexity.
Your approach of using micros() to do this kind of timing is a dead-end street. You'll need to use actual Timers to get this to work well, especially if you're going to be working at hundreds of kHz.
The key is that you don't need much resolution; 50% duty cycle is OK, which essentially is 1 bit resolution. This means you can go pretty fast.
For the Arduino DUE the principle will be similar; the registers will be different though since those are controller-specific.
The Arduino UNO is very well documented so my suggestion would be to start with that one so you can rely on the plenty of guides on the use of Timers. Then when you get it to work and understand the principles, see if you can recreate the success on the DUE.
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DueTimer.h>
// Инициализация на LCD дисплея с адрес 0x27 и размер 16x2
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int potPin = A0;
const int freqPins[] = {9, 10, 11, 7}; // Пинове за транзисторите
int currentPWM = 0;
int freq_kHz = 10; // Начална честота в kHz
int dutyCycle = 50; // Запълване по подразбиране - 50%
bool usePot = true;
void setup() {
Serial.begin(9600);
// Инициализация на LCD дисплея
lcd.init();
lcd.backlight(); // Включване на подсветката на LCD
// Показване на тестово съобщение на LCD дисплея
lcd.setCursor(0, 0);
lcd.print("Initializing...");
delay(2000);
lcd.clear();
for (int i = 0; i < 4; i++) {
pinMode(freqPins[i], OUTPUT);
digitalWrite(freqPins[i], LOW);
}
Serial.println("Enter frequency in kHz to set frequency digitally.");
Serial.println("Enter 'P' to switch to potentiometer input.");
Serial.println("Enter 'D' followed by duty cycle in percent (0 - 100) to set duty cycle.");
updateDisplay();
setupTimer();
}
void loop() {
if (Serial.available() > 0) {
String input = Serial.readStringUntil('\n');
input.trim(); // Премахва излишни интервали и нови редове
handleSerialInput(input);
}
if (usePot) {
int potValue = analogRead(potPin);
int newFreq_kHz = map(potValue, 0, 1023, 1, 1000); // Актуализация на честотата според потенциометъра
if (newFreq_kHz != freq_kHz) {
freq_kHz = newFreq_kHz;
updateDisplay();
setupTimer();
}
}
}
void handleSerialInput(String input) {
Serial.print("Input Received: ");
Serial.println(input); // Показва въведения вход
if (input.equalsIgnoreCase("P")) {
usePot = true;
Serial.println("Switched to potentiometer input.");
} else if (input.startsWith("D")) {
int newDutyCycle = input.substring(1).toInt();
Serial.print("Parsed Duty Cycle: ");
Serial.println(newDutyCycle);
if (newDutyCycle >= 0 && newDutyCycle <= 100) {
dutyCycle = newDutyCycle;
Serial.print("Duty cycle set to: ");
Serial.print(dutyCycle);
Serial.println("%");
updateDisplay();
setupTimer();
} else {
Serial.println("Error: Duty cycle out of range (0 - 100%)");
}
} else {
int newFreq_kHz = input.toInt();
Serial.print("Parsed Frequency: ");
Serial.println(newFreq_kHz);
if (newFreq_kHz >= 1) {
freq_kHz = newFreq_kHz;
usePot = false;
Serial.print("Frequency set to: ");
Serial.print(freq_kHz);
Serial.println(" kHz");
updateDisplay();
setupTimer();
} else {
Serial.println("Error: Frequency must be greater than 0 kHz");
}
}
}
void setupTimer() {
// Изчисляване на периода на базата на честотата
unsigned long period_us = 1000000 / (freq_kHz * 1000); // Преобразуване на kHz в Hz и след това в периода в микросекунди
// Изчисляване на продължителността на импулса на базата на duty cycle
unsigned long pulseWidth_us = (period_us * dutyCycle) / 100;
// Настройка на таймера за новата честота и duty cycle
Timer3.attachInterrupt(pwmCycle).start(period_us);
}
void pwmCycle() {
digitalWrite(freqPins[currentPWM], HIGH);
delayMicroseconds((1000000 / (freq_kHz * 1000)) * dutyCycle / 100);
digitalWrite(freqPins[currentPWM], LOW);
currentPWM = (currentPWM + 1) % 4;
}
void updateDisplay() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Freq: ");
lcd.print(freq_kHz);
lcd.print(" kHz ");
lcd.setCursor(0, 1);
lcd.print("Duty: ");
lcd.print(dutyCycle);
lcd.print("% ");
}
This is my code with real 35 khz and thanks!
Best Regards,
Thanks. As said before, you need to use the Timer registers to get higher frequencies. Using delayMicroseconds() to crate delays won't work. Read the links I posted in #19.
I think your best approach would be to use a Timer in CTC mode.
Section 17.7.2 for Timer2 on the ATMega328P (e.g. Arduino UNO classic, Nano classic).
Frequency is determined as follows:
So for 200kHz and a Nano/UNO running at default 16MHz and a prescaler of 8, you would have:
OCRnx = fclk/(fo * 2 * N) + 1 = 16000000/(200000 * 2 * 8) + 1 = 6
So for Timer2 you need to:
Set the Timer2 to CTC mode
Set one of its associated pins (e.g. D11) to output mode
Set the timer prescaler to 8
Set the compare register OCR2A to 6
The code should look something like this for Timer2 outputting a 50% duty cycle waveform on Arduino pin 11 (=GPIO PB3):
void setup() {
TCCR2A = (1<<COM2A0) + (1<<WGM21); // Toggle output on compare match; run in CTC mode
TCCR2B = (1<<CS21); // prescaler = 8;
OCR2A = 6;
pinMode(11, OUTPUT); //Set pin as OUTPUT; Arduino UNO pin 11 = GPIO PB3 on the Mega 328P, which is associated with Timer 2
}
void loop() {
//No code needed to toggle the output pin; the timer runs as a hardware peripheral in the background
}
I've NOT tested the code above, but you could try and run it on an Arduino UNO or Nano, hook up a scope to pin 11 and see what you get.