Georgs Narbuts 2 weeks ago
committed by GitHub
parent
commit
7427e9ca5f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
15 changed files with 1611 additions and 0 deletions
  1. +21
    -0
      keyboards/kalkulators/chconf.h
  2. +12
    -0
      keyboards/kalkulators/config.h
  3. +23
    -0
      keyboards/kalkulators/halconf.h
  4. +50
    -0
      keyboards/kalkulators/info.json
  5. +471
    -0
      keyboards/kalkulators/kalkulators.c
  6. +31
    -0
      keyboards/kalkulators/kalkulators.h
  7. +274
    -0
      keyboards/kalkulators/keymaps/default/keymap.c
  8. +1
    -0
      keyboards/kalkulators/keymaps/default/rules.mk
  9. +278
    -0
      keyboards/kalkulators/keymaps/via/keymap.c
  10. +2
    -0
      keyboards/kalkulators/keymaps/via/rules.mk
  11. +26
    -0
      keyboards/kalkulators/mcuconf.h
  12. +46
    -0
      keyboards/kalkulators/readme.md
  13. +1
    -0
      keyboards/kalkulators/rules.mk
  14. +319
    -0
      keyboards/kalkulators/tm1638.c
  15. +56
    -0
      keyboards/kalkulators/tm1638.h

+ 21
- 0
keyboards/kalkulators/chconf.h View File

