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