Skip to content
Snippets Groups Projects
action.c 25.14 KiB
#include "action.h"
#include "action_registers.h"
#include "ram.h"

#define SPEED_BASE_SCHEDULE 0x00
#define TIME_BASE_SCHEDULE  0x0A

// private functions
void action_write_cmd(TActionMModule *module,unsigned short int address,unsigned short int length,unsigned char *data)
{
  unsigned int ram_offset;

  ram_offset=address-module->ram_base_address;
  if(ram_in_range(module->ram_base_address+ACTION_CONTROL_OFFSET,address,length))
  {
    if(data[ACTION_CONTROL_OFFSET-ram_offset]&ACTION_START)
      action_start_page(module);
    if(data[ACTION_CONTROL_OFFSET-ram_offset]&ACTION_STOP)
      action_stop_page(module);
  }
  if(ram_in_range(module->ram_base_address+ACTION_PAGE_OFFSET,address,length))// load the page identifier 
    action_load_page(module,data[ACTION_PAGE_OFFSET-ram_offset]);
}

void action_read_cmd(TActionMModule *module,unsigned short int address,unsigned short int length,unsigned char *data)
{
  ram_read_table(module->memory,address,length,data);
}

void action_load_next_step(TActionMModule *action)
{
  // angle variables
  short int next_angle;// information from the flash memory (fixed point 9|7 format)
  long long int max_angle;// fixed point format 48|16 format
  long long int tmp_angle;// fixed point format 48|16 format
  long long int angle;// fixed point format 48|16 format
  long long int target_angles;// fixed point 48|16 format
  long long int next_angles;// fixed point 48|16 format
  long long int action_pre_time_initial_speed_angle;// fixed point 48|16 format
  long long int moving_angle;// fixed point 48|16 format
  // time variables
  long long int accel_time_num;// fixed point 48|16 format
  long long int zero_speed_divider;// fixed point 48|16 format
  long long int non_zero_speed_divider;// fixed point 48|16 format
  // control variables
  unsigned char dir_change;
  // control information
  unsigned char i=0;

  action->current_step_index++;
  if(action->current_step_index>=action->current_page.header.stepnum)// the last step has been executed
  {
    if(action->current_page.header.stepnum==1)
    {
      if(action->stop)
        action->next_index=action->current_page.header.exit;
      else
      {
        action->num_repetitions--;
        if(action->num_repetitions>0)
          action->next_index=action->current_page_index;
        else
          action->next_index=action->current_page.header.next;
      }
      if(action->next_index==0)
        action->end=0x01;
      else
      {
        if(action->next_index!=action->current_page_index)
        {
          pages_get_page(action->next_index,&action->next_page);
          if(!pages_check_checksum(&action->next_page))
            action->end=0x01;
        }
        if(action->next_page.header.repeat==0 || action->next_page.header.stepnum==0)
          action->end=0x01;
      }
    }
    pages_copy_page(&action->next_page,&action->current_page);// make the next page active
    if(action->current_page_index!=action->next_index)
      action->num_repetitions=action->current_page.header.repeat;
    action->current_step_index=0;
    action->current_page_index=action->next_index;
  }
  else if(action->current_step_index==action->current_page.header.stepnum-1)// it is the last step
  {
    if(action->stop)
      action->next_index=action->current_page.header.exit;
    else
    {
      action->num_repetitions--;
      if(action->num_repetitions>0)
        action->next_index=action->current_page_index;
      else
        action->next_index=action->current_page.header.next;
    }
    if(action->next_index==0)
      action->end=0x01;
    else
    {
      if(action->next_index!=action->current_page_index)
      {
        pages_get_page(action->next_index,&action->next_page);
        if(!pages_check_checksum(&action->next_page))
          action->end=0x01;
      }
      if(action->next_page.header.repeat==0 || action->next_page.header.stepnum==0)
        action->end=0x01;
    }
  }
  // compute trajectory
  action->pause_time=(action->current_page.steps[action->current_step_index].pause*action->current_page.header.speed)>>5;
  action->pause_time=action->pause_time<<9;
  action->step_time=(action->current_page.steps[action->current_step_index].time*action->current_page.header.speed)>>5;
  action->step_time=action->step_time<<9;
  if(action->step_time<action->period)
    action->step_time=action->period;// 0.078 in 16|16 format
  max_angle=0;
  for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
  {
    if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
    {
      angle=action->current_page.steps[action->current_step_index].position[i];
      if(angle==0x5A00)// bigger than 180
        target_angles=action->mmodule.manager->servo_values[i].target_angle;
      else
        target_angles=angle<<9;
      action->moving_angles[i]=target_angles-action->mmodule.manager->servo_values[i].target_angle;
      if(action->end)
        next_angles=target_angles;
      else
      {
        if(action->current_step_index==action->current_page.header.stepnum-1)
          next_angle=action->next_page.steps[0].position[i];
        else
          next_angle=action->current_page.steps[action->current_step_index+1].position[i];
        if(next_angle==0x5A00)
          next_angles=target_angles;
        else
          next_angles=next_angle<<9;
      }
      // check changes in direction
      if(((action->mmodule.manager->servo_values[i].target_angle < target_angles) && (target_angles < next_angles)) ||
         ((action->mmodule.manager->servo_values[i].target_angle > target_angles) && (target_angles > next_angles)))
        dir_change=0x00;
      else
        dir_change=0x01;
      // check the type of ending
      if(dir_change || action->pause_time>0 || action->end)
        action->zero_speed_finish[i]=0x01;
      else
        action->zero_speed_finish[i]=0x00;
      // find the maximum angle of motion in speed base schedule
      if(action->current_page.header.schedule==SPEED_BASE_SCHEDULE)// speed base schedule
      {
        // fer el valor absolut
        if(action->moving_angles[i]<0)
          tmp_angle=-action->moving_angles[i];
        else
          tmp_angle=action->moving_angles[i];
        if(tmp_angle>max_angle)
          max_angle=tmp_angle;
      }
      action->mmodule.manager->servo_values[i].cw_compliance=(1<<(action->current_page.header.slope[i]&0x0F));
      action->mmodule.manager->servo_values[i].ccw_compliance=(1<<((action->current_page.header.slope[i]&0xF0)>>4));
    }
  }
  if(action->current_page.header.schedule==SPEED_BASE_SCHEDULE)
    action->step_time=(max_angle*256)/(action->current_page.steps[action->current_step_index].time*720);
  accel_time_num=action->current_page.header.accel;
  accel_time_num=accel_time_num<<9;
  if(action->step_time<=(accel_time_num<<1))
  {
    if(action->step_time==0)
      accel_time_num=0;
    else
    {
      accel_time_num=(action->step_time-action->period)>>1;
      if(accel_time_num==0)
        action->step_time=0;
    }
  }
  action->total_time=action->step_time;
  action->pre_time=accel_time_num;
  action->main_time=action->total_time-action->pre_time;
  non_zero_speed_divider=(action->pre_time>>1)+action->main_time;
  if(non_zero_speed_divider<action->period)
    non_zero_speed_divider=action->period;
  zero_speed_divider=action->main_time;
  if(zero_speed_divider<action->period)
    zero_speed_divider=action->period;
  for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
  {
    if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
    {
      action_pre_time_initial_speed_angle=(action->start_speed[i]*action->pre_time)>>1;
      moving_angle=action->moving_angles[i]<<16;
      if(action->zero_speed_finish[i])
        action->main_speed[i]=(moving_angle-action_pre_time_initial_speed_angle)/zero_speed_divider;
      else
        action->main_speed[i]=(moving_angle-action_pre_time_initial_speed_angle)/non_zero_speed_divider;
//      if((action->main_speed[i]>>16)>info.max_speed)
//        action->main_speed[i]=(info.max_speed*65536);
//      else if((action->main_speed[i]>>16)<-info.max_speed)
//        action->main_speed[i]=-(info.max_speed*65536);
    }
  }
}

