3

コードを使用して単純なロボットを構成しています。私はWinAVRを使用しており、そこで使用されているコードは C に似ていますが、ライブラリなどがないため、単純なもののコードは手動で入力する必要があります (たとえば、10 進数を 16 進数に変換するには、 ASCII文字stdio.hを含む複数の手順が必要です)操作)。

使用されるコードの例は次のとおりです(私が話していることを示すためだけです:))

.
.
.
    DDRA = 0x00;
    A = adc(0); // Right-hand sensor
    u = A>>4;
    l = A&0x0F;
    TransmitByte(h[u]);
    TransmitByte(h[l]);
    TransmitByte(' ');
.
.
.

状況によっては、WinAVR を使用する必要があり、外部ライブラリ ( などstdio.h) を使用できません。とにかくパルス幅1msか2msの信号をサーボモーター経由でかけたい。設定するポートなどはわかっています。私がする必要があるのは、そのポートをクリアする前に設定を維持するために遅延を適用することだけです。

遅延を設定する方法がわかったので、次のような空の for ループを作成する必要があります。

int value= **??**
for(i = 0; i<value; i++)
    ;

1 ミリ秒のループの「値」にはどのような値を入力すればよいですか?

4

7 に答える 7

4

おそらく、妥当な値を計算し、生成された信号を (オシロスコープなどで) 見て、適切な時間範囲に達するまで値を調整する必要があります。どうやら 2 対 1 のマージンがあることを考えると、最初はそこそこ近くでヒットするかもしれませんが、私はあまり気にしません。

最初の概算として、空のループを生成し、1 つのループの命令サイクルをカウントして、1 クロック サイクルの時間を掛けます。これにより、少なくともループの 1 回の実行にかかる時間の妥当な概算が得られるはずです。そのため、必要な時間をそれで割ると、適切な反復回数の球場に入るはずです。

編集:ただし、(少なくともほとんどの)AVRにはオンボードタイマーがあるため、代わりに使用できる可能性があることにも注意してください. これにより、1) 他の処理を実行したり、2) 期間中の電力消費を削減したりできます。

遅延ループを使用する場合は、AVR-libcの遅延ループ ユーティリティを使用して詳細を処理することをお勧めします。

于 2010-10-31T22:27:53.933 に答える
3

私のプログラムが十分に単純であれば、明示的なタイマー プログラミングは必要ありませんが、移植可能でなければなりません。定義された遅延に対する私の選択の 1 つは、AVR Libc遅延関数です。

#include <delay.h>
_delay_ms (2) // Sleeps 2 ms
于 2010-10-31T22:37:27.087 に答える
1

これは本物のロボットに行くのだろうか?あなたが持っているのはCPUだけで、時間を測定できる他の集積回路はありませんか?

両方の答えが「はい」の場合、操作の正確なタイミングがわかっている場合は、ループを使用して正確な遅延を作成できます。コードをアセンブリ コードに出力し、使用された命令の正確なシーケンスを確認します。次に、プロセッサのマニュアルを確認してください。その情報が記載されています。

于 2010-10-31T22:27:55.980 に答える
1

You should almost certainly have an interrupt configured to run code at a predictable interval. If you look in the example programs supplied with your CPU, you'll probably find an example of such.

Typically, one will use a word/longword of memory to hold a timer, which will be incremented each interrupt. If your timer interrupt runs 10,000 times/second and increments "interrupt_counter" by one each time, a 'wait 1 ms' routine could look like:

extern volatile unsigned long interrupt_counter;

unsigned long temp_value = interrupt_counter;

do {} while(10 > (interrupt_counter - temp_value));
/* Would reverse operands above and use less-than if this weren't HTML. */

Note that as written the code will wait between 900 µs and 1000 µs. If one changed the comparison to greater-or-equal, it would wait between 1000 and 1100. If one needs to do something five times at 1 ms intervals, waiting some arbitrary time up to 1 ms for the first time, one could write the code as:

extern volatile unsigned long interrupt_counter;
unsigned long temp_value = interrupt_counter;
for (int i=0; 5>i; i++)
{
    do {} while(!((temp_value - interrupt_counter) & 0x80000000)); /* Wait for underflow */
    temp_value += 10;
    do_action_thing();
}

This should run the do_something()'s at precise intervals even if they take several hundred microseconds to complete. If they sometimes take over 1 ms, the system will try to run each one at the "proper" time (so if one call takes 1.3 ms and the next one finishes instantly, the following one will happen 700 µs later).

于 2010-10-31T23:19:16.163 に答える
1

単純なロボットの作成に一般的に使用されるほとんどの ATmega AVR チップには、サーボの制御に使用できるパルス幅変調(PWM) と呼ばれる機能があります。このブログ投稿は、PWM を使用してサーボを制御するための簡単な紹介として役立つかもしれません。Arduino プラットフォームのサーボ制御ライブラリを見ると、PWM も使用されていることがわかります。

