/** * \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 #include #include #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; } } }