#include "rtapi.h"
#include "rtapi_app.h"
#include "rtapi_string.h"
#include "hal.h"
MODULE_AUTHOR("John Kasunich");
MODULE_DESCRIPTION("Encoder Counter for EMC HAL");
MODULE_LICENSE("GPL");
static int num_chan;
static int default_num_chan=3;
static int howmany;
RTAPI_MP_INT(num_chan, "number of encoder channels");
#define MAX_CHAN 8
char *names[MAX_CHAN] = {0,};
RTAPI_MP_ARRAY_STRING(names, MAX_CHAN, "names of encoder");
typedef struct {
char count_detected;
char index_detected;
char latch_detected;
rtapi_s32 raw_count;
rtapi_u32 timestamp;
rtapi_s32 index_count;
rtapi_s32 latch_count;
} atomic;
typedef struct {
unsigned char state;
unsigned char oldZ;
unsigned char Zmask;
hal_bit_t *x4_mode;
hal_bit_t *counter_mode;
atomic buf[2];
volatile atomic *bp;
hal_s32_t *raw_counts;
hal_bit_t *phaseA;
hal_bit_t *phaseB;
hal_bit_t *phaseZ;
hal_bit_t *index_ena;
hal_bit_t *reset;
hal_bit_t *latch_in;
hal_bit_t *latch_rising;
hal_bit_t *latch_falling;
rtapi_s32 raw_count;
rtapi_u32 timestamp;
rtapi_s32 index_count;
rtapi_s32 latch_count;
hal_s32_t *count;
hal_s32_t *count_latch;
hal_float_t *min_speed;
hal_float_t *pos;
hal_float_t *pos_interp;
hal_float_t *pos_latch;
hal_float_t *vel;
hal_float_t *vel_rpm;
hal_float_t *pos_scale;
hal_bit_t old_latch;
double old_scale;
double scale;
int counts_since_timeout;
} counter_t;
static rtapi_u32 timebase;
static counter_t *counter_array;
#define SM_PHASE_A_MASK 0x01
#define SM_PHASE_B_MASK 0x02
#define SM_LOOKUP_MASK 0x0F
#define SM_CNT_UP_MASK 0x40
#define SM_CNT_DN_MASK 0x80
static const unsigned char lut_x4[16] = {
0x00, 0x44, 0x88, 0x0C, 0x80, 0x04, 0x08, 0x4C,
0x40, 0x04, 0x08, 0x8C, 0x00, 0x84, 0x48, 0x0C
};
static const unsigned char lut_x1[16] = {
0x00, 0x44, 0x08, 0x0C, 0x80, 0x04, 0x08, 0x0C,
0x00, 0x04, 0x08, 0x0C, 0x00, 0x04, 0x08, 0x0C
};
static const unsigned char lut_ctr[16] = {
0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
static int comp_id;
static int export_encoder(counter_t * addr,char * prefix);
static void update(void *arg, long period);
static void capture(void *arg, long period);
int rtapi_app_main(void)
{
int n, retval, i;
counter_t *cntr;
if(num_chan && names[0]) {
rtapi_print_msg(RTAPI_MSG_ERR,"num_chan= and names= are mutually exclusive\n");
return -EINVAL;
}
if(!num_chan && !names[0]) num_chan = default_num_chan;
if(num_chan) {
howmany = num_chan;
} else {
howmany = 0;
for (i = 0; i < MAX_CHAN; i++) {
if ( (names[i] == NULL) || (*names[i] == 0) ){
break;
}
howmany = i + 1;
}
}
if ((howmany <= 0) || (howmany > MAX_CHAN)) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER: ERROR: invalid number of channels: %d\n", howmany);
return -1;
}
comp_id = hal_init("encoder");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "ENCODER: ERROR: hal_init() failed\n");
return -1;
}
counter_array = hal_malloc(howmany * sizeof(counter_t));
if (counter_array == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER: ERROR: hal_malloc() failed\n");
hal_exit(comp_id);
return -1;
}
timebase = 0;
i = 0; for (n = 0; n < howmany; n++) {
cntr = &(counter_array[n]);
if(num_chan) {
char buf[HAL_NAME_LEN + 1];
rtapi_snprintf(buf, sizeof(buf), "encoder.%d", n);
retval = export_encoder(cntr,buf);
} else {
retval = export_encoder(cntr,names[i++]);
}
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER: ERROR: counter %d var export failed\n", n);
hal_exit(comp_id);
return -1;
}
cntr->state = 0;
cntr->oldZ = 0;
cntr->Zmask = 0;
*(cntr->x4_mode) = 1;
*(cntr->counter_mode) = 0;
*(cntr->latch_rising) = 1;
*(cntr->latch_falling) = 1;
cntr->buf[0].count_detected = 0;
cntr->buf[1].count_detected = 0;
cntr->buf[0].index_detected = 0;
cntr->buf[1].index_detected = 0;
cntr->bp = &(cntr->buf[0]);
*(cntr->raw_counts) = 0;
cntr->raw_count = 0;
cntr->timestamp = 0;
cntr->index_count = 0;
cntr->latch_count = 0;
*(cntr->count) = 0;
*(cntr->min_speed) = 1.0;
*(cntr->pos) = 0.0;
*(cntr->pos_latch) = 0.0;
*(cntr->vel) = 0.0;
*(cntr->vel_rpm) = 0.0;
*(cntr->pos_scale) = 1.0;
cntr->old_scale = 1.0;
cntr->scale = 1.0;
cntr->counts_since_timeout = 0;
}
retval = hal_export_funct("encoder.update-counters", update,
counter_array, 0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER: ERROR: count funct export failed\n");
hal_exit(comp_id);
return -1;
}
retval = hal_export_funct("encoder.capture-position", capture,
counter_array, 1, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER: ERROR: capture funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,
"ENCODER: installed %d encoder counters\n", howmany);
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void)
{
hal_exit(comp_id);
}
static void update(void *arg, long period)
{
counter_t *cntr;
atomic *buf;
int n;
unsigned char state;
int latch, old_latch, rising, falling;
cntr = arg;
for (n = 0; n < howmany; n++) {
buf = (atomic *) cntr->bp;
state = cntr->state;
if (*(cntr->phaseA)) {
state |= SM_PHASE_A_MASK;
}
if (*(cntr->phaseB)) {
state |= SM_PHASE_B_MASK;
}
if ( *(cntr->counter_mode) ) {
state = lut_ctr[state & (SM_LOOKUP_MASK & ~SM_PHASE_B_MASK)];
} else if ( *(cntr->x4_mode) ) {
state = lut_x4[state & SM_LOOKUP_MASK];
} else {
state = lut_x1[state & SM_LOOKUP_MASK];
}
if (state & SM_CNT_UP_MASK) {
(*cntr->raw_counts)++;
buf->raw_count = *(cntr->raw_counts);
buf->timestamp = timebase;
buf->count_detected = 1;
} else if (state & SM_CNT_DN_MASK) {
(*cntr->raw_counts)--;
buf->raw_count = *(cntr->raw_counts);
buf->timestamp = timebase;
buf->count_detected = 1;
}
cntr->state = state;
state = cntr->oldZ << 1;
if (*(cntr->phaseZ)) {
state |= 1;
}
cntr->oldZ = state & 3;
if ((state & cntr->Zmask) == 1) {
buf->index_count = *(cntr->raw_counts);
buf->index_detected = 1;
cntr->Zmask = 0;
}
latch = *(cntr->latch_in), old_latch = cntr->old_latch;
rising = latch && !old_latch;
falling = !latch && old_latch;
if((rising && *(cntr->latch_rising))
|| (falling && *(cntr->latch_falling))) {
buf->latch_detected = 1;
buf->latch_count = *(cntr->raw_counts);
}
cntr->old_latch = latch;
cntr++;
}
timebase += period;
}
static void capture(void *arg, long period)
{
counter_t *cntr;
atomic *buf;
int n;
rtapi_s32 delta_counts;
rtapi_u32 delta_time;
double vel, interp;
cntr = arg;
for (n = 0; n < howmany; n++) {
buf = (atomic *) cntr->bp;
if ( buf == &(cntr->buf[0]) ) {
cntr->bp = &(cntr->buf[1]);
} else {
cntr->bp = &(cntr->buf[0]);
}
if ( buf->index_detected ) {
buf->index_detected = 0;
cntr->index_count = buf->index_count;
*(cntr->index_ena) = 0;
}
if ( buf->latch_detected ) {
buf->latch_detected = 0;
cntr->latch_count = buf->latch_count;
}
if (*(cntr->index_ena)) {
cntr->Zmask = 3;
} else {
cntr->Zmask = 0;
}
if ( *(cntr->pos_scale) != cntr->old_scale ) {
cntr->old_scale = *(cntr->pos_scale);
if ((*(cntr->pos_scale) < 1e-20) && (*(cntr->pos_scale) > -1e-20)) {
*(cntr->pos_scale) = 1.0;
}
cntr->scale = 1.0 / *(cntr->pos_scale);
}
if ( *(cntr->min_speed) == 0 ) {
*(cntr->min_speed) = 1;
}
if (*(cntr->reset)) {
cntr->raw_count = *(cntr->raw_counts);
cntr->index_count = cntr->raw_count;
}
if ( buf->count_detected ) {
buf->count_detected = 0;
delta_counts = buf->raw_count - cntr->raw_count;
delta_time = buf->timestamp - cntr->timestamp;
cntr->raw_count = buf->raw_count;
cntr->timestamp = buf->timestamp;
if ( cntr->counts_since_timeout < 2 ) {
cntr->counts_since_timeout++;
} else {
vel = (delta_counts * cntr->scale ) / (delta_time * 1e-9);
*(cntr->vel) = vel;
}
} else {
if ( cntr->counts_since_timeout ) {
delta_time = timebase - cntr->timestamp;
if ( delta_time < 1e9 / ( *(cntr->min_speed) * cntr->scale )) {
vel = ( cntr->scale ) / (delta_time * 1e-9);
if ( vel < 0.0 ) vel = -vel;
if ( vel < *(cntr->vel) ) {
*(cntr->vel) = vel;
}
if ( -vel > *(cntr->vel) ) {
*(cntr->vel) = -vel;
}
} else {
cntr->counts_since_timeout = 0;
*(cntr->vel) = 0;
}
} else {
*(cntr->vel) = 0;
}
}
*(cntr->vel_rpm) = *(cntr->vel) * 60.0;
*(cntr->count) = cntr->raw_count - cntr->index_count;
*(cntr->count_latch) = cntr->latch_count - cntr->index_count;
*(cntr->pos) = *(cntr->count) * cntr->scale;
*(cntr->pos_latch) = *(cntr->count_latch) * cntr->scale;
delta_time = timebase - cntr->timestamp;
interp = *(cntr->vel) * (delta_time * 1e-9);
*(cntr->pos_interp) = *(cntr->pos) + interp;
cntr++;
}
}
static int export_encoder(counter_t * addr,char * prefix)
{
int retval, msg;
msg = rtapi_get_msg_level();
rtapi_set_msg_level(RTAPI_MSG_WARN);
retval = hal_pin_bit_newf(HAL_IN, &(addr->phaseA), comp_id,
"%s.phase-A", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->phaseB), comp_id,
"%s.phase-B", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->phaseZ), comp_id,
"%s.phase-Z", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IO, &(addr->index_ena), comp_id,
"%s.index-enable", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->reset), comp_id,
"%s.reset", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->latch_in), comp_id,
"%s.latch-input", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->latch_rising), comp_id,
"%s.latch-rising", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->latch_falling), comp_id,
"%s.latch-falling", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_s32_newf(HAL_OUT, &(addr->raw_counts), comp_id,
"%s.rawcounts", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_s32_newf(HAL_OUT, &(addr->count), comp_id,
"%s.counts", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_s32_newf(HAL_OUT, &(addr->count_latch), comp_id,
"%s.counts-latched", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IN, &(addr->min_speed), comp_id,
"%s.min-speed-estimate", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->pos), comp_id,
"%s.position", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->pos_interp), comp_id,
"%s.position-interpolated", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->pos_latch), comp_id,
"%s.position-latched", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->vel), comp_id,
"%s.velocity", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->vel_rpm), comp_id,
"%s.velocity-rpm", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_IO, &(addr->pos_scale), comp_id,
"%s.position-scale", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IO, &(addr->x4_mode), comp_id,
"%s.x4-mode", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IO, &(addr->counter_mode), comp_id,
"%s.counter-mode", prefix);
if (retval != 0) {
return retval;
}
rtapi_set_msg_level(msg);
return 0;
}