initial commit

This commit is contained in:
deneb 2025-02-13 00:56:59 +01:00
commit eeabd07d50
12 changed files with 12071 additions and 0 deletions

6
.gitmodules vendored Normal file
View file

@ -0,0 +1,6 @@
[submodule "code/avr-ds3231"]
path = code/avr-ds3231
url = https://github.com/ecnx/avr-ds3231.git
[submodule "code/avr-i2c"]
path = code/avr-i2c
url = https://github.com/ecnx/avr-i2c.git

10
code/.clangd Normal file
View file

@ -0,0 +1,10 @@
CompileFlags:
Compiler: avr-gcc
Add: [
-mmcu=atmega48,
-I/usr/avr/include,
-Iavr-i2c,
-Iavr-ds3231,
-Wall,
-Wextra,
]

6
code/Makefile Normal file
View file

@ -0,0 +1,6 @@
compile:
avr-gcc -DF_CPU=8000000 -Iavr-i2c -Iavr-ds3231 -Os -flto -mmcu=atmega48 main.c avr-ds3231/ds3231.c avr-i2c/i2c.c -o nixie
avr-size -A nixie
flash:
avrdude -p m48 -c usbasp -P usb -B4 -U flash:w:nixie

1
code/avr-ds3231 Submodule

@ -0,0 +1 @@
Subproject commit 8606f788c818af62633931564b64dbeb077357ee

1
code/avr-i2c Submodule

@ -0,0 +1 @@
Subproject commit 474767e1c0ed2cd1d8e30828b769143c9a5547bb

253
code/main.c Normal file
View file

