main.c


1
/*
2
*  C Implementation: main
3
*
4
* Description: 
5
*
6
*
7
* Author: Hagen Meyer <hagen@hcmeyer.de>, (C) 2009
8
*
9
* Copyright: See COPYING file that comes with this distribution
10
*
11
*/
12
13
/** \mainpage
14
 * Nothing here, yet…
15
 */
16
17
/** \file
18
 * \brief The main file
19
 */
20
21
#define DEBUG    0
22
23
#include <stdlib.h>
24
#include <avr/io.h>
25
#include <avr/interrupt.h>
26
#include <util/delay.h>
27
28
/*##########################################################################
29
  CONSTANT DEFINITIONS
30
###########################################################################*/
31
32
/// \brief Register for the motor PWM
33
#define MOTOR_SPEED  OCR0B
34
35
#define SPEED_NORMAL    65
36
#define SPEED_FAST    230
37
#define STOP_PROBABILITY  5
38
39
/// \name LED hardware connections
40
//@{
41
#define PORT_LED01  PORTA
42
#define PORT_LED23  PORTD
43
#define PIN_LED0  1
44
#define PIN_LED1  0
45
#define PIN_LED2  2
46
#define PIN_LED3  3
47
//@}
48
49
/// \name Remote-Control hardware connections
50
//@{
51
#define REMOTE_FAST  _BV(PD0) // RxD
52
#define REMOTE_SLOW  _BV(PD1) // TxD
53
#define REMOTE_STOP  (_BV(PD0)|_BV(PD1)) // both
54
//@}
55
56
/// \name LED commands
57
//@{
58
#define OFF    0
59
#define ON    1
60
#define TOGGLE    2
61
//@}
62
63
#define LED_ALL    4
64
65
/** \name DIP-switch settings
66
 * \brief The different modes selectable with the DIP switches
67
 */
68
//@{
69
#define GAME_MODE_SLOW    0 << 0 ///< DIP1..2 = 00 (booooring)
70
#define GAME_MODE_FAST    1 << 0 ///< DIP1..2 = 10 (not soo boring)
71
#define GAME_MODE_RANDOM  2 << 0 ///< DIP1..2 = 01 (magic :) )
72
#define GAME_MODE_RANDOM_STOP  3 << 0 ///< DIP1..2 = 11 (more magic :) )
73
#define LED_MODE_OFF    0 << 2 ///< DIP3..4 = 00
74
#define LED_MODE_ON    1 << 2 ///< DIP3..4 = 10
75
#define LED_MODE_RAND    2 << 2 ///< DIP3..4 = 01
76
#define REMOTE_MODE_25    0 << 4 ///< DIP5..6 = 00 
77
#define REMOTE_MODE_50    1 << 4 ///< DIP5..6 = 10
78
#define REMOTE_MODE_75    2 << 4 ///< DIP5..6 = 01
79
#define REMOTE_MODE_100    3 << 4 ///< DIP5..6 = 11
80
//@}
81
82
/*##########################################################################
83
  MACROS
84
###########################################################################*/
85
86
/// \brief reads the Game mode from the DIP-switches
87
#define getGameMode()    ((~PINB) & 3)
88
/// \brief reads the LED mode from the DIP-switches
89
#define getLEDMode()    ((~PINB) & (3<<2))
90
/// \brief reads the remote-control mode from the DIP-switches
91
#define getRemoteMode()    ((~PINB) & (3<<4))
92
/// \brief reads the buttons of the remote control
93
#define getRemoteButtons()  ((~PIND) & (_BV(PD0)|_BV(PD1)))
94
/// \brief reads the position of the ON/OFF switch (for the motor)
95
#define isOn()      !(PIND & _BV(PD4))
96
/// \brief starts Timer 1 with prescaler 1024 (~1ms/step)
97
#define runTimer()    TCCR1B = _BV(CS12) | _BV(CS10)
98
/// \brief resets Timer 1
99
#define resetTimer()    TCNT1 = 0
100
/// \brief stops Timer 1
101
#define stopTimer()    TCCR1B = 0
102
/// \brief set the Timer-value
103
#define setTimer(val)    TCNT1 = (val)
104
/// \brief gets the value of Timer 1 (elapsed time in ~ms)
105
#define time()      TCNT1
106
107
/*##########################################################################
108
  PROTOTYPES
109
###########################################################################*/
110
111
int main (void);
112
void switchLED(uint8_t led, uint8_t state);
113
void LEDMagic(void);
114
void GameMagic(void);
115
116
/*##########################################################################
117
  GLOBAL VARIABLES
118
###########################################################################*/
119
120
uint8_t random_nr;
121
uint8_t led_mode;
122
uint8_t remote_prev;    // previous state of the remote
123
uint8_t remote_accepted;  // remote commands which are accepted (selectable probability)
124
uint8_t speed;
125
126
/*##########################################################################
127
  SUBROUTINES
128
###########################################################################*/
129
130
/** \brief Routine to switch the LEDS
131
 *  \param led Which LED to switch (0 to 3, or 4 for all)
132
 *  \param state What to do (see \ref LED commands)
133
 *
134
 */
