2010-10-31 3 views
3

Я использую код для настройки простого робота. Я использую WinAVR, а используемый там код похож на C, но без stdio.h библиотек и т. Д., Поэтому код для простых вещей следует вводить вручную (например, преобразование десятичных чисел в шестнадцатеричные числа является многоступенчатой ​​процедурой, включающей ASCII манипуляция персонажами).Назначить задержки в течение 1 мс или 2 мс в C?

Пример кода используемого (чтобы показать вам, что я говорю о :))

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

Для некоторых обстоятельств, я должен использовать WinAVR и не могут внешние библиотеки (например, stdio.h). ANYWAY, я хочу применить сигнал с шириной импульса 1   мс или 2   мс через серводвигатель. Я знаю, какой порт установить и что такое; все, что мне нужно сделать, это применить задержку, чтобы сохранить этот порт перед очисткой.

Теперь я знаю, как установить задержки, мы должны создать пустой для петель, таких как:

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

Какое значение я должен поставить в «значение» для цикла 1 мс  ?

+0

Вы можете попробовать спросить здесь: http://electronics.stackexchange.com/ –

ответ

4

Скорее всего, вам нужно будет рассчитать разумное значение, а затем посмотреть на генерируемый сигнал (например, с помощью осциллографа) и настроить свое значение, пока не нажмете нужный временной диапазон. Учитывая, что у вас, по-видимому, есть маржа 2: 1, вы, , могли бы ударить его по разумно близкому в первый раз, но я бы не сильно его переживал.

В первом приближении создайте пустой цикл и подсчитайте циклы команд для одного цикла и умножьте это на время на один такт. Это должно дать как минимум разумное приблизительное время, затрачиваемое на однократное выполнение цикла, поэтому разделение времени, которое вам нужно, должно привести вас к шару для правильного количества итераций.

Редактировать: Следует также отметить, что (по крайней мере, большинство) AVR имеют встроенные таймеры, поэтому вы можете использовать их вместо этого. Это может: 1) позволить вам выполнять другую обработку и/или 2) снижать энергопотребление на время.

Если вы используете циклы задержки, вы можете использовать утилиты AVR-libc с задержкой для обработки деталей.

+0

Спасибо за ответ, я думаю, что это мое лучшее решение. Я не могу использовать внешние библиотеки, и мой код должен быть прямым и ДОЛЖЕН использовать цикл, это может быть хорошим подходом. Еще раз спасибо! – NLed

1

Будет ли это идти к настоящему роботу? У вас есть процессор, нет других интегральных схем, которые могут дать определенное время?

Если оба ответа «да», ну ... если вы знаете точное время для операций, вы можете использовать цикл для создания точных задержек. Выведите код в код сборки и посмотрите точную последовательность используемых инструкций. Затем проверьте руководство по процессору, оно будет иметь эту информацию.

+0

Благодарим за ответ – NLed

3

Если моя программа достаточно проста, нет необходимости в явном программировании таймера, но она должна быть портативной. Один из моих вариантов для определенной задержки будет AVR Libc «s delay функции:

#include <delay.h> 
_delay_ms (2) // Sleeps 2 ms 
+0

Спасибо, что ответили, но не можете использовать здесь, кроме конкретных библиотек. – NLed

+0

Я не понимаю, что вы подразумеваете под «отличными от конкретных библиотек». AVR Libc является частью WinAVR. –

1

Большинства ATmega AVR микросхемы, которые обычно используются для создания простых роботов, есть функция, известная как pulse-width modulation (PWM), который может быть использован для управления сервоприводами. This blog post может служить в качестве краткого введения для управления сервоприводами с использованием PWM. Если вы посмотрите на сервоконтроль Arduino platform, вы увидите, что он также использует PWM.

Это может быть лучшим выбором, чем полагаться на выполнение цикла постоянным числом раз, поскольку изменения в флагах оптимизации компилятора и тактовая частота чипа могут потенциально нарушить такую ​​простую функцию задержки.

+0

Спасибо, что ответили, я проверю эти ссылки. – NLed

+0

Ссылка на сообщение в блоге кажется сломанной. –

1

Если вам нужно более точное значение времени, вы должны использовать процедуру обслуживания прерываний на основе внутреннего таймера. Помните, что цикл For - это команда блокировки, поэтому, пока она выполняет итерацию, остальная часть вашей программы блокируется. Вы можете настроить ISR на основе таймера с глобальной переменной, которая подсчитывается по 1 каждый раз при запуске ISR. Затем вы можете использовать эту переменную в инструкции «if», чтобы установить время ширины. Также это ядро, вероятно, поддерживает PWM для использования с сервоприводами типа RC. Так что это может быть лучший маршрут.

+0

Спасибо, я надеюсь, попробую это, если я не смогу заставить мой другой код работать. – NLed

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*********************************** 
+0

Wow thanks alot! Я бы очень хотел опубликовать это как мое решение, но я не могу это использовать, поскольку мой код не содержит дополнительных библиотек! – NLed

1

Вы должны почти наверняка иметь прерывание, настроенное на запуск кода с предсказуемым интервалом. Если вы посмотрите в примерах программ, поставляемых с вашим процессором, вы, вероятно, найдете пример такого.

Как правило, для хранения таймера используется слово/долгослово памяти, которое будет увеличивать каждое прерывание. Если прерывание от таймера работает 10000 раз/сек и приращений «interrupt_counter» на единицу каждый раз, а 'ждать 1   мс процедура может выглядеть следующим образом:

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. */ 

Обратите внимание, что, как написано код будет ждать от 900 до   мкс и 1000   мкс. Если бы кто-то изменил сравнение на более высокий или равный, он подождал бы между 1000 и 1100. Если нужно что-то делать пять раз с интервалами 1   мс, ожидая какое-то произвольное время до 1   мс в первый раз, можно было бы написать код, как:

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(); 
} 

Это следует запустить do_something() «S на точные интервалы, даже если они занимают несколько сотен микросекунд, чтобы закончить. Если они иногда принимают за 1   мс, система будет пытаться запускать каждый из них в «правильное» время (так что если один вызов занимает 1,3   мс, а следующий будет завершен мгновенно, следующий будет 700   мкс позже).

+0

Большое спасибо за ответ и объяснение этого кода, я могу попробовать, если мой другой метод не удастся. – NLed