@ -0,0 +1,21 @@
/* Copyright 2024 QMK
*
* 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 2 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#define CH_CFG_ST_FREQUENCY 10000
#include_next <chconf.h>

+ 12
- 0
keyboards/kalkulators/config.h View File

@ -0,0 +1,12 @@
// Copyright 2023 georgsnarbuts (@georgsnarbuts)
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#define TM1638_CLK_PIN A8
#define TM1638_DIO_PIN A9
#define TM1638_STB_PIN B15
#define VBUS_CHECK_PIN B5
#undef PRINTF_SUPPORT_DECIMAL_SPECIFIERS
#define PRINTF_SUPPORT_DECIMAL_SPECIFIERS 1

+ 23
- 0
keyboards/kalkulators/halconf.h View File

@ -0,0 +1,23 @@
/* Copyright 2024 QMK
*
* 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 2 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#define HAL_USE_ADC TRUE
#define HAL_USE_I2C TRUE
#include_next <halconf.h>

+ 50
- 0
keyboards/kalkulators/info.json View File

@ -0,0 +1,50 @@
{
"manufacturer": "georgsnarbuts",
"keyboard_name": "kalkulators",
"maintainer": "georgsnarbuts",
"bootloader": "stm32-dfu",
"diode_direction": "COL2ROW",
"features": {
"bootmagic": true,
"command": true,
"console": true,
"extrakey": true,
"mousekey": true,
"nkro": true
},
"matrix_pins": {
"cols": ["A6", "A5", "A4", "A3"],
"rows": ["A7", "B0", "A2", "A1", "A0"]
},
"processor": "STM32F072",
"url": "",
"usb": {
"device_version": "1.0.0",
"pid": "0x4B4C",
"vid": "0x474E",
"no_startup_check": true
},
"layouts": {
"LAYOUT_calc": {
"layout": [
{"matrix": [0, 0], "x": 0, "y": 0},
{"matrix": [0, 1], "x": 1, "y": 0},
{"matrix": [0, 2], "x": 2, "y": 0},
{"matrix": [0, 3], "x": 3, "y": 0},
{"matrix": [1, 0], "x": 0, "y": 1},
{"matrix": [1, 1], "x": 1, "y": 1},
{"matrix": [1, 2], "x": 2, "y": 1},
{"matrix": [2, 0], "x": 0, "y": 2},
{"matrix": [2, 1], "x": 1, "y": 2},
{"matrix": [2, 2], "x": 2, "y": 2},
{"matrix": [1, 3], "x": 3, "y": 1, "h": 2},
{"matrix": [3, 0], "x": 0, "y": 3},
{"matrix": [3, 1], "x": 1, "y": 3},
{"matrix": [3, 2], "x": 2, "y": 3},
{"matrix": [4, 0], "x": 0, "y": 4, "w": 2},
{"matrix": [4, 2], "x": 2, "y": 4},
{"matrix": [3, 3], "x": 3, "y": 3, "h": 2}
]
}
}
}

+ 471
- 0
keyboards/kalkulators/kalkulators.c View File

@ -0,0 +1,471 @@
/* Copyright 2024 georgsnarbuts
*
* 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 2 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include QMK_KEYBOARD_H
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <string.h>
#include "kalkulators.h"
#include "halconf.h"
#include "tm1638.h"
#include "usb_util.h"
#include "wait.h"
// calculate the number of whole digits and decimal digits in a double
void countDigitsAndDecimalPlaces(double input, int *wholeDigits, int *decimalPlaces) {
// Convert double to string
char inputString[32]; // Adjust the size based on your needs
snprintf(inputString, sizeof(inputString), "%f", input);
// Remove trailing zeros
char *end = inputString + strlen(inputString) - 1;
while (*end == '0') {
*end-- = '\0';
}
// Initialize counts
*wholeDigits = 0;
*decimalPlaces = 0;
int decimalFound = 0;
// Skip the negative sign if present
int startIdx = (inputString[0] == '-') ? 1 : 0;
// Iterate through each character in the modified input string
for (int i = startIdx; i < strlen(inputString); i++) {
if (isdigit((unsigned char)inputString[i])) {
if (!decimalFound) {
(*wholeDigits)++; // Increment count of whole digits
} else {
(*decimalPlaces)++; // Increment count of decimal places
}
} else if (inputString[i] == '.') {
decimalFound = 1; // Decimal point is found
} else {
// Break the loop if a non-digit or non-decimal point character is encountered
break;
}
}
}
enum custom_keycodes {
KC_cl = QK_KB_0,
KC_div,
KC_times,
KC_minus,
KC_seven,
KC_eight,
KC_nine,
KC_four,
KC_five,
KC_six,
KC_plus,
KC_one,
KC_two,
KC_three,
KC_zero,
KC_dot,
KC_equals
};
typedef struct {
double accumulator;
char current_input[8];
} results;
results number = {0, ""};
typedef struct {
bool dp_pressed;
bool operator_pressed;
bool input_start;
bool negative_number;
bool nonzero_pressed;
bool waiting_for_operator;
bool plus_operator_pressed;
bool minus_operator_pressed;
bool multiplication_operator_pressed;
bool division_operator_pressed;
bool operator_pressed_first_time;
bool enter_pressed;
} operations;
operations operation_state = {false, false, true, false, false, false, false, false, false, false, true, false};
void reset_states(void) {
number.accumulator = 0;
number.current_input[0] = '\0';
operation_state.dp_pressed = false;
operation_state.operator_pressed = false;
operation_state.input_start = true;
operation_state.negative_number = false;
operation_state.nonzero_pressed = false;
operation_state.waiting_for_operator = false;
operation_state.plus_operator_pressed = false;
operation_state.minus_operator_pressed = false;
operation_state.multiplication_operator_pressed = false;
operation_state.division_operator_pressed = false;
operation_state.operator_pressed_first_time = true;
operation_state.enter_pressed = false;
};
void reset_states_after_enter(void) {
number.current_input[0] = '\0';
operation_state.dp_pressed = false;
operation_state.operator_pressed = false;
operation_state.input_start = true;
operation_state.negative_number = false;
operation_state.nonzero_pressed = false;
operation_state.waiting_for_operator = true;
operation_state.plus_operator_pressed = false;
operation_state.minus_operator_pressed = false;
operation_state.multiplication_operator_pressed = false;
operation_state.division_operator_pressed = false;
operation_state.enter_pressed = false;
};
// display a char on the 7seg display
void display_result(const char *text) {
reset();
int x;
sscanf(text, "%d", &x);
print(text);
if ((strcmp(text, "inf") == 0) || ((x > 99999999))) {
displayText("Error ");
reset_states();
return;
}
if (strchr(text, '.') == NULL) {
displayTextRight(text);
} else {
displayTextRightFloat(text);
}
}
void display_final_result(void) {
char output[10];
int wholeDigits, decimalPlaces;
countDigitsAndDecimalPlaces(number.accumulator, &wholeDigits, &decimalPlaces);
snprintf(output, 10, "%*.*f", wholeDigits, decimalPlaces, number.accumulator);
display_result(output);
}
void into_current_input(double input) {
char input_char[11];
snprintf(input_char, sizeof(input_char), "%.0f", input);
// limiting input to 8 digits
if (strlen(number.current_input) + strlen(input_char) <= 8 && strchr(number.current_input, '.') == NULL) {
strcat(number.current_input, input_char);
display_result(number.current_input);
} else if (strlen(number.current_input) + strlen(input_char) <= 9 && strchr(number.current_input, '.') != NULL) {
strcat(number.current_input, input_char);
display_result(number.current_input);
}
};
// equals
void equals_operation(void) {
if ((operation_state.plus_operator_pressed == true)) {
number.accumulator += atof(number.current_input);
display_final_result();
reset_states_after_enter();
} else if ((operation_state.minus_operator_pressed == true)) {
number.accumulator -= atof(number.current_input);
display_final_result();
reset_states_after_enter();
} else if ((operation_state.multiplication_operator_pressed == true)) {
number.accumulator *= atof(number.current_input);
display_final_result();
reset_states_after_enter();
} else if ((operation_state.division_operator_pressed == true)) {
number.accumulator /= atof(number.current_input);
display_final_result();
reset_states_after_enter();
}
};
void plus_operation(void) {
if (number.current_input[0] != '\0') {
equals_operation();
number.accumulator = number.accumulator + atof(number.current_input);
} else {
operation_state.operator_pressed_first_time = true;
}
reset();
operation_state.plus_operator_pressed = true;
operation_state.minus_operator_pressed = false;
operation_state.multiplication_operator_pressed = false;
operation_state.division_operator_pressed = false;
number.current_input[0] = '\0';
if (operation_state.operator_pressed_first_time == true) {
display_result("\0");
} else {
display_final_result();
}
operation_state.operator_pressed_first_time = false;
};
void minus_operation(void) {
if (number.current_input[0] != '\0') {
equals_operation();
number.accumulator = number.accumulator + atof(number.current_input);
} else {
operation_state.operator_pressed_first_time = true;
}
reset();
operation_state.plus_operator_pressed = false;
operation_state.minus_operator_pressed = true;
operation_state.multiplication_operator_pressed = false;
operation_state.division_operator_pressed = false;
number.current_input[0] = '\0';
if (operation_state.operator_pressed_first_time == true) {
display_result("\0");
} else {
display_final_result();
}
operation_state.operator_pressed_first_time = false;
};
void multiply_operation(void) {
if (number.current_input[0] != '\0') {
equals_operation();
number.accumulator = number.accumulator + atof(number.current_input);
} else {
operation_state.operator_pressed_first_time = true;
}
reset();
operation_state.plus_operator_pressed = false;
operation_state.minus_operator_pressed = false;
operation_state.multiplication_operator_pressed = true;
operation_state.division_operator_pressed = false;
number.current_input[0] = '\0';
if (operation_state.operator_pressed_first_time == true) {
display_result("\0");
} else {
display_final_result();
}
operation_state.operator_pressed_first_time = false;
};
void divide_operation(void) {
if (number.current_input[0] != '\0') {
equals_operation();
number.accumulator = number.accumulator + atof(number.current_input);
} else {
operation_state.operator_pressed_first_time = true;
}
reset();
operation_state.plus_operator_pressed = false;
operation_state.minus_operator_pressed = false;
operation_state.multiplication_operator_pressed = false;
operation_state.division_operator_pressed = true;
number.current_input[0] = '\0';
if (operation_state.operator_pressed_first_time == true) {
display_result("\0");
} else {
display_final_result();
}
operation_state.operator_pressed_first_time = false;
};
// clearing the display
void clear_operation(void) {
reset_states();
reset();
displayTextRight("0");
};
// processes input from the process_record_user function
void process_input(double input) {
// Reset states if enter key was pressed and a number is entered
if (operation_state.enter_pressed == true && input < 10) {
reset_states();
print("entered new calculations");
}
// handle number inputs
if (input < 10) {
// handle so that multiple zeros can't be pressed at the start of writing
if (operation_state.input_start == true && input == 0) {
into_current_input(input);
operation_state.input_start = false;
} else {
if (input == 0) {
if (operation_state.nonzero_pressed) {
into_current_input(input);
operation_state.waiting_for_operator = true;
}
} else {
if ((strcmp(number.current_input, "0") == 0)) {
number.current_input[0] = '\0';
}
into_current_input(input);
operation_state.input_start = false;
operation_state.nonzero_pressed = true;
operation_state.waiting_for_operator = true;
}
}
}
// handle clearing
else if (input == 777) {
number.accumulator = 0;
number.current_input[0] = '\0';
clear_operation();
operation_state.enter_pressed = false; // Reset the flag
}
// handle decimal point
else if ((operation_state.input_start == false) && (operation_state.dp_pressed == false) && (input == 111)) {
char buffer[2] = ".";
strcat(number.current_input, buffer);
display_result(number.current_input);
operation_state.dp_pressed = true;
operation_state.nonzero_pressed = true;
}
// handle summation
else if (input == 333 && operation_state.waiting_for_operator == true) {
reset();
plus_operation();
operation_state.nonzero_pressed = false;
operation_state.dp_pressed = false;
operation_state.input_start = true;
operation_state.enter_pressed = false; // Reset the flag
}
// handle minus
else if (input == 444 && operation_state.waiting_for_operator == true) {
reset();
minus_operation();
operation_state.nonzero_pressed = false;
operation_state.dp_pressed = false;
operation_state.input_start = true;
operation_state.enter_pressed = false; // Reset the flag
}
// handle multiplication
else if (input == 555 && operation_state.waiting_for_operator == true) {
reset();
multiply_operation();
operation_state.nonzero_pressed = false;
operation_state.dp_pressed = false;
operation_state.input_start = true;
operation_state.enter_pressed = false; // Reset the flag
}
// handle division
else if (input == 666 && operation_state.waiting_for_operator == true) {
reset();
divide_operation();
operation_state.nonzero_pressed = false;
operation_state.dp_pressed = false;
operation_state.input_start = true;
operation_state.enter_pressed = false; // Reset the flag
}
else if (input == 999) {
equals_operation();
if (!operation_state.waiting_for_operator) {
operation_state.enter_pressed = true; // Set the flag only if a valid calculation was performed
}
}
}
// listens for keypresses
bool process_record_kb(uint16_t keycode, keyrecord_t *record) {
if (record->event.pressed) {
switch (keycode) {
case KC_cl:
process_input(777);
break;
case KC_div:
process_input(666);
break;
case KC_times:
process_input(555);
break;
case KC_minus:
process_input(444);
break;
case KC_seven:
process_input(7);
break;
case KC_eight:
process_input(8);
break;
case KC_nine:
process_input(9);
break;
case KC_four:
process_input(4);
break;
case KC_five:
process_input(5);
break;
case KC_six:
process_input(6);
break;
case KC_plus:
process_input(333);
break;
case KC_one:
process_input(1);
break;
case KC_two:
process_input(2);
break;
case KC_three:
process_input(3);
break;
case KC_zero:
process_input(0);
break;
case KC_dot:
process_input(111);
break;
case KC_equals:
process_input(999);
operation_state.enter_pressed = true;
break;
}
}
return process_record_user(keycode, record);
};

+ 31
- 0
keyboards/kalkulators/kalkulators.h View File

@ -0,0 +1,31 @@
/* Copyright 2024 georgsnarbuts
*
* 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 2 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
void countDigitsAndDecimalPlaces(double input, int *wholeDigits, int *decimalPlaces);
void reset_states(void);
void reset_states_after_enter(void);
void display_result(const char* text);
void display_final_result(void);
void into_current_input(double input);
void equals_operation(void);
void plus_operation(void);
void minus_operation(void);
void multiply_operation(void);
void divide_operation(void);
void clear_operation(void);
void process_input(double input);

+ 274
- 0
keyboards/kalkulators/keymaps/default/keymap.c View File

@ -0,0 +1,274 @@
// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#include QMK_KEYBOARD_H
#include <stdio.h>
#include "halconf.h"
#include "tm1638.h"
#include "wait.h"
#include "usb_util.h"
#include "kalkulators.h"
int brightness_level = 7;
bool show_usb_connection = true;
bool show_calc_connection = true;
bool pc_connection;
int layer;
void keyboard_post_init_user(void) {
// Customise these values to desired behaviour
debug_enable=true;
debug_matrix=true;
debug_keyboard=true;
setPinOutput(TM1638_CLK_PIN);
setPinOutput(TM1638_STB_PIN);
setPinOutput(TM1638_DIO_PIN);
displayBegin();
wait_ms(0.1);
brightness(brightness_level);
if (usb_connected_state() == 1) {
set_single_persistent_default_layer(0);
if (show_usb_connection == true){
layer = 0;
pc_connection = true;
show_usb_connection = false;
reset();
display7Seg(3, 0b01110011);
display7Seg(4, 0b00111001);
wait_ms(500);
reset();
}
show_calc_connection = true;
} else if (usb_connected_state() == 0){
set_single_persistent_default_layer(1);
if (show_calc_connection == true){
layer = 1;
pc_connection = false;
show_calc_connection = false;
reset();
display7Seg(2, 0b00111001);
display7Seg(3, 0b01110111);
display7Seg(4, 0b00111000);
display7Seg(5, 0b00111001);
wait_ms(500);
reset();
display7Seg(7, 0b00111111);
}
show_usb_connection = true;
}
};
enum custom_keycodes {
KC_cl = QK_KB_0,
KC_div,
KC_times,
KC_minus,
KC_seven,
KC_eight,
KC_nine,
KC_four,
KC_five,
KC_six,
KC_plus,
KC_one,
KC_two,
KC_three,
KC_zero,
KC_dot,
KC_equals
};
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/*
*
* TG1 / * -
*
* 7 8 9
* +
* 4 5 6
*
* 1 2 3
* Ent
* 0 .
*
*/
[0] = LAYOUT_calc(
KC_DELETE, KC_PSLS, KC_PAST, KC_PMNS,
KC_P7, KC_P8, KC_P9,
KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_P1, KC_P2, KC_P3,
KC_P0, KC_PDOT, KC_PENT
),
[1] = LAYOUT_calc(
QK_KB_0, KC_div, KC_times, KC_minus,
KC_seven, KC_eight, KC_nine,
KC_four, KC_five, KC_six, KC_plus,
KC_one, KC_two, KC_three,
KC_zero, KC_dot, KC_equals
)
};
void housekeeping_task_user(void) {
if (usb_connected_state() == 1) {
if (show_usb_connection == true){
set_single_persistent_default_layer(0);
layer = 0;
pc_connection = true;
show_usb_connection = false;
reset();
display7Seg(3, 0b01110011);
display7Seg(4, 0b00111001);
wait_ms(500);
reset();
}
show_calc_connection = true;
} else if (usb_connected_state() == 0){
set_single_persistent_default_layer(1);
if (show_calc_connection == true){
pc_connection = false;
show_calc_connection = false;
reset();
reset_states();
display7Seg(2, 0b00111001);
display7Seg(3, 0b01110111);
display7Seg(4, 0b00111000);
display7Seg(5, 0b00111001);
wait_ms(500);
reset();
display7Seg(7, 0b00111111);
}
show_usb_connection = true;
}
}
enum combo_events {
CHANGE_BRIGHTNESS_UP,
CHANGE_BRIGHTNESS_DOWN,
CHANGE_LAYER,
CHANGE_LAYER_ON_PC
};
const uint16_t PROGMEM brightness_combo_up[] = {KC_cl, KC_times, COMBO_END};
const uint16_t PROGMEM brightness_combo_down[] = {KC_cl, KC_div, COMBO_END};
const uint16_t PROGMEM change_layer[] = {KC_minus, KC_plus, COMBO_END};
const uint16_t PROGMEM change_layer_on_pc[] = {KC_PMNS, KC_PPLS, COMBO_END};
combo_t key_combos[] = {
[CHANGE_BRIGHTNESS_UP] = COMBO_ACTION(brightness_combo_up),
[CHANGE_BRIGHTNESS_DOWN] = COMBO_ACTION(brightness_combo_down),
[CHANGE_LAYER] = COMBO_ACTION(change_layer),
[CHANGE_LAYER_ON_PC] = COMBO_ACTION(change_layer_on_pc)
};
void process_combo_event(uint16_t combo_index, bool pressed) {
switch(combo_index) {
case CHANGE_BRIGHTNESS_UP:
if (pressed) {
if (brightness_level < 7){
brightness_level += 1;
brightness(brightness_level);
}
}
break;
case CHANGE_BRIGHTNESS_DOWN:
if (pressed) {
if (brightness_level > 0){
brightness_level -= 1;
brightness(brightness_level);
}
}
break;
case CHANGE_LAYER:
if (pressed) {
print("changing_layer");
if (pc_connection == true){
if (layer == 1){
set_single_persistent_default_layer(0);
layer = 0;
reset();
display7Seg(3, 0b01110011);
display7Seg(4, 0b00111001);
wait_ms(500);
reset();
}
else if (layer == 0){
set_single_persistent_default_layer(1);
layer = 1;
reset();
reset_states();
display7Seg(2, 0b00111001);
display7Seg(3, 0b01110111);
display7Seg(4, 0b00111000);
display7Seg(5, 0b00111001);
wait_ms(500);
reset();
display7Seg(7, 0b00111111);
}
}
}
break;
case CHANGE_LAYER_ON_PC:
if (pressed) {
print("changing_layer");
if (pc_connection == true){
if (layer == 1){
set_single_persistent_default_layer(0);
layer = 0;
reset();
display7Seg(3, 0b01110011);
display7Seg(4, 0b00111001);
wait_ms(500);
reset();
}
else if (layer == 0){
set_single_persistent_default_layer(1);
layer = 1;
reset();
reset_states();
display7Seg(2, 0b00111001);
display7Seg(3, 0b01110111);
display7Seg(4, 0b00111000);
display7Seg(5, 0b00111001);
wait_ms(500);
reset();
display7Seg(7, 0b00111111);
}
}
}
break;
}
}