135
void switchLED(uint8_t led, uint8_t state){
136
  uint8_t *port;
137
  uint8_t operand;
138
  switch(led){
139
    case 0: port = (uint8_t*)&PORT_LED01; operand = _BV(PIN_LED0); break;
140
    case 1: port = (uint8_t*)&PORT_LED01; operand = _BV(PIN_LED1); break;
141
    case 2: port = (uint8_t*)&PORT_LED23; operand = _BV(PIN_LED2); break;
142
    case 3: port = (uint8_t*)&PORT_LED23; operand = _BV(PIN_LED3); break;
143
    case 4: switch(state){
144
      case ON: PORT_LED01 |= _BV(PIN_LED0) | _BV(PIN_LED1);
145
        PORT_LED23 |= _BV(PIN_LED2) | _BV(PIN_LED3);
146
        break;
147
      case OFF: PORT_LED01 &= ~(_BV(PIN_LED0) | _BV(PIN_LED1));
148
        PORT_LED23 &= ~(_BV(PIN_LED2) | _BV(PIN_LED3));
149
        break;
150
      case TOGGLE: PORT_LED01 ^= _BV(PIN_LED0) | _BV(PIN_LED1);
151
        PORT_LED23 ^= _BV(PIN_LED2) | _BV(PIN_LED3);
152
        break;
153
      }
154
      return;
155
    default: return;
156
  }
157
  switch(state){
158
    case ON: *port |= operand; break;
159
    case OFF: *port &= ~operand; break;
160
    case TOGGLE: *port ^= operand; break;
161
  }
162
}
163
164
/** \brief Function for the LED magic driven by the DIP-switches for LED mode
165
 */
166
void LEDMagic() {
167
  uint8_t tmp;
168
  ///////
169
  // LED magic
170
  ///////
171
  if(led_mode == 0) { // random mode
172
    if(time() % 256 == 0) switchLED(random_nr & 3,TOGGLE);
173
  } else if(led_mode == 1) { // circle 1
174
    tmp = time()/128;
175
    switchLED((tmp-1)&3,ON);
176
    switchLED(tmp&3,OFF);
177
  } else if(led_mode == 2) { // circle 2
178
    tmp = ~(time()/128);
179
    switchLED((tmp-1)&3,ON);
180
    switchLED(tmp&3,OFF);
181
  } else if(led_mode == 3) { // toggle
182
    if((time()/512)&1) switchLED(LED_ALL,TOGGLE);
183
  }
184
  ///////
185
  // switch LED modes
186
  ///////
187
  if(getLEDMode() == LED_MODE_ON) {
188
    switchLED(LED_ALL,ON);
189
    led_mode = 0xff;
190
  } else if(getLEDMode() == LED_MODE_OFF) {
191
    switchLED(LED_ALL,OFF);
192
    led_mode = 0xff;
193
  } else if((getLEDMode() == LED_MODE_RAND) && ((time()/1024) % 10 == 0)) { // random LED modes
194
    led_mode = random_nr & 3;
195
    if(led_mode == 3) {
196
      switchLED(LED_ALL,OFF);
197
      switchLED(0,ON);
198
      switchLED(2,ON);
199
    }
200
  }
201
}
202
203
/** \brief Function which drives the motor. Controlled by the DIP-switches for Game mode
204
 */
