Simple PWM

All technical discussions and projects around startKIT
grosdodde
Member
Posts: 14
Joined: Tue Nov 24, 2015 4:33 pm

Simple PWM

Post by grosdodde »

Hello,
I'm new at the XMOS community, I read the programming guide and manipulate the tcp/ip example every thing is fine but since I tried to implement a stupid PWM I'm going crazy.
This is my minimum example for the Startkit the LED flash but not as long as i suspected

Code: Select all

/*
 * Stupid.xc
 *
 */


#include <xs1.h>
#include <timer.h>

out port d1 = XS1_PORT_1A;

void dimm(out port p)
{
    timer tmr;
    unsigned int timestamp;
    unsigned int delay = 100000000;
    unsigned int dimmvalue = delay*0.5;
    unsigned int portstamp;

    tmr :> timestamp;
    p <: 0 @ portstamp;
    while(1)
        {
            select
            {
                case tmr when timerafter(timestamp) :> void:
                    p <: 1 @ portstamp;
                    portstamp += dimmvalue;
                    p @ portstamp <: 0;
                    timestamp += delay;
                break;
            }
        }
}

int main()
{
    par
    {
        dimm(d1);
    }
    return 0;
}

Can anyone help me to make my own PWM ?


grosdodde
Member
Posts: 14
Joined: Tue Nov 24, 2015 4:33 pm

Post by grosdodde »

I'd read this post before I posted my question.
But i don't like to use a ready library and the last post is similar to my code but doesn't work but why?

I also wrote code that works but it doesn't looks good.

Code: Select all

/*
 * PWM.xc
 *
 */


#include <xs1.h>
#include <timer.h>
#include <stdio.h>
#include <xscope.h>


#define TIMER_FREQ 100000000
#define PWM_FREQ  10000

out port d1 = XS1_PORT_1A;
out port d2 = XS1_PORT_1D;

interface dimm_interface
{
    [[clears_notification]]int getnewDimmValue(void);

    [[notification]] slave void newDimmValue(void);
};

//[[combinable]]
void calcdimmvalue(int resolution, int time, server interface dimm_interface inf)
{
    timer tmr;
    int timestamp;
    int delay = (time*TIMER_FREQ)/resolution;
    int dimmvalue = 0;

    tmr :> timestamp;
    while(1)
    {
        select
        {
            case tmr when timerafter(timestamp) :> void:
                if(dimmvalue < resolution)
                {dimmvalue++;}
                else
                {dimmvalue=0;}
                inf.newDimmValue();
                timestamp += delay;
            break;

            case inf.getnewDimmValue()  -> int return_val:
                return_val = dimmvalue;
            break;
        }
    }
}

void dimm(out port p, int freq, int res, client interface dimm_interface inf)
{
    timer tmr;
    double duty = 50;
    unsigned int timestamp;
    unsigned int delay = TIMER_FREQ/(freq*res);
    char ledState = 0;
    unsigned int count;

    tmr :> timestamp;
    while(1)
        {
            select
            {
                case tmr when timerafter(timestamp) :> void:
                     if (count >= res*duty)
                     {
                         if(ledState)
                         {
                             p <: 0;
                             ledState=0;
                         }
                     }
                     else
                    {
                         if(!ledState)
                         {
                             p <: 1;
                             ledState=1;
                         }
                    }
                    if(count >= res){count = 0;}
                    count++;
                    timestamp += delay;
                break;

                case inf.newDimmValue():
                    duty = inf.getnewDimmValue();
                    duty = duty/res;
                break;
            }
        }
}


int main()
{
    interface dimm_interface inf;
    int res = 100;
    par
    {
        calcdimmvalue(res, 2, inf);//resolution,time,interface
        dimm(d1, 1000, res, inf);//port,freq,res,interface
    }
    return 0;
}

But what is wrong with the timestamp call in the code at the top
hkr87
Member
Posts: 12
Joined: Tue Oct 13, 2015 11:36 am

Post by hkr87 »

There is two different ways to create PWM.

The accurate method just uses port counters:

Code: Select all

  int port_count;
  int dim_value = 1000;
  int dim_period = 4000;
  p <: 0 @ port_count;
  port_count += dim_period;
  while(1) {
    p @ port_count <: 0;
    p @ port_count + dim_value <: 1;
    port_count += dim_period;
    select {
    case dim_chan :> dim_value: break;
    default: break;
    }
  }
