2009-06-01 00:22:42     PWM out on gptimer from userspace

Document created by Aaronwu Employee on Aug 22, 2013
Version 1Show Document
  • View in full screen mode

2009-06-01 00:22:42     PWM out on gptimer from userspace

Vivek Vaid (UNITED STATES)

Message: 74882   

 

Hello

 

I am trying to set PWM output on one of the timer pins, from userspace.

 

I am not able to figure out how to use the functions in: uclinux-dist/linux-2.6.x/arch/blackfin/include/asm/gptimers.h from userspace.

 

(Upon reading on usage of i2cdriver from userspace (i2c-tools), i guessed that there should be userspace version of gptimers.h. (like there are <linux/i2c-dev.h> for user and kernelspace). ? )

 

The gptimers wiki page https://docs.blackfin.uclinux.org/doku.php?id=gptimers shows an example of kernelspace. Can it be adapted to userspace?

 

On my BF537 board, I do get 8 char timer devices in /dev/ after enabling the simple-timer interface, but I am trying to find a reference to how to access them through ioctl or read/write.

 

Any pointers or help is appreciated.

 

Thank you,

 

Vivek

QuoteReplyEditDelete

 

 

2009-06-01 00:29:19     Re: PWM out on gptimer from userspace

Mike Frysinger (UNITED STATES)

Message: 74883   

 

the gptimers interface is a kernel API and thus not usable at all from userspace.  use the bfin_simple_timer.c char driver.

QuoteReplyEditDelete

 

 

2009-06-02 02:44:55     Re: PWM out on gptimer from userspace

Vivek Vaid (UNITED STATES)

Message: 74941   

 

Mike

 

Thanks.

 

After Modification of bfin_simple_timer.c, i was able to get pwm-out controlled by ioctl by user-program.

 

This driver is a great starting point for custom drivers by beginners like me.

 

I have a minor question. This driver limits the period/duty to 16 bits. Is there any particular reason ? "

 

    case BFIN_SIMPLE_TIMER_SET_PERIOD:

        if (arg < 2) return -EFAULT;

        n = ((sysclk / 1000) * arg) / 1000;

        if (n > 0xFFFF) n = 0xFFFF;  /* < ----------period and duty limited to 16bits here */

 

It would be nice if the limit is removed or moved to 32bits in upcoming version.

 

Anyway, if I learn how to create/submit patches in time, i will try to update this to a generic one.

 

Or at-leaset add a simple PWM mode.

 

Second question -

 

Currently in my custom driver, i request for a timer pin using constant name (P_TMR5) in function timer_open( ). ie,

 

...

 

set_gptimer_config(timer_code[minor].id, PWM_OUT | PERIOD_CNT );

    err = peripheral_request(P_TMR5, DRV_NAME); /* <<--how to make it generic like timer_code[minor].id*/

...

 

Though in my custom hardware these pins can be fixed, but I was wondering how will i make the reference to pin generic.

 

I am sorry if my post sounds confusing. its quite late.

 

Thank you,

 

Best Regards

 

Vivek

 

if bored, watch my robot dance:   www.youtube.com/watch?v=JqlawTD_9B0

QuoteReplyEditDelete

 

 

2009-06-02 02:53:43     Re: PWM out on gptimer from userspace

Mike Frysinger (UNITED STATES)

Message: 74942   

 

the 16bit limit looks like a bug to me.  i'll remove it in our svn.

 

as for the peripheral_request(), i think it makes sense to create a new gptimer_request() function which you pass the timer id and it takes care of calling peripheral_request() on the right pin.

QuoteReplyEditDelete

 

 

2009-06-04 00:33:18     Re: PWM out on gptimer from userspace

Vivek Vaid (UNITED STATES)

Message: 75139   

 

Mike,

 

For my use I created a generic PWM driver derived from the bfin_simple_timer.c driver. I have tested it on my board (custom) , and attached the driver and demo files here.

 

All this driver does is sets PWM output on the timer pin.

 

If you find thes files useful, you may include them in SVN for next release. However, i think it can be cleaned up a bit more, but I could not because I am a beginner.

 

