Subversion Repositories group.electronics

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
7 pfowler 1
#include <avr/io.h>
2
#include <avr/pgmspace.h>
3
#include <avr/interrupt.h>
4
#include <util/delay.h>
5
#include <avr/wdt.h>
6
#include <avr/eeprom.h>
7
#include <usbdrv/usbdrv.h>
8
 
9
#include <stdlib.h>
10
#include <string.h>
11
 
12
#include "config.h"
13
#include "nes.h"
14
 
15
FUSES = 
16
{
17
  .low = (FUSE_CKSEL1 & FUSE_CKSEL2 & FUSE_CKSEL3 & FUSE_SUT0), // Int osc HF PLL, slow rising power
18
  .high = (FUSE_SPIEN & FUSE_BODLEVEL1), // Enable serial programming, BOD 2.7V
19
  .extended = EFUSE_DEFAULT,
20
};
21
 
22
struct{
23
  union {
24
    uint8_t data;
25
    struct {
26
      uint8_t X:2;
27
      uint8_t Y:2;
28
      uint8_t B:1;
29
      uint8_t A:1;
30
      uint8_t SELECT:1;
31
      uint8_t START:1;
32
    };
33
  };
34
} reportBuffer;
35
 
36
usbMsgLen_t usbFunctionSetup(uchar data[8]) {
37
  usbRequest_t *req = (void *)data;
38
 
39
  return 0; // Nothing implemented
40
}
41
 
42
/* ------------------------------------------------------------------------- */
43
/* ------------------------ Oscillator Calibration ------------------------- */
44
/* ------------------------------------------------------------------------- */
45
 
46
/* Calibrate the RC oscillator to 8.25 MHz. The core clock of 16.5 MHz is
47
 * derived from the 66 MHz peripheral clock by dividing. Our timing reference
48
 * is the Start Of Frame signal (a single SE0 bit) available immediately after
49
 * a USB RESET. We first do a binary search for the OSCCAL value and then
50
 * optimize this value with a neighboorhod search.
51
 * This algorithm may also be used to calibrate the RC oscillator directly to
52
 * 12 MHz (no PLL involved, can therefore be used on almost ALL AVRs), but this
53
 * is wide outside the spec for the OSCCAL value and the required precision for
54
 * the 12 MHz clock! Use the RC oscillator calibrated to 12 MHz for
55
 * experimental purposes only!
56
 */
57
static void calibrateOscillator(void)
58
{
59
uchar       step = 128;
60
uchar       trialValue = 0, optimumValue;
61
int         x, optimumDev, targetValue = (unsigned)(1499 * (double)F_CPU / 10.5e6 + 0.5);
62
 
63
    /* do a binary search: */
64
    do{
65
        OSCCAL = trialValue + step;
66
        x = usbMeasureFrameLength();    /* proportional to current real frequency */
67
        if(x < targetValue)             /* frequency still too low */
68
            trialValue += step;
69
        step >>= 1;
70
    }while(step > 0);
71
    /* We have a precision of +/- 1 for optimum OSCCAL here */
72
    /* now do a neighborhood search for optimum value */
73
    optimumValue = trialValue;
74
    optimumDev = x; /* this is certainly far away from optimum */
75
    for(OSCCAL = trialValue - 1; OSCCAL <= trialValue + 1; OSCCAL++){
76
        x = usbMeasureFrameLength() - targetValue;
77
        if(x < 0)
78
            x = -x;
79
        if(x < optimumDev){
80
            optimumDev = x;
81
            optimumValue = OSCCAL;
82
        }
83
    }
84
    OSCCAL = optimumValue;
85
}
86
/*
87
Note: This calibration algorithm may try OSCCAL values of up to 192 even if
88
the optimum value is far below 192. It may therefore exceed the allowed clock
89
frequency of the CPU in low voltage designs!
90
You may replace this search algorithm with any other algorithm you like if
91
you have additional constraints such as a maximum CPU clock.
92
For version 5.x RC oscillators (those with a split range of 2x128 steps, e.g.
93
ATTiny25, ATTiny45, ATTiny85), it may be useful to search for the optimum in
94
both regions.
95
*/
96
 
97
void hadUsbReset(void) {
98
  calibrateOscillator();
99
  eeprom_write_byte(0, OSCCAL);   /* store the calibrated value in EEPROM */
100
}
101
 
102
// Clock pulse width >= 500ns 
103
#define STROBE_CLK() sbi(NES_PORT, CLK); \
104
                     _delay_us(1); \
105
                     cbi(NES_PORT, CLK); \
106
                     _delay_us(1); 
107
 
108
int main(void) {
109
  uchar calibrationValue;
110
  while (!eeprom_is_ready());
111
  calibrationValue = eeprom_read_byte(0); /* calibration value from last time */
112
  if(calibrationValue != 0xff) {
113
    OSCCAL = calibrationValue;
114
  }
115
 
116
  _delay_ms(10);
117
  ACSR |= (1<<ACD); // Disable analog comparator
118
 
119
 
120
  usbDeviceDisconnect();  /* enforce re-enumeration, do this while interrupts are disabled! */
121
  _delay_ms(500);
122
  usbDeviceConnect();
123
 
124
  wdt_enable(WDTO_1S);
125
  usbInit();
126
  sei();
127
 
128
  sbi(NES_DDR, CLK);
129
  cbi(NES_DDR, OUT);
130
  sbi(NES_DDR, LATCH);
131
 
132
  cbi(NES_PORT, CLK);
133
  cbi(NES_PORT, LATCH);
134
 
135
  for(;;) {
136
    wdt_reset();
137
    usbPoll();
138
    if(usbInterruptIsReady()){
139
 
140
      reportBuffer.data = 0x05; // Center pad, little endian
141
 
142
      sbi(NES_PORT, LATCH);
143
      _delay_us(1); // Latch pulse width >= 500ns
144
      cbi(NES_PORT, LATCH);
145
      _delay_us(1); // Propagation time <= 1000ns
146
 
147
      if(bit_is_clear(NES_PIN, OUT))
148
        reportBuffer.A = 1;
149
 
150
      STROBE_CLK();
151
 
152
      if(bit_is_clear(NES_PIN, OUT))
153
        reportBuffer.B = 1;
154
 
155
      STROBE_CLK();
156
 
157
      if(bit_is_clear(NES_PIN, OUT))
158
        reportBuffer.SELECT = 1;
159
 
160
      STROBE_CLK();
161
 
162
      if(bit_is_clear(NES_PIN, OUT))
163
        reportBuffer.START = 1;
164
 
165
      STROBE_CLK();
166
 
167
      if(bit_is_clear(NES_PIN, OUT))
168
        reportBuffer.Y--;
169
 
170
      STROBE_CLK();
171
 
172
      if(bit_is_clear(NES_PIN, OUT))
173
        reportBuffer.Y++;
174
 
175
      STROBE_CLK();
176
 
177
      if(bit_is_clear(NES_PIN, OUT))
178
        reportBuffer.X--;
179
 
180
      STROBE_CLK();
181
 
182
      if(bit_is_clear(NES_PIN, OUT))
183
        reportBuffer.X++;
184
 
185
      /* called after every poll of the interrupt endpoint */
186
      usbSetInterrupt(&reportBuffer, sizeof(reportBuffer));
187
 
188
    }
189
 
190
  }
191
 
192
}