Page 1 of 2
Simple PWM
Posted: Tue Nov 24, 2015 4:46 pm
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 ?
Re: Simple PWM
Posted: Tue Nov 24, 2015 5:00 pm
by mon2
Re: Simple PWM
Posted: Tue Nov 24, 2015 7:21 pm
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
Re: Simple PWM
Posted: Tue Nov 24, 2015 8:07 pm
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.
Re: Simple PWM
Posted: Wed Nov 25, 2015 8:39 am
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?
Re: Simple PWM
Posted: Wed Nov 25, 2015 8:47 am
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.
Re: Simple PWM
Posted: Wed Nov 25, 2015 9:18 am
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.
*/
Re: Simple PWM
Posted: Wed Nov 25, 2015 9:51 am
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;
}
}
}
Re: Simple PWM
Posted: Wed Nov 25, 2015 10:30 am
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.
Re: Simple PWM
Posted: Wed Nov 25, 2015 12:10 pm
by grosdodde
Thank you very much!
configure_clock_rate(clk, 1, 5);
make a Frequenz of 6.666... Hz total visible