In the process of testing my driver I found some interesting points -

 

1. Setting pwidth=0, causes 100% Duty output. This is a common bug in most PWM logics in SOCs today which they list as anomaly. However, the BF537 HRM says "0% width is not supported", so it is not an anomaly. (So i added a block for that in my driver. Though an ideal workaround would be to disable the timer when user requests 0% duty. But since its not an official anomaly I didnt implement this workaround.).

 

Thanks,

 

Best Regards,

 

Vivek

 

pwmtest.c

bfin_pwm_timer.c

bfin_pwm_timer.h

QuoteReplyEditDelete

 

 

2009-11-18 21:29:37     Re: PWM out on gptimer from userspace

Craig Jacobson (UNITED STATES)

Message: 82514   

 

Vivek, I've been trying to use this driver.

 

It's to the point where it seems to be compiled into the kernal. (Couldn't get it to work as a loadable module. Likely due to my ignorance).

 

However when the pwmtest is run, receive a error: ioctl returned Err =-1.

 

 

 

Have any suggestions? Are there other dependencies? Kernal is based on most recent stable release.

QuoteReplyEditDelete

 

 

2010-06-17 12:23:57     Re: PWM out on gptimer from userspace

Howard Gordon (UNITED STATES)

Message: 90380   

 

Vivek -

 

Rather than adding a driver, I made some simple changes to bfin_simple_timer.c for use on BF537 to support separately setting period and width, but I'm not getting any PWM output from the processor.  In looking at the gptimers.c  code, I don't see where pPORTF_FER is ever set to actually enable the timer outputs.  However, your post indicates that you were successful in getting timer output.

 

Here's my modified code.  Can you see any reason why this would not work ?  My oscilloscope says that it doesn't...

 

=============================

static int timer_ioctl(struct inode *inode, struct file *filp, uint cmd, unsigned long arg){

    int minor = MINOR(inode->i_rdev);

    static unsigned long period, width;

  

    switch (cmd){

    case BFIN_SIMPLE_TIMER_SET_PERIOD:  // set timer period in microseconds

        if (arg < 2) return -EFAULT;

        period = ((sysclk / 1000) * arg) / 1000;

        set_gptimer_period(timer_code[minor].id, period);

        printk("timer_ioctl TIMER_SET_PERIOD: arg=%lu, period=%lu\n", arg, period);

        break;

    case BFIN_SIMPLE_TIMER_SET_WIDTH:   // set timer pulse width in microseconds

        if (arg < 2) return -EFAULT;

        width = ((sysclk / 1000) * arg) / 1000;

        if (width > period) return -EFAULT;

        set_gptimer_pwidth(timer_code[minor].id, width);

        printk("timer_ioctl TIMER_SET_WIDTH: arg=%lu, width=%lu\n", arg, width);

        break;

    case BFIN_SIMPLE_TIMER_START:

        set_gptimer_config(timer_code[minor].id, PWM_OUT | PERIOD_CNT | PULSE_HI);

        enable_gptimers(timer_code[minor].bit);

        break;

    case BFIN_SIMPLE_TIMER_STOP:

        disable_gptimers(timer_code[minor].bit);

        break;

    case BFIN_SIMPLE_TIMER_READ:

        *((unsigned long*)arg) = isr_count[minor];

        break;

    default:

        return -EINVAL;

    }

    return 0;

}

=============================

 

 

Thanks,

Howard

QuoteReplyEditDelete

 

 

2010-06-17 12:28:29     Re: PWM out on gptimer from userspace

Howard Gordon (UNITED STATES)

Message: 90381   

 

Note that I can successfully access the driver.  From my test code, I'm setting timer 2 to 50% duty cycle at 1kHz, and timer 3 to 20% duty cycle at 1kHz

 

timer_open: device(2) opened

timer_ioctl TIMER_SET_PERIOD: arg=1000, period=121651

timer_ioctl TIMER_SET_WIDTH: arg=500, width=60825

timer_open: device(3) opened

timer_ioctl TIMER_SET_PERIOD: arg=1000, period=121651