This sets a port high at one time, low at the next time, and as long as your dim_period is less than 65536 and your dim_value is not to close to 0 or to dim_period all is well and clock cycle accurate.

The other method uses a timer:

Code: Select all

  timer tmr;
  int port_count;
  int dim_value = 1000;
  int dim_period = 4000;
  int state = 0;
  tmr :> port_count;
  port_count += dim_period;
  while(1) {
    select {
    case tmr when timerafter(port_count) :> void:
      p <: state;
      state = !state;
      if (state) port_count += dim_value;
      else port_count += dim_period - dim_value;
      break;
    case dim_chan :> dim_value: break;
    }
  }
This can cope with very large dim_periods, and but it may occasionally be off; because the channel was ready just when you were meant to take the timer branch. The good news is that you can put many other things in the select; even multiple PWM if you want.
Last edited by hkr87 on Wed Nov 25, 2015 8:47 am, edited 1 time in total.
grosdodde
Member
Posts: 14
Joined: Tue Nov 24, 2015 4:33 pm

Post by grosdodde »

Thank you very much. Both works fine
65536 because the stamp Register has only 16 Bit?
If I want to slow it down I have to manipulate the port clock?
hkr87
Member
Posts: 12
Joined: Tue Oct 13, 2015 11:36 am

Post by hkr87 »

Yes and yes - the timestamp register has only 16 bits, and you can connect it to a slower clock.

I just noticed an error in the second code segment - it had an '@ port_count' in the port output that I removed.

You can come up with a creative scheme where you use a timer to wait for approximately the right time and then use the port_counter to create an edge at the precise time.
grosdodde
Member
Posts: 14
Joined: Tue Nov 24, 2015 4:33 pm

Post by grosdodde »

Ok sounds good.
if i look at the description of "configure_clock_rate" I do not understand the terms in the brackets. Does anyone known the maximum factor of division?

Code: Select all

/**
 * Configures a clock to run at a rate of (\a a/\a b) MHz. If the specified rate
 * is not supported by the hardware, an exception is raised.
 * The hardware supports rates of \e ref MHz and rates of the form
 * (\e ref/\e 2n) MHz or (\e tileclk/\e 2n) MHz where \e ref is the reference
 * clock frequency, \e tileclk is the xCORE tile frequency and \e n is a number
 * in the range 1 to 255 inclusive.
*/
grosdodde
Member
Posts: 14
Joined: Tue Nov 24, 2015 4:33 pm

Post by grosdodde »

250 look like the upper limit but with this code only dimm4 produce a visible blinking

Code: Select all

void dimm3(out port p)
{
    int port_count;
    int dim_value = 30000;
    int dim_period = 60000;

    configure_clock_rate(clk, 250, 1);
    configure_out_port(p, clk, 0);
    start_clock(clk);

    p <: 0 @ port_count;
    port_count += dim_period;

    while (1)
    {
        p @ port_count <: 0;
        p @ port_count + (dim_period-dim_value) <: 1;
        port_count += dim_period;
    }
}

void dimm4(out port p)
{
    timer tmr;
    int port_count;
    int dim_period = 250*60000;
    int dim_value = dim_period*0.5;
    int state = 0;
    tmr :> port_count;
    port_count += dim_period;
    while (1) {
        select {
            case tmr when timerafter(port_count) :> void:
                p @ port_count <: state;
                state = !state;
                if (state)
                    port_count += dim_period - dim_value;
                else
                    port_count += dim_value;
            break;
        }
    }
}
hkr87
Member
Posts: 12
Joined: Tue Oct 13, 2015 11:36 am

Post by hkr87 »

configure_clock_rate(clk,250,1) sets the clock frequency to 250/1 MHz, that is 250/1 = 250 MHz.

configure_clock_rate(clk,100,250) should the clock frequency to 100/250 MHz, 400 KHz. With your 60,000 period that should be visible?

Of - and you ought to remove the @ port_counter in your second example; I put it in there by mistake.
grosdodde
Member
Posts: 14
Joined: Tue Nov 24, 2015 4:33 pm

Post by grosdodde »

Thank you very much!
configure_clock_rate(clk, 1, 5);
make a Frequenz of 6.666... Hz total visible
Post Reply