Skip to content

Instantly share code, notes, and snippets.

@Flameeyes
Created August 16, 2020 15:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Flameeyes/b4beb1939bbfd9b85e6a14661271a39e to your computer and use it in GitHub Desktop.
Save Flameeyes/b4beb1939bbfd9b85e6a14661271a39e to your computer and use it in GitHub Desktop.
The changes needed to rewrite Birch Books to ATmega
--- 8051/birch-books.c 2020-08-16 16:01:28.250218679 +0100
+++ atmega48/birch-books.c 2020-08-16 15:27:32.893862293 +0100
@@ -1,38 +1,25 @@
// SPDX-FileCopyrightText: © 2020 The birch-books-smarthome Authors
// SPDX-License-Identifier: MIT
-//
-// Based on CC-0 licensed demo at
-// http://www.colecovision.eu/mcs51/STC89%20DEMO%20BOARD%20LED.shtml
+
+#define F_CPU 1000000
#include <stdbool.h>
-#include <stdint.h>
-#include <at89x52.h>
+#include <avr/interrupt.h>
+#include <avr/io.h>
+#include <util/delay.h>
#include "schedule.h"
-/* We can't use C constants for all of this because SDCC is not able to tell
- * that they are static constants, and sets them in RAM instead.
- */
-
volatile uint16_t ticktime = 0;
volatile bool fastclock = false;
-volatile bool rsttestmode = false;
volatile bool testmode = false;
-#define CFG_P0OUT 0xFF
-// Only 6 bits are configured for output on P2, the other two are inputs.
-#define CFG_P2OUT 0x3F
-
-/* Use a very naive approach for the pressed states — sdcc miscompiles complex
- * boolean operations */
-#define TEST_PRESSED P2_7
-#define FF_PRESSED P2_6
-
-void clockinc(void) __interrupt(5) {
- /* Debounce the Firing flag. */
- TF2 = 0;
+#define TEST_PRESSED (PINB & (1 << PINB1))
+#define FF_PRESSED (PINB & (1 << PINB0))
+
+ISR(TIMER1_COMPA_vect) {
/* If the main loop set the fastclock flag, increase the tick count by 64.
*
@@ -40,24 +27,12 @@
* passed. This "fast forward" should complete the schedule within a minute
* rather than an hour.
*/
- uint8_t tickvalue = 1;
- if (fastclock) {
- tickvalue = 64;
- }
+ uint8_t tickvalue = fastclock ? 64 : 1;
/* We let the ticktime overflow transparently, from 65535 back to 0.
* This simplifies our code quite a bit as we don't need to divide anything.
*/
ticktime += tickvalue;
-
- /* Use the P1 port for debugging, by setting up the output flags based on some
- * of the firmware's internal flags.
- */
- P1_0 = (bool)fastclock;
- P1_1 = (bool)FF_PRESSED;
- P1_2 = (bool)TEST_PRESSED;
- P1_3 = (bool)rsttestmode;
- P1_4 = (bool)testmode;
}
/* Return the internal timer in ticks (1/16th of a second).
@@ -66,59 +41,47 @@
* while interrupts are disabled, to avoid it changing during access.
*/
static uint16_t ticks() {
- EA = 0;
+ cli();
uint16_t ctmp = ticktime;
- EA = 1;
+ sei();
return ctmp;
}
-void main(void) {
- /* Set Timer 2 for auto-reload every 62.5ms.
- *
- * Timer 2 is the only timer with 16-bit auto-reload, making it much easier to
- * set up the timer, as we don't need to manually re-set it.
- *
- * The way you set the T2 timer in 8051-compatible is that you need to set the
- * counter to (65536 - usec), assuming the default behaviour of the timer
- * running at 1MHz (sys_clock/12).
- *
- * The chosen constant (0x0BDB) should execute the timer every 62.5msec, which
- * means it tickets at 1/16th of a second. This allows for a cleaner
- * conversion between ticks and seconds by dividing by 16.
- *
- * You need to set both TH2,TL2 (the current timer) and RCAP2H,RCAP2L (the
- * auto-reset values), to make sure that the timer runs smoothly.
- *
- * Then you need to clear TF2 ("Firing"), both here and in the interrupt
- * routine, and set ET2 ("Enable"), TR2 ("Run"), and EA ("Interrupt Enable").
- */
- TH2 = 0x0B;
- TL2 = 0xDB;
- RCAP2H = 0x0B;
- RCAP2L = 0xDB;
-
- TF2 = 0;
- ET2 = 1;
- TR2 = 1;
- EA = 1;
-
- /* Set the basic output ports to zero, to make sure everything starts off.
- */
- P0 = 0x00;
- P1 = 0x00;
- P2 = 0x00;
+int main() {
+ /* Set ports C (0~5) and D (0~7) for output. */
+ DDRC |= ((1 << DDC0) | (1 << DDC1) | (1 << DDC2) | (1 << DDC3) | (1 << DDC4) |
+ (1 << DDC5));
+ DDRD |= ((1 << DDD0) | (1 << DDD1) | (1 << DDD2) | (1 << DDD3) | (1 << DDD4) |
+ (1 << DDD5) | (1 << DDD6) | (1 << DDD7));
+
+ /* Run a LED self-test at start, for just a second. Do this before setting up
+ * timers, so that the timer starts counting from 0. */
+ PORTC = 0xFF;
+ PORTD = 0xFF;
+ _delay_ms(1000);
+
+ /* Set ports to zero. */
+ PORTC = 0;
+ PORTD = 0;
+
+ /* Set up Timer 1 for 62.5ms. */
+ OCR1A = 62499; // 1/16th of a second at 1MHz clock.
+ TCCR1B |= ((1 << CS10) // Fcpu prescale = 1
+ | (1 << WGM12) // CTC mode
+ );
+ TIMSK1 |= (1 << OCIE1A); // Enable CTC Interrupt
+ sei(); // Enable global interrupts
/* If TEST is pressed at start, consider us in 'self-test mode': chase a
* single room through the output port.
*/
while (TEST_PRESSED) {
- rsttestmode = true;
uint16_t clock_secs = ticks() >> 4;
uint8_t test_index = clock_secs % 10;
- P0 = test_schedule[test_index] & 0xFF;
- P2 = test_schedule[test_index] >> 8;
+ PORTC = test_schedule[test_index] & 0xFF;
+ PORTD = test_schedule[test_index] >> 8;
}
/* This is the main loop for the firmware.
@@ -146,8 +109,8 @@
}
if (testmode) {
- P0 = CFG_P0OUT;
- P2 = CFG_P2OUT;
+ PORTC = 0xFF;
+ PORTD = 0xFF;
} else {
/* The schedule is a 16 "hours" schedule with the two ports setting
* separate environment.
@@ -162,8 +125,8 @@
uint16_t clock_ticks = ticks();
uint8_t virtual_hour = (clock_ticks >> 12) & 0x0F;
- P0 = schedule[virtual_hour] & 0xFF;
- P2 = schedule[virtual_hour] >> 8;
+ PORTC = schedule[virtual_hour] & 0xFF;
+ PORTD = schedule[virtual_hour] >> 8;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment