From 612702451533895e367934a0a144e919e1cc74e3 Mon Sep 17 00:00:00 2001 From: johanknol Date: Fri, 2 Feb 2001 15:19:52 +0000 Subject: [PATCH] adding lcd i2c and rtc support for tinibios git-svn-id: https://sdcc.svn.sourceforge.net/svnroot/sdcc/trunk/sdcc@559 4a8a32a2-be11-0410-ad9d-d568d2c75423 --- device/lib/ds390/i2c390.c | 344 ++++++++++++++++++++++++++++++++++++++ device/lib/ds390/lcd390.c | 174 +++++++++++++++++++ device/lib/ds390/rtc390.c | 111 ++++++++++++ 3 files changed, 629 insertions(+) create mode 100644 device/lib/ds390/i2c390.c create mode 100644 device/lib/ds390/lcd390.c create mode 100755 device/lib/ds390/rtc390.c diff --git a/device/lib/ds390/i2c390.c b/device/lib/ds390/i2c390.c new file mode 100644 index 00000000..aa0f273a --- /dev/null +++ b/device/lib/ds390/i2c390.c @@ -0,0 +1,344 @@ +/* This implemenation is based on an example I once grabbed from + the Philips bbs. + Don't know who wrote it, but is has been hacked so heavely, he/she wouldn't + recogize it anyway */ + +//#define DEBUG_I2C ==> DON'T DO THIS IS A LIBRARY <== + +#ifdef DEBUG_I2C +#include +#else +#include +#endif + +// we are (ab)using the CAN CTX and CRX for serial data and serial clock +#define SCL_HIGH (P5 |= 1) +#define SCL_LOW (P5 &= ~1) +#define SDA_HIGH (P5 |= 2) +#define SDA_LOW (P5 &= ~2) + +#define SDA_OUT(b) (b ? SDA_HIGH : SDA_LOW) +#define SDA_IN ((P5>>1)&1) +#define SCL_IN (P5&1) + +/* + * I2C error values + */ + +#define I2CERR_OK 0 /* No error */ +#define I2CERR_NAK 1 /* No ACK from slave */ +#define I2CERR_LOST 2 /* Arbitration lost */ +#define I2CERR_BUS 3 /* Bus is stuck (not used yet) */ +#define I2CERR_TIMEOUT 4 /* Timeout on bus */ + +char i2cTransmitBuffer[I2C_BUFSIZE]; /* Global transfer buffers */ +char i2cReceiveBuffer[I2C_BUFSIZE]; + +static char i2cError = 0; /* Last error */ + +#define I2CDELAY 1 + +void I2CDelay(volatile long delay) { + while (delay--) + ; +} + +void I2CDumpError(char error); + +/* + * Makes sure that the bus is in a known condition. Returns 1 on success, + * 0 if some other device is pulling on the bus. + */ + +char I2CReset(void) +{ + SDA_LOW; + SCL_LOW; + SCL_HIGH; + SDA_HIGH; + i2cError = 0; + return (SCL_IN && SDA_IN); +} + + +/* + * Generates a start condition on the bus. Returns 0 on success, 1 if some + * other device is holding the bus. + */ + +char I2CStart(void) +{ + SDA_HIGH; + SCL_HIGH; + I2CDelay(I2CDELAY); + SDA_LOW; /* Pull SDA down... */ + I2CDelay(I2CDELAY); + SCL_LOW; /* ...and then SCL -> start condition. */ + I2CDelay(I2CDELAY); + return 0; +} + + +/* + * Generates a stop condition on the bus. Returns 0 on success, 1 if some + * other device is holding the bus. + */ + +char I2CStop(void) +{ + SDA_LOW; + SCL_HIGH; /* Let SCL go up */ + I2CDelay(I2CDELAY); + SDA_HIGH; /* ...and then SDA up -> stop condition. */ + I2CDelay(I2CDELAY); + + return (SCL_IN && SDA_IN); /* Both will be up, if everything is fine */ +} + + +/* + * Clock out one bit. + * Returns 0 on success, 1 if we lose arbitration. + */ + +char BitOutI2C(char bout) +{ + SDA_OUT(bout); /* Put data out on SDA */ + I2CDelay(I2CDELAY); + SCL_HIGH; /* Let SCL go up */ + while(!SCL_IN) /* Wait until all other devices are ready */ + { + // should do a timeout here + } + + if (SDA_IN != bout) /* Arbitration lost, release bus and return */ + { + SDA_HIGH; /* Should be up anyway, but make sure */ + i2cError = I2CERR_LOST; + I2CDumpError(i2cError); + return 1; + } + I2CDelay(I2CDELAY); + SCL_LOW; /* Pull SCL back down */ + I2CDelay(I2CDELAY); + return 0; /* OK */ +} + + +/* + * Clock in one bit. + */ + +char BitInI2C(void) +{ + char bin; + + // SDA is opencollector, so: + SDA_HIGH; + + SCL_HIGH; /* Let SCL go up */ + while(!SCL_IN) /* Wait for other devices */ + { + // should do a timeout here + } + bin = SDA_IN; /* Read in data */ + I2CDelay(I2CDELAY); + SCL_LOW; /* Pull SCL back up */ + I2CDelay(I2CDELAY); + return bin; /* Return the sampled bit */ +} + + +/* + * Send one byte on the bus. No start or stop conditions are generated here, + * but i2cError will be set according to the result. + * Returns 0 on success, 1 if we lose arbitration or if the slave doesn't + * acknowledge the byte. Check i2cError for the actual result on error. + */ + +char ByteOutI2C(char dat) +{ + char bit_count; + + bit_count = 8; + while(bit_count) { + if (dat & 0x80) { + if (BitOutI2C(1)) { + I2CDumpError(i2cError); + return 1; + } + } else { + if (BitOutI2C(0)) { + I2CDumpError(i2cError); + return 1; + } + } + dat <<= 1; + bit_count--; + } + + if (BitInI2C()) { + i2cError = I2CERR_NAK; + I2CDumpError(i2cError); + return 1; + } + return 0; +} + + +/* + * Reads one byte in from the slave. Ack must be 1 if this is the last byte + * to be read during this transfer, 0 otherwise (as per I2C bus specification, + * the receiving master must acknowledge all but the last byte during a + * transfer). + */ + +char I2CByteIn(char ack) +{ + char bit_count, byte_in; + + bit_count = 8; + byte_in = 0; + + while(bit_count) + { + byte_in <<= 1; + if (BitInI2C()) byte_in |= 0x01; + bit_count--; + } + + BitOutI2C(ack); + SDA_HIGH; /* Added 18-Jul-95 - thanks to Ray Bellis */ + return byte_in; +} + + +/* + * Send 'count' bytes to slave 'addr'. + * Returns 0 on success. Stop condition is sent only when send_stop is true. + */ + +char I2CSendStop(char addr, char count, char send_stop) +{ + char byteptr, byte_out; + + if (I2CStart()) return 1; + i2cError = 0; + + byte_out = addr & 0xfe; /* Ensure that it's a write address */ + count++; /* Include slave address to byte count */ + byteptr = 0; + while(count) + { + if (ByteOutI2C(byte_out)) + { + if (i2cError == I2CERR_NAK && send_stop) I2CStop(); + return i2cError; + } + byte_out = i2cTransmitBuffer[byteptr]; + byteptr++; + count--; + } + + if (send_stop) I2CStop(); + return 0; +} + + +/* + * Read in 'count' bytes from slave 'addr'. + * Returns 0 on success. + */ + +char i2c_recv(char addr, char count) +{ + char byteptr, byte_in; + + if (I2CStart()) return 1; + i2cError = 0; + byteptr = 0; + + byte_in = addr | 0x01; + + if (ByteOutI2C(byte_in)) + { + if (i2cError == I2CERR_NAK) I2CStop(); + return i2cError; + } + + while(count) + { + count-=1; + if (count) { + byte_in = I2CByteIn(0); + } else { + byte_in = I2CByteIn(1); /* No ACK during last byte */ + } + i2cReceiveBuffer[byteptr] = byte_in; + byteptr++; + } + + I2CStop(); + + return (i2cError ? 1 : 0); +} + + +/* + * Write 'tx_count' bytes to slave 'addr', then use a repeated start condition + * to read 'rx_count' bytes from the same slave during the same transfer. + * Returns 0 on success, 1 otherwise. On error, check i2cError for the actual + * error value. + */ + +char I2CSendReceive(char addr, char tx_count, char rx_count) +{ + if (I2CSendStop(addr, tx_count, 0)) + { + /* If send fails, abort but don't send a stop condition if we lost + arbitration */ + + if (i2cError != I2CERR_LOST) I2CStop(); + return 1; + } + + SDA_HIGH; /* One of these may be low now, in which case the next */ + SCL_HIGH; /* start condition wouldn't be detected so make */ + I2CDelay(I2CDELAY); /* sure that they're up and wait for one delay slot */ + + if (i2c_recv((char)(addr|0x01), rx_count)) return 1; + return (i2cError ? 1 : 0); +} + +/* + * Dump an error message. + */ + +void I2CDumpError(char error) +{ +#ifdef DEBUG_I2C + switch(error) + { + case 0: + puts("I2C: OK."); + break; + case I2CERR_NAK: + puts("I2C: Slave didn't acknowledge"); + break; + case I2CERR_LOST: + puts("I2C: Lost arbitration with another master"); + break; + case I2CERR_TIMEOUT: + puts("I2C: Timeout on bus"); + break; + case I2CERR_BUS: + puts("I2C: The bus is stuck"); + break; + default: + puts("I2C: Unknown error"); + break; + } +#else + error; // hush the compiler +#endif +} diff --git a/device/lib/ds390/lcd390.c b/device/lib/ds390/lcd390.c new file mode 100644 index 00000000..e0fbaf94 --- /dev/null +++ b/device/lib/ds390/lcd390.c @@ -0,0 +1,174 @@ +/*------------------------------------------------------------------------- + lcd.c - lcd routines for the DS80C390 (tested on TINI) + + Written By - Johan Knol, johan.knol@iduna.nl + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include +#include +#include + +#define LCD_COLLUMNS 20 +#define LCD_ROWS 4 + +/* set the dd ram addresses for the rows + this one is for a 20x4 LCD +*/ +static unsigned char lcdLinesStart[LCD_ROWS]={0, 0x40, 0x14, 0x54}; + +/* Commercial TINI stick connector boards don't support rw + control for the lcd-display. + My own board does, and it makes it much faster. +*/ + +//#define LCD_RW + +xdata at 0x380002 static unsigned char lcdIwr; +xdata at 0x38000a static unsigned char lcdDwr; + +#ifdef LCD_RW + +xdata at 0x380003 static unsigned char lcdIrd; +xdata at 0x38000b static unsigned char lcdDrd; + +#define LcdWait { while (lcdIrd&0x80) ; } + +#else ifdef LCD_RW + +// wait for 100us +#define LcdWait { ClockMicroSecondsDelay(100) ; } + +#endif ifdef LCD_RW + +void LcdInit() { + + ClockMilliSecondsDelay(16); // >15 ms + + lcdIwr=0x38 ; + ClockMilliSecondsDelay(5); // >4.1 ms + + lcdIwr=0x38; + ClockMicroSecondsDelay(101); // >100 us + + lcdIwr=0x38; + ClockMicroSecondsDelay(101); // >100 us + + lcdIwr=0x38; // interface 8 bit + ClockMicroSecondsDelay(41); // >40 us + + lcdIwr=0x0c; // display on + ClockMicroSecondsDelay(41); // >40 us + + LcdClear(); +} + +void LcdOn() { + lcdIwr=0x0c; // display on + LcdWait; +} + +void LcdOff() { + lcdIwr=0x08; // display off + LcdWait; +} + +void LcdCursorOn() { + // TODO +} + +void LcdCursorOff() { + // TODO +} + +void LcdScrollOn() { + // TODO +} + +void LcdScrollOff() { + // TODO +} + +void LcdCharDefine() { + // TODO +} + +void LcdClear() { + lcdIwr=0x01; // display clear + ClockMilliSecondsDelay(6); // > 5ms +} + +void LcdHome() { + lcdIwr=0x80; // set dd ram address 0 + LcdWait; +} + +void LcdGoto(unsigned int collumnRow) { // msb=collumn, lsb=row + lcdIwr=0x80 + \ + lcdLinesStart[collumnRow&0xff] + (collumnRow>>8); + LcdWait; +} + +void LcdPutChar(char c) { + lcdDwr=c; + LcdWait; +} + +void LcdPutString (char *string) { + char c; + while (c=*string++) { + LcdPutChar (c); + } +} + +void LcdLPutString (unsigned int collumnRow, char *string) { + LcdGoto(collumnRow); + LcdPutString(string); +} + +// let's hope that no one ever printf's more than the display width, +// however they will :), so to be sure +static char lcdPrintfBuffer[LCD_COLLUMNS*4]; + +void LcdPrintf (xdata const char *format, ...) reentrant { + va_list arg; + + va_start (arg, format); + vsprintf (lcdPrintfBuffer, format, arg); + puts (lcdPrintfBuffer); + LcdPutString(lcdPrintfBuffer); + + va_end (arg); +} + +void LcdLPrintf (unsigned int collumnRow, xdata const char *format, ...) reentrant { + va_list arg; + + LcdGoto(collumnRow); + + // we can not just call LcdPrintf since we have no idea what is on the stack, + // so we have to do it all over again + va_start (arg, format); + vsprintf (lcdPrintfBuffer, format, arg); + + LcdPutString(lcdPrintfBuffer); + + va_end (arg); +} diff --git a/device/lib/ds390/rtc390.c b/device/lib/ds390/rtc390.c new file mode 100755 index 00000000..926135d9 --- /dev/null +++ b/device/lib/ds390/rtc390.c @@ -0,0 +1,111 @@ +/*------------------------------------------------------------------------- + rtc390.c - rtc routines for the DS1315 (tested on TINI) + + Written By - Johan Knol, johan.knol@iduna.nl + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 2, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + In other words, you are welcome to use, share and improve this program. + You are forbidden to forbid anyone else to use, share and improve + what you give them. Help stamp out software-hoarding! +-------------------------------------------------------------------------*/ + +#include + +#include +#include + +/* this is the address of the ds1315 phantom time chip, although + it doesn't really matter as long as it's in the 300000-3ffff + range since the chip only uses CE3* +*/ + +xdata at 0x310000 static volatile unsigned char rtc; + +// this is the 64bit pattern that has to be recognized by the ds1315 +code unsigned char rtcMagic[8]={0xc5,0x3a,0xa3,0x5c,0xc5,0x3a,0xa3,0x5c}; + +static struct RTCDate{ + int year; + unsigned char month, day, weekDay, hour, minute, second, hundredth; +}; + +#define BCDtoINT(x) (((x)&0x0f)+((x)>>4)*10) +#define INTtoBCD(x) (((x)%10)+(((x)/10)<<4)) + +static void RtcSync(void) { + unsigned char dummy, byte,bitMask; + + // reset rtc chip + dummy=rtc; + + // say the magic word + for (byte=0; byte<8; byte++) { + for (bitMask=0x01; bitMask; bitMask<<=1) { + rtc = (rtcMagic[byte]&bitMask) ? 0xff : 0x00; + } + } +} + +unsigned char RtcRead(struct RTCDate *rtcDate) { + unsigned char rtcBytes[8]; + unsigned char byte,bitMask; + + RtcSync(); + + for (byte=0; byte<8; byte++) { + rtcBytes[byte]=0; + for (bitMask=0x01; bitMask; bitMask<<=1) { + if (rtc&1) { + rtcBytes[byte]|=bitMask; + } + } + } + rtcDate->year=2000 + BCDtoINT(rtcBytes[7]); + rtcDate->month=BCDtoINT(rtcBytes[6]); + rtcDate->day=BCDtoINT(rtcBytes[5]); + rtcDate->weekDay=rtcBytes[4]&0x07; + rtcDate->hour=BCDtoINT(rtcBytes[3]); + rtcDate->minute=BCDtoINT(rtcBytes[2]); + rtcDate->second=BCDtoINT(rtcBytes[1]); + rtcDate->hundredth=BCDtoINT(rtcBytes[0]); + if ((rtcBytes[4]&0x30) || (rtcBytes[3]&0x80)) { + //oscillator not running, reset not active or in 12h mode + return 0; + } + return 1; +} + +void RtcWrite(struct RTCDate *rtcDate) { + unsigned char rtcBytes[8]; + unsigned char byte,bitMask; + + rtcBytes[7]=INTtoBCD(rtcDate->year-2000); + rtcBytes[6]=INTtoBCD(rtcDate->month); + rtcBytes[5]=INTtoBCD(rtcDate->day); + rtcBytes[4]=INTtoBCD(rtcDate->weekDay)&0x07; //set 24h mode + rtcBytes[3]=INTtoBCD(rtcDate->hour)&0x3f; // oscilator on, reset on + rtcBytes[2]=INTtoBCD(rtcDate->minute); + rtcBytes[1]=INTtoBCD(rtcDate->second); + rtcBytes[0]=INTtoBCD(rtcDate->hundredth); + + RtcSync(); + + for (byte=0; byte<8; byte++) { + for (bitMask=0x01; bitMask; bitMask<<=1) { + rtc = (rtcBytes[byte]&bitMask) ? 0xff : 0x00; + } + } +} -- 2.47.2