make it work

This commit is contained in:
2026-05-10 13:50:04 -06:00
parent 79f826176d
commit 19db6ae6ae
11 changed files with 255 additions and 88 deletions
+2
View File
@@ -0,0 +1,2 @@
CompileFlags:
Add: ["-I/opt/avr8-gnu-toolchain-darwin_x86_64/avr/include", "-D__AVR_ATtiny84A__"]
+1
View File
@@ -1 +1,2 @@
a.*
build/
+5 -5
View File
@@ -1,10 +1,10 @@
all: a.bin
all: a.out
a.out: main.c
avr-gcc -g -Os -mmcu=attiny84a $< -o $@
a.out: src/*.c src/*.h
avr-gcc -Wall -g -Os -mmcu=attiny84a src/*.c -o $@
a.bin: a.out
avr-objcopy -O binary $< $@
deploy: a.bin
avrdude -p t84a -c stk500v1 -P/dev/tty.usbmodemflip_Rab3gao3 -U flash:w:$<:r
deploy: a.out
avrdude -p t84a -c stk500v1 -P/dev/tty.usbmodemflip_Rab3gao3 -U flash:w:$<:e
-6
View File
@@ -1,6 +0,0 @@
#include <stdint.h>
#define OSCILLATOR_CALIBRATION 0x76
// #define STARTING_TIME_MS (uint32_t)6*60*1000 // 6 min
#define STARTING_TIME_MS (uint32_t)5*1000 // 5 sec
-77
View File
@@ -1,77 +0,0 @@
#include <stdbool.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "config.h"
#define PLAYER_A_BUTTON 0
#define PLAYER_B_BUTTON 1
uint32_t ms_since_boot = 0;
typedef enum {
STOPPED,
PLAYER_A,
PLAYER_B
} state_type;
state_type state = STOPPED;
uint32_t player_a_timer = STARTING_TIME_MS;
uint32_t player_b_timer = STARTING_TIME_MS;
ISR(TIM1_COMPA_vect)
{
ms_since_boot++;
if(state == PLAYER_A) {
player_a_timer--;
if(player_a_timer == 0) { state = STOPPED; }
} else if(state == PLAYER_B) {
player_b_timer--;
if(player_b_timer == 0) { state = STOPPED; }
}
}
void setupio(void) {
// Set port A to inputs
DDRA = 0x00;
PORTA |= _BV(PORTA0) | _BV(PORTA1); // pull-up
// Set port B to output
DDRB = 0xFF;
}
void setup_clock(void) {
OSCCAL = OSCILLATOR_CALIBRATION;
TCCR1B = 0x09;
OCR1A = 1000;
TIMSK1 = 0x02;
}
bool button_pressed(uint8_t pin) {
return (PINA & (1 << pin)) == 0;
}
int main() {
setupio();
setup_clock();
sei();
while(true) {
cli();
if(button_pressed(PLAYER_A_BUTTON) && player_b_timer != 0) {
state = PLAYER_B;
}
if(button_pressed(PLAYER_B_BUTTON) && player_a_timer != 0) {
state = PLAYER_A;
}
PORTB = 1 << state;
sei();
}
}
+7
View File
@@ -0,0 +1,7 @@
#include "as1115.h"
#include "i2c.h"
int as1115_send_command(uint8_t cmd, uint8_t data) {
uint8_t packet[] = {cmd, data};
return i2c_write(0x00, packet, sizeof packet);
}
+23
View File
@@ -0,0 +1,23 @@
#include <stdint.h>
#define DIGIT_0_REG 0x01
#define DIGIT_1_REG 0x02
#define DIGIT_2_REG 0x03
#define DIGIT_3_REG 0x04
#define DIGIT_4_REG 0x05
#define DIGIT_5_REG 0x06
#define DIGIT_6_REG 0x07
#define DIGIT_7_REG 0x08
#define DECODE_MODE_REG 0x09
#define GLOBAL_INTENSITY_REG 0x0A
#define SCAN_LIMIT_REG 0x0B
#define SHUTDOWN_REG 0x0C
#define SELF_ADDRESSING_REG 0x0D
#define FEATURE_REG 0x0E
#define DISPLAY_TEST_MODE_REG 0x0F
#define DIG_0_1_INTENSITY_REG 0x10
#define DIG_2_3_INTENSITY_REG 0x11
#define DIG_4_5_INTENSITY_REG 0x12
#define DIG_6_7_INTENSITY_REG 0x13
int as1115_send_command(uint8_t cmd, uint8_t data);
+5
View File
@@ -0,0 +1,5 @@
#include <stdint.h>
#define OSCILLATOR_CALIBRATION 0x76
#define STARTING_TIME_MS (uint32_t)10*60*1000 // 10 min
// #define STARTING_TIME_MS (uint32_t)5*1000 // 5 sec
+78
View File
@@ -0,0 +1,78 @@
#include <stdbool.h>
#include <stdint.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "i2c.h"
void i2c_init() {
DDRA |= _BV(SDA_BIT) | _BV(SCL_BIT); // Set SDA and SCL to output
PORTA |= _BV(SDA_BIT) | _BV(SCL_BIT); // Write HIGH to SDA and SCL
}
#define HIGH 1
#define LOW 0
void port_a_put(uint8_t pin, bool value) {
if(value) {
PORTA |= _BV(pin);
DDRA &= ~_BV(pin);
} else {
PORTA &= ~_BV(pin);
DDRA |= _BV(pin);
}
}
int i2c_write_byte(uint8_t byte) {
for (int i = 7; i >= 0; i--) {
port_a_put(SDA_BIT, (byte & 0x80) != 0);
byte <<= 1;
// pulse clock
port_a_put(SCL_BIT, HIGH);
port_a_put(SCL_BIT, LOW);
}
port_a_put(SDA_BIT, HIGH);
// check ACK
port_a_put(SCL_BIT, HIGH);
int ack = PINA & _BV(SDA_BIT);
port_a_put(SCL_BIT, LOW);
return ack;
}
int i2c_write(uint8_t addr, const uint8_t* data, uint8_t len) {
// START condition
port_a_put(SDA_BIT, LOW);
port_a_put(SCL_BIT, LOW);
// write address
int ack = i2c_write_byte(addr);
if (ack) {
port_a_put(SDA_BIT, HIGH);
port_a_put(SCL_BIT, HIGH);
return 1;
}
for (int i = 0; i < len; i++) {
int ack = i2c_write_byte(data[i]);
if (ack) {
port_a_put(SDA_BIT, HIGH);
port_a_put(SCL_BIT, HIGH);
return 1;
}
}
// STOP condition
port_a_put(SCL_BIT, HIGH);
port_a_put(SDA_BIT, HIGH);
return 0;
}
+4
View File
@@ -0,0 +1,4 @@
#include <stdint.h>
void i2c_init();
int i2c_write(uint8_t addr, const uint8_t* data, uint8_t len);
+130
View File
@@ -0,0 +1,130 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "config.h"
#include "as1115.h"
#include "i2c.h"
#define PLAYER_A_BUTTON 0
#define PLAYER_B_BUTTON 1
volatile uint32_t ms_since_boot = 0;
typedef enum {
STOPPED,
PLAYER_A,
PLAYER_B
} state_type;
volatile state_type state = STOPPED;
volatile uint32_t player_a_timer = STARTING_TIME_MS;
volatile uint32_t player_b_timer = STARTING_TIME_MS;
ISR(TIM1_COMPA_vect)
{
ms_since_boot++;
if(state == PLAYER_A) {
player_a_timer--;
if(player_a_timer == 0) { state = STOPPED; }
} else if(state == PLAYER_B) {
player_b_timer--;
if(player_b_timer == 0) { state = STOPPED; }
}
}
void setupio(void) {
// Set port A to inputs
DDRA = 0x00;
PORTA |= _BV(PORTA0) | _BV(PORTA1); // pull-up
// Set port B to output
DDRB = 0xFF;
}
void setup_clock(void) {
OSCCAL = OSCILLATOR_CALIBRATION;
TCCR1B = 0x09;
OCR1A = 1000;
TIMSK1 = 0x02;
}
bool button_pressed(uint8_t pin) {
return (PINA & (1 << pin)) == 0;
}
#define DISPLAY_LEFT 0
#define DISPLAY_RIGHT 1
void render_timer(unsigned long ms, uint8_t display) {
uint8_t digits[4] = {0xFF, 0xFF, 0xFF, 0xFF};
if (ms == 0) {
memset(&digits, 0x0A, 4); // show all dashes
goto render;
}
uint8_t sec = (ms + (1000 - 1)) / 1000 % 60;
uint8_t min = (ms + (1000 - 1)) / 1000 / 60;
digits[3] = sec % 10;
digits[2] = sec / 10;
if (min != 0) {
digits[1] = min % 10;
if (min >= 10) {
digits[0] = min / 10;
}
}
render:
switch(display) {
case DISPLAY_LEFT:
as1115_send_command(0x01, digits[0]);
as1115_send_command(0x02, digits[1]);
as1115_send_command(0x03, digits[2]);
as1115_send_command(0x04, digits[3]);
break;
case DISPLAY_RIGHT:
as1115_send_command(0x05, digits[0]);
as1115_send_command(0x06, digits[1]);
as1115_send_command(0x07, digits[2]);
as1115_send_command(0x08, digits[3]);
break;
}
}
int main() {
cli();
setupio();
i2c_init();
setup_clock();
sei();
render_timer(STARTING_TIME_MS, DISPLAY_LEFT);
render_timer(STARTING_TIME_MS, DISPLAY_RIGHT);
as1115_send_command(SHUTDOWN_REG, 0x01); // turn on
as1115_send_command(DECODE_MODE_REG, 0xFF); // decode
as1115_send_command(SCAN_LIMIT_REG, 0x07); // enable all digits
while(true) {
render_timer(player_a_timer, DISPLAY_LEFT);
render_timer(player_b_timer, DISPLAY_RIGHT);
cli();
if(button_pressed(PLAYER_A_BUTTON) && player_b_timer != 0 && player_a_timer != 0) {
state = PLAYER_B;
}
if(button_pressed(PLAYER_B_BUTTON) && player_b_timer != 0 && player_a_timer != 0) {
state = PLAYER_A;
}
sei();
}
}