#include "rtapi.h"
#include "rtapi_app.h"
#include "hal.h"
MODULE_AUTHOR("John Kasunich");
MODULE_DESCRIPTION("Encoder Ratio Module for HAL");
MODULE_LICENSE("GPL");
static int num_chan;
static int default_num_chan = 1;
RTAPI_MP_INT(num_chan, "number of channels");
static int howmany;
#define MAX_CHAN 8
static char *names[MAX_CHAN] = {0,};
RTAPI_MP_ARRAY_STRING(names,MAX_CHAN,"encoder_ratio names");
typedef struct {
hal_bit_t *master_A;
hal_bit_t *master_B;
hal_bit_t *slave_A;
hal_bit_t *slave_B;
hal_bit_t *enable;
unsigned char master_state;
unsigned char slave_state;
int raw_error;
int master_increment;
int slave_increment;
double output_scale;
hal_float_t *error;
hal_u32_t *master_ppr;
hal_u32_t *slave_ppr;
hal_u32_t *master_teeth;
hal_u32_t *slave_teeth;
} encoder_pair_t;
static encoder_pair_t *encoder_pair_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[16] = {
0x00, 0x44, 0x88, 0x0C, 0x80, 0x04, 0x08, 0x4C,
0x40, 0x04, 0x08, 0x8C, 0x00, 0x84, 0x48, 0x0C
};
static int comp_id;
static int export_encoder_pair(int num, encoder_pair_t * addr, char* prefix);
static void sample(void *arg, long period);
static void update(void *arg, long period);
int rtapi_app_main(void)
{
int n, retval,i;
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_RATIO: ERROR: invalid number of channels: %d\n", howmany);
return -1;
}
comp_id = hal_init("encoder_ratio");
if (comp_id < 0) {
rtapi_print_msg(RTAPI_MSG_ERR, "ENCODER_RATIO: ERROR: hal_init() failed\n");
return -1;
}
encoder_pair_array = hal_malloc(howmany * sizeof(encoder_pair_t));
if (encoder_pair_array == 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER_RATIO: ERROR: hal_malloc() failed\n");
hal_exit(comp_id);
return -1;
}
i = 0; for (n = 0; n < howmany; n++) {
if(num_chan) {
char buf[HAL_NAME_LEN + 1];
rtapi_snprintf(buf, sizeof(buf), "encoder-ratio.%d", n);
retval = export_encoder_pair(n, &(encoder_pair_array[n]), buf);
} else {
retval = export_encoder_pair(n, &(encoder_pair_array[n]), names[i++]);
}
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER_RATIO: ERROR: counter %d var export failed\n", n);
hal_exit(comp_id);
return -1;
}
encoder_pair_array[n].master_state = 0;
encoder_pair_array[n].slave_state = 0;
encoder_pair_array[n].master_increment = 0;
encoder_pair_array[n].slave_increment = 0;
encoder_pair_array[n].raw_error = 0;
encoder_pair_array[n].output_scale = 1.0;
*(encoder_pair_array[n].error) = 0.0;
}
retval = hal_export_funct("encoder-ratio.sample", sample,
encoder_pair_array, 0, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER_RATIO: ERROR: sample funct export failed\n");
hal_exit(comp_id);
return -1;
}
retval = hal_export_funct("encoder-ratio.update", update,
encoder_pair_array, 1, 0, comp_id);
if (retval != 0) {
rtapi_print_msg(RTAPI_MSG_ERR,
"ENCODER_RATIO: ERROR: update funct export failed\n");
hal_exit(comp_id);
return -1;
}
rtapi_print_msg(RTAPI_MSG_INFO,
"ENCODER_RATIO: installed %d encoder_ratio blocks\n", howmany);
hal_ready(comp_id);
return 0;
}
void rtapi_app_exit(void)
{
hal_exit(comp_id);
}
static void sample(void *arg, long period)
{
encoder_pair_t *pair;
int n;
unsigned char state;
pair = arg;
for (n = 0; n < howmany; n++) {
state = pair->master_state;
if (*(pair->master_A)) {
state |= SM_PHASE_A_MASK;
}
if (*(pair->master_B)) {
state |= SM_PHASE_B_MASK;
}
state = lut[state & SM_LOOKUP_MASK];
if ( *(pair->enable) != 0 ) {
if (state & SM_CNT_UP_MASK) {
pair->raw_error -= pair->master_increment;
} else if (state & SM_CNT_DN_MASK) {
pair->raw_error += pair->master_increment;
}
}
pair->master_state = state;
state = pair->slave_state;
if (*(pair->slave_A)) {
state |= SM_PHASE_A_MASK;
}
if (*(pair->slave_B)) {
state |= SM_PHASE_B_MASK;
}
state = lut[state & SM_LOOKUP_MASK];
if (state & SM_CNT_UP_MASK) {
pair->raw_error += pair->slave_increment;
} else if (state & SM_CNT_DN_MASK) {
pair->raw_error -= pair->slave_increment;
}
pair->slave_state = state;
pair++;
}
}
static void update(void *arg, long period)
{
encoder_pair_t *pair;
int n;
pair = arg;
for (n = 0; n < howmany; n++) {
if ( pair->output_scale > 0 ) {
*(pair->error) = pair->raw_error / pair->output_scale;
}
pair->master_increment = *(pair->master_teeth) * *(pair->slave_ppr);
pair->slave_increment = *(pair->slave_teeth) * *(pair->master_ppr);
pair->output_scale = *(pair->master_ppr) * *(pair->slave_ppr) * *(pair->slave_teeth);
pair++;
}
}
static int export_encoder_pair(int num, encoder_pair_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->master_A), comp_id,
"%s.master-A", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->master_B), comp_id,
"%s.master-B", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->slave_A), comp_id,
"%s.slave-A", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->slave_B), comp_id,
"%s.slave-B", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_bit_newf(HAL_IN, &(addr->enable), comp_id,
"%s.enable", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_float_newf(HAL_OUT, &(addr->error), comp_id,
"%s.error", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_u32_newf(HAL_IO, &(addr->master_ppr), comp_id,
"%s.master-ppr", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_u32_newf(HAL_IO, &(addr->slave_ppr), comp_id,
"%s.slave-ppr", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_u32_newf(HAL_IO, &(addr->master_teeth), comp_id,
"%s.master-teeth", prefix);
if (retval != 0) {
return retval;
}
retval = hal_pin_u32_newf(HAL_IO, &(addr->slave_teeth), comp_id,
"%s.slave-teeth", prefix);
if (retval != 0) {
return retval;
}
rtapi_set_msg_level(msg);
return 0;
}