Im trying to implement a PWM controller in software.
I started by copying testsuite/klatency into testsuite/kpwm,
and changed all 'latency' to 'pwm', then sliced out the
many things I dont need.


Ive now got something that makes (w a few minor glitches..)
and runs, albeit incorrectly.

soekris:~/ts# ./run
*
*
* Type ^C to stop this application.
*
*
rtai_hal: no version for "struct_module" found: kernel tainted.
Adeos: Domain RTAI registered.
RTAI: hal/x86 loaded.
Adeos: Domain IShield registered.
RTAI: fusion core v0.8.2 (Satch Boogie) started.
RTAI: starting native API services.
kpwm_rt: initialized
RTAI: kpwm: setting alarm: 145000000d
RTAI: kpwm: sending pipe message
RTAI: kpwm: pipe message rcvr not ready
RTAI: kpwm: sending pipe message
RTAI: kpwm: pipe message rcvr not ready
open(/dev/rtp0): Success, fd: 3
got msg from kernel: Thu Jul  7 05:09:10 2005
got msg from kernel: Thu Jul  7 05:09:11 2005
got msg from kernel: Thu Jul  7 05:09:13 2005
got msg from kernel: Thu Jul  7 05:09:14 2005
got msg from kernel: Thu Jul  7 05:09:15 2005
got msg from kernel: Thu Jul  7 05:09:16 2005
got msg from kernel: Thu Jul  7 05:09:18 2005
got msg from kernel: Thu Jul  7 05:09:19 2005


The problem is that the actual time between messages is 55 seconds, not 1 second.


Im sure its a problem with my code, ie some usage error.
More specifically, I suspect that it might be because I dont have a separate
task servicing the alarm. I have 2 reasons for not doing so, maybe both are wrong..

1. I dont know how to put the alarm-handler into a newly created task
Ive tried several permutations:
a. creating both task and alarm in init, but not starting task
b. starting task
c. moving alarm create,start into handler routine for start-task.

2. its not clear that I need a separate task, afterall, the alarm has a handler routine,
its presumably run automatically in primary mode.


Since the list doesnt accept attachments (tgzs anyway, I tried) Im cut-pasting the file where I think the problem resides. I can send tarball off-list if its helpful.

Any insight you can prod me towards is appreciated.
jimc



#include <rtai/task.h>
#include <rtai/timer.h>
#include <rtai/pipe.h>
#include <rtai/alarm.h>
#include "kpwm.h"

#include <linux/nsc_gpio.h>
extern struct gpio_access_methods *pc87366_access;

#define NAME "kpwm_rt"
MODULE_LICENSE("GPL");


// 3-state pwm signal generator (ONE technically has sub-state)

#define BOTH  0
#define ONE   1
#define NONE  2

int pwm_state = NONE;

// ONE's sub-state determines which output sig to change
int short_sig;  // chooses shorter signal, ie which times out 1st  (0,1)
int long_sig;   // chooses longer signal, ie which times out 1st  (0,1)

#define ONESHOT_LATENCY_BY2   0        // see ONE state in timer_sig_hndlr()
#define TASK_PERIOD_NS    16000000    // 16 mSec

int pwm_period_ns = TASK_PERIOD_NS;
module_param(pwm_period_ns,int,0444);
MODULE_PARM_DESC(pwm_period_ns, "PWM period in ns (default: 16*10e6)");


RT_TASK pwm_task;
RT_PIPE pipe;
RT_ALARM pwm_alarm;

// 1 dwell-timer for each state
RTIME pwm_timer[3] = { 1.5*10e6, 1*10e6, 14.5*10e6 };

// pin minor numbers to write vals to
int pinindex[2] = { 22,23 };

struct _cookie {
   int i;
} cookie;

#define MONMSGS 20 // 000
int loopct = 0;
long totct = 0;

void one_shot (RTIME expiration)
{
   int err;
   xnarch_logerr("kpwm: setting alarm: %lu\n", expiration);

   err = rt_alarm_start(&pwm_alarm, expiration, TM_INFINITE);
   if (err) {
   xnarch_logerr("kpwm: failed to set alarm, code %d\n",err);
   }
}

void writepin (int pin, int val)
{
   xnarch_logerr("kpwm: writing pin %d\n", pin);
   // pc87366_access->gpio_change(pinindex[pin]);
   // printk(KERN_INFO " writing %d to %d\n", val, pin);
}

