150 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/**
 | 
						|
 * \file pwm_timer.c
 | 
						|
 * \brief Controls the actual PWM-output.
 | 
						|
 * \author Thomas Stegemann
 | 
						|
 * \version $Id: pwm_timer.c,v 1.2 2006/09/29 22:30:03 rschaten Exp $
 | 
						|
 *
 | 
						|
 * License: See documentation.
 | 
						|
 */
 | 
						|
 | 
						|
#include <avr/io.h>
 | 
						|
#include <avr/interrupt.h>
 | 
						|
#include <avr/pgmspace.h>
 | 
						|
 | 
						|
#include "boolean.h"
 | 
						|
#include "message_queue.h"
 | 
						|
#include "pwm_timer.h"
 | 
						|
#include "config_pwm_timer_impl.h"
 | 
						|
 | 
						|
/** Structure to contain the global data for the timer. */
 | 
						|
typedef struct S_pwm_Timer_GlobalData {
 | 
						|
    pwm_Channels_Message     message[2]; /**< Array of two messages */
 | 
						|
    pwm_Channels_Message*    pActive; /**< Pointer to the active message */
 | 
						|
    pwm_Channels_Message*    pRead; /**< Pointer to the message to read */
 | 
						|
    pwm_Channels_StepCounter step; /**< Current step in the cycle */
 | 
						|
    pwm_Timer_Cycles         currentCycle; /**< Current cycle */
 | 
						|
    Boolean                  readDone; /**< Indicates if something is read from the queue */
 | 
						|
} pwm_Timer_GlobalData;
 | 
						|
 | 
						|
static pwm_Timer_GlobalData m_data; /**< Global data for the timer. */
 | 
						|
 | 
						|
/**
 | 
						|
 * Initialize the PWM-Timer. Sets basic values, starts the timer and
 | 
						|
 * initializes output-pins.
 | 
						|
 */
 | 
						|
void pwm_Timer_init(void) {
 | 
						|
    messageQueue_init();
 | 
						|
    m_data.step=           0;
 | 
						|
    m_data.currentCycle=   0;
 | 
						|
    m_data.pActive=        &m_data.message[0];
 | 
						|
    m_data.pRead=          &m_data.message[1];
 | 
						|
    m_data.readDone=       False;
 | 
						|
    m_data.pActive->step[0].cycle= pwm_Channels_Brightness_Max;
 | 
						|
    m_data.pActive->step[0].field=      0;
 | 
						|
    /* clk/64 prescaling, CTC mode */
 | 
						|
    /* enable timer1 overflow (=output compare 1a) */
 | 
						|
    TCCR1B= _BV(CS11) | _BV(CS10) | _BV(WGM12);
 | 
						|
    TCCR1A= 0;
 | 
						|
    TIMSK|= _BV(OCIE1A);
 | 
						|
    /* load initial delay */
 | 
						|
    OCR1A=  pwm_Timer_Cycles_Max;
 | 
						|
    /* initialize output pin */
 | 
						|
    DDRC = (1 << CHANNELS) - 1; // set all used channel-pins to output
 | 
						|
    PORTC = 0;
 | 
						|
    sei();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Clean up the timer. Basically, the message-queue is cleaned.
 | 
						|
 */
 | 
						|
void pwm_Timer_cleanup(void) {
 | 
						|
    messageQueue_cleanup();
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Do nothing.
 | 
						|
 */
 | 
						|
void pwm_Timer_idle(void)
 | 
						|
{}
 | 
						|
 | 
						|
/**
 | 
						|
 * Sleeps the required number of cycles. There are two possible ways of
 | 
						|
 * sleeping: 'active' and 'passive'. If we are required to sleep less than the
 | 
						|
 * number of cycles defined in pwm_Timer_Cycles_SleepMax, we execute an empty
 | 
						|
 * loop until we are ready (active sleeping). Otherwise, we set the timer to
 | 
						|
 * wake us after the given number of cycles (passive sleeping).
 | 
						|
 * \param sleep Number of cycles.
 | 
						|
 * \return True if we slept 'actively' (doing the while-loop), otherwise false.
 | 
						|
 */
 | 
						|
static Boolean pwm_Timer_sleep(pwm_Timer_Cycles sleep) {
 | 
						|
    Boolean sleepDone= False;
 | 
						|
    if((sleep < pwm_Timer_Cycles_SleepMax)) {
 | 
						|
        while (TCNT1 < sleep)
 | 
						|
        {}
 | 
						|
        sleepDone= True;
 | 
						|
    } else {
 | 
						|
        OCR1A= sleep;
 | 
						|
    }
 | 
						|
    return sleepDone;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Switch the output-pins to the given pattern.
 | 
						|
 * \param field 8-bit output-pattern.
 | 
						|
 */
 | 
						|
static void pwm_Timer_switchLed(pwm_Channels_Bitfield field) {
 | 
						|
    PORTC= field;
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Timer interrupt routine. Determines the pattern to set and handles the times
 | 
						|
 * to do PWM.
 | 
						|
 */
 | 
						|
SIGNAL(SIG_OUTPUT_COMPARE1A) {
 | 
						|
    pwm_Timer_Cycles sleep= pwm_Timer_Cycles_Max;
 | 
						|
    OCR1A= pwm_Timer_Cycles_Max;
 | 
						|
    sei();
 | 
						|
    do {
 | 
						|
        if((m_data.step == pwm_Channels_StepCounter_Max) || (m_data.currentCycle == pwm_Timer_Cycles_Max)) {
 | 
						|
            /* end of current cycle reached*/ 
 | 
						|
            if(m_data.readDone) {
 | 
						|
                /*
 | 
						|
                  message received
 | 
						|
                  start a new cycle with the new values
 | 
						|
                  swap active message and message for reading
 | 
						|
                  message for reading is free for further messages from queue
 | 
						|
                */
 | 
						|
                pwm_Channels_Message* pSwap= m_data.pActive;
 | 
						|
                m_data.pActive= m_data.pRead;
 | 
						|
                m_data.pRead=   pSwap;
 | 
						|
                m_data.readDone= False;
 | 
						|
                m_data.currentCycle= 0;
 | 
						|
                m_data.step= 0;
 | 
						|
                sleep= 0;
 | 
						|
            } else {
 | 
						|
                /* could not read a new channels message in a whole cycle */
 | 
						|
                /* restart the cycle with the old values */
 | 
						|
                m_data.currentCycle= 0;
 | 
						|
                m_data.step= 0;
 | 
						|
                sleep= 0;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            /* process current step, go to next step */
 | 
						|
            pwm_Timer_switchLed(m_data.pActive->step[m_data.step].field);
 | 
						|
            sleep= m_data.pActive->step[m_data.step].cycle - m_data.currentCycle;
 | 
						|
            m_data.currentCycle= m_data.pActive->step[m_data.step].cycle;
 | 
						|
            m_data.step++;
 | 
						|
        }
 | 
						|
    } while(pwm_Timer_sleep(sleep));
 | 
						|
 | 
						|
    if(!m_data.readDone && (sleep > pwm_Timer_Cycles_ReadMin)) {
 | 
						|
        /*
 | 
						|
          free space for reading and enough time to read: try to read
 | 
						|
        */
 | 
						|
        if(messageQueue_read(m_data.pRead)) {
 | 
						|
            m_data.readDone= True;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 |