timer_ioctl TIMER_SET_WIDTH: arg=200, width=24330

QuoteReplyEditDelete

 

 

2010-06-17 13:13:42     Re: PWM out on gptimer from userspace

Mike Frysinger (UNITED STATES)

Message: 90383   

 

no one writes to any PORT registers directly anymore.  that is a bug in the driver code if you do.  use the portmux framework instead:

https://docs.blackfin.uclinux.org/doku.php?id=portmux

QuoteReplyEditDelete

 

 

2010-06-17 14:31:52     Re: PWM out on gptimer from userspace

Howard Gordon (UNITED STATES)

Message: 90384   

 

Thanks - that fixed it. This is a bit ugly, but the code works -

 

static int timer_ioctl(struct inode *inode, struct file *filp, uint cmd, unsigned long arg){

    int minor = MINOR(inode->i_rdev);

    static unsigned long period, width;

  

    switch (cmd){

    case BFIN_SIMPLE_TIMER_SET_PERIOD:  // set timer period in microseconds

        if (arg < 2) return -EFAULT;

        period = ((sysclk / 1000) * arg) / 1000;

        set_gptimer_period(timer_code[minor].id, period);

        printk("timer_ioctl TIMER_SET_PERIOD: arg=%lu, period=%lu\n", arg, period);

        break;

    case BFIN_SIMPLE_TIMER_SET_WIDTH:   // set timer pulse width in microseconds

        if (arg < 2) return -EFAULT;

        width = ((sysclk / 1000) * arg) / 1000;

        if (width > period) return -EFAULT;

        set_gptimer_pwidth(timer_code[minor].id, width);

        printk("timer_ioctl TIMER_SET_WIDTH: arg=%lu, width=%lu\n", arg, width);

        break;

    case BFIN_SIMPLE_TIMER_START:

        set_gptimer_config(timer_code[minor].id, PWM_OUT | PERIOD_CNT | PULSE_HI);

        enable_gptimers(timer_code[minor].bit);

        switch(minor) {

            case 2:

                peripheral_request(P_TMR2, "/dev/timer");

                break;

            case 3:

                peripheral_request(P_TMR3, "/dev/timer");

                break;

            case 6:

                peripheral_request(P_TMR6, "/dev/timer");

                break;

            case 7:

                peripheral_request(P_TMR7, "/dev/timer");

                break;

        }

        break;

    case BFIN_SIMPLE_TIMER_STOP:

        disable_gptimers(timer_code[minor].bit);

        switch(minor) {

            case 2:

                peripheral_free(P_TMR2);

                break;

            case 3:

                peripheral_free(P_TMR3);

                break;

            case 6:

                peripheral_free(P_TMR6);

                break;

            case 7:

                peripheral_free(P_TMR7);

                break;

        }

        break;

    case BFIN_SIMPLE_TIMER_READ:

        *((unsigned long*)arg) = isr_count[minor];

        break;

    default:

        return -EINVAL;

    }

    return 0;

}

QuoteReplyEditDelete

 

 

2010-06-17 14:40:48     Re: PWM out on gptimer from userspace

Robin Getz (UNITED STATES)

Message: 90385   

 

Howard:

 

Did you miss arch/blackfin/kernel/gptimers.c

 

It appears what you are looking for is a gptimers-dev or something...

 

-Robin

QuoteReplyEditDelete

 

 

2010-06-17 14:56:14     Re: PWM out on gptimer from userspace

Howard Gordon (UNITED STATES)

Message: 90386   

 

Robin -

 

Right - I'm using bfin_simple_timer.c  as the device driver to create /dev/timerx, and that driver is calling functions from gptimers.c.  Longer term, we may choose to create a new device driver for the various SRV1 interfaces under the 2009 framework, but for now, I'm trying to get all of the interfaces working.  The one that continues to give me trouble is simple-gpio, but I'll post about that in a different thread.

 

Howard

QuoteReplyEditDelete

 

 

2010-07-04 10:45:37     Re: PWM out on gptimer from userspace

Howard Gordon (UNITED STATES)

Message: 90878   

 

