diff options
| author | tslil clingman <> | 2021-09-29 14:33:21 -0400 |
|---|---|---|
| committer | tslil clingman <> | 2021-09-29 14:33:21 -0400 |
| commit | 328474f555ab0958e0921c7d04d8d7dbb1789bc4 (patch) | |
| tree | 422a4afe050471fde632d6075a983f7d588ea829 /code/src/main.c | |
Diffstat (limited to 'code/src/main.c')
| -rw-r--r-- | code/src/main.c | 133 |
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; +} |