void action_finish(TActionMModule *action)
{
  // set the internal state machine to the idle state
  action->end=0x00;
  // change the internal state 
  action->running=0x00;
  action->memory->data[action->ram_base_address+ACTION_CONTROL_OFFSET]&=(~ACTION_RUNNING);
}

void action_set_period(void *module,unsigned short int period_ms)
{
  TActionMModule *action=(TActionMModule *)module;

  // load the current period 
  action->period=(period_ms<<16)/1000;
}

void action_set_module(void *module,unsigned char servo_id)
{

}

void action_pre_process(void *module)
{
  TActionMModule *action=(TActionMModule *)module;
  unsigned char i;

  if(action->running==0x01)
  {
    action->current_time+=action->period;
    switch(action->state)
    {
      case ACTION_PRE: if(action->current_time>action->section_time)
                       {
                         for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
                         {
                           if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
                           {
                             /* the last segment of the pre section */
                             action->delta_speed=(action->main_speed[i]-action->start_speed[i]);
                             action->current_speed[i]=action->start_speed[i]+action->delta_speed;
                             action->accel_angles[i]=(action->start_speed[i]*action->section_time+((action->delta_speed*action->section_time)>>1))>>16;
                             action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+action->accel_angles[i];
                             /* update of the state */
                             if(!action->zero_speed_finish[i])
                             {
                               if((action->step_time-action->pre_time)==0)
                                 action->main_angles[i]=0;
                               else
                                 action->main_angles[i]=((action->moving_angles[i]-action->accel_angles[i])*(action->step_time-(action->pre_time<<1)))/(action->step_time-action->pre_time);
                               action->start_angles[i]=action->mmodule.manager->servo_values[i].target_angle;
                             }
                             else
                             {
                               action->main_angles[i]=action->moving_angles[i]-action->accel_angles[i]-(((action->main_speed[i]*action->pre_time)>>1)>>16);
                               action->start_angles[i]=action->mmodule.manager->servo_values[i].target_angle;
                             }
                             /* the first step of the main section */
                             action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+(action->main_angles[i]*(action->current_time-action->section_time))/(action->step_time-(action->pre_time<<1));
                             action->current_speed[i]=action->main_speed[i];
                           }
                         }
                         action->current_time=action->current_time-action->section_time;
                         action->section_time=action->step_time-(action->pre_time<<1);
                         action->state=ACTION_MAIN;
                       }
                       else
                       {
                         for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
                         {
                           if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
                           {
                             action->delta_speed=((action->main_speed[i]-action->start_speed[i])*action->current_time)/action->section_time;
                             action->current_speed[i]=action->start_speed[i]+action->delta_speed;
                             action->accel_angles[i]=((action->start_speed[i]*action->current_time)+((action->delta_speed*action->current_time)>>1))>>16;
                             action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+action->accel_angles[i];
                           }
                         }
                         action->state=ACTION_PRE;
                       }
                       break;
      case ACTION_MAIN: if(action->current_time>action->section_time)
                        {
                          for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
                          {
                            if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
                            {
                              /* last segment of the main section */
                              action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+action->main_angles[i];
                              action->current_speed[i]=action->main_speed[i];
                              /* update state */
                              action->start_angles[i]=action->mmodule.manager->servo_values[i].target_angle;
                              action->main_angles[i]=action->moving_angles[i]-action->main_angles[i]-action->accel_angles[i];
                              /* first segment of the post section */
                              if(action->zero_speed_finish[i])
                              {
                                action->delta_speed=((0.0-action->main_speed[i])*(action->current_time-action->section_time))/action->pre_time;
                                action->current_speed[i]=action->main_speed[i]+action->delta_speed;
                                action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+(((action->main_speed[i]*(action->current_time-action->section_time))+((action->delta_speed*(action->current_time-action->section_time))>>1))>>16);
                              }
                              else
                              {
                                action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+((action->main_angles[i]*(action->current_time-action->section_time))/action->pre_time);
                                action->current_speed[i]=action->main_speed[i];
                              }
                            }
                          }
                          action->current_time=action->current_time-action->section_time;
                          action->section_time=action->pre_time;
                          action->state=ACTION_POST;
                        }
                        else
                        {
                          for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
                          {
                            if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
                            {
                              action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+(action->main_angles[i]*action->current_time)/action->section_time;
                              action->current_speed[i]=action->main_speed[i];
                            }
                          }
                          action->state=ACTION_MAIN;
                        }
                        break;
      case ACTION_POST: if(action->current_time>action->section_time)
                        {
                          for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
                          {
                            if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
                            {
                              /* last segment of the post section */
                              if(action->zero_speed_finish[i])
                              {
                                action->delta_speed=-action->main_speed[i];
                                action->current_speed[i]=action->main_speed[i]+action->delta_speed;
                                action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+(((action->main_speed[i]*action->section_time)+((action->delta_speed*action->section_time)>>1))>>16);
                              }
                              else
                              {
                                action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+action->main_angles[i];
                                action->current_speed[i]=action->main_speed[i];
                              }
                              /* update state */
                              action->start_angles[i]=action->mmodule.manager->servo_values[i].target_angle;
                              action->start_speed[i]=action->current_speed[i];
                            }
                          }
                          /* load the next step */
                          if(action->pause_time==0)
                          {
                            if(action->end)
                            {
                              action->state=ACTION_PAUSE;
                              action_finish(action);
                            }
                            else
                            {
                              action_load_next_step(action);
                              /* first step of the next section */
                              for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
                              {
                                if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
                                {
                                  action->delta_speed=((action->main_speed[i]-action->start_speed[i])*(action->current_time-action->section_time))/action->pre_time;
                                  action->current_speed[i]=action->start_speed[i]+action->delta_speed;
                                  action->accel_angles[i]=(((action->start_speed[i]*(action->current_time-action->section_time))+((action->delta_speed*(action->current_time-action->section_time))>>1))>>16);
                                  action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+action->accel_angles[i];
                                }
                              }
                              action->current_time=action->current_time-action->section_time;
                              action->section_time=action->pre_time;
                              action->state=ACTION_PRE;
                            }
                          }
                          else
                          {
                            action->current_time=action->current_time-action->section_time;
                            action->section_time=action->pause_time;
                            action->state=ACTION_PAUSE;
                          }
                        }
                        else
                        {
                          for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
                          {
                            if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
                            {
                              if(action->zero_speed_finish[i])
                              {
                                action->delta_speed=((0.0-action->main_speed[i])*action->current_time)/action->section_time;
                                action->current_speed[i]=action->main_speed[i]+action->delta_speed;
                                action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+(((action->main_speed[i]*action->current_time)+((action->delta_speed*action->current_time)>>1))>>16);
                              }
                              else
                              {
                                action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+(action->main_angles[i]*action->current_time)/action->section_time;
                                action->current_speed[i]=action->main_speed[i];
                              }
                            }
                          }
                          action->state=ACTION_POST;
                        }
                        break;
      case ACTION_PAUSE: if(action->current_time>action->section_time)
                         {
                           if(action->end)
                           {
                             action->state=ACTION_PAUSE;
                             action_finish(action);
                           }
                           else
                           {
                             // find next page to execute
                             action_load_next_step(action);
                             /* first step of the next section */
                             for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
                             {
                               if(mmanager_get_module(action->mmodule.manager,i)==MM_ACTION)
                               {
                                 action->delta_speed=((action->main_speed[i]-action->start_speed[i])*(action->current_time-action->section_time))/action->pre_time;
                                 action->current_speed[i]=action->start_speed[i]+action->delta_speed;
                                 action->accel_angles[i]=(((action->start_speed[i]*(action->current_time-action->section_time))+((action->delta_speed*(action->current_time-action->section_time))>>1))>>16);
                                 action->mmodule.manager->servo_values[i].target_angle=action->start_angles[i]+action->accel_angles[i];
                               }
                             }
                             action->current_time=action->current_time-action->section_time;
                             action->section_time=action->pre_time;
                             action->state=ACTION_PRE;
                           }
                         }
                         else// pause section still active
                           action->state=ACTION_PAUSE;
                         break;
      default: break;
    }
  }
}

