1 | //-----------------------------------------------------------------------------
|
2 | // F320_FM_RadioMain.c
|
3 | //-----------------------------------------------------------------------------
|
4 | // Copyright 2006 Silicon Laboratories, Inc.
|
5 | // http://www.silabs.com
|
6 | //
|
7 | // Program Description:
|
8 | //
|
9 | // This is the main project module. It initializes all system hardware, and
|
10 | // then enables interrupts. All further processing is done in the various
|
11 | // interrupt handlers.
|
12 | //
|
13 | // Linker options are used to absolutely locate code to specific locations.
|
14 | //
|
15 | // The linker option can be viewed from the Tool Chain Integration window
|
16 | // Project -> Tool Chain Integration -> Linker (tab)
|
17 | //
|
18 | // FID: 32X000042
|
19 | // Target: C8051F320
|
20 | // Tool chain: KEIL C51 7.0.0.1
|
21 | // Silicon Laboratories IDE version 2.3
|
22 | // Command Line: See Readme.txt
|
23 | // Project Name: F320_FM_Radio
|
24 | //
|
25 | // Release 1.1
|
26 | // -Changed Oscillator_Init to start clock multiplier correctly (DM)
|
27 | // -16 JAN 2006
|
28 | //
|
29 | // Release 1.0
|
30 | // -Initial Revision (DM)
|
31 | // -05 AUG 2005
|
32 | //
|
33 |
|
34 | //-----------------------------------------------------------------------------
|
35 | // Includes
|
36 | //-----------------------------------------------------------------------------
|
37 |
|
38 | #include "c8051f320.h" // SFR declarations
|
39 | #include "F320_FM_RadioMain.h" // Main project header
|
40 | #include "F320_Si470x_Interface.h" // Si470x interface header
|
41 | #include "io.h"
|
42 |
|
43 | #define IO3W 0
|
44 | #define IO2W 1
|
45 |
|
46 | #define CTS 0x80
|
47 | #define GET_REV 0x10
|
48 | #define POWER_UP 0x01
|
49 | #define POWER_UP_IN_FUNC_FMRX 0x00
|
50 | #define POWER_UP_IN_FUNC_AMRX 0x01
|
51 | #define POWER_UP_IN_FUNC_FMTX 0x02
|
52 | #define POWER_UP_IN_FUNC_WBRX 0x03
|
53 | #define POWER_UP_IN_FUNC_QUERY 0x0F
|
54 | #define POWER_UP_IN_PATCH 0x20
|
55 | #define POWER_UP_IN_GPO2OEN 0x40
|
56 | #define POWER_UP_IN_CTSIEN 0x80
|
57 | #define POWER_UP_IN_OPMODE_RX_ANALOG 0x05
|
58 | #define POWER_UP_IN_OPMODE_TX_ANALOG 0x50
|
59 | #define GET_INT_STATUS 0x14
|
60 |
|
61 | #define POWERUP_TIME 110
|
62 |
|
63 | u8 io_mode = IO3W;
|
64 |
|
65 | u8 idata cmd[8];
|
66 | u8 idata rsp[15];
|
67 |
|
68 | void si47xx_getPartInformation();
|
69 |
|
70 | //-----------------------------------------------------------------------------
|
71 | // main
|
72 | //
|
73 | // Return Value : None
|
74 | // Parameters : None
|
75 | //
|
76 | // Main program routine.
|
77 | //
|
78 | //-----------------------------------------------------------------------------
|
79 | void main (void)
|
80 | {
|
81 | int j=0;
|
82 |
|
83 | data WORD Temp;
|
84 | data WORD Temp2;
|
85 |
|
86 |
|
87 | PCA0MD &= ~0x40; // Disable Watchdog timer
|
88 | VDM0CN |= 0x80; // Enable the VDD Monitor
|
89 |
|
90 | // Configure comparator0 and crystal drive
|
91 | Crystal_Init ();
|
92 |
|
93 | // Initialize SPI0, should be before crossbar enable
|
94 | SPI0_Init ();
|
95 |
|
96 | Timer0_2_Init (); // Initialize timers
|
97 | PCA0_Init (); // Initialize PCA module (USB)
|
98 | Port_Init (); // Initialize crossbar and GPIO
|
99 |
|
100 | // Select VDD Monitor as a Reset Source
|
101 | RSTSRC = 0x02;
|
102 |
|
103 |
|
104 | si47xx_getPartInformation();
|
105 |
|
106 |
|
107 | EA = 1; // Global Interrupt enable
|
108 |
|
109 | while (1); // Spin forever
|
110 | }
|
111 |
|
112 | //-----------------------------------------------------------------------------
|
113 | // Initialization Subroutines
|
114 | //-----------------------------------------------------------------------------
|
115 |
|
116 | //-----------------------------------------------------------------------------
|
117 | // Oscillator_Init
|
118 | //-----------------------------------------------------------------------------
|
119 | //
|
120 | // Return Value : None
|
121 | // Parameters : None
|
122 | //
|
123 | // This function initializes the clock multiplier and selects it as the system
|
124 | // clock.
|
125 | //
|
126 | //-----------------------------------------------------------------------------
|
127 | void Oscillator_Init (void)
|
128 | {
|
129 | // Configure internal oscillator for its maximum frequency and enable
|
130 | // missing clock detector
|
131 | OSCICN |= 0x03;
|
132 |
|
133 | // Select internal oscillator as input to clock multiplier
|
134 | CLKMUL = 0x00;
|
135 |
|
136 | CLKMUL |= 0x80; // Enable clock multiplier
|
137 | Delay_Main (); // Delay for clock multiplier
|
138 | CLKMUL |= 0xC0; // Initialize the clock multiplier
|
139 |
|
140 | while (!(CLKMUL & 0x20)); // Wait for multiplier to lock
|
141 | CLKSEL = SYS_4X_DIV_2; // Select 24 MHz clock as sysclock
|
142 | }
|
143 |
|
144 | //-----------------------------------------------------------------------------
|
145 | // Crystal_Init
|
146 | //-----------------------------------------------------------------------------
|
147 | //
|
148 | // Return Value : None
|
149 | // Parameters : None
|
150 | //
|
151 | // This function starts the crystal drive circuit and configures the comparator
|
152 | // to operate as a clock buffer for Si470x.
|
153 | //
|
154 | //-----------------------------------------------------------------------------
|
155 | void Crystal_Init (void)
|
156 | {
|
157 | // Turn on crystal drive circuit for 32.768 kHz crystal
|
158 | OSCXCN = 0x61;
|
159 |
|
160 | CPT0CN = 0x85; // Turn on comparator 0, 5 mV hysteresis
|
161 | CPT0MX = 0x00; // Negative = P1.1, Positive = P1.0
|
162 | CPT0MD = 0x00; // Response time = 100 ns
|
163 | }
|
164 |
|
165 | //-----------------------------------------------------------------------------
|
166 | // SPI0_Init
|
167 | //-----------------------------------------------------------------------------
|
168 | //
|
169 | // Return Value : None
|
170 | // Parameters : None
|
171 | //
|
172 | // This function configures the SPI for Si470x.
|
173 | //
|
174 | //-----------------------------------------------------------------------------
|
175 | void SPI0_Init (void)
|
176 | {
|
177 | // Clock idle high, Data centered on 2nd edge, SPI master
|
178 | SPICFG = 0x70;
|
179 | SPI0CN = 0x83; // Enable SPI, 3-wire mode selected
|
180 |
|
181 | // Configure SPI clock rate for 2.4 MHz (Maximux 2.5 MHz for Si470x)
|
182 | SPICKR = ((SYSCLK / (2400000*2)) - 1);
|
183 | }
|
184 |
|
185 | //-----------------------------------------------------------------------------
|
186 | // Port_Init
|
187 | //-----------------------------------------------------------------------------
|
188 | //
|
189 | // Return Value : None
|
190 | // Parameters : None
|
191 | //
|
192 | // This function configures the Crossbar and GPIO ports.
|
193 | //
|
194 | // P0.2 analog XTAL1
|
195 | // P0.3 analog XTAL2
|
196 | //
|
197 | // P1.0 analog COMPARATOR POSITIVE INPUT
|
198 | // P1.1 analog COMPARATOR NEGATIVE INPUT
|
199 | // P1.2 digital push-pull Si 4701 RESET
|
200 | // P1.3 digital push-pull SEN_BAR
|
201 | // P1.4 digital push-pull SCLK
|
202 | // P1.5 digital open-drain MISO
|
203 | // P1.6 digital push-pull MOSI
|
204 | // P1.7 digital push-pull RCLK/COMPARATOR OUTPUT
|
205 | //
|
206 | // P2.0 analog RIGHT AUDIO CHANNEL
|
207 | // P2.1 analog LEFT AUDIO CHANNEL
|
208 | // P2.3 digital push-pull GREEN LED/PCA OUTPUT
|
209 | //
|
210 | // P3.0 digital push-pull RED LED
|
211 | //
|
212 | //-----------------------------------------------------------------------------
|
213 | void Port_Init (void)
|
214 | {
|
215 | P0MDIN = 0x03; // Port 0 pins 2-7 analog
|
216 | P1MDIN = 0xFC; // Port 1 pins 0, 1 analog
|
217 | P2MDIN = 0x0C; // Port 2 pins 0, 1, 4-7 analog
|
218 | P3MDIN = 0x01; // Port 3 all digital
|
219 |
|
220 | P0MDOUT = 0x00; // Port 0 pins set open-drain
|
221 | P1MDOUT = 0xDC; // Port 1 pins 2-4, 6, 7 set push-pull
|
222 | P2MDOUT = 0x0C; // Port 2 pins 2, 3 set push-pull
|
223 | P3MDOUT = 0x01; // Port 3 pin 1 set push-pull
|
224 |
|
225 | P0SKIP = 0xFF; // Port 0 skip pins 0-7
|
226 | P1SKIP = 0x0F; // Port 1 skip pins 0-3
|
227 | P2SKIP = 0xF7; // Port 2 skip pins 0, 1, 2, 4-7
|
228 |
|
229 | P0 = 0x02; // Port 0 pins 1 start high
|
230 | P1 = 0xE3; // Port 1 pins 0, 1, 5-7 start high
|
231 | P2 = 0x03; // Port 2 pins 0, 1 start high
|
232 | P3 = 0x00; // Port 3 starts low
|
233 |
|
234 | XBR0 = 0x22; // Comparator0, SPI enabled
|
235 |
|
236 | // Enable Crossbar, Weak Pull-ups on, 2 PCA modules are on
|
237 | XBR1 = 0x42;
|
238 | }
|
239 |
|
240 |
|
241 | //-----------------------------------------------------------------------------
|
242 | // Timer0_2_Init
|
243 | //-----------------------------------------------------------------------------
|
244 | //
|
245 | // Return Value : None
|
246 | // Parameters : None
|
247 | //
|
248 | // Initializes timer 2 used for ADC conversion start, and timer 0 used for PCA.
|
249 | //
|
250 | //-----------------------------------------------------------------------------
|
251 | void Timer0_2_Init (void)
|
252 | {
|
253 | CKCON = 0x02; // Prescale bits set to system clock/48
|
254 | CKCON &= ~0x04; // Timer0 uses prescaler defined clock
|
255 | TMOD = 0x02; // Timer0 set to mode 2
|
256 | TH0 = (-(SYSCLK/48/30000)); // PCA frequency = 30 kHz
|
257 | TR0 = ON; // Start timer 0
|
258 |
|
259 | TMR2CN = 0x00; // Stop Timer2; Clear TF2;
|
260 | CKCON |= 0x10; // Timer2 clocked based on system clock
|
261 | TMR2RL = (-(SYSCLK/192000)); // Initialize reload value for 192000 Hz
|
262 | TMR2 = 0xFFFF; // Set to reload immediately
|
263 |
|
264 | ET2 = 1; // Enable Timer2 interrupts
|
265 | PT2 = ON; // Set interrupt to high priority
|
266 | }
|
267 |
|
268 | //-----------------------------------------------------------------------------
|
269 | // ADC0_Init
|
270 | //-----------------------------------------------------------------------------
|
271 | //
|
272 | // Return Value : None
|
273 | // Parameters : None
|
274 | //
|
275 | // Configure ADC for single ended conversions on timer 2 overflows.
|
276 | //
|
277 | //-----------------------------------------------------------------------------
|
278 | void ADC0_Init (void)
|
279 | {
|
280 | // Disable internal voltage reference VREF, use VDD instead for ADC
|
281 | REF0CN = 0x0A;
|
282 |
|
283 | AMX0N = 0x1F; // Single ended mode (negative = gnd)
|
284 |
|
285 | ADC0CF = 0x3C; // SAR clock 3 MHz, left adjusted output
|
286 |
|
287 | // Converion on timer 2 overflow with low power tracking mode off
|
288 | ADC0CN = 0x82;
|
289 | }
|
290 |
|
291 | //-----------------------------------------------------------------------------
|
292 | // PCA_Init
|
293 | //-----------------------------------------------------------------------------
|
294 | //
|
295 | // Return Value : None
|
296 | // Parameters : None
|
297 | //
|
298 | // Initializes USB0, enable transceiver and USB0 interrupts.
|
299 | //
|
300 | //-----------------------------------------------------------------------------
|
301 | void PCA0_Init (void)
|
302 | {
|
303 | PCA0CPM0 = 0x42; // PCA module 0 in 8-bit PWM Mode
|
304 | PCA0CPM1 = 0x42; // PCA module 1 in 8-bit PWM Mode
|
305 | PCA0CPM2 = 0x00; // PCA module 2 currently unused
|
306 | PCA0CPM3 = 0x00; // PCA module 3 currently unused
|
307 | PCA0CPM4 = 0x00; // PCA module 4 currently unused
|
308 |
|
309 | // PCA module 0 starts with minimum duty cycle for all pulses
|
310 | PCA0CPL0 = 0xFF;
|
311 | PCA0CPH0 = 0xFF;
|
312 |
|
313 | // PCA module 1 starts with minimum duty cycle for all pulses
|
314 | PCA0CPL1 = 0xFF;
|
315 | PCA0CPH1 = 0xFF;
|
316 |
|
317 | // PCA timer uses timer 0 overflows and runs when CPU is idle
|
318 | PCA0MD = 0x04;
|
319 | PCA0CN = 0x40; // PCA timer running, interrupts cleared
|
320 | }
|
321 |
|
322 | //-----------------------------------------------------------------------------
|
323 | // Delay_Main
|
324 | //-----------------------------------------------------------------------------
|
325 | //
|
326 | // Return Value : None
|
327 | // Parameters : None
|
328 | //
|
329 | // Used for a 500 microsecond pause during hardware configuration.
|
330 | // There are two identical versions of this routine, one for the main program
|
331 | // and another for the USB interrupt. This version is for the main program.
|
332 | // (assuming 24 MHz system clock)
|
333 | //
|
334 | //-----------------------------------------------------------------------------
|
335 | void Delay_Main (void)
|
336 | {
|
337 | data volatile int x;
|
338 | for (x = 0;x < 500;x)
|
339 | x++;
|
340 | }
|
341 |
|
342 | //-----------------------------------------------------------------------------
|
343 | // End Of Basicfunctions
|
344 | //-----------------------------------------------------------------------------
|
345 |
|
346 |
|
347 |
|
348 | void wait_us(u16 us)
|
349 | {
|
350 | u16 j;
|
351 |
|
352 | j = 65535u - 24 * us;
|
353 | TL0 = j; // Load Timer 0 low byte
|
354 | TH0 = j >> 8; // Load Timer 0 high byte
|
355 | TR0 = 1; // Enable Timer 0
|
356 | TF0 = 0; // Clear Timer 0 Overflow flag
|
357 |
|
358 | while(TF0 != 1); // Wait for Timer 0 to overflow
|
359 |
|
360 | TR0 = 0; // Disable Timer 0
|
361 | TF0 = 0; // Clear Timer 0 Overflow Flag
|
362 | }
|
363 |
|
364 | void wait_ns(u16 ns)
|
365 | {
|
366 | u8 i;
|
367 |
|
368 | for ( i = 1; i <= ns / 32; i++ )
|
369 | ;
|
370 | }
|
371 |
|
372 | void wait_ms(u16 ms)
|
373 | {
|
374 | int i;
|
375 |
|
376 | for ( i = 0; i < ms; i++ ) {
|
377 | wait_us(1000);
|
378 | }
|
379 | }
|
380 |
|
381 | void si47xx_lowWrite(u8 number_bytes, u8 idata *data_out)
|
382 | {
|
383 | if(io_mode == IO3W)
|
384 | {
|
385 | io3w_write(number_bytes, data_out);
|
386 | }
|
387 | else // IO2W
|
388 | {
|
389 | io2w_write(number_bytes, data_out);
|
390 | }
|
391 | }
|
392 |
|
393 | void si47xx_lowRead(u8 number_bytes, u8 idata *data_in)
|
394 | {
|
395 | if(io_mode == IO3W)
|
396 | {
|
397 | io3w_read(number_bytes, data_in);
|
398 | }
|
399 | else // IO2W
|
400 | {
|
401 | io2w_read(number_bytes, data_in);
|
402 | }
|
403 | }
|
404 |
|
405 | u8 si47xx_readStatus()
|
406 | {
|
407 | u8 status;
|
408 |
|
409 | si47xx_lowRead(1, &status);
|
410 |
|
411 | return status;
|
412 | }
|
413 |
|
414 | void si47xx_waitForCTS()
|
415 | {
|
416 | int temp=0;
|
417 | u16 i=1000;
|
418 |
|
419 | // Loop until CTS is found or stop due to the counter running out.
|
420 | while (--i && !(si47xx_readStatus() & CTS))
|
421 | {
|
422 | wait_us(500);
|
423 | }
|
424 |
|
425 | temp=1;
|
426 | temp=2;
|
427 |
|
428 | // If the i is equal to 0 then something must have happened.
|
429 | // It is recommended that the controller do some type of error
|
430 | // handling in this case.
|
431 | }
|
432 |
|
433 | //-----------------------------------------------------------------------------
|
434 | // Sends a command to the part and returns the reply bytes
|
435 | //-----------------------------------------------------------------------------
|
436 | void si47xx_command(u8 cmd_size, u8 idata *cmd, u8 reply_size, u8 idata *reply)
|
437 | {
|
438 | // It is always a good idea to check for cts prior to sending a command to
|
439 | // the part.
|
440 | si47xx_waitForCTS();
|
441 |
|
442 | // Write the command to the part
|
443 | si47xx_lowWrite(cmd_size, cmd);
|
444 |
|
445 | // Wait for CTS after sending the command
|
446 | si47xx_waitForCTS();
|
447 |
|
448 | // If the calling function would like to have results then read them.
|
449 | if(reply_size)
|
450 | {
|
451 | si47xx_lowRead(reply_size, reply);
|
452 | }
|
453 | }
|
454 |
|
455 | void si47xx_getPartInformation(void)
|
456 | {
|
457 | data WORD Temp;
|
458 | data WORD Temp2;
|
459 |
|
460 | u8 partNumber;
|
461 | char fwMajor;
|
462 | char fwMinor;
|
463 | u16 patchID;
|
464 | char cmpMajor;
|
465 | char cmpMinor;
|
466 | char chipRev;
|
467 | char status=0xab;
|
468 |
|
469 | cmd[0]=0x0;
|
470 | cmd[1]=0x0;
|
471 | cmd[2]=0x0;
|
472 | cmd[3]=0x0;
|
473 | cmd[4]=0x0;
|
474 | cmd[5]=0x0;
|
475 | cmd[6]=0x0;
|
476 | cmd[7]=0x0;
|
477 |
|
478 | rsp[0]=0xee;
|
479 | rsp[1]=0xee;
|
480 | rsp[2]=0xee;
|
481 | rsp[3]=0xee;
|
482 | rsp[4]=0xee;
|
483 | rsp[5]=0xee;
|
484 | rsp[6]=0xee;
|
485 | rsp[7]=0xee;
|
486 | rsp[8]=0xee;
|
487 | rsp[9]=0xee;
|
488 | rsp[10]=0xee;
|
489 | rsp[11]=0xee;
|
490 | rsp[12]=0xee;
|
491 | rsp[13]=0xee;
|
492 | rsp[14]=0xee;
|
493 |
|
494 |
|
495 |
|
496 |
|
497 | Delay_Main ();
|
498 |
|
499 | // Wait for crystal to stabilize, make sure crystal has been running 1 ms
|
500 | while ((OSCXCN & 0x80) == 0);
|
501 |
|
502 | // Make sure crystal frequency is stable
|
503 | for (Temp.i = 0; Temp.i < 200; Temp.i++)
|
504 | Delay_Main ();
|
505 |
|
506 | RADIO_RESET = ON; // Bring radio out of reset
|
507 | SEN_bar = ON;
|
508 |
|
509 |
|
510 | CONTROL_READ (POWERCONFIG, Temp);
|
511 | Temp.i &= ~DISABLE_PC;
|
512 | Temp.i |= ENABLE_PC;
|
513 | CONTROL_WRITE (POWERCONFIG, Temp); // Enable radio
|
514 |
|
515 |
|
516 | for (Temp.i = 0; Temp.i < 1000; Temp.i++) Delay_Main ();
|
517 |
|
518 |
|
519 |
|
520 | // NOTE: This routine should only be called when the part is powered up.
|
521 | // If you wish to retrieve some of the part information without fully
|
522 | // powering up the part call the POWER_UP command on the part with the
|
523 | // FUNC_DEBUG flag.
|
524 |
|
525 | // Put the ID for the command in the first byte.
|
526 | cmd[0] = GET_REV;
|
527 |
|
528 | // Invoke the command
|
529 | si47xx_command(1, cmd, 9, rsp);
|
530 |
|
531 | // Now take the result and put in the variables we have declared
|
532 | // Status is in the first element of the array so skip that.
|
533 | partNumber = rsp[1];
|
534 | fwMajor = (char)rsp[2];
|
535 | fwMinor = (char)rsp[3];
|
536 | patchID = (u16)(rsp[4] << 8) | (u16)rsp[5];
|
537 | cmpMajor = (char)rsp[6];
|
538 | cmpMinor = (char)rsp[7];
|
539 | chipRev = (char)rsp[8];
|
540 |
|
541 |
|
542 | }
|