1 /*-------------------------------------------------------------------------
2 tinibios.c - startup and serial routines for the DS80C390 (tested on TINI)
4 Copyright (C) 2001, Johan Knol <johan.knol AT iduna.nl>
6 This library is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this library; see the file COPYING. If not, write to the
18 Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
21 As a special exception, if you link this library with other files,
22 some of which are compiled with SDCC, to produce an executable,
23 this library does not by itself cause the resulting executable to
24 be covered by the GNU General Public License. This exception does
25 not however invalidate any other reasons why the executable file
26 might be covered by the GNU General Public License.
27 -------------------------------------------------------------------------*/
32 #define TIMED_ACCESS(sfr,value) { TA=0xaa; TA=0x55; sfr=value; }
34 unsigned char __sdcc_external_startup(void)
36 IE
=0; // disable ALL interrupts
38 // use A19..16 and !CE3..0, no CAN
39 TIMED_ACCESS(P4CNT
,0x3f);
41 // use !PCE3..0, serial 1 at P5.2/3
42 TIMED_ACCESS(P5CNT
,0x27);
47 // watchdog set to 9.1 seconds
50 // default stretch cycles for MOVX
51 //CKCON = (CKCON&0xf8)|(CPU_MOVX_STRETCH&0x07);
54 // use internal 4k RAM as data(stack) memory at 0x400000 and
55 // move CANx memory access to 0x401000 and upwards
56 // use !CE* for program and/or data memory access
57 TIMED_ACCESS(MCON
,0xaf);
59 // select default cpu speed
62 // Copy the Interrupt Vector Table (128 bytes) from 0x10000 to 0x100000
63 // This isn't needed for older bootloaders than the 0515, but it won't harm
71 mov dps
,#0x00 ; make sure no autoincrement in progress
72 mov dptr
,#0x10000 ; from
73 inc dps
; switch to alternate dptr
74 mov dptr
,#0x100000 ; to
84 djnz b
,_Startup390CopyIVT
94 // global interrupt enable, all masks cleared
95 // let the Gods be with us :)
98 Serial0Init(SERIAL_0_BAUD
,1);
99 //Serial1Init(SERIAL_1_BAUD,1);
104 // signal _sdcc_gsinit_startup to initialize data (call _sdcc_init_data)
108 /* Set the cpu speed in clocks per machine cycle, valid values are:
109 1024: Power management mode
111 2: Use frequency multiplier (2x)
112 1: Use frequency multiplier (4x) (Don't do this on a TINI at 18.432MHz)
114 TODO: TINI seems to support only 2 and 4: write only bits in PMR ?
117 unsigned int cpuSpeed
;
119 void CpuSpeed(unsigned int speed
)
122 while (0 && (EXIF
&0x04))
123 ; // cpu operates from ring buffer
125 PMR
= 0x80; // div4, CTM off, multiplier 2x
129 PMR
=0x88; // div4, CTM off, multiplier 4x
130 PMR
=0x98; // div4, CTM on, multiplier 4x
131 while ((EXIF
&0x08)==0) {
132 ; // wait for the multiplier to be ready
134 PMR
= 0x18; // use multiplier
138 PMR
=0x90; // div4, CTM on, multilier 2x
139 while ((EXIF
&0x08)==0) {
140 ; // wait for the multiplier to be ready
142 PMR
= 0x10; // use multiplier
150 PMR
= 0xc0; // div1024, CTM off
156 // now the serial0 stuff
158 // just to make the code more readable
159 #define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE
161 // this is a ring buffer and can overflow at anytime!
162 static volatile unsigned char receive0Buffer
[S0RBS
];
163 static volatile int receive0BufferHead
=0;
164 static volatile int receive0BufferTail
=0;
165 // no buffering for transmit
166 static volatile char transmit0IsBusy
=0;
168 static __data
unsigned char serial0Buffered
;
170 /* Initialize serial0.
172 Available baudrates are from 110 upto 115200 (using 16-bit timer 2)
173 If baud==0, the port is disabled.
175 If buffered!=0, characters received are buffered using an interrupt
178 void Serial0Init (unsigned long baud
, unsigned char buffered
)
181 ES0
=0; // disable interrupts
182 SCON0
&= 0xef; // disable receiver
186 ES0
= 0; // disable serial channel 0 interrupt
187 TR2
= 0; // stop timer 2
189 // set 8 bit uart with variable baud from timer 1/2
190 // enable receiver and clear RI and TI
193 PCON
|= 0x80; // clock is 16x bitrate
194 CKCON
|=0x20; // timer uses xtal/4
196 T2MOD
=0; // no fancy functions
197 T2CON
=0x34; // start timer as a baudrate generator for serial0
202 serial0Buffered
=buffered
;
205 RI_0
=TI_0
=0; // clear "pending" interrupts
206 ES0
= 1; // enable serial channel 0 interrupt
208 RI_0
=0; // receive buffer empty
209 TI_0
=1; // transmit buffer empty
213 void Serial0Baud(unsigned long baud
)
216 baud
=-((long)OSCILLATOR
/(32*baud
));
219 TF2
=0; // clear overflow flag
220 TR2
=1; // start timer
223 void Serial0IrqHandler (void) __interrupt (4)
226 receive0Buffer
[receive0BufferHead
]=SBUF0
;
227 receive0BufferHead
=(receive0BufferHead
+1)&(S0RBS
-1);
228 if (receive0BufferHead
==receive0BufferTail
) {
229 /* buffer overrun, sorry :) */
230 receive0BufferTail
=(receive0BufferTail
+1)&(S0RBS
-1);
240 char Serial0CharArrived(void)
242 if (serial0Buffered
) {
243 if (receive0BufferHead
!=receive0BufferTail
)
244 return receive0Buffer
[receive0BufferTail
];
252 void Serial0PutChar (char c
)
254 if (serial0Buffered
) {
255 while (transmit0IsBusy
)
267 char Serial0GetChar (void)
270 if (serial0Buffered
) {
271 while (receive0BufferHead
==receive0BufferTail
)
273 c
=receive0Buffer
[receive0BufferTail
];
274 ES0
=0; // disable serial interrupts
275 receive0BufferTail
=(receive0BufferTail
+1)&(S0RBS
-1);
276 ES0
=1; // enable serial interrupts
286 void Serial0SendBreak (void)
289 ClockMilliSecondsDelay(2);
293 void Serial0Flush (void)
295 ES0
=0; // disable interrupts
296 receive0BufferHead
=receive0BufferTail
=0;
298 if (serial0Buffered
) {
300 ES0
=1; // enable interrupts
306 /* now let's go for the serial1 stuff, basically it's a replicate of
307 serial0 except it uses timer 1
310 // just to make the code more readable
311 #define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE
313 // this is a ring buffer and can overflow at anytime!
314 static volatile unsigned char receive1Buffer
[S1RBS
];
315 static volatile int receive1BufferHead
=0;
316 static volatile int receive1BufferTail
=0;
317 // no buffering for transmit
318 static volatile char transmit1IsBusy
=0;
320 static __data
unsigned char serial1Buffered
;
322 /* Initialize serial1.
324 Available baudrates are from 4800 upto 115200 (using 8-bit timer 1)
325 If baud==0, the port is disabled.
327 If buffered!=0, characters received are buffered using an interrupt
330 void Serial1Init (unsigned long baud
, unsigned char buffered
)
333 ES1
=0; // disable interrupt
334 SCON1
&= 0xef; // disable receiver
335 return; // and don't touch it
338 ES1
= 0; // disable channel 1 interrupt
339 TR1
= 0; // stop timer 1
341 // set 8 bit uart with variable baud from timer 1
342 // enable receiver and clear RI and TI
345 WDCON
|= 0x80; // clock is 16x bitrate
346 CKCON
|=0x10; // timer uses xtal/4
348 TMOD
= (TMOD
&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter
353 serial1Buffered
=buffered
;
356 RI_1
=TI_1
=0; // clear "pending" interrupts
357 ES1
= 1; // enable serial channel 1 interrupt
359 RI_1
=0; // receive buffer empty
360 TI_1
=1; // transmit buffer empty
364 void Serial1Baud(unsigned long baud
)
367 baud
=-((long)OSCILLATOR
/(32*baud
));
369 TF1
=0; // clear overflow flag
370 TR1
=1; // start timer
373 void Serial1IrqHandler (void) __interrupt (7)
376 receive1Buffer
[receive1BufferHead
]=SBUF1
;
377 receive1BufferHead
=(receive1BufferHead
+1)&(S1RBS
-1);
378 if (receive1BufferHead
==receive1BufferTail
) /* buffer overrun, sorry :) */
379 receive1BufferTail
=(receive1BufferTail
+1)&(S1RBS
-1);
388 char Serial1CharArrived(void)
390 if (serial1Buffered
) {
391 if (receive1BufferHead
!=receive1BufferTail
)
392 return receive1Buffer
[receive1BufferTail
];
400 void Serial1PutChar (char c
)
402 if (serial1Buffered
) {
403 while (transmit1IsBusy
)
415 char Serial1GetChar (void)
418 if (serial1Buffered
) {
419 while (receive1BufferHead
==receive1BufferTail
)
421 c
=receive1Buffer
[receive1BufferTail
];
422 ES1
=0; // disable serial interrupts
423 receive1BufferTail
=(receive1BufferTail
+1)&(S1RBS
-1);
424 ES1
=1; // enable serial interrupts
434 void Serial1SendBreak (void)
437 ClockMilliSecondsDelay(2);
441 void Serial1Flush (void)
443 ES1
=0; // disable interrupts
444 receive1BufferHead
=receive1BufferTail
=0;
446 if (serial1Buffered
) {
448 ES1
=1; // enable interrupts
454 // now let's go for the clock stuff
456 // these REALLY need to be in data space for the irq routine!
457 static __data
unsigned long milliSeconds
=0;
458 static __data
unsigned int timer0ReloadValue
;
460 void ClockInit (void)
462 unsigned long timerReloadValue
=OSCILLATOR
/1000;
465 case 4: timerReloadValue
/=4; break;
466 case 1: // not tested yet
467 case 2: // not tested yet
468 default: timerReloadValue
/=2; break;
470 timer0ReloadValue
=~timerReloadValue
;
471 // initialise timer 0
472 ET0
=0; // disable timer interrupts initially
473 TCON
= (TCON
&0xcc)|0x00; // stop timer, clear overflow
474 TMOD
= (TMOD
&0xf0)|0x01; // 16 bit counter
475 CKCON
|=0x08; // timer uses xtal/4
477 TL0
=timer0ReloadValue
&0xff;
478 TH0
=timer0ReloadValue
>>8;
480 ET0
=1; // enable timer interrupts
481 TR0
=1; // start timer
484 // This needs to be SUPER fast. What we really want is:
487 void junk_ClockIrqHandler (void) __interrupt (10)
489 TL0
= timer0ReloadValue
& 0xff;
490 TH0
= timer0ReloadValue
>> 8;
494 // but look at the code, and the pushes and pops, so:
495 void ClockIrqHandler (void) __interrupt (1) __naked
500 mov _TL0
,_timer0ReloadValue
501 mov _TH0
,_timer0ReloadValue
+1
504 cjne a
,_milliSeconds
+0,_ClockIrqHandlerDone
506 cjne a
,_milliSeconds
+1,_ClockIrqHandlerDone
508 cjne a
,_milliSeconds
+2,_ClockIrqHandlerDone
510 _ClockIrqHandlerDone
:
518 // we can't just use milliSeconds
519 unsigned long ClockTicks(void)
528 void ClockMilliSecondsDelay(unsigned long delay
)
530 long ms
=ClockTicks()+delay
;
532 while (ms
>ClockTicks())
536 // stolen from Kevin Vigor, works only for TINI at default speed
537 void ClockMicroSecondsDelay(unsigned int delay
)
539 delay
; /* shut compiler up. */
543 ; delay is in dpl
/dph
548 orl a
, r1
; quick out
for zero
case.
552 cjne r0
, #0, _usDelayLoop
563 djnz r0
, _usDelayLoop
; 3 cycles x
1 = 3 cycles
565 ; we want
9.216, but more is better
567 djnz r1
, _usDelayLoop