Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions keyboards/dasbob/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

#pragma once

#define FLOW_TAP_TERM 150
// #define HAL_USE_GPT TRUE

#define MATRIX_ROWS 8
#define MATRIX_COLS 5

Expand Down Expand Up @@ -51,16 +54,20 @@
// The default is 40
#define MOUSEKEY_WHEEL_TIME_TO_MAX 100

#define BOTH_SHIFTS_TURNS_ON_CAPS_WORD

// Pick good defaults for enabling homerow modifiers
#define TAPPING_TERM 200
#define PERMISSIVE_HOLD
#define IGNORE_MOD_TAP_INTERRUPT
#define TAPPING_FORCE_HOLD
#define USB_VBUS_PIN 19

#define AUDIO_PWM_DRIVER PWMD2
#define AUDIO_PWM_CHANNEL RP2040_PWM_CHANNEL_B
#define AUDIO_STATE_TIMER GPTD4
#define AUDIO_PIN GP5

#ifdef AUDIO_ENABLE
#define AUDIO_INIT_DELAY
#define AUDIO_CLICKY
Expand Down
11 changes: 10 additions & 1 deletion keyboards/dasbob/keymaps/vial/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@

#pragma once

#define VIAL_KEYBOARD_UID {0x33, 0x7A, 0x81, 0x10, 0xBF, 0xCB, 0x21, 0x73}
#define VIAL_KEYBOARD_UID {0x33, 0x7A, 0x81, 0x10, 0xBF, 0xCB, 0x21, 0x73}
#define DYNAMIC_KEYMAP_LAYER_COUNT 3

#ifdef AUDIO_ENABLE
# define AUDIO_INIT_DELAY
# define AUDIO_CLICKY
# define AUDIO_CLICKY_ON
# define AUDIO_CLICKY_FREQ_RANDOMNESS 1.0f
# define STARTUP_SONG SONG(STARTUP_SOUND)
#endif
3 changes: 1 addition & 2 deletions keyboards/dasbob/keymaps/vial/rules.mk
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
VIA_ENABLE = yes
VIAL_ENABLE = yes
VIAL_INSECURE = yes
VIAL_ENABLE = yes
2 changes: 1 addition & 1 deletion keyboards/dasbob/mcuconf.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@
#undef RP_PWM_USE_PWM2
#define RP_PWM_USE_PWM2 TRUE

#undef RP_PWM_USE_TIM1 //hier bin ich gerade nicht mehr sicher ob das auch für Audio gebraucht wird
#undef RP_PWM_USE_TIM1
#define RP_PWM_USE_TIM1 TRUE
15 changes: 11 additions & 4 deletions keyboards/dasbob/rules.mk
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

# MCU = RP2040
# BOARD =

# Bootloader selection
BOOTLOADER = rp2040
Expand All @@ -7,19 +9,24 @@ BOOTLOADER = rp2040
# change yes to no to disable
#
BOOTMAGIC_ENABLE = yes # Enable Bootmagic Lite
MOUSEKEY_ENABLE = no # Mouse keys
MOUSEKEY_ENABLE = yes # Mouse keys
AUTO_SHIFT_ENABLE = yes
COMBOS_ENABLE = yes
REPEAT_KEY_ENABLE = yes
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
COMMAND_ENABLE = no # Commands for debug and configuration
NKRO_ENABLE = no # Enable N-Key Rollover
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = no # Enable keyboard RGB underglow
UNICODE_ENABLE = yes # Unicode
AUDIO_ENABLE = no # Audio output
AUDIO_ENABLE = yes # Audio output
AUDIO_DRIVER = pwm_hardware
SPLIT_KEYBOARD = yes # Use shared split_common code
OLED_ENABLE = yes
OLED_ENABLE = no
OLED_DRIVER = SSD1306
WPM_ENABLE = yes
LTO_ENABLE = yes
SERIAL_DRIVER = vendor
AUDIO_DRIVER = pwm_hardware
CAPS_WORD_ENABLE = yes
caps_word_enable = yes
4 changes: 2 additions & 2 deletions lib/python/qmk/math.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ def compute(expr):


def _eval(node):
if isinstance(node, ast.Num): # <number>
return node.n
if isinstance(node, ast.Constant): # <number>
return node.value
elif isinstance(node, ast.BinOp): # <left> <operator> <right>
return operators[type(node.op)](_eval(node.left), _eval(node.right))
elif isinstance(node, ast.UnaryOp): # <operator> <operand> e.g., -1
Expand Down
8 changes: 8 additions & 0 deletions platforms/chibios/chibios_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@
# define SPI_SCK_PAL_MODE (PAL_MODE_ALTERNATE_SPI | PAL_RP_PAD_SLEWFAST | PAL_RP_PAD_DRIVE4)
# define SPI_MOSI_PAL_MODE SPI_SCK_PAL_MODE
# define SPI_MISO_PAL_MODE SPI_SCK_PAL_MODE