// public functions
unsigned char action_init(TActionMModule *action,TMemory *memory,unsigned short int ram_base_address)
{
  unsigned char i;

  /* initialize the motion module */
  mmodule_init(&action->mmodule);
  action->mmodule.id=MM_ACTION;
  action->mmodule.set_period=action_set_period;
  action->mmodule.set_module=action_set_module;
  action->mmodule.pre_process=action_pre_process;
  action->mmodule.data=action;

  // init manager_current_angles with the current position of the servos
  for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
  {
    // angle variables
    action->moving_angles[i]=0;// fixed point 48|16 format
    action->start_angles[i]=0;
    // speed variables
    action->start_speed[i]=0;// fixed point 48|16 format
    action->main_speed[i]=0;// fixed point 48|16 format
    // control variables
    action->zero_speed_finish[i]=0x00;
    action->current_speed[i]=0;
  }
  // clear the contents of the next page
  pages_clear_page(&action->next_page);
  pages_clear_page(&action->current_page);
  action->current_page_index=0;
  action->current_step_index=-1;
  // control variables
  action->end=0x00;
  action->stop=0x00;
  action->running=0x00;
  action->next_index=0;
  // time variables (in time units (7.8 ms each time unit))
  action->total_time=0;// fixed point 48|16 format
  action->pre_time=0;// fixed point 48|16 format
  action->main_time=0;// fixed point 48|16 format
  action->step_time=0;// fixed point 48|16 format
  action->pause_time=0;// fixed point 48|16 format
  action->current_time=0;// fixed point 48|16 format
  action->section_time=0;// fixed point 48|16 format
  action->state=ACTION_PAUSE;
  action->num_repetitions=0;
  //action_period=(DYN_MANAGER_DEFAULT_PERIOD_US<<16)/1000000;
  mem_module_init(&action->mem_module);
  action->mem_module.data=action;
  action->mem_module.write_cmd=(void(*)(void *,unsigned short int, unsigned short int,unsigned char *))action_write_cmd;
  action->mem_module.read_cmd=(void(*)(void *,unsigned short int, unsigned short int,unsigned char *))action_read_cmd;
  if(!mem_module_add_ram_segment(&action->mem_module,ram_base_address,RAM_ACTION_LENGTH))
    return 0x00;
  if(!mem_add_module(memory,&action->mem_module))
    return 0x00;
  action->ram_base_address=ram_base_address;
  action->memory=memory;
  
  return 0x01;
}

