I'm producing a game in C on a microprocessor. The score is controlled by how long you can survive; the score increases by 1 every 3 seconds. The score is an integer which is declared globally, but displayed from a function.
int score = 0;//globally declared
void draw_score(int score_d)
{
char score_draw[99];
sprintf(score_draw,"%d", score_d);
draw_string(score_draw, 9, 0);
}
I was thinking of a function which just increases the score by one with a delay on it, however that has not worked.
void score_increaser(int score)
{
score++;
_delay_ms( 3000 );
}
Does it need to be in a while loop? the function itself would go into a while loop in the main anyway.
C is pass by value.
score_increaser() as shown in your question increases just a copy of what is passed in.
To fix this there are (mainly) two options:
As score is defined globally, do not pass in anything:
void score_increaser(void) {
score++;
_delay_ms( 3000 );
}
This modifes the globale score directly.
Pass in the address of score and de-reference it inside the function
void score_increaser(int * pscore) {
(*pscore)++;
_delay_ms( 3000 );
}
Call it like this
...
score_increaser(&score);
...
A 3rd, a bit more complex, approach (which assumes signals are supported on the target platform) would
setup a signal and a referring handler, then
setup a timer to fire a signal every N seconds.
This signal then is handled by the handler, which in turn
increases the global score and
starts the timer again.
This might look like:
#include <signal.h> /* for signal() and sig_atomic_t */
#include <unistd.h> /* for alarm() */
#define DURATION (3) /* Increase score every 3 seconds. */
sig_atomic_t score = 0;
void set_alarm(unsigned);
void handler_alarm(int sig)
{
++score;
set_alarm(DURATION);
}
void set_alarm(unsigned duration)
{
signal(SIGALRM, handler_alarm);
alarm(duration);
}
int main(void)
{
set_alarm(DURATION);
... /* The game's codes here. */
}
This latter approach has the advantage that your game's code does not need to take care about increasing score. score is just increased every 3 seconds as long as the program runs.
I'd recommend using a timer interrupt. Configure the timer to 3 seconds.
volatile int score = 0; //global
void Intr_Init(peripheral_t per)
{
//Initialize the timer interrupt
}
void draw_score(int score_d)
{
char score_draw[99];
sprintf(score_draw,"%d", score_d);
draw_string(score_draw, 9, 0);
}
int main(void)
{
Intr_Init(TIMER);
while(1)
{
//Code that makes your game run
draw_score(score);
}
}
ISR (TIMER1_COMPA_vect)
{
//clear disable interrupt
score++;
//enable interrupt
}
In embedded, you should rely on Timers for better time critical tasks and accuracy. The way Delay routines are implemented is usually a loop or a up/down counter. Whereas a timer is usually based on counting SysTicks.
Another major advantage of Interrupts is that you let processor do its tasks all the while instead of making it block in a delay loop.
score is global value then do not need to pass it in function if that function has access to that global space
void score_increaser() {
score++;
_delay_ms( 3000 );
}
here is a good method for handling the score.
in the 'start game' function,
clear 'score' to 0
setup a timer:
--to expire once each 3 seconds
--enable the automatic reload feature,
--enable the timer interrupt
--enable the timer counter
in the timer interrupt handler function
--increment 'score'
--clear the timer interrupt pending flag
in the 'end game' function
disable the timer counter
disable the timer interrupt
display the 'score' value
You dont need parameter for the score since it's declared globally..
//global
int score = 0;
void score_increaser()
{
_delay_ms(3000);
score++;
}
calling is like: score_increaser(); should do the work..
i suggest you check for score in any other line/function.. maybe you have redeclared it or accidentally changed the value..
hope this helped..
Related
I have the following arduino code:
uint32_t hold_time=600000;
uint32_t curr_time;
uint32_t last_event;
bool on_hold=false;
beginning of main loop
curr_time = millis();
if (on_hold && curr_time - last_event >= hold_time) {
on_hold = false;
}
...
if (!on_hold)
{
run the function();
on_hold = true; // Ignore this function for 1 minute
}
This basically will execute the main loop many times but the run_the_function(); only when it is unlocked so in this example once in every minute. I would like to accomplish the same in standard POSIX C which works on BSDs as well.
Since you asked for POSIX, I will give you POSIX. This is a sample code that is able to run a timer without using pthreads, but only through OS provided timers. It runs a specific function every 2 seconds. You can configure it to make it run every 60 seconds, if you prefer. I have comment thoroughly the code, and I hope it is easy enough to understand:
#include <stdlib.h> // For declaration of exit
#include <stdio.h> // For printf function
#include <signal.h> // Will be used for the signal callbacks
#include <time.h> // Timer and current time stuff
#define TIMER_SECONDS 2 // To test more rapidly I will wait
// only for 2 seconds instead of a minute...
int counter = 0; // Whe want to call the timer a limited number
// of time for this example
// BEFORE READING THIS, READ THE MAIN:
// This function is your "run_the_function" callback. As for now,
// it has no arguments and returns nothing. It asks to the system the current
// time and prints it, just to check if the timer works. It uses **printf**
// and this should be avoided in signal handlers!
static void run_the_function() {
time_t rawtime; // This is the current time variable
struct tm * timeinfo; // This is a strut that contains the time
// broken down to its components
time( &rawtime ); // To get from the system the current time
timeinfo = localtime ( &rawtime ); // For splitting the time in its components
printf("Timer CALLED %d times :: %s", ++counter, asctime(timeinfo));
}
// BEFORE READING THIS, READ THE MAIN
// This is the signal handler, a function that is called when the timer
// signals that has finished to count
static void timer_callback(int sig, siginfo_t *si, void *uc) {
run_the_function();
}
int main() {
timer_t timer_id; // An unique identifier for the timer that you are creating
struct itimerspec intervals; // Specify the intervals for the timer that we are creating
struct sigevent timer_event; // The structure that handles the event generated by the timer
struct sigaction timer_action; // The action for the timer event
// First you need to implement the action to do when the timer reaches zero,
// then you need to say that you want an event for a timer that reaches zero,
// and only at the end you set the timer.
// The function "sigaction" connects your timer event to the timer signal SIGRTMIN.
// The timer_event.sigev_signo instructs to create an EVENT for the signal SIGRTMIN, and
// for that event you prepared a custom action.
// The timer sends the SIGRTMIN signal every time it reaches zero, and when you
// create it, you connect it to the timer_event.
// Now we define what is the action to perform
timer_action.sa_flags = SA_SIGINFO; // The action to perform is to run a callback
timer_action.sa_sigaction = timer_callback; // The callback is "timer_callback"
sigemptyset(&timer_action.sa_mask); // And we are initializing the event structure
if (sigaction(SIGRTMIN, &timer_action, NULL) < 0) // We are binding this action
exit(1); // to a timer event (SIGRTMIN)
timer_event.sigev_notify = SIGEV_SIGNAL; // Instruct the event that it is related to
// a signal.
timer_event.sigev_signo = SIGRTMIN; // Instruct the event that the signal to track is SIGRTMIN
// At this point we are ready to create the timer, that uses the REAL TIME CLOCK of your
// system. When it reaches zero it raise a timer_event, and it also sets the id of the
// created timer.
if (timer_create(CLOCK_REALTIME, &timer_event, &timer_id) < 0)
exit(1);
// We now need to define the times for the timer. Intervals is composed by
// two structure: it_value, that contains the current time (or the starting time
// for the first run of your timer) and it_intervals, the time at which it will be
// reset after completing one lap. If you set it_interval to zero, the timer runs only
// one time. If you set it_value to zero, the timer does not run.
intervals.it_value.tv_sec = TIMER_SECONDS;
intervals.it_value.tv_nsec = 0;
intervals.it_interval.tv_sec = TIMER_SECONDS;
intervals.it_interval.tv_nsec = 0;
// Let's setup the time and the interval of the timer, so that it starts...
if (timer_settime(timer_id, 0, &intervals, NULL) < 0)
exit(1);
// And now we have only to wait for the timer... Easy, isn't it?
printf("Let's go!\n");
while(counter < 5) { /* Do your stuff here*/ };
return 0;
}
You need to compile it with:
gcc test.c -lrt -o test
and run it with:
./test
Let's go!
Timer CALLED 1 times :: Thu May 3 15:48:29 2018
Timer CALLED 2 times :: Thu May 3 15:48:31 2018
Timer CALLED 3 times :: Thu May 3 15:48:33 2018
Timer CALLED 4 times :: Thu May 3 15:48:35 2018
Timer CALLED 5 times :: Thu May 3 15:48:37 2018
What is the best way to create a timer with Microblaze which would allow me to have it work more similarly to a function like delay_ms() or sleep() in more conventional scripts?
Easily, I can create a stupid function like this:
void delay_ms(int i) {
//mind that I am doing this on the top of my head
for(delays=0; delay<(i*((1/frequency of the device)/2)); delays++) {
}
}
... but that would only have processor process nothing until it finishes, while in reality I need it to have the function allow me to do stop one process for a certain period of time while another one continues working.
Such thing is possible, no doubt about that, but what would the simplest solution to this problem be?
(I am using Spartan-3A, but I believe the solution would work for different kits, FPGAs as well.)
TL;DR
Use a micro OS, like FreeRTOS.
Bad answer
Well, if you have no OS, no task commutation but have an external timer, you can
use the following approach:
Enable interruption for your hardware timer, and manage a counter driven by this interrution:
You should have something like
/**timer.c**/
/* The internal counters
* each task have its counter
*/
static int s_timers[NUMBER_OF_TASKS] = {0,0};
/* on each time tick, decrease timers */
void timer_interrupt()
{
int i;
for (i = 0; i < NUMBER_OF_TASKS; ++i)
{
if (s_timer[i] > 0)
{
s_timer[i]--;
}
}
}
/* set wait counter:
* each task says how tick it want to wait
*/
void timer_set_wait(int task_num, int tick_to_wait)
{
s_timer[task_num] = tick_to_wait;
}
/**
* each task can ask if its time went out
*/
int timer_timeout(int task_num)
{
return (0 == s_timer[task_num]);
}
Once you have something like a timer (the code above is easily perfectible),
program your tasks:
/**task-1.c**/
/*TASK ID must be valid and unique in s_timer */
#define TASK_1_ID 0
void task_1()
{
if (timer_timeout(TASK_1_ID))
{
/* task has wait long enough, it can run again */
/* DO TASK 1 STUFF */
printf("hello from task 1\n");
/* Ask to wait for 150 ticks */
timer_set_wait(TASK_1_ID, 150);
}
}
/**task-2.c**/
/*TASK ID must be valid and unique in s_timer */
#define TASK_2_ID 1
void task_2()
{
if (timer_timeout(TASK_2_ID))
{
/* task has wait long enough, it can run again */
/* DO TASK 2 STUFF */
printf("hello from task 2\n");
/* Ask to wait for 250 ticks */
timer_set_wait(TASK_2_ID, 250);
}
}
And schedule (a big word here) the tasks:
/** main.c **/
int main()
{
/* init the program, like set up the timer interruption */
init()
/* do tasks, for ever*/
while(1)
{
task_1();
task_2();
}
return 0;
}
I think what I have described is a lame solution that should not be seriously used.
The code I gave is full of problems, like what happens if a task become to slow to execute...
Instead, you --could-- should use some RT Os, like FreeRTOS which is very helpful in this kind of problems.
I'm trying to set up a hardware interrupt handler in protected mode, using djgpp-2 for compiling in dosbox-0.74. Here's the smallest code possible (timer interrupt), I guess:
#include <dpmi.h>
#include <go32.h>
#include <stdio.h>
unsigned int counter = 0;
void handler(void) {
++counter;
}
void endHandler(void) {}
int main(void) {
_go32_dpmi_seginfo oldInfo, newInfo;
_go32_dpmi_lock_data(&counter, sizeof(counter));
_go32_dpmi_lock_code(handler, endHandler - handler);
_go32_dpmi_get_protected_mode_interrupt_vector(8, &oldInfo);
newInfo.pm_offset = (int) handler;
newInfo.pm_selector = _go32_my_cs();
_go32_dpmi_allocate_iret_wrapper(&newInfo);
_go32_dpmi_set_protected_mode_interrupt_vector(8, &newInfo);
while (counter < 3) {
printf("%u\n", counter);
}
_go32_dpmi_set_protected_mode_interrupt_vector(8, &oldInfo);
_go32_dpmi_free_iret_wrapper(&newInfo);
return 0;
}
Note that I'm not chaining my handler but replacing it. The counter won't increase beyond 1 (therefore never stopping the main loop) making me guess that the handler doesn't return correctly or is called only once. Chaining on the other hand works fine (remove the wrapper-lines and replace set_protected_mode with chain_protected_mode).
Am I missing a line?
You need to chain the old interrupt handler, like in the example Jonathon Reinhart linked to in the documentation, as the old handler will tell the interrupt controller to stop asserting the interrupt. It will also have the added benefit of keeping the BIOS clock ticking, so it doesn't lose a few seconds each time you run the program. Otherwise when your interrupt handler returns the CPU will immediately call the handler again and your program will get stuck in an infinite loop.
Also there's no guarantee that GCC will place endHandler after handler. I'd recommend just simply locking both the page handler starts on and the next page in case it straddles a page:
_go32_dpmi_lock_code((void *) handler, 4096);
Note the cast is required here, as there's no automatic conversion from pointer to a function types to pointer to void.
I have a handler for SysTick exception which counts ticks and calls other functions (f1, f2, f3) whose execution time can be longer than SysTick period. These functions set and clear their active status (global variables) so if a SysTick exception occurs it can detect an overload and return to interrupted function.
I have assigned fixed priority to SysTick exception (let's say 16). I want to somehow make possible for SysTick to generate an exception regardless of it's prior active status, go to SysTickHandler, increase tick counter and return to interrupted function.
One solution which may be useful is to use BASEPRI. It can be set to priority lower than SysTick so it would enable that exception. Unfortunately, using BASEPRI got me nowhere because nothing happened (I set it to max value). BASEPRI value was 0 inside SysTickHandler before I changed it. Should that value be equal to SysTick priority when processor enters handler function? Is exception priority loaded automatically in BASEPRI?
I have also considered for NVIC to have an issue with preempting already active exception but found nothing regarding that in ARM documentation.
Also, return from handler when oveload is detected could set the processor state to thread mode. Let's ignore that for now.
void SysTickHandler(void) {
ticks++;
//set_BASEPRI(max_value);
if (f1_act || f2_act || f3_act) return;
else {
f1();
f2();
f3();
}
}
A simpler example for this problem (without return) would be to increase tick counter when having an infinite loop inside handler.
void SysTickHandler(void) {
ticks++;
set_BASEPRI(max_value);
while(1);
}
If the interrupt becomes pending while its handler is already running, the handler will run to completion and immediately re-enter. Your tick will be aperiodic, and if the functions consistently take longer that one tick period, you may never leave the interrupt context.
It may be possible I suppose to increase the priority of the interrupt in the handler so that it will preempt itself, but even if that were to work, I would hesitate to recommend it.
It sounds that what you actually need is an RTOS.
Sorry to disappoint you, but it seems a overall design problem to me...
Why won't you just set some flag in SysTick and read it somewhere else?
Like:
#include <stdbool.h>
volatile bool flag = false;
//Consider any form of atomicity here
//atomic_bool or LDREX/STREX instructions here. Bitbanding will also work
void sysTickHandler(void) {
ticks++;
if (f1_act || f2_act || f3_act) return;
else {
flag = true; //or increment some counter if you want to keep track of the amount of executions
}
And somewhere else:
int main() {
// some init code
//main loop
for(;;) {
foo();//do sth
bar(x); //do sth else
if (flag) {
f1();
f2();
f3();
flag = false;
}
}
}
Or if we assume that every interrupt wakes the microcontroller and power-down mode is needed, then sth. like this might work:
if (flag) {
f1();
f2();
f3();
flag = false;
}
goToSleep(powerDownModeX); //whatever;
I'm using code to configure a simple robot. I'm using WinAVR, and the code used there is similar to C, but without stdio.h libraries and such, so code for simple stuff should be entered manually (for example, converting decimal numbers to hexadecimal numbers is a multiple-step procedure involving ASCII character manipulation).
Example of code used is (just to show you what I'm talking about :) )
.
.
.
DDRA = 0x00;
A = adc(0); // Right-hand sensor
u = A>>4;
l = A&0x0F;
TransmitByte(h[u]);
TransmitByte(h[l]);
TransmitByte(' ');
.
.
.
For some circumstances, I must use WinAVR and cannot external libraries (such as stdio.h). ANYWAY, I want to apply a signal with pulse width of 1 ms or 2 ms via a servo motor. I know what port to set and such; all I need to do is apply a delay to keep that port set before clearing it.
Now I know how to set delays, we should create empty for loops such as:
int value= **??**
for(i = 0; i<value; i++)
;
What value am I supposed to put in "value" for a 1 ms loop ?
Chances are you'll have to calculate a reasonable value, then look at the signal that's generated (e.g., with an oscilloscope) and adjust your value until you hit the right time range. Given that you apparently have a 2:1 margin, you might hit it reasonably close the first time, but I wouldn't be much on it.
For your first approximation, generate an empty loop and count the instruction cycles for one loop, and multiply that by the time for one clock cycle. That should give at least a reasonable approximation of time taken by a single execution of the loop, so dividing the time you need by that should get you into the ballpark for the right number of iterations.
Edit: I should also note, however, that (at least most) AVRs have on-board timers, so you might be able to use them instead. This can 1) let you do other processing and/or 2) reduce power consumption for the duration.
If you do use delay loops, you might want to use AVR-libc's delay loop utilities to handle the details.
If my program is simple enough there is not a need of explicit timer programming, but it should be portable. One of my choices for a defined delay would be AVR Libc's delay function:
#include <delay.h>
_delay_ms (2) // Sleeps 2 ms
Is this going to go to a real robot? All you have is a CPU, no other integrated circuits that can give a measure of time?
If both answers are 'yes', well... if you know the exact timing for the operations, you can use the loop to create precise delays. Output your code to assembly code, and see the exact sequence of instructions used. Then, check the manual of the processor, it'll have that information.
If you need a more precise time value you should employ an interrupt service routine based on an internal timer. Remember a For loop is a blocking instruction, so while it is iterating the rest of your program is blocked. You could set up a timer based ISR with a global variable that counts up by 1 every time the ISR runs. You could then use that variable in an "if statement" to set the width time. Also that core probably supports PWM for use with the RC type servos. So that may be a better route.
This is a really neat little tasker that I use sometimes. It's for an 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***********************************
Most ATmega AVR chips, which are commonly used to make simple robots, have a feature known as pulse-width modulation (PWM) that can be used to control servos. This blog post might serve as a quick introduction to controlling servos using PWM. If you were to look at the Arduino platform's servo control library, you would find that it also uses PWM.
This might be a better choice than relying on running a loop a constant number of times as changes to compiler optimization flags and the chip's clock speed could potentially break such a simple delay function.
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).