@ -0,0 +1,253 @@
#include <avr/interrupt.h>
#include <avr/io.h>
#include <stdbool.h>
#include <stdint.h>
#include <util/delay.h>
#include <ds3231.h>
#ifndef bit
#define bit(b) (1 << b)
#endif
/**
* high nibble: BCD output digit
* low nibble: button inputs, leftmost button is least significant bit
*/
#define BCD_BTN_PORT PORTD
#define BTN_PIN PIND
#define BCD_BTN_DDR DDRD
/**
* low nibble: nixie anode power
* one bit per digit, leftmost nixie is least significant bit
*/
#define NIXIE_PORT PORTC
#define NIXIE_DDR DDRC
/**
* other control pins
* - bit 0: K155ID1 power (active high)
* - bit 1: HV shutdown (active high; not connected)
* - bit 2: dots anode power
*/
#define ETC_PORT PORTB
#define ETC_DDR DDRB
#define ETC_ID1 bit(0)
#define ETC_HVSD bit(1)
#define ETC_DOTS bit(2)
#define READ_RTC_INTERVAL_MS 71
#define BTN_DEBOUNCE_MS 50
// runtime variables
volatile uint32_t msec = 0;
struct ds3231_clock_t clock = {0};
bool flag_read_rtc = true;
bool set_mode_active = false;
uint8_t set_mode_idx = 0;
uint8_t set_mode_digits[4] = {0};
uint32_t button_inhibit_ms[4] = {0};
bool flag_button_pressed[4] = {false};
bool is_button_pressed(uint8_t id) {
return (BTN_PIN & bit(id)) == 0;
}
void handle_button_press(uint8_t id) {
switch (id) {
// toggle set mode; set new time
case 0: {
if (set_mode_active) {
clock.hours = set_mode_digits[0] * 10 + set_mode_digits[1];
clock.minutes = set_mode_digits[2] * 10 + set_mode_digits[3];
clock.seconds = 0;
ds3231_write_clock(&clock);
set_mode_active = false;
} else {
ds3231_read_clock(&clock);
set_mode_digits[0] = clock.hours / 10;
set_mode_digits[1] = clock.hours % 10;
set_mode_digits[2] = clock.minutes / 10;
set_mode_digits[3] = clock.minutes % 10;
set_mode_active = true;
}
break;
}
// move between digits
case 1: {
if (set_mode_active) {
set_mode_idx = (set_mode_idx + 1) % 4;
}
break;
}
// up
case 2: {
if (set_mode_active) {
set_mode_digits[set_mode_idx]++;
if (set_mode_idx == 0) {
set_mode_digits[set_mode_idx] %= 3;
} else if (set_mode_idx == 1 && set_mode_digits[0] == 2) {
set_mode_digits[set_mode_idx] %= 4;
} else if (set_mode_idx == 2) {
set_mode_digits[set_mode_idx] %= 6;
} else {
set_mode_digits[set_mode_idx] %= 10;
}
}
break;
}
// down
case 3: {
if (set_mode_active) {
set_mode_digits[set_mode_idx]--;
if (set_mode_idx == 0) {
set_mode_digits[set_mode_idx] += 3;
set_mode_digits[set_mode_idx] %= 3;
} else if (set_mode_idx == 1 && set_mode_digits[0] == 2) {
set_mode_digits[set_mode_idx] += 4;
set_mode_digits[set_mode_idx] %= 4;
} else if (set_mode_idx == 2) {
set_mode_digits[set_mode_idx] += 6;
set_mode_digits[set_mode_idx] %= 6;
} else {
set_mode_digits[set_mode_idx] += 10;
set_mode_digits[set_mode_idx] %= 10;
}
}
break;
}
}
}
/**
* sets up timer0 for 1ms interrupts
*/
void timer_setup() {
// Set the compare value to 249 to make the timer trigger once every 250 clock cycles.
TCCR0A = bit(WGM01);
OCR0A = 124;
// Enable the timer and interrupts
TIMSK0 |= bit(OCIE0A);
sei();
// Set clock prescaler for timer0 to 1/64 (= 250kHz)
// This also starts the timer
TCCR0B |= 0b00000011;
// TCCR0B |= 0b00000101;
}
bool error = false;
int main() {
BCD_BTN_DDR = 0xf0; // set BCD pins as output
BCD_BTN_PORT = 0x0f; // enable input pull-ups
NIXIE_DDR = 0x0f;
ETC_DDR = 0x07;
ETC_PORT |= ETC_ID1; // enable power to K155ID1
timer_setup();
// button interrupts
PCMSK2 |= 0x0f; // enable pin change interrupts on button pins
PCICR |= bit(PCIE2);
for (;;) {
if (flag_read_rtc) {
flag_read_rtc = false;
if (ds3231_read_clock(&clock) != 0) {
error = true;
}
if (clock.seconds % 2 == 0) {
ETC_PORT &= ~ETC_DOTS;
} else {
ETC_PORT |= ETC_DOTS;
}
}
for (int i = 0; i < 4; i++) {
if (flag_button_pressed[i]) {
flag_button_pressed[i] = false;
handle_button_press(i);
}
}
}
}
void clear_digits() {
NIXIE_PORT = 0;
BCD_BTN_PORT &= ~(0xf0);
}
void show_digit(uint8_t digit, uint8_t nixie_idx) {
BCD_BTN_PORT &= ~(0xf0);
BCD_BTN_PORT |= digit << 4;
NIXIE_PORT = bit(nixie_idx);
}
// millisecond counter
ISR(TIMER0_COMPA_vect) {
// time
msec++;
if (msec % READ_RTC_INTERVAL_MS == 0) {
flag_read_rtc = true;
}
// dots
if (set_mode_active && msec % 250 == 0) {
ETC_PORT ^= ETC_DOTS;
}
// digits
uint8_t nixie_idx = msec % 4;
if (set_mode_active) {
if (nixie_idx == set_mode_idx || msec % 16 < 4) {
show_digit(set_mode_digits[nixie_idx], nixie_idx);
} else {
clear_digits();
}
} else {
uint32_t digit = nixie_idx == 0 ? clock.hours / 10
: nixie_idx == 1 ? clock.hours % 10
: nixie_idx == 2 ? clock.minutes / 10
: clock.minutes % 10;
show_digit(digit, nixie_idx);
}
// button debounce
for (int i = 0; i < 4; i++) {
if (button_inhibit_ms[i] > 0) {
button_inhibit_ms[i]--;
}
}
}
// button press detection
ISR(PCINT2_vect) {
for (int i = 0; i < 4; i++) {
if (is_button_pressed(i) && button_inhibit_ms[i] == 0) {
flag_button_pressed[i] = true;
button_inhibit_ms[i] = BTN_DEBOUNCE_MS;
}
}
}