+ 1
- 0
keyboards/kalkulators/keymaps/default/rules.mk View File

@ -0,0 +1 @@
COMBO_ENABLE = yes

+ 278
- 0
keyboards/kalkulators/keymaps/via/keymap.c View File

@ -0,0 +1,278 @@
// Copyright 2023 QMK
// SPDX-License-Identifier: GPL-2.0-or-later
#include QMK_KEYBOARD_H
#include <stdio.h>
#include "halconf.h"
#include "tm1638.h"
#include "wait.h"
#include "usb_util.h"
#include "kalkulators.h"
int brightness_level = 7;
bool show_usb_connection = true;
bool show_calc_connection = true;
bool pc_connection;
int layer;
void keyboard_post_init_user(void) {
// Customise these values to desired behaviour
debug_enable=true;
debug_matrix=true;
debug_keyboard=true;
setPinOutput(TM1638_CLK_PIN);
setPinOutput(TM1638_STB_PIN);
setPinOutput(TM1638_DIO_PIN);
displayBegin();
wait_ms(0.1);
brightness(brightness_level);
if (usb_connected_state() == 1) {
set_single_persistent_default_layer(0);
if (show_usb_connection == true){
layer = 0;
pc_connection = true;
show_usb_connection = false;
reset();
display7Seg(3, 0b01110011);
display7Seg(4, 0b00111001);
wait_ms(500);
reset();
}
show_calc_connection = true;
} else if (usb_connected_state() == 0){
set_single_persistent_default_layer(1);
if (show_calc_connection == true){
layer = 1;
pc_connection = false;
show_calc_connection = false;
reset();
display7Seg(2, 0b00111001);
display7Seg(3, 0b01110111);
display7Seg(4, 0b00111000);
display7Seg(5, 0b00111001);
wait_ms(500);
reset();
display7Seg(7, 0b00111111);
}
show_usb_connection = true;
}
};
enum custom_keycodes {
KC_cl = QK_KB_0,
KC_div,
KC_times,
KC_minus,
KC_seven,
KC_eight,
KC_nine,
KC_four,
KC_five,
KC_six,
KC_plus,
KC_one,
KC_two,
KC_three,
KC_zero,
KC_dot,
KC_equals
};
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
/*
*
* TG1 / * -
*
* 7 8 9
* +
* 4 5 6
*
* 1 2 3
* Ent
* 0 .
*
*/
[0] = LAYOUT_calc(
KC_DELETE, KC_PSLS, KC_PAST, KC_PMNS,
KC_P7, KC_P8, KC_P9,
KC_P4, KC_P5, KC_P6, KC_PPLS,
KC_P1, KC_P2, KC_P3,
KC_P0, KC_PDOT, KC_PENT
),
[1] = LAYOUT_calc(
QK_KB_0, KC_div, KC_times, KC_minus,
KC_seven, KC_eight, KC_nine,
KC_four, KC_five, KC_six, KC_plus,
KC_one, KC_two, KC_three,
KC_zero, KC_dot, KC_equals
)
};
void housekeeping_task_user(void) {
if (usb_connected_state() == 1) {
if (show_usb_connection == true){
set_single_persistent_default_layer(0);
layer = 0;
pc_connection = true;
show_usb_connection = false;
reset();
display7Seg(3, 0b01110011);
display7Seg(4, 0b00111001);
wait_ms(500);
reset();
}
show_calc_connection = true;
} else if (usb_connected_state() == 0){
set_single_persistent_default_layer(1);
if (show_calc_connection == true){
pc_connection = false;
show_calc_connection = false;
reset();
reset_states();
display7Seg(2, 0b00111001);
display7Seg(3, 0b01110111);
display7Seg(4, 0b00111000);
display7Seg(5, 0b00111001);
wait_ms(500);
reset();
display7Seg(7, 0b00111111);
}
show_usb_connection = true;
}
}
enum combo_events {
CHANGE_BRIGHTNESS_UP,
CHANGE_BRIGHTNESS_DOWN,
CHANGE_LAYER,
CHANGE_LAYER_ON_PC
};
const uint16_t PROGMEM brightness_combo_up[] = {KC_cl, KC_times, COMBO_END};
const uint16_t PROGMEM brightness_combo_down[] = {KC_cl, KC_div, COMBO_END};
const uint16_t PROGMEM change_layer[] = {KC_minus, KC_plus, COMBO_END};
const uint16_t PROGMEM change_layer_on_pc[] = {KC_PMNS, KC_PPLS, COMBO_END};
combo_t key_combos[] = {
[CHANGE_BRIGHTNESS_UP] = COMBO_ACTION(brightness_combo_up),
[CHANGE_BRIGHTNESS_DOWN] = COMBO_ACTION(brightness_combo_down),
[CHANGE_LAYER] = COMBO_ACTION(change_layer),
[CHANGE_LAYER_ON_PC] = COMBO_ACTION(change_layer_on_pc)
};
void process_combo_event(uint16_t combo_index, bool pressed) {
switch(combo_index) {
case CHANGE_BRIGHTNESS_UP:
if (pressed) {
if (brightness_level < 7){
brightness_level += 1;
brightness(brightness_level);
}
}
break;
case CHANGE_BRIGHTNESS_DOWN:
if (pressed) {
if (brightness_level > 0){
brightness_level -= 1;
brightness(brightness_level);
}
}
break;
case CHANGE_LAYER:
if (pressed) {
print("changing_layer");
if (pc_connection == true){
if (layer == 1){
set_single_persistent_default_layer(0);
layer = 0;
reset();
display7Seg(3, 0b01110011);
display7Seg(4, 0b00111001);
wait_ms(500);
reset();
}
else if (layer == 0){
set_single_persistent_default_layer(1);
layer = 1;
reset();
reset_states();
display7Seg(2, 0b00111001);
display7Seg(3, 0b01110111);
display7Seg(4, 0b00111000);
display7Seg(5, 0b00111001);
wait_ms(500);
reset();
display7Seg(7, 0b00111111);
}
}
}
break;
case CHANGE_LAYER_ON_PC:
if (pressed) {
print("changing_layer");
if (pc_connection == true){
if (layer == 1){
set_single_persistent_default_layer(0);
layer = 0;
reset();
display7Seg(3, 0b01110011);
display7Seg(4, 0b00111001);
wait_ms(500);
reset();
}
else if (layer == 0){
set_single_persistent_default_layer(1);
layer = 1;
reset();
reset_states();
display7Seg(2, 0b00111001);
display7Seg(3, 0b01110111);
display7Seg(4, 0b00111000);
display7Seg(5, 0b00111001);
wait_ms(500);
reset();
display7Seg(7, 0b00111111);
}
}
}
break;
}
}

