aboutsummaryrefslogtreecommitdiff
path: root/code/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'code/src/main.c')
-rw-r--r--code/src/main.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/code/src/main.c b/code/src/main.c
new file mode 100644
index 0000000..de2d35f
--- /dev/null
+++ b/code/src/main.c
@@ -0,0 +1,133 @@
+/*
+ Copyright 2021, tslil clingman
+
+ 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 3 of the License.
+
+ 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, see <https://www.gnu.org/licenses/>.
+*/
+
+#include <stdint.h>
+#include <avr/pgmspace.h>
+#include <util/delay.h>
+
+#include "TinyI2CMaster.h"
+
+#include "font.h"
+
+#define DISP1_ADDR 0x3C
+#define DISP2_ADDR 0x3D
+#define RTC_ADDR 0x68
+
+// -----------------------------------------------------------------------------
+// SSD1306 driver
+
+#define INIT_LEN 7
+PROGMEM const uint8_t init[INIT_LEN] = {0xAE, // display off
+ 0x8D, // charge pump
+ 0x14,
+ 0xAF, // display on
+ 0x20,
+ 0xA6, // Normal display
+ 0x02 // page address mode
+};
+
+void disp_init(const uint8_t addr, const uint8_t upside_down) {
+ i2c_start(addr,0);
+ i2c_write(0x00);
+ for (uint8_t k = 0; k<INIT_LEN; k++)
+ i2c_write(pgm_read_byte(&init[k]));
+ if (upside_down) {
+ i2c_write(0xA1);
+ i2c_write(0xC8);
+ }
+ i2c_stop();
+}
+
+void disp_draw_digit(const uint8_t addr, const uint8_t d, const uint8_t off) {
+ for (uint8_t y=0; y<8; y++, i2c_stop()) {
+ // Set cursor
+ i2c_start(addr,0);
+ i2c_write(0x00);
+ // Page select
+ i2c_write(0xB0|y); // 0<y<8 anyway
+ // Column select specialised to halves of screen
+ i2c_write(0); // 0..15, lower bits
+ i2c_write(0x10|off); // 0x10 .. 0x14, upper bits
+ // Prepare to draw
+ i2c_start(addr,0);
+ i2c_write(0x40);
+ for (uint8_t x=0; x<4; x++) {
+ // Read the byte
+ uint8_t b = pgm_read_byte(&font[d*32+(y*4+x)]);
+ // Unpack and draw it
+ for (uint8_t j=0; j<4; j++, b>>=2)
+ for (uint8_t l=0; l<4; l++)
+ i2c_write(((b&1)?0xF0:0x00)|
+ ((b&2)?0x0F:0x00));
+ }
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Interface
+
+uint8_t bcd_to_dec(uint8_t k) { return (((k)/16)*10) + ((k)%16); }
+
+uint8_t dec_to_bcd(uint8_t k) { return (((k)/10)*16) + ((k)%10); }
+
+int main(void) {
+ // Init screens
+ i2c_init();
+ disp_init(DISP1_ADDR,1);
+ disp_init(DISP2_ADDR,0);
+ // Setup input pullups
+ DDRA = ~(_BV(DDA0) | _BV(DDA1));
+ PORTA = _BV(DDA0) | _BV(DDA1);
+ // Tracking state
+ uint8_t input = 0, hours = 99, minutes = 99;
+ while(1) {
+ // Increase hours?
+ if (!(PINA&_BV(DDA0))) {
+ if ((hours+=1)>23) hours=0;
+ input = 1;
+ }
+ // Increase minutes?
+ if (!(PINA&_BV(DDA1))) {
+ if ((minutes+=1)>59) minutes=0;
+ input = 1;
+ }
+ // Store changes
+ if (input) {
+ input = 0;
+ i2c_start(RTC_ADDR,0);
+ i2c_write(0);
+ i2c_write(0); // Seconds
+ i2c_write(dec_to_bcd(minutes));
+ i2c_write(dec_to_bcd(hours));
+ i2c_stop();
+ }
+ // Draw minutes
+ disp_draw_digit(DISP1_ADDR, (minutes/10)%10, 0);
+ disp_draw_digit(DISP1_ADDR, (minutes%10), 4);
+ // Draw hours
+ disp_draw_digit(DISP2_ADDR, (hours/10)%10, 0);
+ disp_draw_digit(DISP2_ADDR, (hours%10), 4);
+ // Poll for update
+ i2c_start(RTC_ADDR,0);
+ i2c_write(1);
+ i2c_start(RTC_ADDR,2);
+ minutes = bcd_to_dec(i2c_read());
+ hours = bcd_to_dec(i2c_read());
+ i2c_stop();
+ // Delay
+ _delay_ms(100);
+ }
+ return 0;
+}