# define RP2040_PWM_CHANNEL_A 1U
# define RP2040_PWM_CHANNEL_B 2U

# ifndef AUDIO_PWM_PAL_MODE
# define AUDIO_PWM_PAL_MODE (PAL_MODE_ALTERNATE_PWM | PAL_RP_PAD_DRIVE12 | PAL_RP_GPIO_OE)
# endif
# define AUDIO_PWM_COUNTER_FREQUENCY 500000
#endif

// STM32 compatibility
Expand Down
181 changes: 79 additions & 102 deletions platforms/chibios/drivers/audio_pwm_hardware.c
Original file line number Diff line number Diff line change
@@ -1,85 +1,60 @@
/* Copyright 2020 Jack Humbert
* Copyright 2020 JohSchneider
*
* 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/>.
*/

/*
Audio Driver: PWM

the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back.

this driver uses the chibios-PWM system to produce a square-wave on specific output pins that are connected to the PWM hardware.
The hardware directly toggles the pin via its alternate function. see your MCUs data-sheet for which pin can be driven by what timer - looking for TIMx_CHy and the corresponding alternate function.

*/
// Copyright 2022 Stefan Kerkmann
// Copyright 2020 Jack Humbert
// Copyright 2020 JohSchneider
// SPDX-License-Identifier: GPL-2.0-or-later

// Audio Driver: PWM the duty-cycle is always kept at 50%, and the pwm-period is
// adjusted to match the frequency of a note to be played back. This driver uses
// the chibios-PWM system to produce a square-wave on specific output pins that
// are connected to the PWM hardware. The hardware directly toggles the pin via
// its alternate function. see your MCUs data-sheet for which pin can be driven
// by what timer - looking for TIMx_CHy and the corresponding alternate
// function.

#include "audio.h"
#include "ch.h"
#include "hal.h"
#include "gpio.h"

#if !defined(AUDIO_PIN)
# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings"
#endif

#if !defined(AUDIO_PWM_COUNTER_FREQUENCY)
# define AUDIO_PWM_COUNTER_FREQUENCY 100000
#endif

#ifndef AUDIO_PWM_COMPLEMENTARY_OUTPUT
# define AUDIO_PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH
#else
# define AUDIO_PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH
#endif

extern bool playing_note;
extern bool playing_melody;
extern uint8_t note_timbre;

static PWMConfig pwmCFG = {
.frequency = 100000, /* PWM clock frequency */
// CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime
.period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */
.callback = NULL, /* no callback, the hardware directly toggles the pin */
.channels =
{
#if AUDIO_PWM_CHANNEL == 4
{PWM_OUTPUT_DISABLED, NULL}, /* channel 0 -> TIMx_CH1 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */
{PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */
{PWM_OUTPUT_ACTIVE_HIGH, NULL} /* channel 3 -> TIMx_CH4 */
#elif AUDIO_PWM_CHANNEL == 3
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH3 */
{PWM_OUTPUT_DISABLED, NULL}
#elif AUDIO_PWM_CHANNEL == 2
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH2 */
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL}
#else /*fallback to CH1 */
{PWM_OUTPUT_ACTIVE_HIGH, NULL}, /* TIMx_CH1 */
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL},
{PWM_OUTPUT_DISABLED, NULL}
#endif
},
};
static PWMConfig pwmCFG = {.frequency = AUDIO_PWM_COUNTER_FREQUENCY, /* PWM clock frequency */
.period = 2,
.callback = NULL,
.channels = {[(AUDIO_PWM_CHANNEL - 1)] = {.mode = AUDIO_PWM_OUTPUT_MODE, .callback = NULL}}};

static float channel_1_frequency = 0.0f;
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;

if (freq <= 0.0) // a pause/rest has freq=0
return;

pwmcnt_t period = (pwmCFG.frequency / freq);
pwmChangePeriod(&AUDIO_PWM_DRIVER, period);
pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1,
// adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH
PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100));
void channel_1_set_frequency(float freq) {
channel_1_frequency = freq;
pwmcnt_t period;
pwmcnt_t width;

if (freq <= 0.0) {
period = 2;
width = 0;
} else {
period = (pwmCFG.frequency / freq);
width = (pwmcnt_t)(((period) * (pwmcnt_t)((100 - note_timbre) * 100)) / (pwmcnt_t)(10000));
}
chSysLockFromISR();
pwmChangePeriodI(&AUDIO_PWM_DRIVER, period);
pwmEnableChannelI(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, width);
chSysUnlockFromISR();
}