+ 2
- 0
keyboards/kalkulators/keymaps/via/rules.mk View File

@ -0,0 +1,2 @@
VIA_ENABLE = yes
COMBO_ENABLE = yes

+ 26
- 0
keyboards/kalkulators/mcuconf.h View File

@ -0,0 +1,26 @@
/* Copyright 2024 QMK
*
* 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 2 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include_next <mcuconf.h>
#undef STM32_ADC_USE_ADC1
#define STM32_ADC_USE_ADC1 TRUE
#undef STM32_I2C_USE_I2C1
#define STM32_I2C_USE_I2C1 TRUE

+ 46
- 0
keyboards/kalkulators/readme.md View File

@ -0,0 +1,46 @@
# kalkulators
![kalkulators](https://i.imgur.com/hodQbV1h.jpeg)
An 8 digit 7-segment display calculator and numpad.
USB-C, STM32F072, TM1638 LED controller, 1xAA battery.
**When editing layers through keymap or VIA, do not delete/edit the 1st layer, it is needed for the calculator functionality!**
* Keyboard Maintainer: [georgsnarbuts](https://github.com/georgsnarbuts)
Make example for this keyboard (after setting up your build environment):
make kalkulators:default
Flashing example for this keyboard:
make kalkulators:default:flash
See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
## Combos
* **To decrese display brightness**:
Tap "Delete" and "Divide" buttons in unison.
* **To increase display brightness**:
Tap "Delete" and "*" buttons in unison.
* **To switch between calculator and numpad mode when connected to a host computer**:
Tap "+" and "-" buttons in unison.
## Bootloader
Enter the bootloader:
* **Physical reset button**:
1) Turn off the battery power switch on the right side of the board.
2) Toggle the boot switch to the bottom position on the back of the PCB.
3) If board already connected to a host computer through USB-C, press the reset button on the back of the PCB. If not plugged in, then just connect the USB-C cable to a host computer and the PCB.
4) Board should have entered the bootloader.

+ 1
- 0
keyboards/kalkulators/rules.mk View File

@ -0,0 +1 @@
SRC += tm1638.c

+ 319
- 0
keyboards/kalkulators/tm1638.c View File

@ -0,0 +1,319 @@
/* Copyright 2024 georgsnarbuts
*
* 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 2 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdbool.h>
#include "tm1638.h"
#include "gpio.h"
#include "progmem.h"
#include "wait.h"
#include <stdio.h>
#include "halconf.h"
#include "print.h"
#include <math.h>
#include "kalkulators.h"
#ifndef TM1638_CLK_PIN
# error tm1638: no CLK pin defined!
#endif
#ifndef TM1638_STB_PIN
# error tm1638: no STB pin defined!
#endif
#ifndef TM1638_DIO_PIN
# error tm1638: no DIO pin defined!
#endif
static const unsigned char PROGMEM SevenSeg[] = {
0x00, /* (space) */
0x86, /* ! */
0x22, /* " */
0x7E, /* # */
0x6D, /* $ */
0xD2, /* % */
0x46, /* & */
0x20, /* ' */
0x29, /* ( */
0x0B, /* ) */
0x21, /* * */
0x70, /* + */
0x10, /* , */
0x40, /* - */
0x80, /* . */
0x52, /* / */
0x3F, /* 0 */
0x06, /* 1 */
0x5B, /* 2 */
0x4F, /* 3 */
0x66, /* 4 */
0x6D, /* 5 */
0x7D, /* 6 */
0x07, /* 7 */
0x7F, /* 8 */
0x6F, /* 9 */
0x09, /* : */
0x0D, /* ; */
0x61, /* < */
0x48, /* = */
0x43, /* > */
0xD3, /* ? */
0x5F, /* @ */
0x77, /* A */
0x7C, /* B */
0x39, /* C */
0x5E, /* D */
0x79, /* E */
0x71, /* F */
0x3D, /* G */
0x76, /* H */
0x30, /* I */
0x1E, /* J */
0x75, /* K */
0x38, /* L */
0x15, /* M */
0x37, /* N */
0x3F, /* O */
0x73, /* P */
0x6B, /* Q */
0x33, /* R */
0x6D, /* S */
0x78, /* T */
0x3E, /* U */
0x3E, /* V */
0x2A, /* W */
0x76, /* X */
0x6E, /* Y */
0x5B, /* Z */
0x39, /* [ */
0x64, /* \ */
0x0F, /* ] */
0x23, /* ^ */
0x08, /* _ */
0x02, /* ` */
0x5F, /* a */
0x7C, /* b */
0x58, /* c */
0x5E, /* d */
0x7B, /* e */
0x71, /* f */
0x6F, /* g */
0x74, /* h */
0x10, /* i */
0x0C, /* j */
0x75, /* k */
0x30, /* l */
0x14, /* m */
0x54, /* n */
0x5C, /* o */
0x73, /* p */
0x67, /* q */
0x50, /* r */
0x6D, /* s */
0x78, /* t */
0x1C, /* u */
0x1C, /* v */
0x14, /* w */
0x76, /* x */
0x6E, /* y */
0x5B, /* z */
0x46, /* { */
0x30, /* | */
0x70, /* } */
0x01, /* ~ */
};
const unsigned char *pFontSevenSegptr = SevenSeg; /**< Pointer to the seven segment font data table */
void shiftOut(uint8_t val) {
uint8_t i;
for (i = 0; i < 8; i++) {
writePin(TM1638_DIO_PIN, !!(val & (1 << i)));
writePinHigh(TM1638_CLK_PIN);
writePinLow(TM1638_CLK_PIN);
}
}
void sendData(uint8_t data) {
shiftOut(data);
}
void sendCommand(uint8_t value) {
writePinLow(TM1638_STB_PIN);
sendData(value);
writePinHigh(TM1638_STB_PIN);
}
void reset() {
sendCommand(TM_WRITE_INC);
writePinLow(TM1638_STB_PIN);
sendData(TM_SEG_ADR);
for (uint8_t position = 0; position < 16; position++) {
sendData(0x00);
}
writePinHigh(TM1638_STB_PIN);
}
void brightness(uint8_t brightness) {
uint8_t value = 0;
value = TM_BRIGHT_ADR + (TM_BRIGHT_MASK & brightness);
sendCommand(value);
}
void displayBegin() {
sendCommand(TM_ACTIVATE);
brightness(TM_DEFAULT_BRIGHTNESS);
reset();
}
void display7Seg(uint8_t position, uint8_t value) {
sendCommand(TM_WRITE_LOC);
writePinLow(TM1638_STB_PIN);
sendData(TM_SEG_ADR + (position << 1));
sendData(value);
writePinHigh(TM1638_STB_PIN);
}
void displayASCIIwDot(uint8_t position, uint8_t ascii) {
// add 128 or 0x080 0b1000000 to turn on decimal point/dot in seven seg
display7Seg(position, pgm_read_byte(pFontSevenSegptr + (ascii - TM_ASCII_OFFSET)) + TM_DOT_MASK_DEC);
}
void displayASCII(uint8_t position, uint8_t ascii) {
display7Seg(position, pgm_read_byte(pFontSevenSegptr + (ascii - TM_ASCII_OFFSET)));
}
void displayText(const char *text) {
char c, pos;
pos = 0;
while ((c = (*text++)) && pos < TM_DISPLAY_SIZE) {
if (*text == '.' && c != '.') {
displayASCIIwDot(pos++, c);
text++;
} else {
displayASCII(pos++, c);
}
}
}
void displayTextRight(const char *text) {
// Calculate the starting position to display the text on the far right
uint8_t startPos = TM_DISPLAY_SIZE - strlen(text);
// Ensure startPos is not negative
startPos = startPos < 0 ? 0 : startPos;
char c;
while ((c = (*text++)) && startPos < TM_DISPLAY_SIZE) {
if (*text == '.' && c != '.') {
displayASCIIwDot((startPos++), c);
text++;
} else {
displayASCII(startPos++, c);
}
}
}
void displayTextRightFloat(const char *text) {
// Calculate the starting position to display the text on the far right
// if there is "." in the text
uint8_t startPos = TM_DISPLAY_SIZE - strlen(text) + 1;
// Ensure startPos is not negative
startPos = startPos < 0 ? 0 : startPos;
char c;
while ((c = (*text++)) && startPos < TM_DISPLAY_SIZE) {
if (*text == '.' && c != '.') {
displayASCIIwDot((startPos++), c);
text++;
} else {
displayASCII(startPos++, c);
}
}
}
void displayIntNum(unsigned long number, bool leadingZeros) {
char values[TM_DISPLAY_SIZE + 1];
char TextDisplay[5] = "%";
char TextLeft[3] = "ld";
char TextRight[4] = "8ld";
if (strcmp(TextAlignment, "TMAlignTextLeft") == 0) {
strcat(TextDisplay, TextLeft); // %ld
} else if (strcmp(TextAlignment, "TMAlignTextRight") == 0) {
strcat(TextDisplay, TextRight); // %8ld
}
snprintf(values, TM_DISPLAY_SIZE + 1, leadingZeros ? "%08ld" : TextDisplay, number);
displayText(values);
}
void displayHex(uint8_t position, uint8_t hex) {
uint8_t offset = 0;
hex = hex % 16;
if (hex <= 9) {
display7Seg(position, pgm_read_byte(pFontSevenSegptr + (hex + TM_HEX_OFFSET)));
// 16 is offset in reduced ASCII table for number 0
} else if ((hex >= 10) && (hex <= 15)) {
// Calculate offset in reduced ASCII table for AbCDeF
switch (hex) {
case 10:
offset = 'A';
break;
case 11:
offset = 'b';
break;
case 12:
offset = 'C';
break;
case 13:
offset = 'd';
break;
case 14:
offset = 'E';
break;
case 15:
offset = 'F';
break;
}
display7Seg(position, pgm_read_byte(pFontSevenSegptr + (offset - TM_ASCII_OFFSET)));
}
}
void DisplayDecNumNibble(uint16_t numberUpper, uint16_t numberLower, bool leadingZeros) {
char valuesUpper[TM_DISPLAY_SIZE + 1];
char valuesLower[TM_DISPLAY_SIZE / 2 + 1];
char TextDisplay[5] = "%";
char TextLeft[4] = "-4d";
char TextRight[3] = "4d";
if (strcmp(TextAlignment, "TMAlignTextLeft") == 0) {
strcat(TextDisplay, TextLeft); // %-4d
} else if (strcmp(TextAlignment, "TMAlignTextRight") == 0) {
strcat(TextDisplay, TextRight); // %4d
}
snprintf(valuesUpper, TM_DISPLAY_SIZE / 2 + 1, leadingZeros ? "%04d" : TextDisplay, numberUpper);
snprintf(valuesLower, TM_DISPLAY_SIZE / 2 + 1, leadingZeros ? "%04d" : TextDisplay, numberLower);
strcat(valuesUpper, valuesLower);
displayText(valuesUpper);
};