205
void GameMagic() {
206
  ///////
207
  // Remote control
208
  ///////
209
  uint8_t remote_curr = getRemoteButtons();
210
  if(remote_curr != remote_prev) {
211
    if((remote_curr == 0) || (getRemoteMode() == REMOTE_MODE_100)) {
212
      remote_accepted = remote_curr;
213
    } else if((getRemoteMode() == REMOTE_MODE_25) && ((random_nr & 3) == 0)) {
214
      remote_accepted = remote_curr;
215
    } else if((getRemoteMode() == REMOTE_MODE_50) && ((random_nr & 3) < 2)) {
216
      remote_accepted = remote_curr;
217
    } else if((getRemoteMode() == REMOTE_MODE_75) && ((random_nr & 3) < 3)) {
218
      remote_accepted = remote_curr;
219
    }
220
    remote_prev = remote_curr;
221
  }
222
  ///////
223
  // Game magic
224
  ///////
225
  if(getGameMode() == GAME_MODE_SLOW) { // boooring
226
    speed = SPEED_NORMAL;
227
  } else if(getGameMode() == GAME_MODE_FAST) { // not so boring
228
    speed = SPEED_FAST;
229
  } else { // magic :)
230
    if(time() % 256 == 0) {
231
      if ((random_nr >= SPEED_NORMAL) && (random_nr <= SPEED_FAST)) {
232
        speed = random_nr;
233
      } else if ((random_nr < SPEED_NORMAL) && (random_nr >= SPEED_NORMAL - 20)) { // higher probability for the lowest speed
234
          speed = SPEED_NORMAL;
235
      } else if ((random_nr > SPEED_FAST) && (random_nr <= SPEED_FAST + 10)) { // higher probability for the highest speed
236
        speed = SPEED_FAST;
237
      } else if (getGameMode() == GAME_MODE_RANDOM_STOP && random_nr < STOP_PROBABILITY) {
238
        speed = 0;
239
      }
240
    }
241
  }
242
  if(remote_accepted == REMOTE_FAST) {
243
    MOTOR_SPEED = SPEED_FAST;
244
  } else if(remote_accepted == REMOTE_SLOW) {
245
    MOTOR_SPEED = SPEED_NORMAL;
246
  } else if(remote_accepted == REMOTE_STOP) {
247
    MOTOR_SPEED = 0;
248
  } else {
249
    MOTOR_SPEED = speed;
250
  }
251
}
252
253
/*##########################################################################
254
  MAIN ROUTINE
255
###########################################################################*/
256
257
/** \brief The main function */
258
int main (void) {
259
/*##########################################################################
260
  INITIALIZATION
261
###########################################################################*/
262
  ACSR = _BV(ACD);    // disable analog comparator
263
  
264
  // LED0 and 1 as output
265
  DDRA = _BV(PA0) | _BV(PA1);
266
267
  // DIP-switches (PB0..5) as input w/ pullup
268
  PORTB = (uint8_t)(~(3<<6));
269
270
  // LED2 and 3 as output, Motor-PWM as output, ON/OFF switch as input w/ pullup, Remote-Switches as input w/ pullup
271
  DDRD = _BV(PD2) | _BV(PD3) | _BV(PD5);
272
  PORTD = _BV(PD0) | _BV(PD1) | _BV(PD4);
273
274
  MOTOR_SPEED = 0;
275
  // Non-Inverting 8 bit phase correct PWM (MAX=0xFF) on PD5, no prescaler
276
  TCCR0A = _BV(COM0B1) | _BV(WGM00);
277
  TCCR0B = _BV(CS00);
278
279
  extern unsigned short __heap_start;
280
  unsigned int seed = *((unsigned int *)__heap_start);
281
  srand(seed);       // seed random number generator with uninitialized sram value
282
/*##########################################################################
283
  MAIN LOOP
284
###########################################################################*/
285
  led_mode = 0xff;
286
  remote_prev = 0;
287
  for(;;) {
288
    random_nr = (uint8_t)rand(); // number from 0 to 0xFF
289
    if(isOn()) {
290
      runTimer();
291
      if(time() < 500) { // start countdount
292
        if(getLEDMode() != LED_MODE_OFF) switchLED(0,ON);
293
        speed = SPEED_NORMAL;
294
        MOTOR_SPEED = speed;
295
      } else if(time() < 1000) {
296
        if(getLEDMode() != LED_MODE_OFF) switchLED(1,ON);
297
      } else if(time() < 1500) {
298
        if(getLEDMode() != LED_MODE_OFF) switchLED(2,ON);
299
      } else if(time() < 2000) {
300
        if(getLEDMode() != LED_MODE_OFF) switchLED(3,ON);
301
      } else if(time() > 65000) { // set timer back to avoid a new countdown
302
        setTimer(3000);
303
      } else if(time() >= 3000) { // start the magic ;)
304
        LEDMagic();
305
        GameMagic();
306
      }
307
    }
308
    else { // switch off everything
309
        MOTOR_SPEED = 0;
310
      switchLED(LED_ALL,OFF);
311
      stopTimer();
312
      resetTimer();
313
    }
314
  }
315
}