summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--avr/Tupfile6
-rw-r--r--avr/cmd_date.c188
-rw-r--r--avr/command_tbl.c10
-rw-r--r--avr/date.c139
-rw-r--r--avr/debug.c71
-rw-r--r--avr/i2c.c389
-rw-r--r--avr/main.c13
-rw-r--r--avr/pcf8583.c106
-rw-r--r--include/cmd_mem.h2
-rw-r--r--include/common.h10
-rw-r--r--include/config.h6
-rw-r--r--include/crc.h4
-rw-r--r--include/debug.h4
-rw-r--r--include/i2c.h63
-rw-r--r--include/rtc.h49
15 files changed, 979 insertions, 81 deletions
diff --git a/avr/Tupfile b/avr/Tupfile
index 60022d6..d0bed48 100644
--- a/avr/Tupfile
+++ b/avr/Tupfile
@@ -3,9 +3,9 @@ include_rules
PROG = stamp-test
SRC = main.c
SRC += cli.c cli_readline.c command.c command_tbl.c
-SRC += cmd_help.c cmd_echo.c cmd_mem.c cmd_boot.c
-SRC += env.c xmalloc.c
-SRC += timer.c con-utils.c serial.c
+SRC += cmd_help.c cmd_echo.c cmd_date.c cmd_mem.c cmd_boot.c
+SRC += env.c xmalloc.c date.c
+SRC += timer.c con-utils.c serial.c i2c.c pcf8583.c
SRC += background.c z180-serv.c z80-if.c
SRC_Z = ../z180/hdrom.c
diff --git a/avr/cmd_date.c b/avr/cmd_date.c
new file mode 100644
index 0000000..bc93efc
--- /dev/null
+++ b/avr/cmd_date.c
@@ -0,0 +1,188 @@
+/*
+ * (C) Copyright 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * RTC, Date & Time support: get and set date & time
+ */
+#include <common.h>
+#include <string.h>
+#include <avr/pgmspace.h>
+#include <command.h>
+#include <rtc.h>
+#include <i2c.h>
+
+
+static const char * const weekdays[] = {
+ "Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur",
+};
+
+int mk_date (const char *, struct rtc_time *);
+
+command_ret_t do_date(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ struct rtc_time tm;
+ int rcode = CMD_RET_SUCCESS;
+
+ (void) cmdtp; (void) flag;
+
+ switch (argc) {
+ case 2: /* set date & time */
+ if (strcmp_P(argv[1],PSTR("reset")) == 0) {
+ my_puts_P(PSTR("Reset RTC...\n"));
+ rtc_reset ();
+ } else {
+ /* initialize tm with current time */
+ rcode = rtc_get (&tm);
+
+ if(!rcode) {
+ /* insert new date & time */
+ if (mk_date (argv[1], &tm) != 0) {
+ my_puts_P(PSTR("## Bad date format\n"));
+ break;
+ }
+ /* and write to RTC */
+ rcode = rtc_set (&tm);
+ if(rcode)
+ my_puts_P(PSTR("## Set date failed\n"));
+ } else {
+ my_puts_P(PSTR("## Get date failed\n"));
+ }
+ }
+ /* FALL TROUGH */
+ case 1: /* get date & time */
+ rcode = rtc_get (&tm);
+
+ if (rcode) {
+ my_puts_P(PSTR("## Get date failed\n"));
+ break;
+ }
+ /* TODO: flash */
+ printf_P(PSTR("Date: %4d-%02d-%02d (%sday) Time: %2d:%02d:%02d\n"),
+ tm.tm_year, tm.tm_mon, tm.tm_mday,
+ (tm.tm_wday<0 || tm.tm_wday>6) ?
+ "unknown " : weekdays[tm.tm_wday],
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+
+ break;
+ default:
+ rcode = CMD_RET_USAGE;
+ }
+
+ return rcode;
+}
+
+/*
+ * simple conversion of two-digit string with error checking
+ */
+static int cnvrt2 (const char *str, int *valp)
+{
+ int val;
+
+ if ((*str < '0') || (*str > '9'))
+ return (-1);
+
+ val = *str - '0';
+
+ ++str;
+
+ if ((*str < '0') || (*str > '9'))
+ return (-1);
+
+ *valp = 10 * val + (*str - '0');
+
+ return (0);
+}
+
+/*
+ * Convert date string: MMDDhhmm[[CC]YY][.ss]
+ *
+ * Some basic checking for valid values is done, but this will not catch
+ * all possible error conditions.
+ */
+int mk_date (const char *datestr, struct rtc_time *tmp)
+{
+ int len, val;
+ char *ptr;
+
+ ptr = strchr (datestr,'.');
+ len = strlen (datestr);
+
+ /* Set seconds */
+ if (ptr) {
+ int sec;
+
+ *ptr++ = '\0';
+ if ((len - (ptr - datestr)) != 2)
+ return (-1);
+
+ len = strlen (datestr);
+
+ if (cnvrt2 (ptr, &sec))
+ return (-1);
+
+ tmp->tm_sec = sec;
+ } else {
+ tmp->tm_sec = 0;
+ }
+
+ if (len == 12) { /* MMDDhhmmCCYY */
+ int year, century;
+
+ if (cnvrt2 (datestr+ 8, &century) ||
+ cnvrt2 (datestr+10, &year) ) {
+ return (-1);
+ }
+ tmp->tm_year = 100 * century + year;
+ } else if (len == 10) { /* MMDDhhmmYY */
+ int year, century;
+
+ century = tmp->tm_year / 100;
+ if (cnvrt2 (datestr+ 8, &year))
+ return (-1);
+ tmp->tm_year = 100 * century + year;
+ }
+
+ switch (len) {
+ case 8: /* MMDDhhmm */
+ /* fall thru */
+ case 10: /* MMDDhhmmYY */
+ /* fall thru */
+ case 12: /* MMDDhhmmCCYY */
+ if (cnvrt2 (datestr+0, &val) ||
+ val > 12) {
+ break;
+ }
+ tmp->tm_mon = val;
+ if (cnvrt2 (datestr+2, &val) ||
+ val > ((tmp->tm_mon==2) ? 29 : 31)) {
+ break;
+ }
+ tmp->tm_mday = val;
+
+ if (cnvrt2 (datestr+4, &val) ||
+ val > 23) {
+ break;
+ }
+ tmp->tm_hour = val;
+
+ if (cnvrt2 (datestr+6, &val) ||
+ val > 59) {
+ break;
+ }
+ tmp->tm_min = val;
+
+ /* calculate day of week */
+ GregorianDay (tmp);
+
+ return (0);
+ default:
+ break;
+ }
+
+ return (-1);
+}
+
diff --git a/avr/command_tbl.c b/avr/command_tbl.c
index 2fb41b9..6fcbee7 100644
--- a/avr/command_tbl.c
+++ b/avr/command_tbl.c
@@ -16,10 +16,20 @@ extern command_ret_t do_restart(cmd_tbl_t *, int, int, char * const []);
extern command_ret_t do_dump_mem(cmd_tbl_t *, int, int, char * const []);
extern command_ret_t do_eep_cp(cmd_tbl_t *, int, int, char * const []);
extern command_ret_t do_busreq_pulse(cmd_tbl_t *, int, int, char * const []);
+extern command_ret_t do_date(cmd_tbl_t *, int, int, char * const []);
cmd_tbl_t cmd_tbl[] = {
+CMD_TBL_ITEM(
+ date, 2, 1, do_date,
+ "get/set/reset date & time",
+ "[MMDDhhmm[[CC]YY][.ss]]\ndate reset\n"
+ " - without arguments: print date & time\n"
+ " - with numeric argument: set the system date & time\n"
+ " - with 'reset' argument: reset the RTC"
+),
+
#ifdef DEBUG
CMD_TBL_ITEM(
!mdr, 3, 1, do_dump_mem,
diff --git a/avr/date.c b/avr/date.c
new file mode 100644
index 0000000..c85361f
--- /dev/null
+++ b/avr/date.c
@@ -0,0 +1,139 @@
+/*
+ * (C) Copyright 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * Date & Time support for RTC
+ */
+
+#include <common.h>
+#include <command.h>
+#include <rtc.h>
+
+
+#define FEBRUARY 2
+#define STARTOFTIME 1970
+#define SECDAY 86400L
+#define SECYR (SECDAY * 365)
+#define leapyear(year) ((year) % 4 == 0)
+#define days_in_year(a) (leapyear(a) ? 366 : 365)
+#define days_in_month(a) (month_days[(a) - 1])
+
+
+static const FLASH int MonthOffset[] = {
+ 0,31,59,90,120,151,181,212,243,273,304,334
+};
+
+/*
+ * This only works for the Gregorian calendar - i.e. after 1752 (in the UK)
+ */
+void GregorianDay(struct rtc_time * tm)
+{
+ int leapsToDate;
+ int lastYear;
+ int day;
+
+ lastYear=tm->tm_year-1;
+
+ /*
+ * Number of leap corrections to apply up to end of last year
+ */
+ leapsToDate = lastYear/4 - lastYear/100 + lastYear/400;
+
+ /*
+ * This year is a leap year if it is divisible by 4 except when it is
+ * divisible by 100 unless it is divisible by 400
+ *
+ * e.g. 1904 was a leap year, 1900 was not, 1996 is, and 2000 will be
+ */
+ if((tm->tm_year%4==0) &&
+ ((tm->tm_year%100!=0) || (tm->tm_year%400==0)) &&
+ (tm->tm_mon>2)) {
+ /*
+ * We are past Feb. 29 in a leap year
+ */
+ day=1;
+ } else {
+ day=0;
+ }
+
+ day += lastYear*365 + leapsToDate + MonthOffset[tm->tm_mon-1] + tm->tm_mday;
+
+ tm->tm_wday=day%7;
+}
+
+void to_tm(unsigned long tim, struct rtc_time * tm)
+{
+ char month_days[12] = {31,28,31,30,31,30,31,31,30,31,30,31};
+ register int i;
+ register long hms, day;
+
+ day = tim / SECDAY;
+ hms = tim % SECDAY;
+
+ /* Hours, minutes, seconds are easy */
+ tm->tm_hour = hms / 3600;
+ tm->tm_min = (hms % 3600) / 60;
+ tm->tm_sec = (hms % 3600) % 60;
+
+ /* Number of years in days */
+ for (i = STARTOFTIME; day >= days_in_year(i); i++) {
+ day -= days_in_year(i);
+ }
+ tm->tm_year = i;
+
+ /* Number of months in days left */
+ if (leapyear(tm->tm_year)) {
+ days_in_month(FEBRUARY) = 29;
+ }
+ for (i = 1; day >= days_in_month(i); i++) {
+ day -= days_in_month(i);
+ }
+ days_in_month(FEBRUARY) = 28;
+ tm->tm_mon = i;
+
+ /* Days are what is left over (+1) from all that. */
+ tm->tm_mday = day + 1;
+
+ /*
+ * Determine the day of week
+ */
+ GregorianDay(tm);
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+unsigned long
+mktime (unsigned int year, unsigned int mon,
+ unsigned int day, unsigned int hour,
+ unsigned int min, unsigned int sec)
+{
+ if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+
+ return (((
+ (unsigned long) (year/4 - year/100 + year/400 + 367*mon/12 + day) +
+ year*365 - 719499
+ )*24 + hour /* now have hours */
+ )*60 + min /* now have minutes */
+ )*60 + sec; /* finally seconds */
+}
+
diff --git a/avr/debug.c b/avr/debug.c
index fcbd2c1..16df702 100644
--- a/avr/debug.c
+++ b/avr/debug.c
@@ -12,8 +12,6 @@
*/
#ifdef DEBUG
-//uint8_t eeprom_read_byte (const uint8_t *__p)
-
static void print_blanks(uint_fast8_t count)
{
while(count--)
@@ -29,14 +27,17 @@ void dump_mem(const uint8_t *startaddr, int len,
uint8_t (*readfkt)(const uint8_t *), char *title)
{
uint8_t buf[16];
+ char *indent = NULL;
uint8_t llen = 16;
uint8_t pre = (size_t) startaddr % 16;
const uint8_t *addr = (uint8_t *) ((size_t) startaddr & ~0x0f);
len += pre;
uint8_t i;
- if (title && *title)
+ if (title && *title) {
printf_P(PSTR("%s\n"),title);
+ indent = " ";
+ }
while (len) {
if (len < 16)
@@ -45,7 +46,7 @@ void dump_mem(const uint8_t *startaddr, int len,
for (i = pre; i < llen; i++)
buf[i] = readfkt(addr + i);
- printf_P(PSTR("%04x:"), addr);
+ printf_P(PSTR("%s%04x:"),indent, addr);
for (i = 0; i < llen; i++) {
if ((i % 8) == 0)
putchar(' ');
@@ -67,37 +68,17 @@ void dump_mem(const uint8_t *startaddr, int len,
}
}
-#if 0
-void dump_ram(const uint8_t *startaddr, int len, char *title)
+void dump_eep(const uint8_t *addr, unsigned int len, char *title)
{
- uint8_t llen = 16;
- uint8_t pre = (size_t) startaddr % 16;
- const uint8_t *addr = (uint8_t *) ((size_t) startaddr & ~0x0f);
- len += pre;
- uint8_t i;
-
- if (title && *title)
- printf_P(PSTR("%s\n"),title);
+ dump_mem(addr, len, eeprom_read_byte, title);
+}
- while (len) {
- if (len < 16)
- llen = len;
+void dump_ram(const uint8_t *addr, unsigned int len, char *title)
+{
+ dump_mem(addr, len, ram_read_byte, title);
+}
- printf_P(PSTR(" %.4x:"), (size_t) addr);
- print_blanks(3 * pre);
- for (i = pre; i < llen; i++)
- printf_P(PSTR(" %.2x"), addr[i]);
- print_blanks(3 * (16 - i + 1) + pre);
- for (i = pre; i < llen; i++)
- printf_P(PSTR("%c"), isprint(addr[i]) ? addr[i] : '.');
- putchar('\n');
- pre = 0;
- addr += 16;
- len -= llen;
- }
-}
-#endif
#if 0
void dump_heap(void)
{
@@ -109,35 +90,9 @@ void dump_heap(void)
}
#endif
-#if 0
-/* TODO: combine with dump_ram() */
-void dump_eep(const uint8_t *addr, unsigned int len,
- uint8_t (*readfkt)(const uint8_t *))
-{
- uint_fast8_t i;
- uint8_t buf[16];
-
- printf_P(PSTR("eeprom dump:"));
- while (len) {
- printf_P(PSTR("\n 0x%.4x:"), (unsigned int) addr);
- for (i = 0; i<16; i++)
- buf[i] = readfkt(addr + i);
- for (i = 0; i<16; i++)
- printf_P(PSTR(" %.2x"), buf[i]);
- printf_P(PSTR(" "));
- for (i = 0; i<16; i++)
- printf_P(PSTR("%c"), isprint(buf[i]) ? buf[i] : '.');
-
- addr += 16;
- len -= len > 16 ? 16 : len;
- }
- putchar('\n');
-}
-#endif
-
/*
- * EEPROM Display
+ * Memory Display
* md addr {len}
*/
command_ret_t do_dump_mem(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
diff --git a/avr/i2c.c b/avr/i2c.c
new file mode 100644
index 0000000..df97fea
--- /dev/null
+++ b/avr/i2c.c
@@ -0,0 +1,389 @@
+
+/*
+ * I2C (TWI) master interface.
+ */
+
+#include "common.h"
+#include <avr/interrupt.h>
+#include <util/delay.h>
+#include <string.h>
+
+#include "config.h"
+#include "timer.h"
+#include "debug.h"
+#include "i2c.h"
+
+#ifdef DEBUG
+//# define DEBUG_I2C
+#endif
+
+/* General TWI Master status codes */
+#define TWI_START 0x08 /* START has been transmitted */
+#define TWI_REP_START 0x10 /* Repeated START has been transmitted */
+#define TWI_ARB_LOST 0x38 /* Arbitration lost */
+
+/* TWI Master Transmitter status codes */
+#define TWI_MTX_ADR_ACK 0x18 /* SLA+W has been transmitted and ACK received */
+#define TWI_MTX_ADR_NACK 0x20 /* SLA+W has been transmitted and NACK received */
+#define TWI_MTX_DATA_ACK 0x28 /* Data byte has been transmitted and ACK received */
+#define TWI_MTX_DATA_NACK 0x30 /* Data byte has been transmitted and NACK received */
+
+/* TWI Master Receiver status codes */
+#define TWI_MRX_ADR_ACK 0x40 /* SLA+R has been transmitted and ACK received */
+#define TWI_MRX_ADR_NACK 0x48 /* SLA+R has been transmitted and NACK received */
+#define TWI_MRX_DATA_ACK 0x50 /* Data byte has been received and ACK transmitted */
+#define TWI_MRX_DATA_NACK 0x58 /* Data byte has been received and NACK transmitted */
+
+/* TWI Miscellaneous status codes */
+#define TWI_NO_STATE 0xF8 /* No relevant state information available */
+#define TWI_BUS_ERROR 0x00 /* Bus error due to an illegal START or STOP condition */
+
+
+/*
+ * TWINT: TWI Interrupt Flag
+ * TWEA: TWI Enable Acknowledge Bit
+ * TWSTA: TWI START Condition Bit
+ * TWSTO: TWI STOP Condition Bit
+ * TWEN: TWI Enable Bit
+ * TWIE: TWI Interrupt Enable
+ *
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)| (1<<TWEA)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)
+ *
+ * default:
+ * (1<<TWEN)| (1<<TWINT)| (1<<TWSTO)
+ *
+ * Init:
+ * (1<<TWEN)
+ *
+ * start read/write:
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ * (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA)
+ *
+ * wait ready:
+ * (1<<TWIE)|(1<<TWSTO)
+ *
+ *
+ *
+ *i2c_result
+ *
+ * 0b10000000 Busy (Transmission in progress)
+ * 0b01000000 Timeout
+ * 0b00001000 Start transmitted
+ * 0b00000100 Slave acknowledged address
+ * 0b00000010 Data byte(s) transmitted/received
+ * 0b00000001 Transmission completed
+ *
+ *
+ *----------------------------------------------------------------------
+ */
+
+#define TWI_C_DISABLE 0x00
+#define TWI_C_ENABLE (1<<TWEN)
+
+
+
+ typedef struct i2c_msg_s {
+ uint8_t stat;
+ #define XMIT_DONE (1<<0)
+ #define DATA_ACK (1<<1)
+ #define ADDR_ACK (1<<2)
+ #define START (1<<3)
+ #define TIMEOUT (1<<6)
+ #define BUSY (1<<7)
+ uint8_t idx;
+ uint8_t len;
+ uint8_t buf[CONFIG_SYS_I2C_BUFSIZE];
+} i2c_msg_t;
+
+static i2c_msg_t xmit;
+
+ISR(TWI_vect)
+{
+ uint8_t next_twcr;
+ uint8_t tmp_idx;
+
+ uint8_t twsr = TWSR;
+
+ switch (twsr & 0xf8) {
+
+ case TWI_START:
+ case TWI_REP_START:
+ xmit.idx = 0; /* reset xmit_buf index */
+ xmit.stat = BUSY | START;
+
+ tmp_idx = xmit.idx;
+ if (tmp_idx < xmit.len) { /* all bytes transmited? */
+ TWDR = xmit.buf[tmp_idx];
+ xmit.idx = ++tmp_idx;
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
+ } else {
+ xmit.stat |= XMIT_DONE;
+ xmit.stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ }
+ break;
+
+ case TWI_MTX_ADR_ACK:
+ xmit.stat |= ADDR_ACK;
+
+ tmp_idx = xmit.idx;
+ if (tmp_idx < xmit.len) { /* all bytes transmited? */
+ TWDR = xmit.buf[tmp_idx];
+ xmit.idx = ++tmp_idx;
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
+ } else {
+ xmit.stat |= XMIT_DONE;
+ xmit.stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ }
+ break;
+
+ case TWI_MTX_DATA_ACK:
+ xmit.stat |= DATA_ACK;
+
+ tmp_idx = xmit.idx;
+ if (tmp_idx < xmit.len) { /* all bytes transmited? */
+ TWDR = xmit.buf[tmp_idx];
+ xmit.idx = ++tmp_idx;
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
+ } else {
+ xmit.stat |= XMIT_DONE;
+ xmit.stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ }
+
+ break;
+
+ case TWI_MTX_DATA_NACK:
+ xmit.stat |= XMIT_DONE;
+ xmit.stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ break;
+
+ case TWI_MRX_ADR_ACK:
+ xmit.stat |= ADDR_ACK;
+ tmp_idx = xmit.idx;
+ if (tmp_idx < xmit.len-1) {
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA);
+ } else {
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
+ }
+ break;
+
+ case TWI_MRX_DATA_ACK:
+ xmit.stat |= DATA_ACK;
+ tmp_idx = xmit.idx;
+ xmit.buf[tmp_idx] = TWDR;
+ xmit.idx = ++tmp_idx;
+ if (tmp_idx < xmit.len-1) {
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWEA);
+ } else {
+
+ next_twcr = (1<<TWEN)|(1<<TWIE)|(1<<TWINT);
+ }
+ break;
+
+ case TWI_MRX_DATA_NACK:
+ xmit.stat |= ADDR_ACK | DATA_ACK;
+
+ tmp_idx = xmit.idx;
+ xmit.buf[tmp_idx] = TWDR;
+ xmit.idx = ++tmp_idx;
+ /* fall thru */
+
+ default:
+ xmit.stat &= ~BUSY;
+ next_twcr = (1<<TWEN)|(0<<TWIE)|(1<<TWINT)|(1<<TWSTO);
+ break;
+ }
+
+#ifdef DEBUG_I2C
+ debug("|%02x", twsr);
+#endif
+ TWCR = next_twcr;
+}
+
+
+/*------------------------------------------------------------------*/
+
+static uint8_t twps;
+static uint8_t twbr;
+
+
+static void _init(void)
+{
+ xmit.stat = 0;
+#ifdef DEBUG_I2C
+ memset((void *) xmit.buf, 0xdf, sizeof(xmit.buf));
+#endif
+
+ /* Disable TWI, disable TWI interrupt. */
+ /* (Reset TWI hardware state machine.) */
+ TWCR = TWI_C_DISABLE;
+ _delay_us(5);
+
+ TWBR = twbr;
+ TWDR = 0xff;
+ TWCR = TWI_C_ENABLE + twps;
+}
+
+void i2c_init(uint32_t speed)
+{
+ twps = 0;
+ uint32_t tmptwbr = F_CPU /2 / speed - 8;
+
+ while (tmptwbr > 255) {
+ tmptwbr >>= 4;
+ twps += 1;
+ }
+ debug_cond((twps > 3), "TWCLK too low: %lu Hz\n", speed);
+
+ twbr = (uint8_t) tmptwbr;
+ _init();
+}
+
+
+int_fast8_t i2c_waitready(void)
+{
+ uint32_t timer = get_timer(0);
+ uint8_t timeout = 0;
+
+ do {
+ if (get_timer(timer) >= 30) {
+ timeout = TIMEOUT;
+ _init();
+ }
+ } while ((TWCR & ((1<<TWIE)|(1<<TWSTO))) != 0 && !timeout);
+
+ xmit.stat |= timeout;
+
+#ifdef DEBUG_I2C
+ dump_ram((uint8_t *) &xmit, 4, "=== i2c_wait ready: (done)");
+ _delay_ms(30);
+#endif
+ return xmit.stat;
+}
+
+//static
+int i2c_send(uint8_t chip, uint16_t addr, uint8_t alen, uint8_t *buffer, int8_t len)
+{
+ uint8_t i, n;
+ uint8_t rc;
+
+ rc = i2c_waitready();
+ if ((rc & (BUSY | TIMEOUT)) != 0)
+ return rc;
+
+ xmit.stat = BUSY;
+ xmit.buf[0] = chip<<1;
+ for (i = 1; i < alen+1; i++) {
+ xmit.buf[i] = (uint8_t) addr;
+ addr >>= 8;
+ }
+ for (n = len + i; i < n; i++)
+ xmit.buf[i] = *buffer++;
+ xmit.len = i;
+
+#ifdef DEBUG_I2C
+ dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_send");
+ _delay_ms(30);
+#endif
+ /* Enable TWI, TWI int and initiate start condition */
+ TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);
+
+ rc = xmit.stat;
+
+ return rc;
+}
+
+//static
+int i2c_recv(uint8_t chip, uint8_t *buffer, int8_t len)
+{
+ uint8_t rc;
+
+ rc = i2c_waitready();
+ if ((rc & (BUSY | TIMEOUT)) != 0)
+ return rc;
+
+ xmit.stat = BUSY;
+ xmit.len = len + 1;
+ xmit.buf[0] = (chip<<1) | 1;
+
+#ifdef DEBUG_I2C
+ dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_recv: before start");
+ _delay_ms(30);
+#endif
+ /* Enable TWI, TWI int and initiate start condition */
+ TWCR = (1<<TWEN)|(1<<TWIE)|(1<<TWINT)|(1<<TWSTA);
+ rc = i2c_waitready();
+
+#ifdef DEBUG_I2C
+ dump_ram((uint8_t *) &xmit, 0x20, "=== i2c_recv: after completion");
+ _delay_ms(30);
+#endif
+ if (rc & DATA_ACK) {
+ /* at least 1 byte received */
+ for (uint8_t i=1, n=xmit.idx; i < n; i++)
+ *buffer++ = xmit.buf[i];
+ }
+
+ return rc;
+}
+
+/*
+ * Read/Write interface:
+ * chip: I2C chip address, range 0..127
+ * addr: Memory (register) address within the chip
+ * alen: Number of bytes to use for addr (typically 1, 2 for larger
+ * memories, 0 for register type devices with only one
+ * register)
+ * buffer: Where to read/write the data
+ * len: How many bytes to read/write
+ *
+ * Returns: 0 on success, not 0 on failure
+ */
+
+int i2c_write(uint8_t chip, unsigned int addr, uint_fast8_t alen,
+ uint8_t *buffer, uint_fast8_t len)
+{
+ int rc;
+
+ if ((alen > 2) || (1 + alen + len > CONFIG_SYS_I2C_BUFSIZE)) {
+ debug("** i2c_write: buffer overflow, alen: %u, len: %u\n",
+ alen, len);
+ return -1;
+ }
+
+ i2c_send(chip, addr, alen, buffer, len);
+ rc = i2c_waitready();
+ debug("** i2c_write: result=0x%02x\n",rc);
+
+ return (rc & XMIT_DONE) != 0;
+}
+
+int i2c_read(uint8_t chip, unsigned int addr, uint_fast8_t alen,
+ uint8_t *buffer, uint_fast8_t len)
+{
+ int rc;
+
+ if ((alen > 2) || (1 + len > CONFIG_SYS_I2C_BUFSIZE)) {
+ debug("** i2c_read: parameter error: alen: %u, len: %u\n",
+ alen, len);
+ return -1;
+ }
+
+ if (alen != 0) {
+ i2c_send(chip, addr, alen, NULL, 0);
+ }
+ rc = i2c_recv(chip, buffer, len);
+ debug("** i2c_read: result=0x%02x\n",rc);
+
+ return !((rc & (XMIT_DONE|DATA_ACK)) == (XMIT_DONE|DATA_ACK));
+}
+
+
+
diff --git a/avr/main.c b/avr/main.c
index c6a0f00..9103ddc 100644
--- a/avr/main.c
+++ b/avr/main.c
@@ -5,22 +5,14 @@
#include "common.h"
#include <util/delay.h>
-//#include <avr/power.h>
-//#include <avr/pgmspace.h>
#include <avr/interrupt.h>
-//#include <util/atomic.h>
-//#include <avr/sleep.h>
-//#include <string.h>
-
-#include <util/delay.h>
-
#include <stdlib.h>
#include <stdio.h>
-
#include "config.h"
#include "debug.h"
#include "z80-if.h"
+#include "i2c.h"
#include "con-utils.h"
#include "serial.h"
#include "timer.h"
@@ -92,7 +84,7 @@ void print_reset_reason(void)
uint8_t r = mcusr & 0x1f;
const FLASH char * const FLASH *p = rreasons;
- printf_P(PSTR("Reset reason(s): "));
+ printf_P(PSTR("Reset reason(s): %s"), r ? "" : "none");
for ( ; r; p++, r >>= 1) {
if (r & 1) {
my_puts_P(*p);
@@ -217,6 +209,7 @@ int main(void)
#endif
env_init();
+ i2c_init(34920);
printf_P(PSTR("\n(ATMEGA1281+HD64180)_stamp Tester\n"));
diff --git a/avr/pcf8583.c b/avr/pcf8583.c
new file mode 100644
index 0000000..d33c9b0
--- /dev/null
+++ b/avr/pcf8583.c
@@ -0,0 +1,106 @@
+/*
+ * Date & Time support for Philips PCF8583 RTC
+ */
+
+/* #define DEBUG */
+
+#include "common.h"
+#include <stdlib.h>
+#include "debug.h"
+#include "command.h"
+#include "rtc.h"
+#include "i2c.h"
+
+#define REG_CS 0x00 /* control/status */
+#define REG_CSEC 0x01 /* hundredth of a second */
+#define REG_SEC 0x02 /* seconds */
+#define REG_MIN 0x03 /* minutes */
+#define REG_HOUR 0x04 /* hours */
+#define REG_YRDATE 0x05 /* year/date */
+#define REG_WDMON 0x06 /* weekdays/months */
+#define NR_OF_REGS 7
+
+
+/* ------------------------------------------------------------------------- */
+
+static uint_fast8_t bcd2bin(uint8_t val)
+{
+ return (val >> 4) * 10 + (val & 0x0f);
+}
+
+static uint8_t bin2bcd (uint_fast8_t val)
+{
+ div_t d = div(val, 10);
+
+ return (d.quot << 4) | d.rem;
+}
+
+
+int rtc_get (struct rtc_time *tmp)
+{
+ int rel = 0;
+ uint8_t rtcbuf[NR_OF_REGS];
+ uint16_t year;
+
+ i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, rtcbuf, NR_OF_REGS);
+ i2c_read(CONFIG_SYS_I2C_RTC_ADDR, 0x10, 1, (uint8_t *) &year, 2);
+
+ debug("Get RTC year: %u, year/date: %02x, wdays/month: %02x, "
+ "hour: %02x, min: %02x, sec: %02x, (stat: %02x)\n", year,
+ rtcbuf[6], rtcbuf[5], rtcbuf[4], rtcbuf[3], rtcbuf[2], rtcbuf[0]);
+
+ tmp->tm_sec = bcd2bin (rtcbuf[REG_SEC] & 0x7F);
+ tmp->tm_min = bcd2bin (rtcbuf[REG_MIN] & 0x7F);
+ tmp->tm_hour = bcd2bin (rtcbuf[REG_HOUR] & 0x3F);
+ tmp->tm_mday = bcd2bin (rtcbuf[REG_YRDATE] & 0x3F);
+ tmp->tm_mon = bcd2bin (rtcbuf[REG_WDMON] & 0x1F);
+ while (year%4 < (rtcbuf[REG_YRDATE]>>6)) {
+ year++;
+ /* TODO: update RTC ram */
+ }
+ tmp->tm_year = year;
+ tmp->tm_wday = rtcbuf[REG_WDMON] >> 5;
+ tmp->tm_yday = 0;
+ tmp->tm_isdst= 0;
+
+
+ debug ( "Get DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+
+ return rel;
+}
+
+int rtc_set (struct rtc_time *tmp)
+{
+ uint8_t rtcbuf[NR_OF_REGS];
+
+ debug ( "Set DATE: %4d-%02d-%02d (wday=%d) TIME: %2d:%02d:%02d\n",
+ tmp->tm_year, tmp->tm_mon, tmp->tm_mday, tmp->tm_wday,
+ tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
+
+ rtcbuf[REG_CS] = 0x84;
+ rtcbuf[REG_CSEC] = 0x00;
+ rtcbuf[REG_WDMON ] = bin2bcd(tmp->tm_mon) | ((tmp->tm_wday) << 5);
+ rtcbuf[REG_YRDATE] = ((tmp->tm_year % 4) << 6) | bin2bcd(tmp->tm_mday);
+ rtcbuf[REG_HOUR ] = bin2bcd(tmp->tm_hour);
+ rtcbuf[REG_MIN ] = bin2bcd(tmp->tm_min);
+ rtcbuf[REG_SEC ] = bin2bcd(tmp->tm_sec);
+
+ i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, rtcbuf, NR_OF_REGS);
+ i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0x10, 1, (uint8_t *) &tmp->tm_year, 2);
+ rtcbuf[REG_CS] = 0x04;
+ i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, rtcbuf, 1);
+
+
+ return 0;
+}
+
+void rtc_reset (void)
+{
+ uint8_t c = 0;
+
+ i2c_write(CONFIG_SYS_I2C_RTC_ADDR, 0, 1, &c, 1);
+}
+
+
diff --git a/include/cmd_mem.h b/include/cmd_mem.h
index cf379ce..1802338 100644
--- a/include/cmd_mem.h
+++ b/include/cmd_mem.h
@@ -1,8 +1,6 @@
#ifndef CMD_MEM_H
#define CMD_MEM_H
-//#include "common.h"
-
#include "command.h"
#include "cmd_mem.h"
diff --git a/include/common.h b/include/common.h
index a92f62c..e8879a6 100644
--- a/include/common.h
+++ b/include/common.h
@@ -43,19 +43,15 @@ void my_puts(const char *s)
fputs(s, stdout);
}
-#ifdef __AVR__
static inline
void my_puts_P(const char *s)
{
+#ifdef __AVR__
fputs_P(s, stdout);
-}
-
#else
-static inline
-void my_puts_P(const char *s)
-{
fputs(s, stdout);
-}
#endif /* __AVR__ */
+}
+
#endif /* COMMON_H */
diff --git a/include/config.h b/include/config.h
index 08d97bd..91b1fae 100644
--- a/include/config.h
+++ b/include/config.h
@@ -12,6 +12,12 @@
//#define CONFIG_CMD_MEMTEST
//#define CONFIG_MX_CYCLIC
+#define CONFIG_CMD_DATE 1
+
+#define CONFIG_SYS_I2C_RTC_ADDR 0x50
+#define CONFIG_SYS_I2C_BUFSIZE 64
+#define CONFIG_SYS_I2C_CLOCK 100000L /* 100kHz */
+
#define CONFIG_SYS_CBSIZE 250
#define CONFIG_SYS_ENV_NAMELEN 16
#define CONFIG_SYS_MAXARGS 8
diff --git a/include/crc.h b/include/crc.h
index c38bae4..927b5ff 100644
--- a/include/crc.h
+++ b/include/crc.h
@@ -1,12 +1,14 @@
#ifndef CRC_H
#define CRC_H
+#ifdef __AVR__
#include <util/crc16.h>
-
static inline
uint16_t crc16(uint16_t crc, uint8_t data)
{
return _crc_ccitt_update(crc, data);
}
+#else /* !__AVR__ */
+#endif /* __AVR__ */
#endif /* CRC_H */
diff --git a/include/debug.h b/include/debug.h
index 7c19e40..8fdc830 100644
--- a/include/debug.h
+++ b/include/debug.h
@@ -3,7 +3,9 @@
#define DEBUG_H_
#include "common.h"
+#ifdef __AVR__
#include <avr/pgmspace.h>
+#endif
#ifdef DEBUG
#define _DEBUG 1
@@ -31,6 +33,8 @@
#endif /* 0 */
+void dump_eep(const uint8_t *addr, unsigned int len, char *title);
+void dump_ram(const uint8_t *addr, unsigned int len, char *title);
void printfreelist(const char * title);
diff --git a/include/i2c.h b/include/i2c.h
new file mode 100644
index 0000000..50b1fd0
--- /dev/null
+++ b/include/i2c.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 Sergey Kubushyn <ksi@koi8.net>
+ * Copyright (C) 2009 - 2013 Heiko Schocher <hs@denx.de>
+ * Changes for multibus/multiadapter I2C support.
+ *
+ * (C) Copyright 2001
+ * Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * The original I2C interface was
+ * (C) 2000 by Paolo Scaffardi (arsenio@tin.it)
+ * AIRVENT SAM s.p.a - RIMINI(ITALY)
+ * but has been changed substantially.
+ */
+
+#ifndef _I2C_H_
+#define _I2C_H_
+
+/*
+ * Configuration items.
+ */
+/* TODO: config.h? */
+#define I2C_RXTX_LEN 128 /* maximum tx/rx buffer length */
+
+
+/*
+ * Initialization, must be called once on start up, may be called
+ * repeatedly to change the speed.
+ */
+void i2c_init(uint32_t speed);
+
+/*
+ * Probe the given I2C chip address. Returns 0 if a chip responded,
+ * not 0 on failure.
+ */
+int i2c_probe(uint8_t chip);
+
+/*
+ * Read/Write interface:
+ * chip: I2C chip address, range 0..127
+ * addr: Memory (register) address within the chip
+ * alen: Number of bytes to use for addr (typically 1, 2 for larger
+ * memories, 0 for register type devices with only one
+ * register)
+ * buffer: Where to read/write the data
+ * len: How many bytes to read/write
+ *
+ * Returns: 0 on success, not 0 on failure
+ */
+int i2c_read(uint8_t chip, unsigned int addr, uint_fast8_t alen,
+ uint8_t *buffer, uint_fast8_t len);
+int i2c_write(uint8_t chip, unsigned int addr, uint_fast8_t alen,
+ uint8_t *buffer, uint_fast8_t len);
+
+/*
+ * Utility routines to read/write registers.
+ */
+uint8_t i2c_reg_read(uint8_t addr, uint8_t reg);
+
+void i2c_reg_write(uint8_t addr, uint8_t reg, uint8_t val);
+
+#endif /* _I2C_H_*/
diff --git a/include/rtc.h b/include/rtc.h
new file mode 100644
index 0000000..15215a9
--- /dev/null
+++ b/include/rtc.h
@@ -0,0 +1,49 @@
+/*
+ * (C) Copyright 2001
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+/*
+ * Generic RTC interface.
+ */
+#ifndef _RTC_H_
+#define _RTC_H_
+
+
+/*
+ * The struct used to pass data from the generic interface code to
+ * the hardware dependend low-level code ande vice versa. Identical
+ * to struct rtc_time used by the Linux kernel.
+ *
+ * Note that there are small but significant differences to the
+ * common "struct time":
+ *
+ * struct time: struct rtc_time:
+ * tm_mon 0 ... 11 1 ... 12
+ * tm_year years since 1900 years since 0
+ */
+
+struct rtc_time {
+ int tm_sec;
+ int tm_min;
+ int tm_hour;
+ int tm_mday;
+ int tm_mon;
+ int tm_year;
+ int tm_wday;
+ int tm_yday;
+ int tm_isdst;
+};
+
+int rtc_get (struct rtc_time *);
+int rtc_set (struct rtc_time *);
+void rtc_reset (void);
+
+void GregorianDay (struct rtc_time *);
+void to_tm (unsigned long, struct rtc_time *);
+unsigned long mktime (unsigned int, unsigned int, unsigned int,
+ unsigned int, unsigned int, unsigned int);
+
+#endif /* _RTC_H_ */