+ 56
- 0
keyboards/kalkulators/tm1638.h View File

@ -0,0 +1,56 @@
/* Copyright 2024 georgsnarbuts
*
* 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 2 of the License, or
* (at your option) any later version.
*
* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
extern const unsigned char * pFontSevenSegptr;
#define TextAlignment "TMAlignTextRight"
#define TM_ACTIVATE 0x8F /**< Start up device */
#define TM_BUTTONS_MODE 0x42 /**< Buttons mode */
#define TM_WRITE_LOC 0x44 /**< Write to a memory location */
#define TM_WRITE_INC 0x40 /**< Incremental write */
#define TM_SEG_ADR 0xC0 /**< leftmost segment Address C0 C2 C4 C6 C8 CA CC CE */
#define TM_LEDS_ADR 0xC1 /**< Leftmost LED address C1 C3 C5 C7 C9 CB CD CF */
#define TM_BRIGHT_ADR 0x88 /**< Brightness address */
#define TM_BRIGHT_MASK 0x07 /**< Brightness mask */
#define TM_DEFAULT_BRIGHTNESS 0x02 /**< Brightness can be 0x00 to 0x07 , 0x00 is least bright */
#define TM_DISPLAY_SIZE 8 /**< Size of display in digits */
#define TM_ASCII_OFFSET 32 /**< ASCII table offset to jump over first missing 32 chars */
#define TM_HEX_OFFSET 16 /**< ASCII table offset to reach the number position */
#define TM_DOT_MASK_DEC 128 /**< 0x80 Mask to switch on decimal point in seven segement */
#define TM_RED_LED 0x02 /**< Turn LED red color Model 3 only */
#define TM_GREEN_LED 0x01 /**< Turn LED green color Model 3 only */
#define TM_OFF_LED 0x00 /**< Switch off LED */
void displayBegin(void);
void reset(void);
void brightness(uint8_t brightness);
void sendCommand(uint8_t value);
void sendData(uint8_t data);
void shiftOut(uint8_t val);
void display7Seg(uint8_t position, uint8_t value);
void displayASCIIwDot(uint8_t position, uint8_t ascii);
void displayASCII(uint8_t position, uint8_t ascii);
void displayText(const char *text);
void displayTextRight(const char *text);
void displayTextRightFloat(const char *text);
void displayIntNum(unsigned long number, bool leadingZeros);
void displayHex(uint8_t position, uint8_t hex);
void DisplayDecNumNibble(uint16_t numberUpper, uint16_t numberLower, bool leadingZeros);

Loading…
Cancel
Save