USB-LED-Fader/firmware/pwm_timer.c
2006-09-26 18:18:27 +00:00

140 lines
4.4 KiB
C

/**
* \file pwm_timer.c
* \brief Controls the actual PWM-output.
* \author Thomas Stegemann
* \version $Id: pwm_timer.c,v 1.1 2006/09/26 18:18:27 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)) {
if(m_data.readDone) {
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 {
/* error could not read a new channels message in a whole cycle */
/* wait a complete cycle for the next message */
//sleep= pwm_Timer_Cycles_Max;
m_data.currentCycle= 0;
m_data.step= 0;
sleep= 0;
}
} else {
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)) {
if(messageQueue_read(m_data.pRead)) {
m_data.readDone= True;
}
}
}