Pro Micro Microcontroller PWM

  • Topic Is Sticky
  • 1.7K Views
  • Last Post 16 April 2023
Chris posted this 28 June 2020

My Friends,

Many years now I have been super interested in PWM. Years ago I wrote a PWM project for Arduino, it was terrible, but it was a good start. Since then, I have written many more...

Today I want to share something of a bit more value than my first project, A PWM Project that has a lot more value:

 

The Code:

////////////////////////////////////////////////////////////////////////////////////////////////////
// PWM Software written by Chris Sykes.                                                           //
// NOTE: 16 bit Timers currently used. 32 Bit Timers need all shorts changed to longs!            //
// Software written and shared as is, no warrantys are given. Use at your own risk.               //
////////////////////////////////////////////////////////////////////////////////////////////////////
// Your MCU Clock Frequency: 16000000 or 16e6, retrieved by F_CPU.                                //
long fclock = F_CPU;


////////////////////////////////////////////////////////////////////////////////////////////////////
// Most MCU's have:                                                                               //
//   0,                                                                                           //
//   8,                                                                                           //
//   64,                                                                                          //
//   256,                                                                                         //
//   1024                                                                                         //
// as Prescaler Values:                                                                           //
short Prescaler = 1024;


////////////////////////////////////////////////////////////////////////////////////////////////////
// The MCU Setup Method:                                                                          //
void setup() {//////////////////////////////////////////////////////////////////////////////////////

  // Init Serial:
  Serial.begin(115200);

  // Confgure Timer:
  ConfgureTimer(1.275, 21.525);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Configure Timer:                                                                               //
void ConfgureTimer(double frequency, double dutycycle){/////////////////////////////////////////////

  // The counter simply overruns when it passes its maximum 16-bit value
  // (MAX = 0xFFFF (65535)) and then restarts from the BOTTOM (0x0000).

  // Timer One PWM Pin:
  // Check your Datasheet!
  pinMode(10, OUTPUT);

  // Reset Timer One:
  TCCR1A = 0;
  TCCR1B = 0;

  // ComA and B need to be set for Compare Match Pins:
  TCCR1A |= (1 << COM1A1) | (1 << COM1B1);

  // Configure Prescaler (CSX):
  SetPrescaler(Prescaler);

  // Set PWM Mode:
  SetPWMMode();

  // Set Frequency:
  OCR1A = CalculateFrequency(frequency);

  // Set Duty Cycle:
  OCR1B = CalculateDutyCycle(OCR1A, dutycycle);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Set Prescaler:                                                                                 //
void SetPrescaler(short prescaler){/////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  // See your Datasheet! See: Clock Select Bit Description                                                 //
  //-------------------------------------------------------------------------------------------------------//
  // CS02     CS01     CS00     Description                                                                //
  //  0        0        0        No clock source (Timer/Counter stopped)                                   //
  //  0        0        1        clkI/O/(No prescaling)                                                    //
  //  0        1        0        clkI/O/8 (From prescaler)                                                 //
  //  0        1        1        clkI/O/64 (From prescaler)                                                //
  //  1        0        0        clkI/O/256 (From prescaler)                                               //
  //  1        0        1        clkI/O/1024 (From prescaler)                                              //
  //  1        1        0        External clock source on T0 pin. Clock on falling edge.                   //
  //  1        1        1        External clock source on T0 pin. Clock on rising edge.                    //
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////

  // Configure Prescaler (CSX):
  switch (prescaler) {
    case 0:
      //  Set 0: clkI/O/(No prescaling)
      TCCR1B |= (0 << CS12) | (0 << CS11) | (1 << CS10);
      break;
    case 8:
      // Set 8: clkI/O/8 (From prescaler)
      TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
      break;
    case 64:
      // Set 64: clkI/O/64 (From prescaler)
      TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10);
      break;
    case 256:
      // Set 256: clkI/O/256 (From prescaler)
      TCCR1B |= (1 << CS12) | (0 << CS11) | (0 << CS10);
      break;
    case 1024:
      // Set 1024: clkI/O/1024 (From prescaler)
      TCCR1B |= (1 << CS12) | (0 << CS11) | (1 << CS10);
      break;
    default:
      // Set Off: No clock source (Timer/Counter stopped)
      TCCR1B |= (0 << CS12) | (0 << CS11) | (0 << CS10);
      break;
  }
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Set PWM Mode:                                                                                  //
void SetPWMMode(){//////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // See your Datasheet!                                                                                            //
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // Waveform Generation Mode Bit Description                                                                       //
  //----------------------------------------------------------------------------------------------------------------//
  //                                     Timer/Counter Mode of                    Update of       TOV Flag (1)(2)   //
  // Mode    WGM3   WGM2   WGM1   WGM0   Operation                     TOP        OCRx at         Set on            //
  //  0      0      0      0      0      Normal                        0xFFFF     Immediate       MAX               //
  //  1      0      0      0      1      PWM, Phase Correct, 8-bit     0x00FF     TOP             BOTTOM            //
  //  2      0      0      1      0      PWM, Phase Correct, 9-bit     0x01FF     TOP             BOTTOM            //
  //  3      0      0      1      1      PWM, Phase Correct, 10-bit    0x03FF     TOP             BOTTOM            //
  //  4      0      1      0      0      CTC                           OCRnA      Immediate       MAX               //
  //  5      0      1      0      1      Fast PWM, 8-bit               0x00FF     TOP             TOP               //
  //  6      0      1      1      0      Fast PWM, 9-bit               0x01FF     TOP             TOP               //
  //  7      0      1      1      1      Fast PWM, 10-bit              0x03FF     TOP             TOP               //
  //  8      1      0      0      0      PWM, PnFC                     ICRn       BOTTOM          BOTTOM            //
  //  9      1      0      0      1      PWM, PnFC                     OCRnA      BOTTOM          BOTTOM            //
  //  10     1      0      1      0      PWM, Phase Correct            ICRn       TOP             BOTTOM            //
  //  11     1      0      1      1      PWM, Phase Correct            OCRnA      TOP             BOTTOM            //
  //  12     1      1      0      0      CTC                           ICRn       Immediate       MAX               //
  //  13     1      1      0      1      (Reserved)                    –          –               –                 //
  //  14     1      1      1      0      Fast PWM                      ICRn       TOP             TOP               //
  //  15     1      1      1      1      Fast PWM                      OCRnA      TOP             TOP               //
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // NOTE: PnFC = Phase and Frequency Correct.

  // Fast PWM needs bits (WGM10 & WGM12) set to One:
  TCCR1A |= (1 << WGM10) | (1 << WGM11);

  // Configure Fast PWM Mode (WGM12 and 13):
  TCCR1B |= (1 << WGM12) | (1 << WGM13);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// The MCU Loop Method:                                                                           //
void loop() {///////////////////////////////////////////////////////////////////////////////////////

  // Print Output:
  PrintOutput();

  // Delay:
  delay(3000);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Calculate the Frequency:                                                                       //
short CalculateFrequency(double frequency){/////////////////////////////////////////////////////////

  // Calculate OCR1A from specified Frequency:
  return (short)(fclock / (frequency * Prescaler)) - 1L;
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Calculate the DutyCycle:                                                                       //
short CalculateDutyCycle(int period, double duty){//////////////////////////////////////////////////

  // Calculate OCR1B from OCR1A and specified Duty Cycle:
  return (short)(period * (duty / 100L));
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Calculate Register Resolution:                                                                 //
double CalculateResolution(short ocrx){////////////////////////////////////////////////////////////

  return (log((double)ocrx + 1.0D)/log(2.0D));
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Print Output Data:                                                                             //
void PrintOutput(){/////////////////////////////////////////////////////////////////////////////////

  // Shorts, Longs and other variable's dont mix well, convert to Double:
  double Frequency = ((double)fclock / (((double)OCR1A + 1.0) * (double)Prescaler));
  short Period = (short)((double)fclock / (Frequency * (double)Prescaler)) - 1.0;

  // Display Output:
  Serial.print("Frequency: ");
  Serial.print(Frequency);
  Serial.print("Hz. Resolution: ");
  Serial.print(CalculateResolution(OCR1A));
  Serial.print(" bits. Period: ");
  Serial.print(Period);
  Serial.print("==");
  Serial.print(OCR1A);
  Serial.println(" comboblets");  
}

 

Of course this is just the start, to get you started! You can use many Arduino Microcontrollers with this, from the Arduino Mega, Pin 12, to the Arduino Pro Micro, Pin 10, currently testing the Code on the Pro Micro:

 

 

Of course, the Mega has different Pin Outs. This Code is very adaptable! Please post here if you need help changing the Code to suit your Microcontroller.

PWM can be used to drive any Mosfet, Transistor, SCR, Triac, Relay and many more Switching Options! For about $6.00 one of these can be purchased: MOSFET High Power

 

Of course, some sort of interface to update your Microsontroller can be used, USB Serial, WiFi or anything like this can be used to change the Frequency and Duty Cycle, but more Code will be required.

For less than $10.00 this is a great option to get started!

@Zanzal and other Dev's, please feel free to add Code or suggestions to improve the Code.

Best wishes, stay safe and well My Friends,

   Chris

Order By: Standard | Newest | Votes
Chris posted this 28 June 2020

My Friends,

The above example is a super cheap and fun example of getting PWM Working for your projects!

I want to share an example, Serial Communications, that you can look at to get your Frequency and Duty Cycle updated from your Computer. The Arduino Page gives a good starting example: https://www.arduino.cc/en/tutorial/SoftwareSerialExample

/*
  Software serial multple serial test

 Receives from the hardware serial, sends to software serial.
 Receives from software serial, sends to hardware serial.

 The circuit:
 * RX is digital pin 2 (connect to TX of other device)
 * TX is digital pin 3 (connect to RX of other device)

 Note:
 Not all pins on the Mega and Mega 2560 support change interrupts,
 so only the following can be used for RX:
 10, 11, 12, 13, 50, 51, 52, 53, 62, 63, 64, 65, 66, 67, 68, 69

 Not all pins on the Leonardo support change interrupts,
 so only the following can be used for RX:
 8, 9, 10, 11, 14 (MISO), 15 (SCK), 16 (MOSI).

 created back in the mists of time
 modified 25 May 2012
 by Tom Igoe
 based on Mikal Hart's example

 This example code is in the public domain.

 */
#include <SoftwareSerial.h>

SoftwareSerial mySerial(2, 3); // RX, TX

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Native USB only
  }


  Serial.println("Goodnight moon!");

  // set the data rate for the SoftwareSerial port
  mySerial.begin(38400);
  mySerial.println("Hello, world?");
}

void loop() // run over and over
{
  if (mySerial.available())
    Serial.write(mySerial.read());
  if (Serial.available())
    mySerial.write(Serial.read());
}

 

Of course, you will need a Client and Server sort of setup, Software listening to commands on your MCU, the Server, and software issuing Commands, Client, on your computer.

Devs, anyone willing to put your info in to help come up with a solution?

C# is one of my preferred Languages, although I know a few, not as well as C# however, here is some C# Code for Serial Communication:

using System.IO.Ports;

/// <summary>
/// Configure the Baud Rate and Setup in the ComboBox...
/// </summary>
private void BaudRateComboBoxConfig()
{
    // Clear all existing Items...
    this.baudRateComboBox.Items.Clear();

    // Add an empty line...
   this.baudRateComboBox.Items.Add("");

            /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Baud Rates Available....
    this.baudRateComboBox.Items.AddRange(BaudRates);
        }



/// <summary>
/// Get the selected Baud Rate...
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BaudRateComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
    if (this.baudRateComboBox.Text != "")
    {
                /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Sets Baudrate....
    this.serialPort.BaudRate = Convert.ToInt32(this.baudRateComboBox.Text);
    }

    // Clear all existing Items...
    this.portNameComboBox.Items.Clear();

    // Add an empty line...
    this.portNameComboBox.Items.Add("");

            /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Gets Availabe COM ports...
    foreach (string str in SerialPort.GetPortNames())
    {
        SerialPort tmp = new SerialPort(str);
        if (tmp.IsOpen == false)
            this.portNameComboBox.Items.Add(str);
    }

    if (this.portNameComboBox.Items.Count > 0)
    {
                /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Set the Port ComboBox to Enabled=true now a Baudrate is selected....
    this.portNameComboBox.Enabled = true;
    }
    else
    {
                /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Display Error Message - No Device Found....
    if (this.notificationLabel.Text == noDeviceFoundMessage)
    {
        String message = noDeviceFoundMessage;
        String caption = "Error - No Device Found...";
        MessageBoxButtons buttons = MessageBoxButtons.OK;
        MessageBoxIcon icon = MessageBoxIcon.Error;
        MessageBox.Show(message, caption, buttons, icon);
    }

                /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Display Error Message - No Device Found....
    this.notificationLabel.Text = noDeviceFoundMessage;

                /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Clear Baud Rates....
    this.baudRateComboBox.Items.Clear();

                /////////////////////////////////////////////////////////////////////////////////////////////////////////
    // Baud Rates Available....
    this.BaudRateComboBoxConfig();

    return;
    }
}



/// <summary>
/// Connect/Disconnect...
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ConnectButton_Click(object sender, EventArgs e)
{


    #region this.connectButton.Text == "Disconnect"..


    if (portNameComboBox.Text.Length >= 3 && this.connectButton.Text == "Disconnect")
    {

        if (this.serialPort.IsOpen)
        {

        try
        {

            this.serialPort.Close();

            this.portNameComboBox.Enabled = true;
            this.baudRateComboBox.Enabled = true;

            this.baudRateComboBox.SelectedIndex = this.BaudRate;
            this.portNameComboBox.SelectedIndex = this.COMPort;

            // Set the Connect Button Text to Disconenct...
            this.connectButton.Text = "Connect";
        }
        catch (Exception exc)
        {

            MessageBox.Show("Error: " + exc.Message);
        }
        }

            // Return and dont go any further...
            return;
    }


        #endregion


        #region this.connectButton.Text == "Connect"...


        if (portNameComboBox.Text.Length >= 3 && this.connectButton.Text == "Connect")
        {


            // Set the Port Name....
            this.serialPort.PortName = portNameComboBox.Text;
            this.serialPort.Parity = Parity.None;
            this.serialPort.StopBits = StopBits.One;
            this.serialPort.DataBits = 8;


            try
            {


                this.BaudRate = this.baudRateComboBox.SelectedIndex;
                this.COMPort = this.portNameComboBox.SelectedIndex;

                // Open Serial Ports and disable Buttons....
                this.serialPort.Open();

                this.portNameComboBox.Enabled = false;
                this.baudRateComboBox.Enabled = false;

                this.notificationLabel.Text = "Connected to : " +                 portNameComboBox.SelectedItem.ToString() + " , with a baudrate of : " + baudRateComboBox.SelectedItem.ToString() + "...";

                // Set the Connect Button Text to Disconenct...
                this.connectButton.Text = "Disconnect";
            }
            catch (Exception ex)
            {

                MessageBox.Show(ex.Message, "ERROR: " + ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);

                    this.portNameComboBox.Enabled = true;
                    this.baudRateComboBox.Enabled = true;
            }

            if (serialPort.IsOpen)
            {
                this.Center();
                }

                // Dont go any further...
                return;
            }
            else { notificationLabel.Text = "Please select Baud Rate then select Com Port..."; }

            #endregion
}

 

My opinion is, comming up with a good smart Serial Communication Scheme is more difficult than the Code itself.

I came up with a string like this: <1|0002000|050|100|1>

The Code:

// Our Serial Data String should look something like this : <1|0002000|050|100|1> 
this.TXDString = String.Format("<{0}|{1:0000000}|{2:000}|{3}|{4}>", CPU.ConvertPortToInt(Port.ToString()), Frequency, Duty, divider, Output.ConvertModeToInt(Mode.ToString()));

 

I think this could be done a lot better and more efficient. All that we need is 2 numbers, each up to 16 bits for:

  • OCR1A
  • OCR1B

 

Something like this:  <OCR1A|OCR1B>

Best wishes, stay safe and well My Friends,

   Chris

Chris posted this 29 June 2020

My Friends,

I spent five minutes on the Serial Receive:

////////////////////////////////////////////////////////////////////////////////////////////////////
// The MCU Loop Method:                                                                           //
void loop() {///////////////////////////////////////////////////////////////////////////////////////

  // Print Output:
  PrintOutput();

  // Look for Serial Data:
  if(Serial.available()){

    // Read Serial String:
    String data = Serial.readString();

    // Check Data and Process:
    if(data.length() > 0 && data.startsWith("<"))
      ProcessSerialData(data);
  }
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Process the Serial Data:                                                                       //
void ProcessSerialData(String data){////////////////////////////////////////////////////////////////

  // Get Frequency (F) and DutyCycle (D) from: <65535|32767>
  long F = data.substring(1, data.indexOf('|')).toInt();
  long D = data.substring(data.indexOf('|') + 1, data.indexOf('>')).toInt();

  // Set Frequency:
  OCR1A = F;

  // Set Duty Cycle:
  OCR1B = D;
}

 

A simple bit of Software is now needed for the PC, just a .NET App to send via Serial, the Commands.

Best wishes, stay safe and well My Friends,

   Chris

Chris posted this 29 June 2020

My Friends,

I couldn't help myself, I have written a very simple, extremely simply, not pretty looking controller:

 

namespace SerialPWMController
{



    #region Using Statements:



    using System;
    using System.IO.Ports;
    using System.Windows.Forms;



    #endregion




    public partial class Form1 : Form
    {



        #region Fields:



        /// <summary>
        /// The Microcontroller Clock Frequency:
        /// </summary>
        int fclock = 16000000;



        /// <summary>
        /// The Clock Frequency Prescaler:
        /// </summary>
        int Prescaler = 1024;



        /// <summary>
        /// Create a new SerialPort Reference.
        /// <para>
        ///     See: https://docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport?view=dotnet-plat-ext-3.1
        /// </para>
        /// </summary>
        SerialPort serialPort = new SerialPort();



        #endregion



        #region Properties:



        /// <summary>
        /// Serial Port Name:
        /// </summary>
        public string PortName
        {
            get { return serialPort.PortName; }
            internal set { serialPort.PortName = value; }
        }



        /// <summary>
        /// Serial Port BaudRate:
        /// </summary>
        public int BaudRate
        {
            get { return serialPort.BaudRate; }
            internal set { serialPort.BaudRate = value; }
        }



        /// <summary>
        /// Serial Port Parity:
        /// </summary>
        public Parity Parity
        {
            get { return serialPort.Parity; }
            internal set { serialPort.Parity = value; }
        }



        /// <summary>
        /// Serial Port DataBits:
        /// </summary>
        public int DataBits
        {
            get { return serialPort.DataBits; }
            internal set { serialPort.DataBits = value; }
        }



        /// <summary>
        /// Serial Port StopBits:
        /// </summary>
        public StopBits StopBits
        {
            get { return serialPort.StopBits; }
            internal set { serialPort.StopBits = value; }
        }



        /// <summary>
        /// Serial Port Handshake:
        /// </summary>
        public Handshake Handshake
        {
            get { return serialPort.Handshake; }
            internal set { serialPort.Handshake = value; }
        }



        #endregion



        /// <summary>
        /// Form Constructor.
        /// </summary>
        public Form1()
        {

            // Form Init:
            InitializeComponent();

            // Add Port Names:
            foreach (string PortName in SerialPort.GetPortNames())
                if (!PortNameComboBox.Items.Contains(PortName))
                    PortNameComboBox.Items.Add(PortName);

            // Standard Baud Rates:
            int[] BaudRates = new int[] { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000 };

            // Add Port Names:
            foreach (int BaudRate in BaudRates)
                BaudRateComboBox.Items.Add(BaudRate);

            // Set Selected Index:
            BaudRateComboBox.SelectedItem = 115200;
        }



        /// <summary>
        /// Form Load Event.
        /// </summary>
        private void Form1_Load(object sender, EventArgs e)
        {

            // Set the read/write timeouts
            serialPort.ReadTimeout = 500;
            serialPort.WriteTimeout = 500;
        }



        /// <summary>
        /// Calculate the Frequency:
        /// </summary>
        /// <param name="frequency">The desired Frequency: 3.73Hz</param>
        /// <returns>int representing the resolution on the Register (16 bits).</returns>
        int CalculateFrequency(double frequency)
        {

            // Calculate OCR1A from specified Frequency:
            return fclock / (int)(frequency * Prescaler) - 1;
        }



        /// <summary>
        /// Calculate the Duty Cycle:
        /// </summary>
        /// <param name="period">Period, or 1 / F</param>
        /// <param name="duty">The desired Duty Cycle: 50.0%</param>
        /// <returns>int representing the resolution on the Register (16 bits).</returns>
        int CalculateDutyCycle(int period, double duty)
        {

            // Calculate OCR1B from OCR1A and specified Duty Cycle:
            return (short)(period * (duty / 100L));
        }



        /// <summary>
        /// Calculate Register Resolution:
        /// </summary>
        /// <param name="ocrx">The Register Value, E.G: CalculateFrequency(1024.57)</param>
        /// <returns>double representing the Resolution at the current Register Value.</returns>
        double CalculateResolution(int ocrx)
        {

            return (Math.Log((double)ocrx + 1.0D) / Math.Log(2.0D));
        }



        /// <summary>
        /// Send Button Click Event.
        /// </summary>
        private void SendButton_Click(object sender, EventArgs e)
        {

            // Parse the Values:
            double.TryParse(FrequencyTextBox.Text, out double Frequency);
            double.TryParse(DutyCycleTextBox.Text, out double DutyCycle);

            // Calculate Resolutions:
            int FrequencyResolution = CalculateFrequency(Frequency);
            int DutyCycleResolution = CalculateDutyCycle(FrequencyResolution, DutyCycle);

            // Set Resolution:
            ResolutionLabel.Text = CalculateResolution(FrequencyResolution).ToString();

            // Send the Data:
            serialPort.WriteLine(String.Format("<{0}|{1}>", FrequencyResolution, DutyCycleResolution));
        }



        /// <summary>
        /// Serial Data Received Event.
        /// </summary>
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            // Handle Data here...
            string data = ((SerialPort)sender).ReadExisting();
            Invoke(new Action(() => {
                richTextBox1.AppendText(data + Environment.NewLine);
            }));
        }



        /// <summary>
        /// Connect Button Click Event.
        /// </summary>
        private void ConnectButton_Click(object sender, EventArgs e)
        {

            if (ConnectButton.Text == "Connect")
            {

                // Configure Serial Properties:
                string Port = PortNameComboBox.SelectedItem.ToString();
                int Baud = Convert.ToInt32(BaudRateComboBox.SelectedItem.ToString());
                serialPort = new SerialPort(Port);
                BaudRate = Baud;
                Parity = Parity.None;
                DataBits = 8;
                StopBits = StopBits.One;
                Handshake = Handshake.None;

                // 
                serialPort.RtsEnable = true;

                // Attach Data Reveived Event:
                serialPort.DataReceived += SerialPort_DataReceived;

                // Open the Serial Port:
                serialPort.Open();

                // Change Button Text:
                ConnectButton.Text = "Disconnect";

                // Return:
                return;
            }

            if (ConnectButton.Text == "Disconnect")
            {

                // Close the Serial Port:
                serialPort.Close();
                serialPort.Dispose();

                // Change Button Text:
                ConnectButton.Text = "Connect";

                // Return:
                return;
            }
        }
    }
}

 

Of course, this is just the  start, much more can be done, lots of stuff to catch errors, smoother handing of Connection and pretty it all up much nicer!

Also, the Prescalar needs to be handled! 1024 is good at low Frequency, but needs to be changed as the Resolution drops and vice versa.

Code in Zip format is attached below:

Best wishes, stay safe and well My Friends,

   Chris

Attached Files

Chris posted this 02 July 2020

My Friends,

I have got the code to a point now, where it is a very useful project! Utilising a wide range of PWM: 0.001 - 8 MHz or so.

Here is the Arduino Code:

////////////////////////////////////////////////////////////////////////////////////////////////////
// PWM Software written by Chris Sykes.                                                           //
// NOTE: 16 bit Timers currently used. 32 Bit Timers need all shorts changed to longs!            //
// Software written and shared as is, no warrantys are given. Use at your own risk.               //
////////////////////////////////////////////////////////////////////////////////////////////////////
// Your MCU Clock Frequency: 16000000 or 16e6, retrieved by F_CPU.                                //
long fclock = F_CPU;


////////////////////////////////////////////////////////////////////////////////////////////////////
// Most MCU's have:                                                                               //
//   0,                                                                                           //
//   8,                                                                                           //
//   64,                                                                                          //
//   256,                                                                                         //
//   1024                                                                                         //
// as Prescaler Values:                                                                           //
long Prescaler = 8;


////////////////////////////////////////////////////////////////////////////////////////////////////
// Serial Connection Hook:                                                                        //
bool Hook = true;


////////////////////////////////////////////////////////////////////////////////////////////////////
// The MCU Setup Method:                                                                          //
void setup() {//////////////////////////////////////////////////////////////////////////////////////

  // Init Serial:
  Serial.begin(115200);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Configure Timer:                                                                               //
void ConfgureTimer(){///////////////////////////////////////////////////////////////////////////////

  // The counter simply overruns when it passes its maximum 16-bit value
  // (MAX = 0xFFFF (65535)) and then restarts from the BOTTOM (0x0000).

  // Timer One PWM Pin: 
  // Mega = Pin 12
  // Pro Micro = Pin 10
  // Check your Datasheet!
  pinMode(10, OUTPUT);

  // Reset Timer One:
  TCCR1A = 0;
  TCCR1B = 0;

  // ComA and B need to be set for Compare Match Pins:
  TCCR1A |= (1 << COM1A1) | (1 << COM1B1);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Set Prescaler:                                                                                 //
void SetPrescaler(short prescaler){/////////////////////////////////////////////////////////////////

  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
  // See your Datasheet! See: Clock Select Bit Description                                                 //
  //-------------------------------------------------------------------------------------------------------//
  // CS02     CS01     CS00     Description                                                                //
  //  0        0        0        No clock source (Timer/Counter stopped)                                   //
  //  0        0        1        clkI/O/(No prescaling)                                                    //
  //  0        1        0        clkI/O/8 (From prescaler)                                                 //
  //  0        1        1        clkI/O/64 (From prescaler)                                                //
  //  1        0        0        clkI/O/256 (From prescaler)                                               //
  //  1        0        1        clkI/O/1024 (From prescaler)                                              //
  //  1        1        0        External clock source on T0 pin. Clock on falling edge.                   //
  //  1        1        1        External clock source on T0 pin. Clock on rising edge.                    //
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////

  // Configure Prescaler (CSX):
  switch (prescaler) {
    case 1:
      //  Set 0: clkI/O/(No prescaling)
      TCCR1B |= (1 << CS10);
      break;
    case 8:
      // Set 8: clkI/O/8 (From prescaler)
      TCCR1B |= (1 << CS11);
      break;
    case 64:
      // Set 64: clkI/O/64 (From prescaler)
      TCCR1B |= (1 << CS11) | (1 << CS10);
      break;
    case 256:
      // Set 256: clkI/O/256 (From prescaler)
      TCCR1B |= (1 << CS12);
      break;
    case 1024:
      // Set 1024: clkI/O/1024 (From prescaler)
      TCCR1B |= (1 << CS12) | (1 << CS10);
      break;
    default:
      // Set Off: No clock source (Timer/Counter stopped)
      TCCR1B |= (0 << CS12) | (0 << CS11) | (0 << CS10);
      break;
  }
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Set PWM Mode:                                                                                  //
void SetPWMMode(){//////////////////////////////////////////////////////////////////////////////////

  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // See your Datasheet!                                                                                            //
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // Waveform Generation Mode Bit Description                                                                       //
  //----------------------------------------------------------------------------------------------------------------//
  //                                     Timer/Counter Mode of                    Update of       TOV Flag (1)(2)   //
  // Mode    WGM3   WGM2   WGM1   WGM0   Operation                     TOP        OCRx at         Set on            //
  //  0      0      0      0      0      Normal                        0xFFFF     Immediate       MAX               //
  //  1      0      0      0      1      PWM, Phase Correct, 8-bit     0x00FF     TOP             BOTTOM            //
  //  2      0      0      1      0      PWM, Phase Correct, 9-bit     0x01FF     TOP             BOTTOM            //
  //  3      0      0      1      1      PWM, Phase Correct, 10-bit    0x03FF     TOP             BOTTOM            //
  //  4      0      1      0      0      CTC                           OCRnA      Immediate       MAX               //
  //  5      0      1      0      1      Fast PWM, 8-bit               0x00FF     TOP             TOP               //
  //  6      0      1      1      0      Fast PWM, 9-bit               0x01FF     TOP             TOP               //
  //  7      0      1      1      1      Fast PWM, 10-bit              0x03FF     TOP             TOP               //
  //  8      1      0      0      0      PWM, PnFC                     ICRn       BOTTOM          BOTTOM            //
  //  9      1      0      0      1      PWM, PnFC                     OCRnA      BOTTOM          BOTTOM            //
  //  10     1      0      1      0      PWM, Phase Correct            ICRn       TOP             BOTTOM            //
  //  11     1      0      1      1      PWM, Phase Correct            OCRnA      TOP             BOTTOM            //
  //  12     1      1      0      0      CTC                           ICRn       Immediate       MAX               //
  //  13     1      1      0      1      (Reserved)                    –          –               –                 //
  //  14     1      1      1      0      Fast PWM                      ICRn       TOP             TOP               //
  //  15     1      1      1      1      Fast PWM                      OCRnA      TOP             TOP               //
  ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // NOTE: PnFC = Phase and Frequency Correct.

  // Fast PWM needs bits (WGM10 & WGM12) set to One:
  TCCR1A |= (1 << WGM11);

  // Configure Fast PWM Mode (WGM12 and 13):
  TCCR1B |= (1 << WGM12) | (1 << WGM13);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// The MCU Loop Method:                                                                           //
void loop() {///////////////////////////////////////////////////////////////////////////////////////

  // Look for Serial Connection:
  if(Serial && Hook){

    // Connection Hook:
    Hook = false;

    // Print Welcome Message:
    Serial.println("Welcome to Serial PWM Controller by Chris Sykes!");
  }

  // Look for Serial Data:
  if(Serial.available()){

    // Read Serial String:
    String data = Serial.readString();

    // Check Data and Process: && data.endsWith(">") would be nice...
    if(data.length() > 0 && data.startsWith("<"))
      ProcessSerialData(data);

    // Flush Serial:
    Serial.flush();
  }
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Process the Serial Data:                                                                       //
void ProcessSerialData(String data){////////////////////////////////////////////////////////////////

  // Get Frequency: <15624|3906|1024> = 1Hz @ 25% Duty Cycle.
  long F = data.substring(1, data.indexOf('|')).toInt();
  long D = data.substring(data.indexOf('|') + 1, data.lastIndexOf('|')).toInt();
  Prescaler = data.substring(data.lastIndexOf('|') + 1, data.indexOf('>')).toInt();

  // Print Data:
  Serial.print("MCU set Frequency Register to: ");
  Serial.print(F);
  Serial.print(" or F: ");
  Serial.println(ConvertToFrequency(F));
  Serial.print("MCU set Duty Cycle Register to: ");
  Serial.println(D);
  Serial.print("MCU set Prescaler to: ");
  Serial.println(Prescaler);
  Serial.print("MCU Register Resolution: ");
  Serial.println(CalculateResolution(F));


  // Confgure Timer:
  ConfgureTimer();

  // Set the Prescaler:
  SetPrescaler(Prescaler);

  // Set PWM Mode:
  SetPWMMode();

  // Set Frequency:
  ICR1 = F;

  // Set Duty Cycle:
  OCR1B = D;
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Calculate the Frequency:                                                                       //
short ConvertToFrequency(long ocrx){////////////////////////////////////////////////////////////////

  // Calculate OCR1A from specified Frequency:
  return (short)(fclock / (Prescaler * (1 + ocrx)));
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Calculate the Frequency:                                                                       //
short CalculateFrequency(double frequency){/////////////////////////////////////////////////////////

  // Calculate OCR1A from specified Frequency:
  return (short)(fclock / ((frequency * Prescaler) - 1));
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Calculate the DutyCycle:                                                                       //
short CalculateDutyCycle(int period, double duty){//////////////////////////////////////////////////

  // Calculate OCR1B from OCR1A and specified Duty Cycle:
  return (short)(period * (duty / 100L));
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Calculate Register Resolution:                                                                 //
double CalculateResolution(long ocrx){//////////////////////////////////////////////////////////////

  return (log((double)ocrx + 1.0D)/log(2.0D));
}

 

Here is the Windows Forms Code:

namespace SerialPWMController
{



    #region Using Statements:



    using System;
    using System.IO.Ports;
    using System.Windows.Forms;



    #endregion




    public partial class Form1 : Form
    {



        #region Fields:



        /// <summary>
        /// The Microcontroller Clock Frequency:
        /// </summary>
        int fclock = 16000000;



        /// <summary>
        /// The Clock Frequency Prescaler:
        /// </summary>
        int Prescaler = 1024;



        /// <summary>
        /// Create a new SerialPort Reference.
        /// <para>
        ///     See: https://docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport?view=dotnet-plat-ext-3.1
        /// </para>
        /// </summary>
        SerialPort serialPort = new SerialPort();



        #endregion



        #region Properties:



        /// <summary>
        /// The System Frequency.
        /// </summary>
        public double Frequency { get; set; } = 1.00;



        /// <summary>
        /// The System Duty Cycle.
        /// </summary>
        public double DutyCycle { get; set; } = 50.00;



        /// <summary>
        /// The Frequency Track Bar Multiplier.
        /// </summary>
        private int Multiplier { get; set; } = 1;



        /// <summary>
        /// Serial Port Name:
        /// </summary>
        public string PortName
        {
            get { return serialPort.PortName; }
            internal set { serialPort.PortName = value; }
        }



        /// <summary>
        /// Serial Port BaudRate:
        /// </summary>
        public int BaudRate
        {
            get { return serialPort.BaudRate; }
            internal set { serialPort.BaudRate = value; }
        }



        /// <summary>
        /// Serial Port Parity:
        /// </summary>
        public Parity Parity
        {
            get { return serialPort.Parity; }
            internal set { serialPort.Parity = value; }
        }



        /// <summary>
        /// Serial Port DataBits:
        /// </summary>
        public int DataBits
        {
            get { return serialPort.DataBits; }
            internal set { serialPort.DataBits = value; }
        }



        /// <summary>
        /// Serial Port StopBits:
        /// </summary>
        public StopBits StopBits
        {
            get { return serialPort.StopBits; }
            internal set { serialPort.StopBits = value; }
        }



        /// <summary>
        /// Serial Port Handshake:
        /// </summary>
        public Handshake Handshake
        {
            get { return serialPort.Handshake; }
            internal set { serialPort.Handshake = value; }
        }



        #endregion



        /// <summary>
        /// Form Constructor.
        /// </summary>
        public Form1()
        {

            // Form Init:
            InitializeComponent();

            // Add Port Names:
            foreach (string PortName in SerialPort.GetPortNames())
                if (!PortNameComboBox.Items.Contains(PortName))
                    PortNameComboBox.Items.Add(PortName);

            // Standard Baud Rates:
            int[] BaudRates = new int[] { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000 };

            // Add Port Names:
            foreach (int BaudRate in BaudRates)
                BaudRateComboBox.Items.Add(BaudRate);

            // Set Selected Index:
            BaudRateComboBox.SelectedItem = 115200;

            // Attach Frequency TrackBar Value Changed Event Handler:
            this.FrequencyTrackBar.ValueChanged += new System.EventHandler(this.FrequencyTrackBar_ValueChanged);
        }



        /// <summary>
        /// Form Load Event.
        /// </summary>
        private void Form1_Load(object sender, EventArgs e)
        {

            // Set Initial Frequency:
            FrequencyTextBox.Text = "1111.111";

            // Set Initial Duty Cycle:
            DutyCycleTextBox.Text = DutyCycle.ToString();

            // Set Duty Cycle Track Bar:
            DutyCycleTrackBar.Value = (int)(50.0 * 1000.00);
        }



        /// <summary>
        /// Calculate the Frequency:
        /// </summary>
        /// <param name="frequency">The desired Frequency: 3.73Hz</param>
        /// <returns>int representing the resolution on the Register (16 bits).</returns>
        int CalculateFrequency(double frequency)
        {

            // Calculate OCR1A from specified Frequency:
            return (int)(Convert.ToDouble(fclock) / (frequency * Convert.ToDouble(Prescaler)) - 1.0);
        }



        /// <summary>
        /// Calculate the Duty Cycle:
        /// </summary>
        /// <param name="period">Period, or 1 / F</param>
        /// <param name="duty">The desired Duty Cycle: 50.0%</param>
        /// <returns>int representing the resolution on the Register (16 bits).</returns>
        int CalculateDutyCycle(int period, double duty)
        {

            // Calculate OCR1B from OCR1A and specified Duty Cycle:
            return (short)(Convert.ToDouble(period) * (duty / 100.00));
        }



        /// <summary>
        /// Calculate Register Resolution:
        /// </summary>
        /// <param name="ocrx">The Register Value, E.G: CalculateFrequency(1024.57)</param>
        /// <returns>double representing the Resolution at the current Register Value.</returns>
        double CalculateResolution(int ocrx)
        {

            return (Math.Log((double)ocrx + 1.0D) / Math.Log(2.0D));
        }



        /// <summary>
        /// Send Button Click Event.
        /// </summary>
        private void SendButton_Click(object sender, EventArgs e)
        {

            // Parse the Frequency Value:
            if (!double.TryParse(FrequencyTextBox.Text, out double Frequency))
            {

                // Show Error:
                richTextBox1.AppendText("Input Error!" + Environment.NewLine);

                // Return:
                return;
            }

            // Parse the Duty Cycle Value:
            if (!double.TryParse(DutyCycleTextBox.Text, out double DutyCycle))
            {

                // Show Error:
                richTextBox1.AppendText("Input Error!" + Environment.NewLine);

                // Return:
                return;
            }

            // Check Values:
            if (Frequency == 0.00 || DutyCycle == 0.00)
            {

                // Show Error:
                richTextBox1.AppendText("Error!\r\nCant Divide by Zero!" + Environment.NewLine);

                // Return:
                return;
            }

            // Check Values:
            if (Frequency < 0.00 || DutyCycle < 0.00)
            {

                // Show Error:
                richTextBox1.AppendText("Error!\r\nOnly Positive Numbers can be used!" + Environment.NewLine);

                // Return:
                return;
            }

            // Calculate Prescaler:
            Prescaler = CalculatePrescaler(Frequency);

            // Calculate Resolutions:
            int FrequencyResolution = CalculateFrequency(Frequency);
            int DutyCycleResolution = CalculateDutyCycle(FrequencyResolution, DutyCycle);

            // Set Resolution:
            ResolutionLabel.Text = CalculateResolution(FrequencyResolution).ToString();

            try
            {

                // Send the Data:
                serialPort.WriteLine(String.Format("<{0}|{1}|{2}>", FrequencyResolution, DutyCycleResolution, Prescaler));
            }
            catch
            {

                // Show Error:
                richTextBox1.AppendText("Error!\r\nSerial is not Connected, please connect and try again!" + Environment.NewLine);

                // Return:
                return;
            }
        }



        /// <summary>
        /// Calculate the Prescaler.
        /// </summary>
        /// <param name="frequency"></param>
        /// <returns></returns>
        private int CalculatePrescaler(double frequency)
        {

            if (frequency >= 300000.00)
                return 1;

            if (frequency <= 300000.00 && frequency >= 10000.00)
                return 8;

            if (frequency < 10000.00 && frequency >= 3000.00)
                return 64;

            if (frequency < 3000.00 && frequency >= 500.00)
                return 256;

            if (frequency < 500.00 && frequency >= 0.00)
                return 1024;

            return 0;
        }



        /// <summary>
        /// Serial Data Received Event.
        /// </summary>
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            // Handle Data here...
            string data = ((SerialPort)sender).ReadExisting();
            Invoke(new Action(() =>
            {
                richTextBox1.AppendText(data + Environment.NewLine);
                richTextBox1.ScrollToCaret();
            }));
        }



        /// <summary>
        /// Connect Button Click Event.
        /// </summary>
        private void ConnectButton_Click(object sender, EventArgs e)
        {

            if (ConnectButton.Text == "Connect")
            {

                // RichTextBox Clear:
                richTextBox1.Clear();

                // Configure Serial Properties:
                string Port = PortNameComboBox.SelectedItem.ToString();
                int Baud = Convert.ToInt32(BaudRateComboBox.SelectedItem.ToString());
                serialPort = new SerialPort(Port);
                BaudRate = Baud;
                Parity = Parity.None;
                DataBits = 8;
                StopBits = StopBits.One;
                Handshake = Handshake.None;

                // Gets or sets a value indicating whether the Request to Send (RTS) signal is enabled during serial communication.
                serialPort.RtsEnable = true;

                // Attach Data Reveived Event:
                serialPort.DataReceived += SerialPort_DataReceived;

                try
                {
                    // Open the Serial Port:
                    serialPort.Open();

                    // Change Button Text:
                    ConnectButton.Text = "Disconnect";

                    // Return:
                    return;
                }
                catch (Exception exc)
                {

                    // Show Error:
                    richTextBox1.AppendText(exc.Message + Environment.NewLine);

                    // Return:
                    return;
                }
            }

            if (ConnectButton.Text == "Disconnect")
            {

                // Close the Serial Port:
                serialPort.Close();
                serialPort.Dispose();

                // Change Button Text:
                ConnectButton.Text = "Connect";

                // Return:
                return;
            }
        }



        /// <summary>
        /// The Frequency TrackBar Value Changed Event.
        /// </summary>
        private void FrequencyTrackBar_ValueChanged(object sender, EventArgs e)
        {

            // Detach Frequency TrackBar Value Changed Event Handler:
            FrequencyTrackBar.ValueChanged -= new System.EventHandler(FrequencyTrackBar_ValueChanged);

            // Get TrackBar Frequency Value:
            int Value = (sender as TrackBar).Value;

            // Check Frequency Values:
            if (Value >= FrequencyTrackBar.Maximum)
            {

                // Increment the Multiplier:
                Multiplier = Multiplier + 100;

                // Check for Ones Value:
                if (Multiplier == 101)
                {
                    Multiplier = 100;
                    Frequency = 1.00;
                }

                // Reset Track Bar after Value Underflow:
                FrequencyTrackBar.Value = FrequencyTrackBar.Minimum + 1;
            }
            else
            if (Value <= FrequencyTrackBar.Minimum)
            {

                // Decrement the Multiplier:
                Multiplier = Multiplier - 100;

                // Reset Multiplier:
                if (Multiplier <= 0)
                {
                    Multiplier = 1;
                    Frequency = 0.001;
                }

                // Check Zero Values:
                if (Frequency >= 1)
                    // Reset Track Bar after Value Overflow:
                    FrequencyTrackBar.Value = FrequencyTrackBar.Maximum - 1;
            }

            // Reattach Frequency TrackBar Value Changed Event Handler:
            FrequencyTrackBar.ValueChanged += new System.EventHandler(FrequencyTrackBar_ValueChanged);

            // Set the Frequency:
            // Track Bar Max Value is: 100000 so / 1000 gives increments of 100, with 3 Decimal places: 100.123
            FrequencyTextBox.Text = ((Frequency * Convert.ToDouble(Multiplier)) + (Convert.ToDouble(Value) / 1000.0D)).ToString();
        }



        /// <summary>
        /// The Duty Cycle TrackBar Value Changed Event.
        /// </summary>
        private void DutyCycleTrackBar_ValueChanged(object sender, EventArgs e)
        {

            // Calculate Duty Cycle:
            DutyCycle = Math.Round(DutyCycleTrackBar.Value / 1000.00D, 3);

            // Set the Duty Cycle Text Box:
            this.DutyCycleTextBox.Text = DutyCycle.ToString();
        }



        /// <summary>
        /// Frequency TextBox Leave Event.
        /// </summary>
        private void FrequencyTextBox_Leave(object sender, EventArgs e)
        {

            // Get the Frequency:
            double.TryParse(FrequencyTextBox.Text, out double FrequencyOut);

            // Get Hundreds:
            int Hundreds = Convert.ToInt32(Math.Floor(FrequencyOut / 100));

            // Check Multiplier:
            if (Hundreds <= 0.00 && FrequencyOut < 100.00)
                // Set Multiplier:
                Multiplier = 1;
            else
                if (Hundreds <= 0.00 && FrequencyOut > 100.00)
                // Set Multiplier:
                Multiplier = 100 * 1;
            else
                // Set Multiplier:
                Multiplier = 100 * Hundreds;

            // Detach Frequency TrackBar Value Changed Event Handler:
            FrequencyTrackBar.ValueChanged -= new System.EventHandler(FrequencyTrackBar_ValueChanged);

            // Calculate the Frequency Divider, including the 3 Decimal places:
            double FrequencyDivider = Convert.ToInt32(FrequencyOut * 1000);

            // Set the Frequency TrackBar Value:
            if (FrequencyDivider < FrequencyTrackBar.Maximum)
                FrequencyTrackBar.Value = Convert.ToInt32(FrequencyDivider);
            else
            {
                FrequencyTrackBar.Value = Convert.ToInt32(FrequencyDivider % FrequencyTrackBar.Maximum);
            }

            // Reattach Frequency TrackBar Value Changed Event Handler:
            FrequencyTrackBar.ValueChanged += new System.EventHandler(FrequencyTrackBar_ValueChanged);
        }
    }
}

 

A Video:

 

 

Guys, this project has been very helpful in my work!

Best wishes, stay safe and well My Friends,

   Chris

Attached Files

baerndorfer posted this 02 July 2020

this looks very interesting! i will try out thx a lot

meanwhile i try to move one of my arduino projects to the faster ESP32 board - the idea is to get a faster switching and more chanels because the esp32 should provide PWM functionality on mostly all pins.

regards from austria!

  • Liked by
  • Augenblick
  • Chris
Chris posted this 02 July 2020

Hey Baerndorfer,

Yes exactly! 32 bit boards or at least registers are much faster and have a lot more resolution! I have also used the STM32F405 and this was awesome! Much more programming however. Setting GPIO Ports, then enabling Timers, its a lot different to program than the Arduino's.

Best wishes, stay safe and well My Friend,

   Chris

 

Ourbobby posted this 03 August 2020

Hi Chris,

              The market for electronics and opportunities is getting younger!

Raspberry Pi For Kids (Updated) Mad BOOK NEW

https://www.ebay.com.au/itm/Raspberry-Pi-For-Kids-Updated-Mad-BOOK-NEW/352120949439?epid=237881065&hash=item51fc0b4abf:g:frQAAOSwu~ZZctQf

 

Regards

 

ourbobby.

Kritischer posted this 22 August 2020

It seems that being able to generate a precise PWM pulse with and duty cycle is going to be a key component in everything I try to do from here on out so this seems like a great place to put in some time to build a portable precision generator. I know this isn't the only way but being able to generate custom pulses is going to help me at work and will save time in the long run I can dedicate to OU projects.

I'm going to try to get this code to run on my 2560 Mega along with a rotary encoder to set pulse width and PRF, and I2C LCD as output. It needs to run using interrupts because I need the main loop() for other functions and it's much cleaner that way.

It's been 15 years since I've programmed a microcontroller and I'll tell you... it's hard work feeling this stupid for as long as I have.

I'm plagued with ambitions that outweigh my abilities and analysis paralysis is a symptom of the disease. Now that I've found this site I feel like I'm at home because at least I don't see people in this community actively DISCOURAGING people...

I went on a Arduino buying spree and now that I have everything I need I need to learn how to make small, incremental, features to work. Small wins are what keep me going and in the past I fail because I'm too ambitious too soon and that's a recipe for failure I've spent too many years repeating.

If I make progress it will all go here.

Chris posted this 22 August 2020

Hey Kritischer,

If I may, best advice I ever heard: "Little Steps for Little Feet" - Sir Richard Feynman

Bite off little chunks and before you know it, you will be very far advanced! I promise you! Ask others here. This code, with some modifications will work on the MEGA.

Best wishes, stay safe and well My Friend,

   Chris

Chris posted this 13 April 2023

My Friends,

For the very cheap Pro Micro, I have made a bit of a change, to improve the current project!

 

 

A small update:

 

I have added Track Bars to control Frequency and Duty Cycle.

 

C# WinForms Code:

namespace SerialPWMController
{



    #region Using Statements:



    using System;
    using System.IO.Ports;
    using System.Windows.Forms;



    #endregion




    public partial class Form1 : Form
    {



        #region Fields:



        /// <summary>
        /// The Microcontroller Clock Frequency:
        /// </summary>
        int fclock = 16000000;



        /// <summary>
        /// The Clock Frequency Prescaler:
        /// </summary>
        int Prescaler = 1024;



        /// <summary>
        /// Create a new SerialPort Reference.
        /// <para>
        ///     See: https://docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport?view=dotnet-plat-ext-3.1
        /// </para>
        /// </summary>
        SerialPort serialPort = new SerialPort();



        #endregion



        #region Properties:



        /// <summary>
        /// The System Frequency.
        /// </summary>
        public double Frequency { get; set; } = 1.00;



        /// <summary>
        /// The System Duty Cycle.
        /// </summary>
        public double DutyCycle { get; set; } = 50.00;



        /// <summary>
        /// The Frequency Track Bar Multiplier.
        /// </summary>
        private int Multiplier { get; set; } = 1;



        /// <summary>
        /// Serial Port Name:
        /// </summary>
        public string PortName
        {
            get { return serialPort.PortName; }
            internal set { serialPort.PortName = value; }
        }



        /// <summary>
        /// Serial Port BaudRate:
        /// </summary>
        public int BaudRate
        {
            get { return serialPort.BaudRate; }
            internal set { serialPort.BaudRate = value; }
        }



        /// <summary>
        /// Serial Port Parity:
        /// </summary>
        public Parity Parity
        {
            get { return serialPort.Parity; }
            internal set { serialPort.Parity = value; }
        }



        /// <summary>
        /// Serial Port DataBits:
        /// </summary>
        public int DataBits
        {
            get { return serialPort.DataBits; }
            internal set { serialPort.DataBits = value; }
        }



        /// <summary>
        /// Serial Port StopBits:
        /// </summary>
        public StopBits StopBits
        {
            get { return serialPort.StopBits; }
            internal set { serialPort.StopBits = value; }
        }



        /// <summary>
        /// Serial Port Handshake:
        /// </summary>
        public Handshake Handshake
        {
            get { return serialPort.Handshake; }
            internal set { serialPort.Handshake = value; }
        }



        #endregion



        /// <summary>
        /// Form Constructor.
        /// </summary>
        public Form1()
        {

            // Form Init:
            InitializeComponent();

            // Add Port Names:
            foreach (string PortName in SerialPort.GetPortNames())
                if (!PortNameComboBox.Items.Contains(PortName))
                    PortNameComboBox.Items.Add(PortName);

            // Standard Baud Rates:
            int[] BaudRates = new int[] { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 256000 };

            // Add Port Names:
            foreach (int BaudRate in BaudRates)
                BaudRateComboBox.Items.Add(BaudRate);

            // Set Selected Index:
            BaudRateComboBox.SelectedItem = 115200;

            // Attach Frequency TrackBar Value Changed Event Handler:
            this.FrequencyTrackBar.ValueChanged += new System.EventHandler(this.FrequencyTrackBar_ValueChanged);
        }



        /// <summary>
        /// Form Load Event.
        /// </summary>
        private void Form1_Load(object sender, EventArgs e)
        {

            // Set Initial Frequency:
            FrequencyTextBox.Text = "1111.111";

            // Set Initial Duty Cycle:
            DutyCycleTextBox.Text = DutyCycle.ToString();

            // Set Duty Cycle Track Bar:
            DutyCycleTrackBar.Value = (int)(50.0 * 1000.00);
        }



        /// <summary>
        /// Calculate the Frequency:
        /// </summary>
        /// <param name="frequency">The desired Frequency: 3.73Hz</param>
        /// <returns>int representing the resolution on the Register (16 bits).</returns>
        int CalculateFrequency(double frequency)
        {

            // Calculate OCR1A from specified Frequency:
            return (int)(Convert.ToDouble(fclock) / (frequency * Convert.ToDouble(Prescaler)) - 1.0);
        }



        /// <summary>
        /// Calculate the Duty Cycle:
        /// </summary>
        /// <param name="period">Period, or 1 / F</param>
        /// <param name="duty">The desired Duty Cycle: 50.0%</param>
        /// <returns>int representing the resolution on the Register (16 bits).</returns>
        int CalculateDutyCycle(int period, double duty)
        {

            // Calculate OCR1B from OCR1A and specified Duty Cycle:
            return (short)(Convert.ToDouble(period) * (duty / 100.00));
        }



        /// <summary>
        /// Calculate Register Resolution:
        /// </summary>
        /// <param name="ocrx">The Register Value, E.G: CalculateFrequency(1024.57)</param>
        /// <returns>double representing the Resolution at the current Register Value.</returns>
        double CalculateResolution(int ocrx)
        {

            return (Math.Log((double)ocrx + 1.0D) / Math.Log(2.0D));
        }



        /// <summary>
        /// Send Button Click Event.
        /// </summary>
        private void SendButton_Click(object sender, EventArgs e)
        {

            // Parse the Frequency Value:
            if (!double.TryParse(FrequencyTextBox.Text, out double Frequency))
            {

                // Show Error:
                richTextBox1.AppendText("Input Error!" + Environment.NewLine);

                // Return:
                return;
            }

            // Parse the Duty Cycle Value:
            if (!double.TryParse(DutyCycleTextBox.Text, out double DutyCycle))
            {

                // Show Error:
                richTextBox1.AppendText("Input Error!" + Environment.NewLine);

                // Return:
                return;
            }

            // Check Values:
            if (Frequency == 0.00 || DutyCycle == 0.00)
            {

                // Show Error:
                richTextBox1.AppendText("Error!\r\nCant Divide by Zero!" + Environment.NewLine);

                // Return:
                return;
            }

            // Check Values:
            if (Frequency < 0.00 || DutyCycle < 0.00)
            {

                // Show Error:
                richTextBox1.AppendText("Error!\r\nOnly Positive Numbers can be used!" + Environment.NewLine);

                // Return:
                return;
            }

            // Calculate Prescaler:
            Prescaler = CalculatePrescaler(Frequency);

            // Calculate Resolutions:
            int FrequencyResolution = CalculateFrequency(Frequency);
            int DutyCycleResolution = CalculateDutyCycle(FrequencyResolution, DutyCycle);

            // Set Resolution:
            ResolutionLabel.Text = CalculateResolution(FrequencyResolution).ToString();

            try
            {

                // Send the Data:
                serialPort.WriteLine(String.Format("<{0}|{1}|{2}>", FrequencyResolution, DutyCycleResolution, Prescaler));
            }
            catch
            {

                // Show Error:
                richTextBox1.AppendText("Error!\r\nSerial is not Connected, please connect and try again!" + Environment.NewLine);

                // Return:
                return;
            }
        }



        /// <summary>
        /// Calculate the Prescaler.
        /// </summary>
        /// <param name="frequency"></param>
        /// <returns></returns>
        private int CalculatePrescaler(double frequency)
        {

            if (frequency >= 300000.00)
                return 1;

            if (frequency <= 300000.00 && frequency >= 10000.00)
                return 8;

            if (frequency < 10000.00 && frequency >= 3000.00)
                return 64;

            if (frequency < 3000.00 && frequency >= 500.00)
                return 256;

            if (frequency < 500.00 && frequency >= 0.00)
                return 1024;

            return 0;
        }



        /// <summary>
        /// Serial Data Received Event.
        /// </summary>
        private void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {

            // Handle Data here...
            string data = ((SerialPort)sender).ReadExisting();
            Invoke(new Action(() =>
            {
                richTextBox1.AppendText(data + Environment.NewLine);
                richTextBox1.ScrollToCaret();
            }));
        }



        /// <summary>
        /// Connect Button Click Event.
        /// </summary>
        private void ConnectButton_Click(object sender, EventArgs e)
        {

            if (ConnectButton.Text == "Connect")
            {

                // RichTextBox Clear:
                richTextBox1.Clear();

                // Configure Serial Properties:
                string Port = PortNameComboBox.SelectedItem.ToString();
                int Baud = Convert.ToInt32(BaudRateComboBox.SelectedItem.ToString());
                serialPort = new SerialPort(Port);
                BaudRate = Baud;
                Parity = Parity.None;
                DataBits = 8;
                StopBits = StopBits.One;
                Handshake = Handshake.None;

                // Gets or sets a value indicating whether the Request to Send (RTS) signal is enabled during serial communication.
                serialPort.RtsEnable = true;

                // Attach Data Reveived Event:
                serialPort.DataReceived += SerialPort_DataReceived;

                try
                {
                    // Open the Serial Port:
                    serialPort.Open();

                    // Change Button Text:
                    ConnectButton.Text = "Disconnect";

                    // Return:
                    return;
                }
                catch (Exception exc)
                {

                    // Show Error:
                    richTextBox1.AppendText(exc.Message + Environment.NewLine);

                    // Return:
                    return;
                }
            }

            if (ConnectButton.Text == "Disconnect")
            {

                // Close the Serial Port:
                serialPort.Close();
                serialPort.Dispose();

                // Change Button Text:
                ConnectButton.Text = "Connect";

                // Return:
                return;
            }
        }



        /// <summary>
        /// The Frequency TrackBar Value Changed Event.
        /// </summary>
        private void FrequencyTrackBar_ValueChanged(object sender, EventArgs e)
        {

            // Detach Frequency TrackBar Value Changed Event Handler:
            FrequencyTrackBar.ValueChanged -= new System.EventHandler(FrequencyTrackBar_ValueChanged);

            // Get TrackBar Frequency Value:
            int Value = (sender as TrackBar).Value;

            // Check Frequency Values:
            if (Value >= FrequencyTrackBar.Maximum)
            {

                // Increment the Multiplier:
                Multiplier = Multiplier + 100;

                // Check for Ones Value:
                if (Multiplier == 101)
                {
                    Multiplier = 100;
                    Frequency = 1.00;
                }

                // Reset Track Bar after Value Underflow:
                FrequencyTrackBar.Value = FrequencyTrackBar.Minimum + 1;
            }
            else
            if (Value <= FrequencyTrackBar.Minimum)
            {

                // Decrement the Multiplier:
                Multiplier = Multiplier - 100;

                // Reset Multiplier:
                if (Multiplier <= 0)
                {
                    Multiplier = 1;
                    Frequency = 0.001;
                }

                // Check Zero Values:
                if (Frequency >= 1)
                    // Reset Track Bar after Value Overflow:
                    FrequencyTrackBar.Value = FrequencyTrackBar.Maximum - 1;
            }

            // Reattach Frequency TrackBar Value Changed Event Handler:
            FrequencyTrackBar.ValueChanged += new System.EventHandler(FrequencyTrackBar_ValueChanged);

            // Set the Frequency:
            // Track Bar Max Value is: 100000 so / 1000 gives increments of 100, with 3 Decimal places: 100.123
            FrequencyTextBox.Text = ((Frequency * Convert.ToDouble(Multiplier)) + (Convert.ToDouble(Value) / 1000.0D)).ToString();
        }



        /// <summary>
        /// The Duty Cycle TrackBar Value Changed Event.
        /// </summary>
        private void DutyCycleTrackBar_ValueChanged(object sender, EventArgs e)
        {

            // Calculate Duty Cycle:
            DutyCycle = Math.Round(DutyCycleTrackBar.Value / 1000.00D, 3);

            // Set the Duty Cycle Text Box:
            this.DutyCycleTextBox.Text = DutyCycle.ToString();
        }



        /// <summary>
        /// Frequency TextBox Leave Event.
        /// </summary>
        private void FrequencyTextBox_Leave(object sender, EventArgs e)
        {

            // Get the Frequency:
            double.TryParse(FrequencyTextBox.Text, out double FrequencyOut);

            // Get Hundreds:
            int Hundreds = Convert.ToInt32(Math.Floor(FrequencyOut / 100));

            // Check Multiplier:
            if (Hundreds <= 0.00 && FrequencyOut < 100.00)
                // Set Multiplier:
                Multiplier = 1;
            else
                if (Hundreds <= 0.00 && FrequencyOut > 100.00)
                // Set Multiplier:
                Multiplier = 100 * 1;
            else
                // Set Multiplier:
                Multiplier = 100 * Hundreds;

            // Detach Frequency TrackBar Value Changed Event Handler:
            FrequencyTrackBar.ValueChanged -= new System.EventHandler(FrequencyTrackBar_ValueChanged);

            // Calculate the Frequency Divider, including the 3 Decimal places:
            double FrequencyDivider = Convert.ToInt32(FrequencyOut * 1000);

            // Set the Frequency TrackBar Value:
            if (FrequencyDivider < FrequencyTrackBar.Maximum)
                FrequencyTrackBar.Value = Convert.ToInt32(FrequencyDivider);
            else
            {
                FrequencyTrackBar.Value = Convert.ToInt32(FrequencyDivider % FrequencyTrackBar.Maximum);
            }

            // Reattach Frequency TrackBar Value Changed Event Handler:
            FrequencyTrackBar.ValueChanged += new System.EventHandler(FrequencyTrackBar_ValueChanged);
        }
    }
}

 

Below, attached is the zipped Executable.

 

Akula Lantern N#4 PWM can be achieved:

 

If you remember the Video:

 

 

Please see the link here: Akula's 30 W Lantern Chris's Replication

 

Now, in the older Circuits, Akula shows some differences:

 

So, some things in the Circuit may not be correct! Ignore the differences, and concentrate on the Mosfet like I have said:

 

My Friends,

Trust is something that must be earned, its easily tested, and many have shown how Un-Trust Worthy they really are!

A Self Running Machine was posted to a thread previously, and the thread was deleted. Deleted by the Thread Owner. I had given some constructive criticism, on the way the thread was handled. I posted the Secret that the Thread Owner was trying to avoid. Trying to claim as their own amazing discovery.

 

The thread was posted some time ago, November last year, and I posted an argument, showing that the discovery that was claimed was already known:

 

Oh My, what some people get up to:

 

 

So you see the secret, how it can lay right in front of your eyes and not notice it?

I replicated and also showed the secret:

 

This took me all of about three minutes to throw together.

Here is a simple analogy:

 

 

With some work, and the Circuit:

You can get this occurring:

 

 

Maybe then you can get this occuring:

 

Best Wishes and keep on keeping on My Friends!

   Chris

Attached Files

AETHERIC_MIND posted this 13 April 2023

Hi Chris,
If I understanding it right, the blue trace is the L1 input and the yellow trace is the L2 non inductive coil that is briefly unshorted to allow current amplification. Is that correct?

The micro-controler PWM will allow us to precisely trigger the unshorting mosfet when the input pulse became off.

There is a lot of  to similarity to make from those scope shot and the Experiments  / Timing .

Thx for all those old new incite. It's a motivation trigger!

Now it's time to test it on the bench.

 

Ps: If it make no + value, you can erase my  post.

''Change is never easy but it's worth inertia''

Chris posted this 14 April 2023

Hey AETHERIC_MIND,

Yes.

Sorry, Blue trace is Input Signal to the Mosfet. Yellow Trace is the Collector Trace, same as in the Akula Schematic.

 

A Mosfet is a Switch, a plain old Switch, On or Off, and the only thing that one can control on a Mosfet is the Time it takes to turn On or turn Off the Switch, that's all! So Rise Time, or Decay Time is all we can Control! Because the Mosfet has a Gate Capacitance, maybe, for the IRF540N will be 1220pF Gate Capacitance, using a Resistance in Series:

 

we can Control the Rise and Decay Times via the equation:

R x C = T 

Where:

  • R = Resistance in Ohms. Or suitable Units!
  • C = Capacitance in Farads. Or suitable Units!
  • T = Time in Seconds. Or suitable Units!

 

 

So if we want a Rise time of 22ns, then we can calculate:

Ref: RC Time Calculator.

 

Of course, another Mosfet will have a totally different Gate Capacitance, so one will need to check the Datasheet, and verify the Gate Capacitance.

Experiment is the Path Forward, so anyone expecting this to work first time is kidding themselves, it will take a little work to see the effects and of course to maximise the effect to gain the correct understanding to gain Energy in the System! Anyone working hard to achieve the goal, WILL see these effects and gain a much greater understanding! The above link, about the RC Time Calculation is well worth a read!

Best Wishes,

   Chris

donovan posted this 16 April 2023

Hi,

Controlling the rise time of the MOSFET, counter intuitive for most.......Brilliant.  If the power dissipation of the MOSFET is within limits, not big deal!!

Will have to experiment with that.

So, just to understands this concept better......if we control the risetime of the MOSFET, then this allows us to have a current response that is more in line with the natural resonance of the system?

Donovan

Chris posted this 16 April 2023

Hey Donovan,

Yes, like striking a Bell, one finds a Resonance of the Bell by striking it, this is Mechanical Resonance.

 

When one hits an object at the right Frequency, the Object must respond with the same Frequency, some times you can see somethings damage themselves:

 

Of course, everything has its Resonance, Akula showed us, a Magnetic Field can strike Ferrite at its Resonance Frequency, around the 20 - 27ns range.

You can read more here: YoElMiCrO's Ferro-Magnetic Resonance - Would have been nice to see more progress here, but we saw important information Hidden from other Members, a secret stash of Success that can be turned into a Money Source! Very Sadly!

For <Name Banked Out for Security Purposes> , I know he is a skilled EE and designer, with much experience and math skills,, but note that he is working commercially with his research. He had told me more than once, that he made self running devices,band had shared circuits, although lately he told me that he still could not make the phenomenon stable. So there are gaps still... I will post something, to try to prepare the field , so we can get a common base to discuss some more polemic theories.

Ref: PM to me, 09 November 2021

 

We have the Information to make the Eternal Flashlight! Some tried to Commercialise it!

Best Wishes,

   Chris

We're Light Years Ahead!
Members Online:
What is a Scalar:

In physics, scalars are physical quantities that are unaffected by changes to a vector space basis. Scalars are often accompanied by units of measurement, as in "10 cm". Examples of scalar quantities are mass, distance, charge, volume, time, speed, and the magnitude of physical vectors in general.

You need to forget the Non-Sense that some spout with out knowing the actual Definition of the word Scalar! Some people talk absolute Bull Sh*t!

The pressure P in the formula P = pgh, pgh is a scalar that tells you the amount of this squashing force per unit area in a fluid.

A Scalar, having both direction and magnitude, can be anything! The Magnetic Field, a Charge moving, yet some Numb Nuts think it means Magic Science!

Message from God:

Hello my children. This is Yahweh, the one true Lord. You have found creation's secret. Now share it peacefully with the world.

Ref: Message from God written inside the Human Genome

God be in my head, and in my thinking.

God be in my eyes, and in my looking.

God be in my mouth, and in my speaking.

Oh, God be in my heart, and in my understanding.

Your Support:

More than anything else, your contributions to this forum are most important! We are trying to actively get all visitors involved, but we do only have a few main contributors, which are very much appreciated! If you would like to see more pages with more detailed experiments and answers, perhaps a contribution of another type maybe possible:

PayPal De-Platformed me!

They REFUSE to tell me why!

We now use Wise!

Donate
Use E-Mail: Chris at aboveunity.com

The content I am sharing is not only unique, but is changing the world as we know it! Please Support Us!

Thank You So Much!

Weeks High Earners:
The great Nikola Tesla:

Ere many generations pass, our machinery will be driven by a power obtainable at any point of the universe. This idea is not novel. Men have been led to it long ago by instinct or reason. It has been expressed in many ways, and in many places, in the history of old and new. We find it in the delightful myth of Antheus, who drives power from the earth; we find it among the subtle speculations of one of your splendid mathematicians, and in many hints and statements of thinkers of the present time. Throughout space there is energy. Is this energy static or kinetic? If static, our hopes are in vain; if kinetic - and this we know it is for certain - then it is a mere question of time when men will succeed in attaching their machinery to the very wheelwork of nature.

Experiments With Alternate Currents Of High Potential And High Frequency (February 1892).

Close