BIN
code/nixie Executable file

Binary file not shown.

1
kicad/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
nixie-backups/*

2
kicad/nixie.kicad_pcb Normal file
View file

@ -0,0 +1,2 @@
(kicad_pcb (version 20240108) (generator "pcbnew") (generator_version "8.0")
)

83
kicad/nixie.kicad_prl Normal file
View file

@ -0,0 +1,83 @@
{
"board": {
"active_layer": 0,
"active_layer_preset": "",
"auto_track_width": true,
"hidden_netclasses": [],
"hidden_nets": [],
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
"pads": 1.0,
"tracks": 1.0,
"vias": 1.0,
"zones": 0.6
},
"selection_filter": {
"dimensions": true,
"footprints": true,
"graphics": true,
"keepouts": true,
"lockedItems": false,
"otherItems": true,
"pads": true,
"text": true,
"tracks": true,
"vias": true,
"zones": true
},
"visible_items": [
0,
1,
2,
3,
4,
5,
8,
9,
10,
11,
12,
13,
15,
16,
17,
18,
19,
20,
21,
22,
23,
24,
25,
26,
27,
28,
29,
30,
32,
33,
34,
35,
36,
39,
40
],
"visible_layers": "fffffff_ffffffff",
"zone_display_mode": 0
},
"git": {
"repo_password": "",
"repo_type": "",
"repo_username": "",
"ssh_key": ""
},
"meta": {
"filename": "nixie.kicad_prl",
"version": 3
},
"project": {
"files": []
}
}

392
kicad/nixie.kicad_pro Normal file
View file

@ -0,0 +1,392 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"rules": {},
"track_widths": [],
"via_dimensions": []
},
"ipc2581": {
"dist": "",
"distpn": "",
"internal_id": "",
"mfg": "",
"mpn": ""
},
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"conflicting_netclasses": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"endpoint_off_grid": "warning",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"missing_bidi_pin": "warning",
"missing_input_pin": "warning",
"missing_power_pin": "error",
"missing_unit": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"simulation_model_issue": "ignore",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "nixie.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.2,
"via_diameter": 0.6,
"via_drill": 0.3,
"wire_width": 6
}
],
"meta": {
"version": 3
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"plot": "",
"pos_files": "",
"specctra_dsn": "",
"step": "",
"svg": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",
"keep_line_breaks": false,
"keep_tabs": false,
"name": "CSV",
"ref_delimiter": ",",
"ref_range_delimiter": "",
"string_delimiter": "\""
},
"bom_presets": [],
"bom_settings": {
"exclude_dnp": false,
"fields_ordered": [
{
"group_by": false,
"label": "Reference",
"name": "Reference",
"show": true
},
{
"group_by": true,
"label": "Value",
"name": "Value",
"show": true
},
{
"group_by": false,
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Footprint",
"name": "Footprint",
"show": true
},
{
"group_by": false,
"label": "Qty",
"name": "${QUANTITY}",
"show": true
},
{
"group_by": true,
"label": "DNP",
"name": "${DNP}",
"show": true
}
],
"filter_string": "",
"group_symbols": true,
"name": "Grouped By Value",
"sort_asc": true,
"sort_field": "Reference"
},
"connection_grid_size": 50.0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"operating_point_overlay_i_precision": 3,
"operating_point_overlay_i_range": "~A",
"operating_point_overlay_v_precision": 3,
"operating_point_overlay_v_range": "~V",
"overbar_offset_ratio": 1.23,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"page_layout_descr_file": "",
"plot_directory": "",
"spice_current_sheet_as_root": false,
"spice_external_command": "spice \"%I\"",
"spice_model_current_sheet_as_root": true,
"spice_save_all_currents": false,
"spice_save_all_dissipations": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"7c670055-6eae-4e4c-be40-3c8d8a0ecd74",
"Root"
]
],
"text_variables": {}
}

11316
kicad/nixie.kicad_sch Normal file

File diff suppressed because it is too large Load diff