I thought this was solved with modifications shown above, but I only seem to be able to enable TIMER3 and only rarely have been able to enable TIMER2.  Is TIMER2 used elsewhere in the system such that there might be a conflict ?  I'm now working from the 2009 uClinux release, though I saw the same problem with 2008.

 

Here's a snippet of my application code:

 

    tmr2 = open("/dev/timer2", O_RDWR);

    if (!tmr2)

        puts("unable to open timer2!!");

    ioctl(tmr2, BFIN_SIMPLE_TIMER_SET_PERIOD, 1000);  // set period to 1000usec = 1kHz

    ioctl(tmr2, BFIN_SIMPLE_TIMER_SET_WIDTH, 500);  // set width to 500usec = 50% duty cycle

    ioctl(tmr2, BFIN_SIMPLE_TIMER_START, 0);

    tmr3 = open("/dev/timer3", O_RDWR);

    if (!tmr3)

        puts("unable to open timer3!!");

    ioctl(tmr3, BFIN_SIMPLE_TIMER_SET_PERIOD, 1000);  // set period to 1000usec = 1kHz

    ioctl(tmr3, BFIN_SIMPLE_TIMER_SET_WIDTH, 500);  // set width to 500usec = 50% duty cycle

    ioctl(tmr3, BFIN_SIMPLE_TIMER_START, 0);

 

I added some kprintf's to the driver to trace this and everything looks normal -

 

timer_ioctl TIMER_SET_PERIOD: arg=1000, period=121651

timer_ioctl TIMER_SET_WIDTH: arg=500, width=60825

timer_ioctl TIMER_START: /dev/timer2

timer_ioctl TIMER_SET_PERIOD: arg=1000, period=121651

timer_ioctl TIMER_SET_WIDTH: arg=500, width=60825

timer_ioctl TIMER_START: /dev/timer3

 

Any suggestions ?

 

bfin_simple_timer.h

bfin_simple_timer.c

QuoteReplyEditDelete

 

 

2010-07-04 13:24:51     Re: PWM out on gptimer from userspace

Mike Frysinger (UNITED STATES)

Message: 90880   

 

read the /proc/gpio and /proc/interrupt files to figure out what is being used in your configuration

QuoteReplyEditDelete

 

 

2010-07-04 14:09:27     Re: PWM out on gptimer from userspace

Howard Gordon (UNITED STATES)

Message: 90881   

 

# cat /proc/interrupts

  6:     130165      CORE  Blackfin Timer Tick

16:        189      INTN  i2c-bfin-twi

18:          0      INTN  BFIN_UART_RX

19:         33      INTN  BFIN_UART_TX

82:          8      GPIO  BFIN_UART_CTS

NMI:          0      CORE  Non Maskable Interrupt

Err:          0

 

# cat /proc/timer_list

Timer List Version: v0.4

HRTIMER_MAX_CLOCK_BASES: 2

now at 124383735000 nsecs

 

cpu: 0

clock 0:

  .base:       005a170c

  .index:      0

  .resolution: 4000000 nsecs

  .get_time:   _ktime_get_real

active timers:

clock 1:

  .base:       005a1730

  .index:      1

  .resolution: 4000000 nsecs

  .get_time:   _ktime_get

active timers:

 

# cat /proc/gpio

GPIO_0:     bfin-uart         Peripheral

GPIO_1:     bfin-uart         Peripheral

GPIO_2:     bfin-uart         Peripheral

GPIO_3:     bfin-uart         Peripheral

GPIO_8:     blackfin-cam         Peripheral

GPIO_9:     blackfin-cam         Peripheral

 

 

Timer2 is GPIO_7, but it's also PPI Frame Sync 3.  blackfin_cam.c doesn't seem to be doing anything to FS3 with BF537.

QuoteReplyEditDelete

 

 

2010-07-11 20:31:40     Re: PWM out on gptimer from userspace

Howard Gordon (UNITED STATES)

Message: 91151   

 

The code start working when I switched to a different processor card, so the problem is solved.  I have tested on various BF537 version 1.0.2 and 1.0.3 boards without seeing a recurrence, so there was just something specific to the board I was using.

Outcomes