Table of Contents

PWM


Pulse Width Modulation (PWM) is a very useful feature found on most microcontrollers. PWM is a method of generating a square wave signal of uniform frequency with variable duty cycle. PWM is often used to generate analog voltages, but has many other uses such as generating digital pulses for driving servo motors or driving infrared LEDs for communication. The ratio of the pulse width to it's period is called the duty cycle. Through software, you can control both the PWM frequency and duty cycle.

Tip

We usually use GetDefault() for most peripherals. For example, there is only one GPIO controller on most systems. This is not the case with PWM. Never use the Default controller and always select the proper channel on the corresponding controller.

Tip

PWM2.3 is channel 3 on controller 2.

Energy Level

PWM is perfect for dimming an LED or controlling the speed of a motor. When the duty cycle is 50%, half the energy is transferred to the attached load.

var controller = PwmController.FromName(SC20260.Timer.Pwm.Controller3.Id);
var led = controller.OpenChannel(SC20260.Timer.Pwm.Controller3.PB0);
controller.SetDesiredFrequency(10000);

double duty = 0.5, speed = 0.01;

led.Start();

while (true) {
    if (duty <= 0 || duty >= 1.0) {
        speed *= -1;    //Reverse direction.
        duty += speed;
    }

    led.SetActiveDutyCyclePercentage(duty);
    duty += speed;

    Thread.Sleep(10);   //Always give the system time to think!
}  

Musical Tones

Musical notes have specific frequencies; C for example is about 261Hz. Plugging these numbers into an array and knowing the length of each tone is all that is needed to play some simple music. When playing notes by changing the frequency, keep the duty cycle set to 0.5.

The following example is written for the SC20100S Dev Board.

using GHIElectronics.TinyCLR.Devices.Pwm;
using GHIElectronics.TinyCLR.Pins;
using System.Threading;

class Program {
    const int NOTE_C = 261;
    const int NOTE_D = 294;
    const int NOTE_E = 330;
    const int NOTE_F = 349;
    const int NOTE_G = 392;

    const int WHOLE_DURATION = 2000;
    const int EIGHTH = WHOLE_DURATION / 8;
    const int QUARTER = WHOLE_DURATION / 4;
    const int QUARTERDOT = WHOLE_DURATION / 3;
    const int HALF = WHOLE_DURATION / 2;
    const int WHOLE = WHOLE_DURATION;

    //Make sure the two below arrays match in length. Each duration element corresponds to
    //  one note element.
    private static int[] note = { NOTE_E, NOTE_E, NOTE_F, NOTE_G, NOTE_G, NOTE_F, NOTE_E,
                          NOTE_D, NOTE_C, NOTE_C, NOTE_D, NOTE_E, NOTE_E, NOTE_D,
                          NOTE_D, NOTE_E, NOTE_E, NOTE_F, NOTE_G, NOTE_G, NOTE_F,
                          NOTE_E, NOTE_D, NOTE_C, NOTE_C, NOTE_D, NOTE_E, NOTE_D,
                          NOTE_C, NOTE_C};

    private static int[] duration = { QUARTER, QUARTER, QUARTER, QUARTER, QUARTER, QUARTER,
                              QUARTER, QUARTER, QUARTER, QUARTER, QUARTER, QUARTER,
                              QUARTERDOT, EIGHTH, HALF, QUARTER, QUARTER, QUARTER, QUARTER,
                              QUARTER, QUARTER, QUARTER, QUARTER, QUARTER, QUARTER,
                              QUARTER, QUARTER, QUARTERDOT, EIGHTH, WHOLE};

    private static void Main() {
        var controller = PwmController.FromName(SC20100.Timer.Pwm.Controller3.Id);
        var toneOut = controller.OpenChannel(SC20100.Timer.Pwm.Controller3.PB1);
        toneOut.SetActiveDutyCyclePercentage(0.5);

        while (true) {
            toneOut.Start();

            for (int i = 0; i < note.Length; i++) {
                controller.SetDesiredFrequency(note[i]);
                Thread.Sleep(duration[i]);
            }

            toneOut.Stop();

            Thread.Sleep(1000);
        }
    }
}

Software PWM

The built-in support for PWM uses hardware to generate signals very accurately with zero processing. However, hardware-PWM only worked on specific pins. TinyCLR supports PWM on any pin, generated by software.

While software PWM is not very accurate and requires constant processor time to generate the signal, it works well at lower frequencies. This is good for dimming lights or driving servos.


var softwarePwmController = PwmController.FromName(SC20260.Timer.Pwm.Software.Id);
var pwmPinPB3 = softwarePwmController.OpenChannel(SC20260.GpioPin.PB3);

softwarePwmController.SetDesiredFrequency(1000); // set frequency 1KHz
pwmPinPB3.SetActiveDutyCyclePercentage(0.5);

pwmPinPB3.Start();

Thread.Sleep(10 * 1000); //Delay 10 second

pwmPinPB3.Stop();

Tip

On SITCore, the range the range of PWM frequency is 15.4Hz to 10KHz.


Servo Motors

Servo motors use PWM to generate the necessary signal. A NuGet package is provided to simplify the use of servos. The example below uses the software PWM feature, allowing any digital pin to be used. Hardware PWM can be used as well.

Tip

As a rule, servos have three wires. Be sure the central wire is connected to 5V. The lighter color wire on one side of the connector is a signal and should be connected to a PWM pin. The third one, which is usually a darker color, is ground.

Tip

Needed NuGets: System.Threading, GHIElectronics.TinyCLR.Pins, GHIElectronics.TinyCLR.Devices.Pwm, GHIElectronics.TinyCLR.Drivers.Motor.Servo

var softwarePwmController = PwmController.FromName(SC20260.Timer.Pwm.Software.Id);
var pwmPinPB3 = softwarePwmController.OpenChannel(SC20260.GpioPin.PB3);

var servo = new ServoController(softwarePwmController, pwmPinPB3);

servo.Set(0); // 0 degree

Thread.Sleep(2000);

servo.Set(45.0); // 45 degree

Thread.Sleep(2000);

servo.Set(90.0);  // 90 degree

Thread.Sleep(2000);

servo.Set(180.0); // 180 degree

Thread.Sleep(4000);
 
Tip

Typically, servos will have a 1ms - 2ms pulse width, but not always.