From a49245d3579351b177ae9b72c967b9cb35d455af Mon Sep 17 00:00:00 2001 From: johanknol Date: Fri, 2 Feb 2001 09:31:24 +0000 Subject: [PATCH] Initial import of tinibios git-svn-id: https://sdcc.svn.sourceforge.net/svnroot/sdcc/trunk/sdcc@554 4a8a32a2-be11-0410-ad9d-d568d2c75423 --- device/include/tinibios.h | 59 ++++ device/lib/ds390/tinibios.c | 521 ++++++++++++++++++++++++++++++++++++ 2 files changed, 580 insertions(+) create mode 100755 device/include/tinibios.h create mode 100755 device/lib/ds390/tinibios.c diff --git a/device/include/tinibios.h b/device/include/tinibios.h new file mode 100755 index 00000000..8a8972b8 --- /dev/null +++ b/device/include/tinibios.h @@ -0,0 +1,59 @@ +#ifndef TINIBIOS_H + +#define TINIBIOS_H + +#include + +#define Serial0GetChar getchar +#define Serial0PutChar putchar + +void Serial0Init (unsigned long baud, unsigned char buffered); +char Serial0GetChar(void); +void Serial0PutChar(char); +char Serial0CharArrived(void); +void Serial0Baud(unsigned long baud); +void Serial0SendBreak(void); +void Serial0Flush(void); + +void Serial1Init (unsigned long baud, unsigned char buffered); +char Serial1GetChar(void); +void Serial1PutChar(char); +char Serial1CharArrived(void); +void Serial1Baud(unsigned long baud); +void Serial1SendBreak(void); +void Serial1Flush(void); + +unsigned long ClockTicks(); +void ClockMilliSecondsDelay(unsigned long ms); +void ClockMicroSecondsDelay(unsigned int us); + +#define SERIAL_0_BAUD 115200L +#define SERIAL_1_BAUD 9600L + +// these need to be binary numbers +#define SERIAL_0_RECEIVE_BUFFER_SIZE 1024 +#define SERIAL_1_RECEIVE_BUFFER_SIZE 64 + +// I know someone is fooling with the crystals +#define OSCILLATOR 18432000L + +/* Set the cpu speed in clocks per machine cycle, valid values are: + 1024: Divide-by-1024 (power management) mode (screws ALL timers and serial) + 4: Standard 8051 divide-by-4 mode + 2: Use 2x xtal multiplier + 1: Use 4x xtal multiplier (Don't do this with a TINI at 18.432MHz) +*/ +#define CPU_SPEED 2 +void CpuSpeed(unsigned int speed); + +// The MOVX stretch cycles, see datasheet +#define CPU_MOVX_STRETCH 0x01 + +// internal functions used by tinibios.c +unsigned char _sdcc_external_startup(void); +void Serial0IrqHandler (void) interrupt 4; +void Serial1IrqHandler (void) interrupt 7; +void ClockInit(); +void ClockIrqHandler (void) interrupt 1; + +#endif TINIBIOS_H diff --git a/device/lib/ds390/tinibios.c b/device/lib/ds390/tinibios.c new file mode 100755 index 00000000..bb4b3891 --- /dev/null +++ b/device/lib/ds390/tinibios.c @@ -0,0 +1,521 @@ +#include +#include + +#define TIMED_ACCESS(sfr,value) { TA=0xaa; TA=0x55; sfr=value; } + +unsigned char _sdcc_external_startup(void) +{ + IE=0; // disable ALL interrupts + + // use A19..16 and !CE3..0, no CAN + TIMED_ACCESS(P4CNT,0x3f); + + // use !PCE3..0, serial 1 at P5.2/3 + TIMED_ACCESS(P5CNT,0x27); + + // disable watchdog + EWT=0; + + // watchdog set to 9.1 seconds + // CKCON|=0xc0; + + // default stretch cycles for MOVX + CKCON = (CKCON&0xf8)|(CPU_MOVX_STRETCH&0x07); + + // use internal 4k RAM as data(stack) memory at 0x400000 and + // move CANx memory access to 0x401000 and upwards + // use !CE* for program and/or data memory access + TIMED_ACCESS(MCON,0xaf); + + // select default cpu speed + CpuSpeed(CPU_SPEED); + + _asm + ; save the 24-bit return address + pop ar2; msb + pop ar1 + pop ar0; lsb + + + mov _ESP,#0x00; reinitialize the stack + mov _SP,#0x00 + + mov _TA,#0xaa; timed access + mov _TA,#0x55 + mov _ACON,#0x06; 24 bit addresses, 10 bit stack at 0x400000 + + ; restore the 24-bit return address + push ar0; lsb + push ar1 + push ar2; msb + _endasm; + + // Copy the Interrupt Vector Table (128 bytes) from 0x10000 to 0x100000 + // This isn't needed for older bootloaders than the 0515, but it won't harm + _asm + push dpx + push dph + push dpl + push dps + push b + push acc + mov dps,#0x00 ; make sure no autoincrement in progress + mov dptr,#0x10000 ; from + inc dps ; switch to alternate dptr + mov dptr,#0x100000 ; to + mov b,#0x80 ; count + +_Startup390CopyIVT: + inc dps + movx a,@dptr + inc dptr + inc dps + movx @dptr,a + inc dptr + djnz b,_Startup390CopyIVT + + pop acc + pop b + pop dps + pop dpl + pop dph + pop dpx + _endasm; + + // global interrupt enable, all masks cleared + // let the Gods be with us :) + IE = 0x80; + + Serial0Init(SERIAL_0_BAUD,1); + //Serial1Init(SERIAL_1_BAUD,1); + ClockInit(); + //RtcInit(); + //WatchDogInit(); + + // signal _sdcc_gsinit_startup to initialize data (call _sdcc_init_data) + return 0; +} + +/* Set the cpu speed in clocks per machine cycle, valid values are: + 1024: Power management mode + 4: Divide-by-4 mode + 2: Use frequency multiplier (2x) + 1: Use frequency multiplier (4x) (Don't do this on a TINI at 18.432MHz) + + TODO: TINI seems to support only 2 and 4: write only bits in PMR ? +*/ + +unsigned int cpuSpeed; + +void CpuSpeed(unsigned int speed) { + + while (0 && (EXIF&0x04)) + ; // cpu operates from ring buffer + PMR = 0x80; // div4, CTM off, multiplier 2x + switch (speed) + { + case 1: + PMR=0x88; // div4, CTM off, multiplier 4x + PMR=0x98; // div4, CTM on, multiplier 4x + while ((EXIF&0x08)==0) { + ; // wait for the multiplier to be ready + } + PMR = 0x18; // use multiplier + cpuSpeed=speed; + break; + case 2: + PMR=0x90; // div4, CTM on, multilier 2x + while ((EXIF&0x08)==0) { + ; // wait for the multiplier to be ready + } + PMR = 0x10; // use multiplier + cpuSpeed=speed; + break; + case 4: + // nothing to do + cpuSpeed=speed; + break; + case 1024: + PMR = 0xc0; // div1024, CTM off + cpuSpeed=speed; + break; + } +} + +// now the serial0 stuff + +// just to make the code more readable +#define S0RBS SERIAL_0_RECEIVE_BUFFER_SIZE + +// this is a ring buffer and can overflow at anytime! +static volatile unsigned char receive0Buffer[S0RBS]; +static volatile int receive0BufferHead=0; +static volatile int receive0BufferTail=0; +// no buffering for transmit +static volatile char transmit0IsBusy=0; + +static data unsigned char serial0Buffered; + +/* Initialize serial0. + + Available baudrates are from 110 upto 115200 (using 16-bit timer 2) + If baud==0, the port is disabled. + + If buffered!=0, characters received are buffered using an interrupt +*/ + +void Serial0Init (unsigned long baud, unsigned char buffered) { + + if (baud==0) { + ES0=0; // disable interrupts + SCON0 &= 0xef; // disable receiver + return; + } + + ES0 = 0; // disable serial channel 0 interrupt + TR2 = 0; // stop timer 2 + + // set 8 bit uart with variable baud from timer 1/2 + // enable receiver and clear RI and TI + SCON0 = 0x50; + + PCON |= 0x80; // clock is 16x bitrate + CKCON|=0x20; // timer uses xtal/4 + + T2MOD=0; // no fancy functions + T2CON=0x34; // start timer as a baudrate generator for serial0 + + // set the baud rate + Serial0Baud(baud); + + serial0Buffered=buffered; + + if (buffered) { + RI_0=TI_0=0; // clear "pending" interrupts + ES0 = 1; // enable serial channel 0 interrupt + } else { + RI_0=0; // receive buffer empty + TI_0=1; // transmit buffer empty + } +} + +void Serial0Baud(unsigned long baud) { + TR2=0; // stop timer + baud=-((long)OSCILLATOR/(32*baud)); + TL2=RCAP2L= baud; + TH2=RCAP2H= baud>>8; + TF2=0; // clear overflow flag + TR2=1; // start timer +} + +void Serial0IrqHandler (void) interrupt 4 { + if (RI_0) { + receive0Buffer[receive0BufferHead]=SBUF0; + receive0BufferHead=(receive0BufferHead+1)&(S0RBS-1); + if (receive0BufferHead==receive0BufferTail) { + /* buffer overrun, sorry :) */ + receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1); + } + RI_0=0; + } + if (TI_0) { + TI_0=0; + transmit0IsBusy=0; + } +} + +char Serial0CharArrived(void) { + if (serial0Buffered) { + if (receive0BufferHead!=receive0BufferTail) + return receive0Buffer[receive0BufferTail]; + } else { + if (RI_0) + return SBUF0; + } + return 0; +} + +void Serial0PutChar (char c) +{ + if (serial0Buffered) { + while (transmit0IsBusy) + ; + transmit0IsBusy=1; + SBUF0=c; + } else { + while (!TI_0) + ; + SBUF0=c; + TI_0=0; + } +} + +char Serial0GetChar (void) +{ + char c; + if (serial0Buffered) { + while (receive0BufferHead==receive0BufferTail) + ; + c=receive0Buffer[receive0BufferTail]; + ES0=0; // disable serial interrupts + receive0BufferTail=(receive0BufferTail+1)&(S0RBS-1); + ES0=1; // enable serial interrupts + } else { + while (!RI_0) + ; + c=SBUF0; + RI_0=0; + } + return c; +} + +void Serial0SendBreak() { + P3 &= ~0x02; + ClockMilliSecondsDelay(2); + P3 |= 0x02; +} + +void Serial0Flush() { + ES0=0; // disable interrupts + receive0BufferHead=receive0BufferTail=0; + RI_0=0; + if (serial0Buffered) { + TI_0=0; + ES0=1; // enable interrupts + } else { + TI_0=1; + } +} + +/* now let's go for the serial1 stuff, basically it's a replicate of + serial0 except it uses timer 1 +*/ + +// just to make the code more readable +#define S1RBS SERIAL_1_RECEIVE_BUFFER_SIZE + +// this is a ring buffer and can overflow at anytime! +static volatile unsigned char receive1Buffer[S1RBS]; +static volatile int receive1BufferHead=0; +static volatile int receive1BufferTail=0; +// no buffering for transmit +static volatile char transmit1IsBusy=0; + +static data unsigned char serial1Buffered; + +/* Initialize serial1. + + Available baudrates are from 4800 upto 115200 (using 8-bit timer 1) + If baud==0, the port is disabled. + + If buffered!=0, characters received are buffered using an interrupt +*/ + +void Serial1Init (unsigned long baud, unsigned char buffered) { + + if (baud=0) { + ES1=0; // disable interrupt + SCON1 &= 0xef; // disable receiver + } + + ES1 = 0; // disable channel 1 interrupt + TR1 = 0; // stop timer 1 + + // set 8 bit uart with variable baud from timer 1 + // enable receiver and clear RI and TI + SCON1 = 0x50; + + WDCON |= 0x80; // clock is 16x bitrate + CKCON|=0x10; // timer uses xtal/4 + + TMOD = (TMOD&0x0f) | 0x20; // timer 1 is an 8bit auto-reload counter + + // set the baud rate + Serial1Baud(baud); + + serial0Buffered=buffered; + + if (buffered) { + RI_1=TI_1=0; // clear "pending" interrupts + ES1 = 1; // enable serial channel 1 interrupt + } else { + RI_1=0; // receive buffer empty + TI_1=1; // transmit buffer empty + } +} + +void Serial1Baud(unsigned long baud) { + TR1=0; // stop timer + baud=-((long)OSCILLATOR/(32*baud)); + TL1=TH1 = baud; + TF1=0; // clear overflow flag + TR1=1; // start timer +} + +void Serial1IrqHandler (void) interrupt 7 { + if (RI_1) { + receive1Buffer[receive1BufferHead]=SBUF1; + receive1BufferHead=(receive1BufferHead+1)&(S1RBS-1); + if (receive1BufferHead==receive1BufferTail) /* buffer overrun, sorry :) */ + receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1); + RI_1=0; + } + if (TI_1) { + TI_1=0; + transmit1IsBusy=0; + } +} + +char Serial1CharArrived(void) { + if (serial1Buffered) { + if (receive1BufferHead!=receive1BufferTail) + return receive1Buffer[receive1BufferTail]; + } else { + if (RI_1) + return SBUF1; + } + return 0; +} + +void Serial1PutChar (char c) +{ + if (serial1Buffered) { + while (transmit1IsBusy) + ; + transmit1IsBusy=1; + SBUF1=c; + } else { + while (!TI_1) + ; + SBUF1=c; + TI_1=0; + } +} + +char Serial1GetChar (void) +{ + char c; + if (serial1Buffered) { + while (receive1BufferHead==receive1BufferTail) + ; + c=receive1Buffer[receive1BufferTail]; + ES1=0; // disable serial interrupts + receive1BufferTail=(receive1BufferTail+1)&(S1RBS-1); + ES1=1; // enable serial interrupts + } else { + while (!RI_1) + ; + c=SBUF1; + RI_1=0; + } + return c; +} + +void Serial1SendBreak() { + P5 &= ~0x08; + ClockMilliSecondsDelay(2); + P5 |= 0x08; +} + +void Serial1Flush() { + ES1=0; // disable interrupts + receive1BufferHead=receive1BufferTail=0; + RI_1=0; + if (serial1Buffered) { + TI_1=0; + ES1=1; // enable interrupts + } else { + TI_1=1; + } +} + +// now let's go for the clock stuff + +//#define TIMER_0_RELOAD_VALUE 18432000L/2/1000 // appr. 1ms + +static data unsigned long milliSeconds=0; +static data unsigned int timer0ReloadValue; + +void ClockInit() { + unsigned long timerReloadValue=OSCILLATOR/1000; + + switch (cpuSpeed) { + case 4: timerReloadValue/=4; break; + case 1: // not tested yet + case 2: // not tested yet + default: timerReloadValue/=2; break; + } + timer0ReloadValue=timerReloadValue; + // initialise timer 0 + ET0=0; // disable timer interrupts initially + TCON = (TCON&0xcc)|0x00; // stop timer, clear overflow + TMOD = (TMOD&0xf0)|0x01; // 16 bit counter + CKCON|=0x80; // timer uses xtal/4 + + TL0=~(timer0ReloadValue&0xff); + TH0=~(timer0ReloadValue>>8); + + ET0=1; // enable timer interrupts + TR0=1; // start timer +} + +void ClockIrqHandler (void) interrupt 1 { + // we have lost some time here + TL0=~(timer0ReloadValue&0xff); + TH0=~(timer0ReloadValue>>8); + milliSeconds++; + // that's all for now :) +} + +// we can't just use milliSeconds +unsigned long ClockTicks(void) { + unsigned long ms; + ET0=0; + ms=milliSeconds; + ET0=1; + return ms; +} + +void ClockMilliSecondsDelay(unsigned long delay) { + long ms=ClockTicks()+delay; + + while (ms>ClockTicks()) + ; +} + +// stolen from Kevin Vigor, works only for TINI at default speed +void ClockMicroSecondsDelay(unsigned int delay) { + delay; /* shut compiler up. */ + + _asm + + ; delay is in dpl/dph + mov r0, dpl + mov r1, dph + + mov a, r0 + orl a, r1 ; quick out for zero case. + jz _usDelayDone + + inc r1 + cjne r0, #0, _usDelayLoop + dec r1 + + _usDelayLoop: + nop + nop + nop + nop + nop + nop + nop ; 7 nops + djnz r0, _usDelayLoop ; 3 cycles x 1 = 3 cycles + ; 10 cycles per iter + ; we want 9.216, but more is better + ; than less. + djnz r1, _usDelayLoop +_usDelayDone: + + _endasm; + +} -- 2.47.2