mirror of
https://github.com/quorten/macsehw.git
synced 2024-06-10 11:29:31 +00:00
Fix a bunch of issues with ATTiny85 RTC implementation.
Decouple from Arduino libraries, use a small header file instead. Now compiles with plain avr-gcc. Still needs more work.
This commit is contained in:
parent
097581b80a
commit
1f8d0f8163
|
@ -1,6 +1,9 @@
|
||||||
|
#include <avr/io.h>
|
||||||
#include <avr/wdt.h>
|
#include <avr/wdt.h>
|
||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
#include <EEPROM.h>
|
#include <avr/sleep.h>
|
||||||
|
|
||||||
|
#include "arduino_sdef.h"
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* *
|
* *
|
||||||
|
@ -17,101 +20,53 @@
|
||||||
* *
|
* *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
const int ONE_SEC_PIN = 1; // A 1Hz square wave on PB5
|
/*********************************************
|
||||||
const int RTC_ENABLE_PIN = 5; // Active low chip enable on PB0
|
* ATMEL ATTINY85 / ARDUINO *
|
||||||
const int SERIAL_DATA_PIN = 6; // Bi-directional serial data line on PB1
|
* *
|
||||||
const int SERIAL_CLOCK_PIN = 7; // Serial clock input on PB2
|
* +-\/-+ *
|
||||||
|
* Ain0 (D 5) PB5 1| |8 Vcc *
|
||||||
|
* Ain3 (D 3) PB3 2| |7 PB2 (D 2) Ain1 *
|
||||||
|
* Ain2 (D 4) PB4 3| |6 PB1 (D 1) pwm1 *
|
||||||
|
* GND 4| |5 PB0 (D 0) pwm0 *
|
||||||
|
* +----+ *
|
||||||
|
*********************************************/
|
||||||
|
|
||||||
const int PRAM_SIZE = 256; // Mac Plus used the xPRAM chip with 256 bytes, time is a separate 4 additional bytes
|
const int ONE_SEC_PIN = 5; // A 1Hz square wave on PB5
|
||||||
//const int PRAM_SIZE = 20; // Models earlier than the Plus had 20 bytes of PRAM
|
const int RTC_ENABLE_PIN = 0; // Active low chip enable on PB0
|
||||||
|
const int SERIAL_DATA_PIN = 1; // Bi-directional serial data line on PB1
|
||||||
|
const int SERIAL_CLOCK_PIN = 2; // Serial clock input on PB2
|
||||||
|
|
||||||
|
#if NoXPRAM
|
||||||
|
// Models earlier than the Plus had 20 bytes of PRAM
|
||||||
|
const int PRAM_SIZE = 20;
|
||||||
|
#else
|
||||||
|
// Mac Plus used the xPRAM chip with 256 bytes
|
||||||
|
const int PRAM_SIZE = 256;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
volatile boolean lastSerClock = 0;
|
||||||
volatile byte serialBitNum = 0;
|
volatile byte serialBitNum = 0;
|
||||||
volatile byte address = 0;
|
volatile byte address = 0;
|
||||||
volatile byte serialData = 0;
|
volatile byte serialData = 0;
|
||||||
|
|
||||||
|
enum SerialStateType { SERIAL_DISABLED, RECEIVING_COMMAND,
|
||||||
enum SerialStateType { SERIAL_DISABLED, RECEIVING_COMMAND, SENDING_DATA, RECEIVING_DATA };
|
SENDING_DATA, RECEIVING_DATA };
|
||||||
volatile SerialStateType serialState = SERIAL_DISABLED;
|
volatile SerialStateType serialState = SERIAL_DISABLED;
|
||||||
|
|
||||||
volatile unsigned long seconds = 0;
|
// Number of seconds since midnight, January 1, 1904. Clock is
|
||||||
volatile byte pram[PRAM_SIZE] = {}; // 256 Bytes of PRAM, the first four of which count the number of seconds since 1/1/1904
|
// initialized to January 1st, 1984? Or is this done by the ROM when
|
||||||
|
// the validity status is invalid?
|
||||||
/*
|
volatile unsigned long seconds = 60 * 60 * 24 * (365 * 4 + 1) * 20;
|
||||||
* The following is potential locations of various bits of PRAM data, none of this is in any way certain:
|
volatile byte pram[PRAM_SIZE] = {}; // PRAM initialized as zeroed data
|
||||||
* Sound volume is in pram[0x08]
|
|
||||||
* Alert sound is in param[0x7c - 0x7d]
|
|
||||||
* Machine location and timezone is in pram[0xE4 - 0xEF]
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* An interrupt to both increment the seconds counter and generate the square wave
|
|
||||||
*/
|
|
||||||
void halfSecondInterrupt() {
|
|
||||||
PINB = 1<<PINB0; // Flip the one-second pin
|
|
||||||
if(!(PINB & (1<<PINB0))) { // If the one-second pin is low
|
|
||||||
seconds++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The actual serial communication can be done in the main loop, this way the clock still gets incremented
|
|
||||||
*/
|
|
||||||
void handleRTCEnableInterrupt() {
|
|
||||||
serialBitNum = 0;
|
|
||||||
address = 0;
|
|
||||||
serialData = 0;
|
|
||||||
if(!(PINB&(1<<RTC_ENABLE_PIN))){ // Simulates a falling interrupt
|
|
||||||
serialState = RECEIVING_COMMAND;
|
|
||||||
// enableRTC = true;
|
|
||||||
} else { // Simulates a rising interrupt
|
|
||||||
clearState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearState() {
|
|
||||||
DDRB &= ~(1<<DDB1); // Return the pin to input mode
|
|
||||||
PORTB |= (1<<PORTB1); // Set pullup resistor
|
|
||||||
serialState = SERIAL_DISABLED;
|
|
||||||
serialBitNum = 0;
|
|
||||||
address = 0;
|
|
||||||
serialData = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The ATtiny has EEPROM, lets use it to store the contents of PRAM in case of power failure,
|
|
||||||
* this is an improvement over the original, still a good idea to keep the chip powered by a
|
|
||||||
* battery or supercapacitor so that the clock continues to advance.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void savePRAM() {
|
|
||||||
noInterrupts(); // Don't update the seconds counter while we're saving it to ROM, probably unnecessary
|
|
||||||
for(int i = 0; i < 4; i++) {
|
|
||||||
EEPROM.update(i,(seconds>>(8*i))&0xff);
|
|
||||||
}
|
|
||||||
interrupts(); // Go ahead and interrupt us while we save the rest
|
|
||||||
for(int i = 0; i < PRAM_SIZE; i++) {
|
|
||||||
EEPROM.update(i+4,pram[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void goToSleep() {
|
|
||||||
bitClear(MCUCR,SM0); // The two SM bits must be set to 00 to enter idle mode
|
|
||||||
bitClear(MCUCR,SM1); // Sleeping in other modes will disable the timer
|
|
||||||
bitSet(MCUCR,SE);
|
|
||||||
__asm__("sleep" "\n\t");
|
|
||||||
bitClear(MCUCR,SE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
noInterrupts(); // Disable interrupts while we set things up
|
noInterrupts(); // Disable interrupts while we set things up
|
||||||
|
|
||||||
pinMode(ONE_SEC_PIN, OUTPUT); // The 1Hz square wave (used, I think, for interrupts elsewhere in the system)
|
pinModePB(ONE_SEC_PIN, OUTPUT); // The 1Hz square wave (used, I think, for interrupts elsewhere in the system)
|
||||||
pinMode(RTC_ENABLE_PIN, INPUT_PULLUP); // The processor pulls this pin low when it wants access
|
pinModePB(RTC_ENABLE_PIN, INPUT_PULLUP); // The processor pulls this pin low when it wants access
|
||||||
pinMode(SERIAL_CLOCK_PIN, INPUT_PULLUP); // The serial clock is driven by the processor
|
pinModePB(SERIAL_CLOCK_PIN, INPUT_PULLUP); // The serial clock is driven by the processor
|
||||||
pinMode(SERIAL_DATA_PIN, INPUT_PULLUP); // We'll need to switch this to output when sending data
|
pinModePB(SERIAL_DATA_PIN, INPUT_PULLUP); // We'll need to switch this to output when sending data
|
||||||
|
|
||||||
wdt_disable(); // Disable watchdog
|
wdt_disable(); // Disable watchdog
|
||||||
bitSet(ACSR,ACD); // Disable Analog Comparator, don't need it, saves power
|
bitSet(ACSR,ACD); // Disable Analog Comparator, don't need it, saves power
|
||||||
bitSet(PRR,PRTIM1); // Disable Timer 1, only using Timer 0, Timer 1 uses around ten times as much current
|
bitSet(PRR,PRTIM1); // Disable Timer 1, only using Timer 0, Timer 1 uses around ten times as much current
|
||||||
|
@ -121,38 +76,77 @@ void setup() {
|
||||||
bitSet(GIMSK,PCIE); // Pin Change Interrupt Enable
|
bitSet(GIMSK,PCIE); // Pin Change Interrupt Enable
|
||||||
bitSet(PCMSK,PCINT0); // turn on RTC enable interrupt
|
bitSet(PCMSK,PCINT0); // turn on RTC enable interrupt
|
||||||
|
|
||||||
// for(int i = 0; i < 4; i++) {
|
|
||||||
// seconds += ((unsigned long)EEPROM.read(i))<<(8*i);
|
|
||||||
// }
|
|
||||||
// for(int i = 0; i < PRAM_SIZE; i--) { // Preload PRAM with saved values
|
|
||||||
// pram[i] = EEPROM.read(i+4);
|
|
||||||
// }
|
|
||||||
|
|
||||||
//set up timer
|
//set up timer
|
||||||
bitSet(GTCCR,TSM); // Turns off timers while we set it up
|
bitSet(GTCCR,TSM); // Turns off timers while we set it up
|
||||||
bitSet(TIMSK,TOIE0); // Set Timer/Counter0 Overflow Interrupt Enable
|
bitSet(TIMSK,TOIE0); // Set Timer/Counter0 Overflow Interrupt Enable
|
||||||
TCCR0B = 0b111; // Set prescaler, 32,768Hz/64 = 512Hz, fills up the 8-bit counter (256) once every half second
|
TCCR0B = 0b111; // Set prescaler, 32,768Hz/64 = 512Hz, fills up the 8-bit counter (256) once every half second
|
||||||
TCNT0 = 0; // Clear the counter
|
TCNT0 = 0; // Clear the counter
|
||||||
bitClear(GTCCR,TSM); // Turns timers back on
|
bitClear(GTCCR,TSM); // Turns timers back on
|
||||||
|
|
||||||
interrupts(); //We're done setting up, enable those interrupts again
|
interrupts(); //We're done setting up, enable those interrupts again
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void clearState() {
|
||||||
|
// Return the pin to input mode, set pullup resistor
|
||||||
|
pinModePB(SERIAL_DATA_PIN, INPUT_PULLUP);
|
||||||
|
serialState = SERIAL_DISABLED;
|
||||||
|
lastSerClock = 0;
|
||||||
|
serialBitNum = 0;
|
||||||
|
address = 0;
|
||||||
|
serialData = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An interrupt to both increment the seconds counter and generate the
|
||||||
|
* square wave
|
||||||
|
*/
|
||||||
|
void halfSecondInterrupt() {
|
||||||
|
PINB = 1<<ONE_SEC_PIN; // Flip the one-second pin
|
||||||
|
if(!(PINB&(1<<ONE_SEC_PIN))) { // If the one-second pin is low
|
||||||
|
seconds++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The actual serial communication can be done in the main loop, this
|
||||||
|
* way the clock still gets incremented
|
||||||
|
*/
|
||||||
|
void handleRTCEnableInterrupt() {
|
||||||
|
if(!(PINB&(1<<RTC_ENABLE_PIN))){ // Simulates a falling interrupt
|
||||||
|
serialState = RECEIVING_COMMAND;
|
||||||
|
// enableRTC = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
if(digitalRead(RTC_ENABLE_PIN)) {
|
if((PINB&(1<<RTC_ENABLE_PIN))) {
|
||||||
clearState();
|
clearState();
|
||||||
goToSleep();
|
set_sleep_mode(0); // Sleep mode 0 == default, timers still running.
|
||||||
} else if(digitalRead(SERIAL_CLOCK_PIN)) {
|
sleep_mode();
|
||||||
switch(serialState) {
|
} else {
|
||||||
|
// Compute rising and falling edge trigger flags for the serial
|
||||||
|
// clock.
|
||||||
|
boolean curSerClock = PINB&(1<<SERIAL_CLOCK_PIN);
|
||||||
|
boolean serClockRising = !lastSerClock && curSerClock;
|
||||||
|
boolean serClockFalling = lastSerClock && !curSerClock;
|
||||||
|
lastSerClock = curSerClock;
|
||||||
|
|
||||||
|
// TODO FIXME: We need to implement an artificial delay between
|
||||||
|
// the clock's rising edge and the update of the data line output
|
||||||
|
// because of a bug in the ROM. Is 10 microseconds a good wait
|
||||||
|
// time?
|
||||||
|
|
||||||
|
if(serClockRising) {
|
||||||
|
switch(serialState) {
|
||||||
|
|
||||||
case RECEIVING_COMMAND:
|
case RECEIVING_COMMAND:
|
||||||
bitWrite(address,7-serialBitNum,digitalRead(SERIAL_DATA_PIN));
|
bitWrite(address,7-serialBitNum,digitalReadPB(SERIAL_DATA_PIN));
|
||||||
serialBitNum++;
|
serialBitNum++;
|
||||||
if(serialBitNum > 7) {
|
if(serialBitNum > 7) {
|
||||||
boolean writeRequest = address&(1<<7); // the MSB determines if it's a write request or not
|
boolean writeRequest = address&(1<<7); // the MSB determines if it's a write request or not
|
||||||
address &= ~(1<<7); // Discard the first bit, it's not part of the address
|
address &= ~(1<<7); // Discard the first bit, it's not part of the address
|
||||||
serialBitNum = 0;
|
serialBitNum = 0;
|
||||||
if(writeRequest) {
|
if(writeRequest) {
|
||||||
serialState = RECEIVING_DATA;
|
serialState = RECEIVING_DATA;
|
||||||
serialBitNum = 0;
|
serialBitNum = 0;
|
||||||
} else {
|
} else {
|
||||||
|
@ -163,13 +157,13 @@ void loop() {
|
||||||
}
|
}
|
||||||
serialState = SENDING_DATA;
|
serialState = SENDING_DATA;
|
||||||
serialBitNum = 0;
|
serialBitNum = 0;
|
||||||
pinMode(SERIAL_DATA_PIN, OUTPUT); // Set the pin to output mode
|
pinModePB(SERIAL_DATA_PIN, OUTPUT); // Set the pin to output mode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RECEIVING_DATA:
|
case RECEIVING_DATA:
|
||||||
bitWrite(serialData,7-serialBitNum,digitalRead(SERIAL_DATA_PIN));
|
bitWrite(serialData,7-serialBitNum,digitalReadPB(SERIAL_DATA_PIN));
|
||||||
serialBitNum++;
|
serialBitNum++;
|
||||||
if(serialBitNum > 7) {
|
if(serialBitNum > 7) {
|
||||||
if(address < 4) {
|
if(address < 4) {
|
||||||
|
@ -179,17 +173,17 @@ void loop() {
|
||||||
} else {
|
} else {
|
||||||
pram[address] = serialData;
|
pram[address] = serialData;
|
||||||
}
|
}
|
||||||
// savePRAM();
|
|
||||||
clearState();
|
clearState();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SENDING_DATA:
|
case SENDING_DATA:
|
||||||
digitalWrite(SERIAL_DATA_PIN,bitRead(serialData,7-serialBitNum));
|
digitalWritePB(SERIAL_DATA_PIN,bitRead(serialData,7-serialBitNum));
|
||||||
serialBitNum++;
|
serialBitNum++;
|
||||||
if(serialBitNum > 7) {
|
if(serialBitNum > 7) {
|
||||||
clearState();
|
clearState();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -204,3 +198,14 @@ ISR(PCINT0_vect) {
|
||||||
ISR(TIMER0_OVF) {
|
ISR(TIMER0_OVF) {
|
||||||
halfSecondInterrupt();
|
halfSecondInterrupt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Arduino main function.
|
||||||
|
int main(void) {
|
||||||
|
setup();
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
78
firmware/rtc/arduino_sdef.h
Normal file
78
firmware/rtc/arduino_sdef.h
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
#ifndef ARDUINO_SDEF_H
|
||||||
|
#define ARDUINO_SDEF_H
|
||||||
|
|
||||||
|
/********************************************************************/
|
||||||
|
// Simplified Arduino.h definitions.
|
||||||
|
typedef bool boolean;
|
||||||
|
typedef uint8_t byte;
|
||||||
|
|
||||||
|
#define HIGH 0x1
|
||||||
|
#define LOW 0x0
|
||||||
|
|
||||||
|
#define INPUT 0x0
|
||||||
|
#define OUTPUT 0x1
|
||||||
|
#define INPUT_PULLUP 0x2
|
||||||
|
|
||||||
|
#define interrupts() sei()
|
||||||
|
#define noInterrupts() cli()
|
||||||
|
|
||||||
|
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
|
||||||
|
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
|
||||||
|
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
|
||||||
|
#define bitWrite(value, bit, bitvalue) ((bitvalue) ? bitSet(value, bit) : bitClear(value, bit))
|
||||||
|
// END simplified Arduino.h definitions.
|
||||||
|
|
||||||
|
/********************************************************************/
|
||||||
|
// Simplified wiring_digital.c definitions.
|
||||||
|
// Only suitable for single source code file projects.
|
||||||
|
|
||||||
|
void pinModePB(uint8_t portbit, uint8_t mode)
|
||||||
|
{
|
||||||
|
uint8_t bit = _BV(portbit);
|
||||||
|
|
||||||
|
if (mode == INPUT) {
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
DDRB &= ~bit;
|
||||||
|
PORTB &= ~bit;
|
||||||
|
SREG = oldSREG;
|
||||||
|
} else if (mode == INPUT_PULLUP) {
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
DDRB &= ~bit;
|
||||||
|
PORTB |= bit;
|
||||||
|
SREG = oldSREG;
|
||||||
|
} else {
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
DDRB |= bit;
|
||||||
|
SREG = oldSREG;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void digitalWritePB(uint8_t portbit, uint8_t val)
|
||||||
|
{
|
||||||
|
uint8_t bit = _BV(portbit);
|
||||||
|
|
||||||
|
uint8_t oldSREG = SREG;
|
||||||
|
cli();
|
||||||
|
|
||||||
|
if (val == LOW) {
|
||||||
|
PORTB &= ~bit;
|
||||||
|
} else {
|
||||||
|
PORTB |= bit;
|
||||||
|
}
|
||||||
|
|
||||||
|
SREG = oldSREG;
|
||||||
|
}
|
||||||
|
|
||||||
|
int digitalReadPB(uint8_t portbit)
|
||||||
|
{
|
||||||
|
uint8_t bit = _BV(portbit);
|
||||||
|
|
||||||
|
if (PINB & bit) return HIGH;
|
||||||
|
return LOW;
|
||||||
|
}
|
||||||
|
// END simplified wiring_digital.c definitions.
|
||||||
|
|
||||||
|
#endif /* not ARDUINO_SDEF_H */
|
3
firmware/rtc/domake.sh
Executable file
3
firmware/rtc/domake.sh
Executable file
|
@ -0,0 +1,3 @@
|
||||||
|
#! /bin/sh
|
||||||
|
avr-gcc -c -Os -mmcu=attiny85 MacRTC.cpp
|
||||||
|
avr-gcc -mmcu=attiny85 -o MacRTC.axf MacRTC.o
|
Loading…
Reference in New Issue
Block a user