void send_msg(void)
{
   struct rtai_kpwm_stat *s;
   RT_PIPE_MSG *msg;

   msg = rt_pipe_alloc(sizeof(struct rtai_kpwm_stat));
   if (!msg) {
   xnarch_logerr("kpwm: cannot allocate pipe message\n");
   return;
   }
   s = (struct rtai_kpwm_stat *)P_MSGPTR(msg);
   s->dwell_0 = MONMSGS;
   s->dwell_1 = MONMSGS;
/* Do not care if the user-space side of the pipe is not yet
      open; just enter the next sampling loop then retry. But in
      the latter case, we need to free the unsent message by
      ourselves. */
xnarch_logerr("kpwm: sending pipe message\n");

   if (rt_pipe_send(&pipe,msg,sizeof(*s),0) != sizeof(*s)) {
   rt_pipe_free(msg);
   xnarch_logerr("kpwm: pipe message rcvr not ready\n");
   }
}


void pwm_generator (RT_ALARM *alm, void *cookie)
{
   // for (;;) {
   xnarch_logerr("pwm: setting alarm\n");
switch (pwm_state) { case NONE:
   pwm_state = BOTH;           // transition to next state
   one_shot(pwm_timer[BOTH]);  // start its timer
// activate both signals
   writepin (0, 1);
   writepin (1, 1);
   break;
case BOTH:
   pwm_state = ONE;            // transition to next state
/* if both signals should be the 'same' (ex: car is at rest), then
      special handling *may* be in order...
Theres no need for the ONE state (of non-zero duration), and
      having it enter that state, then set the timer to leave it,
      will cause a non-zero dwell-time in that state.
We fix by immediately transitioning out of ONE to NONE, and not
      using the timer to do so.
The actual criterion for 'same' is {x-y < ONESHOT_LATENCY / 2},
      ie 1/2 the average latency.  if x-y is closer to 0 than the
      measured latency, we skip the ONE state.
HOWEVER .. be careful here, cuz RTAI does some of its own
      calibrating, which could skew/invalidate this.
   */
   // drop the shorter signal
   writepin (0,0);
if (pwm_timer[ONE] < ONESHOT_LATENCY_BY2) {
       writepin (1, 0);    // take care of 'our' signal
       goto S1;        // transition immediately
   }
   one_shot(pwm_timer[ONE]);    // start its timer
break; case ONE:
   S1:
   pwm_state = NONE;           // transition to next state
   one_shot(pwm_timer[NONE]);  // start its timer
// drop the other longer signal
   writepin (0,0);
   break;
default:
   pwm_state = NONE;           // transition to next state
   }
   /* do whatever monitor stuff seems useful */
   totct++;
   if (loopct++ > MONMSGS) {
   loopct = 0;
   send_msg();
   }
}

void start_pwm (void* cookie)
{
   int err;
   err = rt_alarm_create(&pwm_alarm, "pwm_alarm", &pwm_generator, &cookie);
   if (err) {
   xnarch_logerr("pwm: cannot create alarm, code %d\n",err);
   return;
   }

   xnarch_logerr("start_pwm called\n");
   one_shot (pwm_timer[NONE]);
}

void gpio_init (void)
{
   // pc87366_access->gpio_set_low(pinindex[0]);
   // pc87366_access->gpio_set_low(pinindex[1]);

   // turn on output enable
   pc87366_access->gpio_config(pinindex[0], ~0, 1);
   pc87366_access->gpio_config(pinindex[1], ~0, 1);
}


int __pwm_init (void)
{
   int err;
err = rt_timer_start(TM_ONESHOT);
   if (err) {
   xnarch_logerr("pwm: cannot start oneshot timer, code %d\n", err);
   return 1;
   }

   err = rt_task_create(&pwm_task,"kpwm",0,99,0);
   if (err) {
   xnarch_logerr("pwm: failed to create pwm task, code %d\n",err);
   return 2;
   }

   err = rt_pipe_create(&pipe,"kpwmctl",0);
   if (err) {
   xnarch_logerr("pwm: failed to open real-time pipe, code %d\n",err);
   return 3;
   }

   err = rt_task_start(&pwm_task,&start_pwm,&cookie);
   if (err) {
   xnarch_logerr("pwm: failed to start pwm task, code %d\n",err);
   return 4;
   }

   printk(KERN_INFO NAME ": initialized\n");

   // gpio_init();

   send_msg();
   for (err=0; err<100000000; err++) ;
   send_msg();
   return 0;
}

void __pwm_exit (void)

{
   int err;

   rt_timer_stop();

   rt_task_delete(&pwm_task);

   err = rt_pipe_delete(&pipe);

   if(err)
       xnarch_logerr("Warning: could not delete pipe: err=%d.\n",err);

   /* turn off output enable on 2 pins */
   // pc87366_access->gpio_config(pinindex[0], ~1, 0);
   // pc87366_access->gpio_config(pinindex[1], ~1, 0);

   printk(KERN_INFO NAME ": exited after %d loops\n", loopct);
}

module_init(__pwm_init);
module_exit(__pwm_exit);

/*
   Local variables:
       compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
       c-basic-offset: 4
   End:
*/

In part, this is cuz its not clear

Reply via email to