Microcontroller PWM cheap and easy start to getting something working

  • Topic Is Sticky
  • 329 Views
  • Last Post 3 days ago
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

  • Liked by
  • strape
  • Augenblick
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

  • Liked by
  • strape
  • Augenblick
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

  • Liked by
  • strape
  • Augenblick
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

 

  • Liked by
  • Augenblick
  • baerndorfer
Ourbobby posted this 3 days ago

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.

Members Online:
Since Nov 27 2018
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:

Donate (PayPal)

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

Donate (Patreon)

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 go 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