-1
int main(void)
{
   
    DDRC = DDRC | (1<<2);
    DDRC = DDRC | (1<<3);
 
    while (1)
    {  
     //openSolenoidValves(100,60);
     //startStepperMotor();


    } 
void openSolenoidValves(double air, double oxygen) {
                
                PORTC = PORTC | (1<<2);  //open oxygen(normally closed valve)
                PORTC = PORTC & (~(1<<3)); //open air (normally open valve)
                _delay_ms(oxygen);
                PORTC = PORTC & (~(1<<2));//close oxygen
                _delay_ms(air-oxygen);
                PORTC = PORTC | (1<<3);//close air
                _delay_ms(air);
}

void startStepperMotor(){
//this function also has delays
}

I want to start both openSolenoidValve function and startStepperMotor function same time.But both functions have delays. Is there any method to do that? (MicroController-Atmega32)

Yesitha
  • 75
  • 1
  • 5

4 Answers4

2

A single core processor cannot execute two threads of execution simultaneously in the sense of parallel processing, but you don't actually need that to solve your problem. It is an issue of scheduling processor time rather then simultaneous execution. In your case since both functions spend most of their time in delays doing nothing - that "do nothing" time can be shared - doing nothing concurrently is easy.

One solution would be to use an RTOS. Simple RTOS kernels such as FreeRTOS run on AVR, but the footprint may be prohibitive for parts with tiny memories as each thread must have its own stack.

If you already have an RTOS port for your target and are familiar with such things (which I guess your are not since you asked the question), it is probably the simplest solution; your code might look something like (pseudo-code and not any particular real RTOS API):

int main(void)
{
    DDRC = DDRC | (1<<2);
    DDRC = DDRC | (1<<3);
    
    osCreateThread( solenoidTask, SOLONOID_PRIORITY ) ;
    osCreateThread( stepperTask, STEPPER_PRIORITY ) ;
    osStartKernel() ;  // does not return
} 

void solenoidTask()
{
    for(;;)
    {
        openSolenoidValves( 100, 60 ) ;
    }
}

void stepperTask()
{
    for(;;)
    {
        startStepperMotor( 100, 60 ) ;
    }
}

void openSolenoidValves(double air, double oxygen) 
{
    PORTC = PORTC | (1<<2);  //open oxygen(normally closed valve)
    PORTC = PORTC & (~(1<<3)); //open air (normally open valve)

    osDelay( oxygen ) ;

    PORTC = PORTC & (~(1<<2));//close oxygen

    osDelay( air - oxygen ) ;

    PORTC = PORTC | (1<<3);//close air

    osDelay( air ) ;
}

void startStepperMotor()
{
    // use osDelay() - not busy wait delay.
    // this function also has delays
    ...
}

However getting an RTOS running on your target may be an unacceptable overhead for simple projects if you do not have it already set-up. In that case you need to avoid delays by polling for time and taking actions only when it is time to do so. For that you need a "current time" or "tick" function, which is not part of the standard AVR library (clock() for example is not implemented). I'll get to that, but say we have a free-running 1ms counter with interface millis() (note that the Arduino library does have such a function). Then you might create a "timer" API like:

#include <stdint.h>
#include <stdbool.h>

typedef struct
{
    unsigned start ;
    unsigned expiry ;
} sTimer ;

void startTimer( sTimer* timer, uint32_t expiry )
{
    timer.expiry = expiry ;
    timer.start = millis() ;
}

bool isTimerExpired( sTimer* timer )
{
    return millis() - start >= expiry ;
}

Then you would re-write your openSolenoidValves() function as as a state machine as follows for example:

void openSolenoidValves(double air, double oxygen) 
{
    static enum
    {
        START,
        ALL_OPEN,
        OXY_CLOSE,
        AIR_CLOSE
    } state = ALL_OPEN ;

    sTimer timer ;

    switch( state )
    {
        case START :
        {
            startTImer( &timer, oxygen ) ;
            PORTC = PORTC | (1<<2);    //open oxygen(normally closed valve)
            PORTC = PORTC & (~(1<<3)); //open air (normally open valve)
            state = ALL_OPEN ;
        }
        break ;

        case ALL_OPEN :
        {
            if( isTimerExpired( &timer ) )
            {
                PORTC = PORTC & (~(1<<2)) ; // close oxygen
                startTimer( &timer, air - oxygen ) ;
                state = OXY_CLOSED ;
            }
        }
        break ;

        case OXY_CLOSED :
        {
            if( isTimerExpired( &timer ) )
            {
                PORTC = PORTC | (1<<3); // close air
                startTimer( &timer, air ) ;
                state = AIR_CLOSED ;
            }
        }
        break ;

        case AIR_CLOSED :
        {
            if( isTimerExpired( &timer ) )
            {
                state = START ;
            }
        }
        break ;
    }
}

In each state, the function is called and does nothing if the current timer has not expired and returns immediately. When the timer expire it performs the necessary solenoid operation and switches state - and returns immediately.

You implement the stepper function in a similar manner, then your executive loop can be:

for(;;)
{  
    openSolenoidValves( 100, 60 ) ;
    startStepperMotor() ;
} 

As you can see even for quite simple scheduling it can get rather complicated compared to RTOS threads. However if you design it with state machine diagrams before attempting to write the code, the implementation can be quite mechanistic within a basic and re-usable state-machine framework. Note also that you need not wait only on time, you could for example poll inputs to trigger events to advance the state-machines.

Note also if you were using the Arduino Sketch framework, you would not have the endless for(;;) or while(1) loop, the state-machine functions would simply be called in the loop() function (which is in turn called withing an endless loop provided as part of the framework).

Now if you are not using Arduino for example and have no millis() or similat system tick function, on AVR you can create one using a timer peripheral incrementing a counter in the reload interrupt. For example:

#include <stdint.h> ;
#include <avr/io.h> ;
#include <avr/interrupt.h> ;

// Timer reload value for 1ms
#define SYSTICK_RELOAD (CORE_CLK_FREQ / 1000UL)

// Millisecond counter
volatile uint32_t tick_millisec = 0 ;

ISR (TIMER1_COMPA_vect)
{
    tick_millisec++;
}

void sysTickInit()
{
    // CTC mode, Clock/1
    TCCR1B |= (1 << WGM12) | (1 << CS10);

    // Load the output compare
    OCR1AH = (SYSTICK_RELOAD >> 8);
    OCR1AL = SYSTICK_RELOAD ;

    // Enable the compare match interrupt
    TIMSK1 |= (1 << OCIE1A);

    // Enable interrupts
    sei();
}

uint32_t millis()
{
    uint32_t now = 0 ;

    // Read tick count and re-read if it is not consistent
    // (due interrupt pre-emption and update during non-atomic access)
    do
    {
        now = tick_millisec ;
    } while( now != tick_millisec ) ;

    return now ;
}

With a main() like:

int main( void )
{
    sysTickInit() ;

    DDRC = DDRC | (1<<2) ;
    DDRC = DDRC | (1<<3) ;

    for(;;)
    {  
        openSolenoidValves( 100, 60 ) ;
        startStepperMotor() ;
    } 
}

See also Pressing button for 3 seconds and how to measure its time with Atmega8 1MHz? for other non-blocking scheduling using system tick polling.

Clifford
  • 88,407
  • 13
  • 85
  • 165
1

The ATmega32 only has one core, unfortunately. There is such a thing called a "real time operating system" that lets you run multiple tasks like this at once and I'm sure you could select one that works on the ATmega32 and port your code to it, but that is probably way more trouble than it is worth. Concurrent execution of code can be achieved more easily by writing your code to behave in a non-blocking way (i.e. remove all blocking delays) and/or using interrupts if appropriate.

David Grayson
  • 84,103
  • 24
  • 152
  • 189
0

Yes this is totally possible. It's called cooperative multitasking. Start by studying Blink Without Delay, and my answer here: How to do high-resolution, timestamp-based, non-blocking, single-threaded cooperative multi-tasking

Example:

int main(void)
{
    doSetupStuff();
    configureHardwareTimer();

    while (1)
    {
        doTask1();
        doTask2();
        doTask3();
    }
}

See my answer above for examples of how to write the 3 tasks in non-blocking ways.

When done correctly, this technique works on literally any platform, from AVR to Arduino to STM32 to Windows, Linux, Raspberry Pi, etc., and can be done in any programming language.

And, it is designed to be single threaded, so there is no need for an OS (Operating System), nor multiple cores, nor a full scheduler. In effect, this is a type of light-weight, cooperative (as opposed to preemptive) scheduler.

It is so light-weight that I believe it could be done on even the tiniest of AVR microcontrollers which have just 64 bytes of RAM, all the way up to the biggest of PCs with hundreds of gigabytes of RAM.

Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
0

This millis() function help me to solve my problem.

Just like the millis() function in Arduino, this function returns the time in milliseconds since the program started.

As Developers' details, This function has only been tested on the atmega328p but may work on many other AVRs as well.

Implementing this function is easy - take a look at the example:

  1. First you must initiate the clock with init_millis()
  2. After calling init_millis(), call sei() to enable global interrupts
  3. Use millis() whenever you like to get the time in milliseconds since the program started

Developers' repo => https://github.com/monoclecat/avr-millis-function#:~:text=avr%2Dmillis%2Dfunction-,Just%20like%20the%20millis()%20function%20in%20Arduino%2C%20this%20function,many%20other%20AVRs%20as%20well.

Yesitha
  • 75
  • 1
  • 5
  • Please provide an textual description of the function that helped you. – YesThatIsMyName Jun 27 '22 at 10:00
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – YesThatIsMyName Jun 27 '22 at 10:00