これは、コンパイラの最適化フラグとチップのクロック速度を変更すると、このような単純な遅延機能が壊れる可能性があるため、ループを一定回数実行することに依存するよりも良い選択かもしれません。

于 2010-10-31T22:45:28.177 に答える
1

より正確な時間値が必要な場合は、内部タイマーに基づく割り込みサービス ルーチンを使用する必要があります。For ループはブロッキング命令であるため、反復処理中はプログラムの残りの部分がブロックされることに注意してください。ISR が実行されるたびに 1 ずつカウントアップするグローバル変数を使用して、タイマー ベースの ISR を設定できます。次に、その変数を「if ステートメント」で使用して、幅の時間を設定できます。また、そのコアはおそらく RC タイプのサーボで使用するための PWM をサポートしています。そのため、より良いルートになる可能性があります。

于 2010-10-31T22:50:02.233 に答える
1

これは、私が時々使用する、本当にすてきな小さなタスカーです。AVR用です。

************************Header File***********************************

// Scheduler data structure for storing task data
typedef struct
{
   // Pointer to task
   void (* pTask)(void);
   // Initial delay in ticks
   unsigned int Delay;
   // Periodic interval in ticks
   unsigned int Period;
   // Runme flag (indicating when the task is due to run)
   unsigned char RunMe;
} sTask;

// Function prototypes
//-------------------------------------------------------------------

void SCH_Init_T1(void);
void SCH_Start(void);
// Core scheduler functions
void SCH_Dispatch_Tasks(void);
unsigned char SCH_Add_Task(void (*)(void), const unsigned int, const unsigned int);
unsigned char SCH_Delete_Task(const unsigned char);

// Maximum number of tasks
// MUST BE ADJUSTED FOR EACH NEW PROJECT
#define SCH_MAX_TASKS (1)

************************Header File***********************************

************************C File***********************************

#include "SCH_AVR.h"
#include <avr/io.h>
#include <avr/interrupt.h>


// The array of tasks
sTask SCH_tasks_G[SCH_MAX_TASKS];


/*------------------------------------------------------------------*-

  SCH_Dispatch_Tasks()

  This is the 'dispatcher' function.  When a task (function)
  is due to run, SCH_Dispatch_Tasks() will run it.
  This function must be called (repeatedly) from the main loop.

-*------------------------------------------------------------------*/

void SCH_Dispatch_Tasks(void)
{
   unsigned char Index;

   // Dispatches (runs) the next task (if one is ready)
   for(Index = 0; Index < SCH_MAX_TASKS; Index++)
   {
      if((SCH_tasks_G[Index].RunMe > 0) && (SCH_tasks_G[Index].pTask != 0))
      {
         (*SCH_tasks_G[Index].pTask)();  // Run the task
         SCH_tasks_G[Index].RunMe -= 1;   // Reset / reduce RunMe flag

         // Periodic tasks will automatically run again
         // - if this is a 'one shot' task, remove it from the array
         if(SCH_tasks_G[Index].Period == 0)
         {
            SCH_Delete_Task(Index);
         }
      }
   }
}

/*------------------------------------------------------------------*-

  SCH_Add_Task()

  Causes a task (function) to be executed at regular intervals 
  or after a user-defined delay

  pFunction - The name of the function which is to be scheduled.
              NOTE: All scheduled functions must be 'void, void' -
              that is, they must take no parameters, and have 
              a void return type. 

  DELAY     - The interval (TICKS) before the task is first executed

  PERIOD    - If 'PERIOD' is 0, the function is only called once,
              at the time determined by 'DELAY'.  If PERIOD is non-zero,
              then the function is called repeatedly at an interval
              determined by the value of PERIOD (see below for examples
              which should help clarify this).


  RETURN VALUE:  

  Returns the position in the task array at which the task has been 
  added.  If the return value is SCH_MAX_TASKS then the task could 
  not be added to the array (there was insufficient space).  If the
  return value is < SCH_MAX_TASKS, then the task was added 
  successfully.  

  Note: this return value may be required, if a task is
  to be subsequently deleted - see SCH_Delete_Task().

  EXAMPLES:

  Task_ID = SCH_Add_Task(Do_X,1000,0);
  Causes the function Do_X() to be executed once after 1000 sch ticks.            

  Task_ID = SCH_Add_Task(Do_X,0,1000);
  Causes the function Do_X() to be executed regularly, every 1000 sch ticks.            

  Task_ID = SCH_Add_Task(Do_X,300,1000);
  Causes the function Do_X() to be executed regularly, every 1000 ticks.
  Task will be first executed at T = 300 ticks, then 1300, 2300, etc.            

-*------------------------------------------------------------------*/