TMotionModule *action_get_module(TActionMModule *action)
{
  return &action->mmodule;
}

unsigned char action_load_page(TActionMModule *action,unsigned char page_id)
{
  action->next_index=page_id;
  pages_get_page(action->next_index,&action->current_page);
  pages_get_page(action->current_page.header.next,&action->next_page);
  if(!pages_check_checksum(&action->current_page))
    return 0x00;
  if(!pages_check_checksum(&action->next_page))
    return 0x00;
  action->memory->data[action->ram_base_address+ACTION_PAGE_OFFSET]=page_id;

  return 0x01;
}

void action_start_page(TActionMModule *action)
{
  unsigned char i;

  for(i=0;i<PAGE_MAX_NUM_SERVOS;i++)
    action->start_angles[i]=action->mmodule.manager->servo_values[i].target_angle;
  action->stop=0x00;
  action->current_time=0;
  action->section_time=0; 
  action->current_step_index=-1;
  /* clear the interrupt flag */
  action->running=0x01;
  action->memory->data[action->ram_base_address+ACTION_CONTROL_OFFSET]|=ACTION_RUNNING;
}

void action_stop_page(TActionMModule *action)
{
  action->stop=0x01;
}

unsigned char action_is_running(TActionMModule *action)
{
  return action->running;
}

unsigned char action_get_current_page(TActionMModule *action)
{
  return action->current_page_index;
}

unsigned char action_get_current_step(TActionMModule *action)
{
  return action->current_step_index;
}