float channel_1_get_frequency(void) {
Expand All @@ -93,56 +68,58 @@ void channel_1_start(void) {

void channel_1_stop(void) {
pwmStop(&AUDIO_PWM_DRIVER);
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);
pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, 0);
pwmStop(&AUDIO_PWM_DRIVER);
}

static void gpt_callback(GPTDriver *gptp);
GPTConfig gptCFG = {
/* a whole note is one beat, which is - per definition in musical_notes.h - set to 64
the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4
the tempo (which might vary!) is in bpm (beats per minute)
therefore: if the timer ticks away at .frequency = (60*64)Hz,
and the .interval counts from 64 downwards - audio_update_state is
called just often enough to not miss any notes
*/
.frequency = 60 * 64,
.callback = gpt_callback,
};

void audio_driver_initialize(void) {
static virtual_timer_t audio_vt;
static void audio_callback(virtual_timer_t *vtp, void *p);

// a regular timer task, that checks the note to be currently played and updates
// the pwm to output that frequency.
static void audio_callback(virtual_timer_t *vtp, void *p) {
float freq; // TODO: freq_alt

if (audio_update_state()) {
freq = audio_get_processed_frequency(0); // freq_alt would be index=1
channel_1_set_frequency(freq);
}

chSysLockFromISR();
chVTSetI(&audio_vt, TIME_MS2I(16), audio_callback, NULL);
chSysUnlockFromISR();
}

void audio_driver_initialize_impl(void) {
pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG);

// connect the AUDIO_PIN to the PWM hardware
#if defined(USE_GPIOV1) // STM32F103C8
palSetLineMode(AUDIO_PIN, PAL_MODE_ALTERNATE_PUSHPULL);
#if defined(USE_GPIOV1) // STM32F103C8, RP2040
palSetLineMode(AUDIO_PIN, AUDIO_PWM_PAL_MODE);
#else // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command)
palSetLineMode(AUDIO_PIN, PAL_MODE_ALTERNATE(AUDIO_PWM_PAL_MODE));
#endif

gptStart(&AUDIO_STATE_TIMER, &gptCFG);
chVTObjectInit(&audio_vt);
}

void audio_driver_start(void) {
void audio_driver_start_impl(void) {
channel_1_stop();
channel_1_start();

if (playing_note || playing_melody) {
gptStartContinuous(&AUDIO_STATE_TIMER, 64);
if ((playing_note || playing_melody) && !chVTIsArmed(&audio_vt)) {
// a whole note is one beat, which is - per definition in
// musical_notes.h - set to 64 the longest note is
// BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4 the tempo (which
// might vary!) is in bpm (beats per minute) therefore: if the timer
// ticks away at 64Hz (~16.6ms) audio_update_state is called just often
// enough to not miss any notes
chVTSet(&audio_vt, TIME_MS2I(16), audio_callback, NULL);
}
}

void audio_driver_stop(void) {
void audio_driver_stop_impl(void) {
channel_1_stop();
gptStopTimer(&AUDIO_STATE_TIMER);
}

/* a regular timer task, that checks the note to be currently played
* and updates the pwm to output that frequency
*/
static void gpt_callback(GPTDriver *gptp) {
float freq; // TODO: freq_alt

if (audio_update_state()) {
freq = audio_get_processed_frequency(0); // freq_alt would be index=1
channel_1_set_frequency(freq);
}
chVTReset(&audio_vt);
}
14 changes: 13 additions & 1 deletion quantum/audio/audio.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,18 @@ static bool audio_initialized = false;
static bool audio_driver_stopped = true;
audio_config_t audio_config;

void audio_driver_initialize(void) {
audio_driver_initialize_impl();
}

void audio_driver_stop(void) {
audio_driver_stop_impl();
}

void audio_driver_start(void) {
audio_driver_start_impl();
}

void audio_init() {
if (audio_initialized) {
return;
Expand All @@ -124,7 +136,7 @@ void audio_init() {
}
audio_config.raw = eeconfig_read_audio();
#else // EEPROM settings
audio_config.enable = true;
audio_config.enable = true;
# ifdef AUDIO_CLICKY_ON
audio_config.clicky_enable = true;
# endif
Expand Down
6 changes: 3 additions & 3 deletions quantum/audio/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,9 @@ void audio_startup(void);
// hardware interface

// implementation in the driver_avr/arm_* respective parts
void audio_driver_initialize(void);
void audio_driver_start(void);
void audio_driver_stop(void);
void audio_driver_initialize_impl(void);
void audio_driver_start_impl(void);
void audio_driver_stop_impl(void);

/**
* @brief get the number of currently active tones
Expand Down