unsigned char SCH_Add_Task(void (*pFunction)(), const unsigned int DELAY, const unsigned int PERIOD)
{
   unsigned char Index = 0;

   // First find a gap in the array (if there is one)
   while((SCH_tasks_G[Index].pTask != 0) && (Index < SCH_MAX_TASKS))
   {
      Index++;
   }

   // Have we reached the end of the list?   
   if(Index == SCH_MAX_TASKS)
   {
      // Task list is full, return an error code
      return SCH_MAX_TASKS;  
   }

   // If we're here, there is a space in the task array
   SCH_tasks_G[Index].pTask = pFunction;
   SCH_tasks_G[Index].Delay =DELAY;
   SCH_tasks_G[Index].Period = PERIOD;
   SCH_tasks_G[Index].RunMe = 0;

   // return position of task (to allow later deletion)
   return Index;
}

/*------------------------------------------------------------------*-

  SCH_Delete_Task()

  Removes a task from the scheduler.  Note that this does
  *not* delete the associated function from memory: 
  it simply means that it is no longer called by the scheduler. 

  TASK_INDEX - The task index.  Provided by SCH_Add_Task(). 

  RETURN VALUE:  RETURN_ERROR or RETURN_NORMAL

-*------------------------------------------------------------------*/

unsigned char SCH_Delete_Task(const unsigned char TASK_INDEX)
{
   // Return_code can be used for error reporting, NOT USED HERE THOUGH!
   unsigned char Return_code = 0;

   SCH_tasks_G[TASK_INDEX].pTask = 0;
   SCH_tasks_G[TASK_INDEX].Delay = 0;
   SCH_tasks_G[TASK_INDEX].Period = 0;
   SCH_tasks_G[TASK_INDEX].RunMe = 0;

   return Return_code;
}

/*------------------------------------------------------------------*-

  SCH_Init_T1()

  Scheduler initialisation function.  Prepares scheduler
  data structures and sets up timer interrupts at required rate.
  You must call this function before using the scheduler.  

-*------------------------------------------------------------------*/

void SCH_Init_T1(void)
{
   unsigned char i;

   for(i = 0; i < SCH_MAX_TASKS; i++)
   {
      SCH_Delete_Task(i);
   }

   // Set up Timer 1
   // Values for 1ms and 10ms ticks are provided for various crystals

   OCR1A = 15000;   // 10ms tick, Crystal 12 MHz
   //OCR1A = 20000;   // 10ms tick, Crystal 16 MHz
   //OCR1A = 12500;   // 10ms tick, Crystal 10 MHz
   //OCR1A = 10000;   // 10ms tick, Crystal 8  MHz

   //OCR1A = 2000;    // 1ms tick, Crystal 16 MHz
   //OCR1A = 1500;    // 1ms tick, Crystal 12 MHz
   //OCR1A = 1250;    // 1ms tick, Crystal 10 MHz
   //OCR1A = 1000;    // 1ms tick, Crystal 8  MHz

   TCCR1B = (1 << CS11) | (1 << WGM12);  // Timer clock = system clock/8
   TIMSK |= 1 << OCIE1A;   //Timer 1 Output Compare A Match Interrupt Enable
}

/*------------------------------------------------------------------*-

  SCH_Start()

  Starts the scheduler, by enabling interrupts.

  NOTE: Usually called after all regular tasks are added,
  to keep the tasks synchronised.

  NOTE: ONLY THE SCHEDULER INTERRUPT SHOULD BE ENABLED!!! 

-*------------------------------------------------------------------*/

void SCH_Start(void)
{
      sei();
}

/*------------------------------------------------------------------*-

  SCH_Update

  This is the scheduler ISR.  It is called at a rate 
  determined by the timer settings in SCH_Init_T1().

-*------------------------------------------------------------------*/

ISR(TIMER1_COMPA_vect)
{
   unsigned char Index;
   for(Index = 0; Index < SCH_MAX_TASKS; Index++)
   {
      // Check if there is a task at this location
      if(SCH_tasks_G[Index].pTask)
      {
         if(SCH_tasks_G[Index].Delay == 0)
         {
            // The task is due to run, Inc. the 'RunMe' flag
            SCH_tasks_G[Index].RunMe += 1;

            if(SCH_tasks_G[Index].Period)
            {
               // Schedule periodic tasks to run again
               SCH_tasks_G[Index].Delay = SCH_tasks_G[Index].Period;
               SCH_tasks_G[Index].Delay -= 1;
            }
         }
         else
         {
            // Not yet ready to run: just decrement the delay
            SCH_tasks_G[Index].Delay -= 1;
         }
      }
   }
}

// ------------------------------------------------------------------


************************C File***********************************
于 2010-10-31T23:08:48.363 に答える