From d96380e65496912e0f68e6531565f4b45efd1623 Mon Sep 17 00:00:00 2001 From: Joel Challis Date: Sat, 21 Mar 2020 05:20:04 +0000 Subject: Initial arm->chibios pass - simplify some platform logic (#8450) --- build_keyboard.mk | 5 +- build_test.mk | 1 + common_features.mk | 12 +- drivers/arm/analog.c | 276 ----------- drivers/arm/analog.h | 41 -- drivers/arm/i2c_master.c | 114 ----- drivers/arm/i2c_master.h | 106 ----- drivers/arm/ws2812.c | 95 ---- drivers/arm/ws2812.h | 16 - drivers/arm/ws2812_pwm.c | 207 -------- drivers/arm/ws2812_spi.c | 90 ---- drivers/chibios/analog.c | 276 +++++++++++ drivers/chibios/analog.h | 41 ++ drivers/chibios/i2c_master.c | 114 +++++ drivers/chibios/i2c_master.h | 106 +++++ drivers/chibios/ws2812.c | 95 ++++ drivers/chibios/ws2812.h | 16 + drivers/chibios/ws2812_pwm.c | 207 ++++++++ drivers/chibios/ws2812_spi.c | 90 ++++ keyboards/hs60/v2/ansi/rules.mk | 2 +- keyboards/hs60/v2/hhkb/rules.mk | 2 +- keyboards/hs60/v2/iso/rules.mk | 2 +- keyboards/nk65/rules.mk | 2 +- keyboards/wilba_tech/wt_rgb_backlight.c | 2 +- quantum/audio/audio.c | 805 -------------------------------- quantum/audio/audio_arm.c | 702 ---------------------------- quantum/audio/audio_avr.c | 805 ++++++++++++++++++++++++++++++++ quantum/audio/audio_chibios.c | 702 ++++++++++++++++++++++++++++ quantum/backlight/backlight_arm.c | 184 -------- quantum/backlight/backlight_chibios.c | 184 ++++++++ tmk_core/chibios.mk | 2 +- tmk_core/common.mk | 14 +- 32 files changed, 2651 insertions(+), 2665 deletions(-) delete mode 100644 drivers/arm/analog.c delete mode 100644 drivers/arm/analog.h delete mode 100644 drivers/arm/i2c_master.c delete mode 100644 drivers/arm/i2c_master.h delete mode 100644 drivers/arm/ws2812.c delete mode 100644 drivers/arm/ws2812.h delete mode 100644 drivers/arm/ws2812_pwm.c delete mode 100644 drivers/arm/ws2812_spi.c create mode 100644 drivers/chibios/analog.c create mode 100644 drivers/chibios/analog.h create mode 100644 drivers/chibios/i2c_master.c create mode 100644 drivers/chibios/i2c_master.h create mode 100644 drivers/chibios/ws2812.c create mode 100644 drivers/chibios/ws2812.h create mode 100644 drivers/chibios/ws2812_pwm.c create mode 100644 drivers/chibios/ws2812_spi.c delete mode 100644 quantum/audio/audio.c delete mode 100644 quantum/audio/audio_arm.c create mode 100644 quantum/audio/audio_avr.c create mode 100644 quantum/audio/audio_chibios.c delete mode 100644 quantum/backlight/backlight_arm.c create mode 100644 quantum/backlight/backlight_chibios.c diff --git a/build_keyboard.mk b/build_keyboard.mk index b086420653..bfadede376 100644 --- a/build_keyboard.mk +++ b/build_keyboard.mk @@ -231,13 +231,16 @@ endif # We can assume a ChibiOS target When MCU_FAMILY is defined since it's # not used for LUFA ifdef MCU_FAMILY - FIRMWARE_FORMAT?=bin PLATFORM=CHIBIOS + PLATFORM_KEY=chibios + FIRMWARE_FORMAT?=bin else ifdef ARM_ATSAM PLATFORM=ARM_ATSAM + PLATFORM_KEY=arm_atsam FIRMWARE_FORMAT=bin else PLATFORM=AVR + PLATFORM_KEY=avr FIRMWARE_FORMAT?=hex endif diff --git a/build_test.mk b/build_test.mk index cac2cba509..d13d9a515b 100644 --- a/build_test.mk +++ b/build_test.mk @@ -41,6 +41,7 @@ all: elf VPATH += $(COMMON_VPATH) PLATFORM:=TEST +PLATFORM_KEY:=test ifneq ($(filter $(FULL_TESTS),$(TEST)),) include tests/$(TEST)/rules.mk diff --git a/common_features.mk b/common_features.mk index b71dbc77e2..64ddc85fd3 100644 --- a/common_features.mk +++ b/common_features.mk @@ -35,11 +35,7 @@ ifeq ($(strip $(AUDIO_ENABLE)), yes) MUSIC_ENABLE := 1 SRC += $(QUANTUM_DIR)/process_keycode/process_audio.c SRC += $(QUANTUM_DIR)/process_keycode/process_clicky.c - ifeq ($(PLATFORM),AVR) - SRC += $(QUANTUM_DIR)/audio/audio.c - else - SRC += $(QUANTUM_DIR)/audio/audio_arm.c - endif + SRC += $(QUANTUM_DIR)/audio/audio_$(PLATFORM_KEY).c SRC += $(QUANTUM_DIR)/audio/voices.c SRC += $(QUANTUM_DIR)/audio/luts.c endif @@ -315,11 +311,7 @@ ifeq ($(strip $(BACKLIGHT_ENABLE)), yes) else SRC += $(QUANTUM_DIR)/backlight/backlight_driver_common.c ifeq ($(strip $(BACKLIGHT_DRIVER)), pwm) - ifeq ($(PLATFORM),AVR) - SRC += $(QUANTUM_DIR)/backlight/backlight_avr.c - else - SRC += $(QUANTUM_DIR)/backlight/backlight_arm.c - endif + SRC += $(QUANTUM_DIR)/backlight/backlight_$(PLATFORM_KEY).c else SRC += $(QUANTUM_DIR)/backlight/backlight_$(strip $(BACKLIGHT_DRIVER)).c endif diff --git a/drivers/arm/analog.c b/drivers/arm/analog.c deleted file mode 100644 index 6f6db64010..0000000000 --- a/drivers/arm/analog.c +++ /dev/null @@ -1,276 +0,0 @@ -/* Copyright 2019 Drew Mills - * - * 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 . - */ - -#include "quantum.h" -#include "analog.h" -#include "ch.h" -#include - -#if !HAL_USE_ADC -# error "You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC." -#endif - -#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4 -# error "You need to set one of the 'STM32_ADC_USE_ADCx' settings to TRUE in your mcuconf.h to use the ADC." -#endif - -#if STM32_ADC_DUAL_MODE -# error "STM32 ADC Dual Mode is not supported at this time." -#endif - -#if STM32_ADCV3_OVERSAMPLING -# error "STM32 ADCV3 Oversampling is not supported at this time." -#endif - -// Otherwise assume V3 -#if defined(STM32F0XX) || defined(STM32L0XX) -# define USE_ADCV1 -#elif defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX) -# define USE_ADCV2 -#endif - -// BODGE to make v2 look like v1,3 and 4 -#ifdef USE_ADCV2 -# if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_3) -# define ADC_SMPR_SMP_1P5 ADC_SAMPLE_3 -# define ADC_SMPR_SMP_7P5 ADC_SAMPLE_15 -# define ADC_SMPR_SMP_13P5 ADC_SAMPLE_28 -# define ADC_SMPR_SMP_28P5 ADC_SAMPLE_56 -# define ADC_SMPR_SMP_41P5 ADC_SAMPLE_84 -# define ADC_SMPR_SMP_55P5 ADC_SAMPLE_112 -# define ADC_SMPR_SMP_71P5 ADC_SAMPLE_144 -# define ADC_SMPR_SMP_239P5 ADC_SAMPLE_480 -# endif - -# if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_1P5) -# define ADC_SMPR_SMP_1P5 ADC_SAMPLE_1P5 -# define ADC_SMPR_SMP_7P5 ADC_SAMPLE_7P5 -# define ADC_SMPR_SMP_13P5 ADC_SAMPLE_13P5 -# define ADC_SMPR_SMP_28P5 ADC_SAMPLE_28P5 -# define ADC_SMPR_SMP_41P5 ADC_SAMPLE_41P5 -# define ADC_SMPR_SMP_55P5 ADC_SAMPLE_55P5 -# define ADC_SMPR_SMP_71P5 ADC_SAMPLE_71P5 -# define ADC_SMPR_SMP_239P5 ADC_SAMPLE_239P5 -# endif - -// we still sample at 12bit, but scale down to the requested bit range -# define ADC_CFGR1_RES_12BIT 12 -# define ADC_CFGR1_RES_10BIT 10 -# define ADC_CFGR1_RES_8BIT 8 -# define ADC_CFGR1_RES_6BIT 6 -#endif - -/* User configurable ADC options */ -#ifndef ADC_COUNT -# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F4XX) -# define ADC_COUNT 1 -# elif defined(STM32F3XX) -# define ADC_COUNT 4 -# else -# error "ADC_COUNT has not been set for this ARM microcontroller." -# endif -#endif - -#ifndef ADC_NUM_CHANNELS -# define ADC_NUM_CHANNELS 1 -#elif ADC_NUM_CHANNELS != 1 -# error "The ARM ADC implementation currently only supports reading one channel at a time." -#endif - -#ifndef ADC_BUFFER_DEPTH -# define ADC_BUFFER_DEPTH 1 -#endif - -// For more sampling rate options, look at hal_adc_lld.h in ChibiOS -#ifndef ADC_SAMPLING_RATE -# define ADC_SAMPLING_RATE ADC_SMPR_SMP_1P5 -#endif - -// Options are 12, 10, 8, and 6 bit. -#ifndef ADC_RESOLUTION -# define ADC_RESOLUTION ADC_CFGR1_RES_10BIT -#endif - -static ADCConfig adcCfg = {}; -static adcsample_t sampleBuffer[ADC_NUM_CHANNELS * ADC_BUFFER_DEPTH]; - -// Initialize to max number of ADCs, set to empty object to initialize all to false. -static bool adcInitialized[ADC_COUNT] = {}; - -// TODO: add back TR handling??? -static ADCConversionGroup adcConversionGroup = { - .circular = FALSE, - .num_channels = (uint16_t)(ADC_NUM_CHANNELS), -#if defined(USE_ADCV1) - .cfgr1 = ADC_CFGR1_CONT | ADC_RESOLUTION, - .smpr = ADC_SAMPLING_RATE, -#elif defined(USE_ADCV2) -# if !defined(STM32F1XX) - .cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without... -# endif - .smpr2 = ADC_SMPR2_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN9(ADC_SAMPLING_RATE), - .smpr1 = ADC_SMPR1_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN15(ADC_SAMPLING_RATE), -#else - .cfgr = ADC_CFGR_CONT | ADC_RESOLUTION, - .smpr = {ADC_SMPR1_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN9(ADC_SAMPLING_RATE), ADC_SMPR2_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN15(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN16(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN17(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN18(ADC_SAMPLING_RATE)}, -#endif -}; - -// clang-format off -__attribute__((weak)) adc_mux pinToMux(pin_t pin) { - switch (pin) { -#if defined(STM32F0XX) - case A0: return TO_MUX( ADC_CHSELR_CHSEL0, 0 ); - case A1: return TO_MUX( ADC_CHSELR_CHSEL1, 0 ); - case A2: return TO_MUX( ADC_CHSELR_CHSEL2, 0 ); - case A3: return TO_MUX( ADC_CHSELR_CHSEL3, 0 ); - case A4: return TO_MUX( ADC_CHSELR_CHSEL4, 0 ); - case A5: return TO_MUX( ADC_CHSELR_CHSEL5, 0 ); - case A6: return TO_MUX( ADC_CHSELR_CHSEL6, 0 ); - case A7: return TO_MUX( ADC_CHSELR_CHSEL7, 0 ); - case B0: return TO_MUX( ADC_CHSELR_CHSEL8, 0 ); - case B1: return TO_MUX( ADC_CHSELR_CHSEL9, 0 ); - case C0: return TO_MUX( ADC_CHSELR_CHSEL10, 0 ); - case C1: return TO_MUX( ADC_CHSELR_CHSEL11, 0 ); - case C2: return TO_MUX( ADC_CHSELR_CHSEL12, 0 ); - case C3: return TO_MUX( ADC_CHSELR_CHSEL13, 0 ); - case C4: return TO_MUX( ADC_CHSELR_CHSEL14, 0 ); - case C5: return TO_MUX( ADC_CHSELR_CHSEL15, 0 ); -#elif defined(STM32F3XX) - case A0: return TO_MUX( ADC_CHANNEL_IN1, 0 ); - case A1: return TO_MUX( ADC_CHANNEL_IN2, 0 ); - case A2: return TO_MUX( ADC_CHANNEL_IN3, 0 ); - case A3: return TO_MUX( ADC_CHANNEL_IN4, 0 ); - case A4: return TO_MUX( ADC_CHANNEL_IN1, 1 ); - case A5: return TO_MUX( ADC_CHANNEL_IN2, 1 ); - case A6: return TO_MUX( ADC_CHANNEL_IN3, 1 ); - case A7: return TO_MUX( ADC_CHANNEL_IN4, 1 ); - case B0: return TO_MUX( ADC_CHANNEL_IN12, 2 ); - case B1: return TO_MUX( ADC_CHANNEL_IN1, 2 ); - case B2: return TO_MUX( ADC_CHANNEL_IN12, 1 ); - case B12: return TO_MUX( ADC_CHANNEL_IN2, 3 ); - case B13: return TO_MUX( ADC_CHANNEL_IN3, 3 ); - case B14: return TO_MUX( ADC_CHANNEL_IN4, 3 ); - case B15: return TO_MUX( ADC_CHANNEL_IN5, 3 ); - case C0: return TO_MUX( ADC_CHANNEL_IN6, 0 ); // Can also be ADC2 - case C1: return TO_MUX( ADC_CHANNEL_IN7, 0 ); // Can also be ADC2 - case C2: return TO_MUX( ADC_CHANNEL_IN8, 0 ); // Can also be ADC2 - case C3: return TO_MUX( ADC_CHANNEL_IN9, 0 ); // Can also be ADC2 - case C4: return TO_MUX( ADC_CHANNEL_IN5, 1 ); - case C5: return TO_MUX( ADC_CHANNEL_IN11, 1 ); - case D8: return TO_MUX( ADC_CHANNEL_IN12, 3 ); - case D9: return TO_MUX( ADC_CHANNEL_IN13, 3 ); - case D10: return TO_MUX( ADC_CHANNEL_IN7, 2 ); // Can also be ADC4 - case D11: return TO_MUX( ADC_CHANNEL_IN8, 2 ); // Can also be ADC4 - case D12: return TO_MUX( ADC_CHANNEL_IN9, 2 ); // Can also be ADC4 - case D13: return TO_MUX( ADC_CHANNEL_IN10, 2 ); // Can also be ADC4 - case D14: return TO_MUX( ADC_CHANNEL_IN11, 2 ); // Can also be ADC4 - case E7: return TO_MUX( ADC_CHANNEL_IN13, 2 ); - case E8: return TO_MUX( ADC_CHANNEL_IN6, 2 ); // Can also be ADC4 - case E9: return TO_MUX( ADC_CHANNEL_IN2, 2 ); - case E10: return TO_MUX( ADC_CHANNEL_IN14, 2 ); - case E11: return TO_MUX( ADC_CHANNEL_IN15, 2 ); - case E12: return TO_MUX( ADC_CHANNEL_IN16, 2 ); - case E13: return TO_MUX( ADC_CHANNEL_IN3, 2 ); - case E14: return TO_MUX( ADC_CHANNEL_IN1, 3 ); - case E15: return TO_MUX( ADC_CHANNEL_IN2, 3 ); - case F2: return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2 - case F4: return TO_MUX( ADC_CHANNEL_IN5, 0 ); -#elif defined(STM32F4XX) // TODO: add all pins - case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); - //case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 ); -#elif defined(STM32F1XX) // TODO: add all pins - case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); -#endif - } - - // return an adc that would never be used so intToADCDriver will bail out - return TO_MUX(0, 0xFF); -} -// clang-format on - -static inline ADCDriver* intToADCDriver(uint8_t adcInt) { - switch (adcInt) { -#if STM32_ADC_USE_ADC1 - case 0: - return &ADCD1; -#endif -#if STM32_ADC_USE_ADC2 - case 1: - return &ADCD2; -#endif -#if STM32_ADC_USE_ADC3 - case 2: - return &ADCD3; -#endif -#if STM32_ADC_USE_ADC4 - case 3: - return &ADCD4; -#endif - } - - return NULL; -} - -static inline void manageAdcInitializationDriver(uint8_t adc, ADCDriver* adcDriver) { - if (!adcInitialized[adc]) { - adcStart(adcDriver, &adcCfg); - adcInitialized[adc] = true; - } -} - -int16_t analogReadPin(pin_t pin) { - palSetLineMode(pin, PAL_MODE_INPUT_ANALOG); - - return adc_read(pinToMux(pin)); -} - -int16_t analogReadPinAdc(pin_t pin, uint8_t adc) { - palSetLineMode(pin, PAL_MODE_INPUT_ANALOG); - - adc_mux target = pinToMux(pin); - target.adc = adc; - return adc_read(target); -} - -int16_t adc_read(adc_mux mux) { -#if defined(USE_ADCV1) - // TODO: fix previous assumption of only 1 input... - adcConversionGroup.chselr = 1 << mux.input; /*no macro to convert N to ADC_CHSELR_CHSEL1*/ -#elif defined(USE_ADCV2) - adcConversionGroup.sqr3 = ADC_SQR3_SQ1_N(mux.input); -#else - adcConversionGroup.sqr[0] = ADC_SQR1_SQ1_N(mux.input); -#endif - - ADCDriver* targetDriver = intToADCDriver(mux.adc); - if (!targetDriver) { - return 0; - } - - manageAdcInitializationDriver(mux.adc, targetDriver); - if (adcConvert(targetDriver, &adcConversionGroup, &sampleBuffer[0], ADC_BUFFER_DEPTH) != MSG_OK) { - return 0; - } - -#ifdef USE_ADCV2 - // fake 12-bit -> N-bit scale - return (*sampleBuffer) >> (12 - ADC_RESOLUTION); -#else - // already handled as part of adcConvert - return *sampleBuffer; -#endif -} diff --git a/drivers/arm/analog.h b/drivers/arm/analog.h deleted file mode 100644 index e61c394265..0000000000 --- a/drivers/arm/analog.h +++ /dev/null @@ -1,41 +0,0 @@ -/* Copyright 2019 Drew Mills - * - * 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 . - */ - -#pragma once - -#include -#include "quantum.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - uint16_t input; - uint8_t adc; -} adc_mux; -#define TO_MUX(i, a) \ - (adc_mux) { i, a } - -int16_t analogReadPin(pin_t pin); -int16_t analogReadPinAdc(pin_t pin, uint8_t adc); -adc_mux pinToMux(pin_t pin); - -int16_t adc_read(adc_mux mux); - -#ifdef __cplusplus -} -#endif diff --git a/drivers/arm/i2c_master.c b/drivers/arm/i2c_master.c deleted file mode 100644 index ede915fa4a..0000000000 --- a/drivers/arm/i2c_master.c +++ /dev/null @@ -1,114 +0,0 @@ -/* Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * - * 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 . - */ - -/* This library is only valid for STM32 processors. - * This library follows the convention of the AVR i2c_master library. - * As a result addresses are expected to be already shifted (addr << 1). - * I2CD1 is the default driver which corresponds to pins B6 and B7. This - * can be changed. - * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that - * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. Pins B6 and B7 are used - * but using any other I2C pins should be trivial. - */ -#include "quantum.h" -#include "i2c_master.h" -#include -#include - -static uint8_t i2c_address; - -static const I2CConfig i2cconfig = { -#ifdef USE_I2CV1 - I2C1_OPMODE, - I2C1_CLOCK_SPEED, - I2C1_DUTY_CYCLE, -#else - // This configures the I2C clock to 400khz assuming a 72Mhz clock - // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html - STM32_TIMINGR_PRESC(I2C1_TIMINGR_PRESC) | STM32_TIMINGR_SCLDEL(I2C1_TIMINGR_SCLDEL) | STM32_TIMINGR_SDADEL(I2C1_TIMINGR_SDADEL) | STM32_TIMINGR_SCLH(I2C1_TIMINGR_SCLH) | STM32_TIMINGR_SCLL(I2C1_TIMINGR_SCLL), 0, 0 -#endif -}; - -static i2c_status_t chibios_to_qmk(const msg_t* status) { - switch (*status) { - case I2C_NO_ERROR: - return I2C_STATUS_SUCCESS; - case I2C_TIMEOUT: - return I2C_STATUS_TIMEOUT; - // I2C_BUS_ERROR, I2C_ARBITRATION_LOST, I2C_ACK_FAILURE, I2C_OVERRUN, I2C_PEC_ERROR, I2C_SMB_ALERT - default: - return I2C_STATUS_ERROR; - } -} - -__attribute__((weak)) void i2c_init(void) { - // Try releasing special pins for a short time - palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_INPUT); - palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_INPUT); - - chThdSleepMilliseconds(10); -#if defined(USE_GPIOV1) - palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); - palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); -#else - palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); - palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); -#endif -} - -i2c_status_t i2c_start(uint8_t address) { - i2c_address = address; - i2cStart(&I2C_DRIVER, &i2cconfig); - return I2C_STATUS_SUCCESS; -} - -i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) { - i2c_address = address; - i2cStart(&I2C_DRIVER, &i2cconfig); - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); -} - -i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) { - i2c_address = address; - i2cStart(&I2C_DRIVER, &i2cconfig); - msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); -} - -i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { - i2c_address = devaddr; - i2cStart(&I2C_DRIVER, &i2cconfig); - - uint8_t complete_packet[length + 1]; - for (uint8_t i = 0; i < length; i++) { - complete_packet[i + 1] = data[i]; - } - complete_packet[0] = regaddr; - - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); -} - -i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { - i2c_address = devaddr; - i2cStart(&I2C_DRIVER, &i2cconfig); - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®addr, 1, data, length, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); -} - -void i2c_stop(void) { i2cStop(&I2C_DRIVER); } diff --git a/drivers/arm/i2c_master.h b/drivers/arm/i2c_master.h deleted file mode 100644 index 3d3891289f..0000000000 --- a/drivers/arm/i2c_master.h +++ /dev/null @@ -1,106 +0,0 @@ -/* Copyright 2018 Jack Humbert - * Copyright 2018 Yiancar - * - * 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 . - */ - -/* This library follows the convention of the AVR i2c_master library. - * As a result addresses are expected to be already shifted (addr << 1). - * I2CD1 is the default driver which corresponds to pins B6 and B7. This - * can be changed. - * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that - * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. - */ -#pragma once - -#include "ch.h" -#include - -#ifdef I2C1_BANK -# define I2C1_SCL_BANK I2C1_BANK -# define I2C1_SDA_BANK I2C1_BANK -#endif - -#ifndef I2C1_SCL_BANK -# define I2C1_SCL_BANK GPIOB -#endif - -#ifndef I2C1_SDA_BANK -# define I2C1_SDA_BANK GPIOB -#endif - -#ifndef I2C1_SCL -# define I2C1_SCL 6 -#endif -#ifndef I2C1_SDA -# define I2C1_SDA 7 -#endif - -#ifdef USE_I2CV1 -# ifndef I2C1_OPMODE -# define I2C1_OPMODE OPMODE_I2C -# endif -# ifndef I2C1_CLOCK_SPEED -# define I2C1_CLOCK_SPEED 100000 /* 400000 */ -# endif -# ifndef I2C1_DUTY_CYCLE -# define I2C1_DUTY_CYCLE STD_DUTY_CYCLE /* FAST_DUTY_CYCLE_2 */ -# endif -#else -// The default timing values below configures the I2C clock to 400khz assuming a 72Mhz clock -// For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html -# ifndef I2C1_TIMINGR_PRESC -# define I2C1_TIMINGR_PRESC 0U -# endif -# ifndef I2C1_TIMINGR_SCLDEL -# define I2C1_TIMINGR_SCLDEL 7U -# endif -# ifndef I2C1_TIMINGR_SDADEL -# define I2C1_TIMINGR_SDADEL 0U -# endif -# ifndef I2C1_TIMINGR_SCLH -# define I2C1_TIMINGR_SCLH 38U -# endif -# ifndef I2C1_TIMINGR_SCLL -# define I2C1_TIMINGR_SCLL 129U -# endif -#endif - -#ifndef I2C_DRIVER -# define I2C_DRIVER I2CD1 -#endif - -#ifndef USE_GPIOV1 -// The default PAL alternate modes are used to signal that the pins are used for I2C -# ifndef I2C1_SCL_PAL_MODE -# define I2C1_SCL_PAL_MODE 4 -# endif -# ifndef I2C1_SDA_PAL_MODE -# define I2C1_SDA_PAL_MODE 4 -# endif -#endif - -typedef int16_t i2c_status_t; - -#define I2C_STATUS_SUCCESS (0) -#define I2C_STATUS_ERROR (-1) -#define I2C_STATUS_TIMEOUT (-2) - -void i2c_init(void); -i2c_status_t i2c_start(uint8_t address); -i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout); -i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); -i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); -i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); -void i2c_stop(void); diff --git a/drivers/arm/ws2812.c b/drivers/arm/ws2812.c deleted file mode 100644 index bdca565d88..0000000000 --- a/drivers/arm/ws2812.c +++ /dev/null @@ -1,95 +0,0 @@ -#include "quantum.h" -#include "ws2812.h" -#include "ch.h" -#include "hal.h" - -/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */ - -#ifndef NOP_FUDGE -# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) -# define NOP_FUDGE 0.4 -# else -# error("NOP_FUDGE configuration required") -# define NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot -# endif -#endif - -#define NUMBER_NOPS 6 -#define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE) -#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives -#define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC) -#define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE) - -#define wait_ns(x) \ - do { \ - for (int i = 0; i < NS_TO_CYCLES(x); i++) { \ - __asm__ volatile("nop\n\t" \ - "nop\n\t" \ - "nop\n\t" \ - "nop\n\t" \ - "nop\n\t" \ - "nop\n\t"); \ - } \ - } while (0) - -// These are the timing constraints taken mostly from the WS2812 datasheets -// These are chosen to be conservative and avoid problems rather than for maximum throughput - -#define T1H 900 // Width of a 1 bit in ns -#define T1L (1250 - T1H) // Width of a 1 bit in ns - -#define T0H 350 // Width of a 0 bit in ns -#define T0L (1250 - T0H) // Width of a 0 bit in ns - -// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased -// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time. -#define RES 10000 // Width of the low gap between bits to cause a frame to latch - -void sendByte(uint8_t byte) { - // WS2812 protocol wants most significant bits first - for (unsigned char bit = 0; bit < 8; bit++) { - bool is_one = byte & (1 << (7 - bit)); - // using something like wait_ns(is_one ? T1L : T0L) here throws off timings - if (is_one) { - // 1 - writePinHigh(RGB_DI_PIN); - wait_ns(T1H); - writePinLow(RGB_DI_PIN); - wait_ns(T1L); - } else { - // 0 - writePinHigh(RGB_DI_PIN); - wait_ns(T0H); - writePinLow(RGB_DI_PIN); - wait_ns(T0L); - } - } -} - -void ws2812_init(void) { setPinOutput(RGB_DI_PIN); } - -// Setleds for standard RGB -void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) { - static bool s_init = false; - if (!s_init) { - ws2812_init(); - s_init = true; - } - - // this code is very time dependent, so we need to disable interrupts - chSysLock(); - - for (uint8_t i = 0; i < leds; i++) { - // WS2812 protocol dictates grb order - sendByte(ledarray[i].g); - sendByte(ledarray[i].r); - sendByte(ledarray[i].b); -#ifdef RGBW - sendByte(ledarray[i].w); -#endif - } - - wait_ns(RES); - - chSysUnlock(); -} diff --git a/drivers/arm/ws2812.h b/drivers/arm/ws2812.h deleted file mode 100644 index 41c22a00b8..0000000000 --- a/drivers/arm/ws2812.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "quantum/color.h" - -/* User Interface - * - * Input: - * ledarray: An array of GRB data describing the LED colors - * number_of_leds: The number of LEDs to write - * - * The functions will perform the following actions: - * - Set the data-out pin as output - * - Send out the LED data - * - Wait 50us to reset the LEDs - */ -void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds); diff --git a/drivers/arm/ws2812_pwm.c b/drivers/arm/ws2812_pwm.c deleted file mode 100644 index 1a17210298..0000000000 --- a/drivers/arm/ws2812_pwm.c +++ /dev/null @@ -1,207 +0,0 @@ -#include "ws2812.h" -#include "quantum.h" -#include "hal.h" - -/* Adapted from https://github.com/joewa/WS2812-LED-Driver_ChibiOS/ */ - -#ifdef RGBW -# error "RGBW not supported" -#endif - -#ifndef WS2812_PWM_DRIVER -# define WS2812_PWM_DRIVER PWMD2 // TIMx -#endif -#ifndef WS2812_PWM_CHANNEL -# define WS2812_PWM_CHANNEL 2 // Channel -#endif -#ifndef WS2812_PWM_PAL_MODE -# define WS2812_PWM_PAL_MODE 2 // DI Pin's alternate function value -#endif -#ifndef WS2812_DMA_STREAM -# define WS2812_DMA_STREAM STM32_DMA1_STREAM2 // DMA Stream for TIMx_UP -#endif -#ifndef WS2812_DMA_CHANNEL -# define WS2812_DMA_CHANNEL 2 // DMA Channel for TIMx_UP -#endif - -#ifndef WS2812_PWM_TARGET_PERIOD -//# define WS2812_PWM_TARGET_PERIOD 800000 // Original code is 800k...? -# define WS2812_PWM_TARGET_PERIOD 80000 // TODO: work out why 10x less on f303/f4x1 -#endif - -/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ - -#define WS2812_PWM_FREQUENCY (STM32_SYSCLK / 2) /**< Clock frequency of PWM, must be valid with respect to system clock! */ -#define WS2812_PWM_PERIOD (WS2812_PWM_FREQUENCY / WS2812_PWM_TARGET_PERIOD) /**< Clock period in ticks. 1 / 800kHz = 1.25 uS (as per datasheet) */ - -/** - * @brief Number of bit-periods to hold the data line low at the end of a frame - * - * The reset period for each frame must be at least 50 uS; so we add in 50 bit-times - * of zeroes at the end. (50 bits)*(1.25 uS/bit) = 62.5 uS, which gives us some - * slack in the timing requirements - */ -#define WS2812_RESET_BIT_N (50) -#define WS2812_COLOR_BIT_N (RGBLED_NUM * 24) /**< Number of data bits */ -#define WS2812_BIT_N (WS2812_COLOR_BIT_N + WS2812_RESET_BIT_N) /**< Total number of bits in a frame */ - -/** - * @brief High period for a zero, in ticks - * - * Per the datasheet: - * WS2812: - * - T0H: 200 nS to 500 nS, inclusive - * - T0L: 650 nS to 950 nS, inclusive - * WS2812B: - * - T0H: 200 nS to 500 nS, inclusive - * - T0L: 750 nS to 1050 nS, inclusive - * - * The duty cycle is calculated for a high period of 350 nS. - */ -#define WS2812_DUTYCYCLE_0 (WS2812_PWM_FREQUENCY / (1000000000 / 350)) - -/** - * @brief High period for a one, in ticks - * - * Per the datasheet: - * WS2812: - * - T1H: 550 nS to 850 nS, inclusive - * - T1L: 450 nS to 750 nS, inclusive - * WS2812B: - * - T1H: 750 nS to 1050 nS, inclusive - * - T1L: 200 nS to 500 nS, inclusive - * - * The duty cycle is calculated for a high period of 800 nS. - * This is in the middle of the specifications of the WS2812 and WS2812B. - */ -#define WS2812_DUTYCYCLE_1 (WS2812_PWM_FREQUENCY / (1000000000 / 800)) - -/* --- PRIVATE MACROS ------------------------------------------------------- */ - -/** - * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given bit - * - * @param[in] led: The led index [0, @ref RGBLED_NUM) - * @param[in] byte: The byte number [0, 2] - * @param[in] bit: The bit number [0, 7] - * - * @return The bit index - */ -#define WS2812_BIT(led, byte, bit) (24 * (led) + 8 * (byte) + (7 - (bit))) - -/** - * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given red bit - * - * @note The red byte is the middle byte in the color packet - * - * @param[in] led: The led index [0, @ref RGBLED_NUM) - * @param[in] bit: The bit number [0, 7] - * - * @return The bit index - */ -#define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 1, (bit)) - -/** - * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given green bit - * - * @note The red byte is the first byte in the color packet - * - * @param[in] led: The led index [0, @ref RGBLED_NUM) - * @param[in] bit: The bit number [0, 7] - * - * @return The bit index - */ -#define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 0, (bit)) - -/** - * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given blue bit - * - * @note The red byte is the last byte in the color packet - * - * @param[in] led: The led index [0, @ref RGBLED_NUM) - * @param[in] bit: The bit index [0, 7] - * - * @return The bit index - */ -#define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 2, (bit)) - -/* --- PRIVATE VARIABLES ---------------------------------------------------- */ - -static uint32_t ws2812_frame_buffer[WS2812_BIT_N + 1]; /**< Buffer for a frame */ - -/* --- PUBLIC FUNCTIONS ----------------------------------------------------- */ -/* - * Gedanke: Double-buffer type transactions: double buffer transfers using two memory pointers for -the memory (while the DMA is reading/writing from/to a buffer, the application can -write/read to/from the other buffer). - */ - -void ws2812_init(void) { - // Initialize led frame buffer - uint32_t i; - for (i = 0; i < WS2812_COLOR_BIT_N; i++) ws2812_frame_buffer[i] = WS2812_DUTYCYCLE_0; // All color bits are zero duty cycle - for (i = 0; i < WS2812_RESET_BIT_N; i++) ws2812_frame_buffer[i + WS2812_COLOR_BIT_N] = 0; // All reset bits are zero - -#if defined(USE_GPIOV1) - palSetLineMode(RGB_DI_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); -#else - palSetLineMode(RGB_DI_PIN, PAL_MODE_ALTERNATE(WS2812_PWM_PAL_MODE) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUPDR_FLOATING); -#endif - - // PWM Configuration - //#pragma GCC diagnostic ignored "-Woverride-init" // Turn off override-init warning for this struct. We use the overriding ability to set a "default" channel config - static const PWMConfig ws2812_pwm_config = { - .frequency = WS2812_PWM_FREQUENCY, - .period = WS2812_PWM_PERIOD, // Mit dieser Periode wird UDE-Event erzeugt und ein neuer Wert (Länge WS2812_BIT_N) vom DMA ins CCR geschrieben - .callback = NULL, - .channels = - { - [0 ... 3] = {.mode = PWM_OUTPUT_DISABLED, .callback = NULL}, // Channels default to disabled - [WS2812_PWM_CHANNEL - 1] = {.mode = PWM_OUTPUT_ACTIVE_HIGH, .callback = NULL}, // Turn on the channel we care about - }, - .cr2 = 0, - .dier = TIM_DIER_UDE, // DMA on update event for next period - }; - //#pragma GCC diagnostic pop // Restore command-line warning options - - // Configure DMA - // dmaInit(); // Joe added this - dmaStreamAlloc(WS2812_DMA_STREAM - STM32_DMA1_STREAM1, 10, NULL, NULL); - dmaStreamSetPeripheral(WS2812_DMA_STREAM, &(WS2812_PWM_DRIVER.tim->CCR[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register - dmaStreamSetMemory0(WS2812_DMA_STREAM, ws2812_frame_buffer); - dmaStreamSetTransactionSize(WS2812_DMA_STREAM, WS2812_BIT_N); - dmaStreamSetMode(WS2812_DMA_STREAM, STM32_DMA_CR_CHSEL(WS2812_DMA_CHANNEL) | STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3)); - // M2P: Memory 2 Periph; PL: Priority Level - - // Start DMA - dmaStreamEnable(WS2812_DMA_STREAM); - - // Configure PWM - // NOTE: It's required that preload be enabled on the timer channel CCR register. This is currently enabled in the - // ChibiOS driver code, so we don't have to do anything special to the timer. If we did, we'd have to start the timer, - // disable counting, enable the channel, and then make whatever configuration changes we need. - pwmStart(&WS2812_PWM_DRIVER, &ws2812_pwm_config); - pwmEnableChannel(&WS2812_PWM_DRIVER, WS2812_PWM_CHANNEL - 1, 0); // Initial period is 0; output will be low until first duty cycle is DMA'd in -} - -void ws2812_write_led(uint16_t led_number, uint8_t r, uint8_t g, uint8_t b) { - // Write color to frame buffer - for (uint8_t bit = 0; bit < 8; bit++) { - ws2812_frame_buffer[WS2812_RED_BIT(led_number, bit)] = ((r >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0; - ws2812_frame_buffer[WS2812_GREEN_BIT(led_number, bit)] = ((g >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0; - ws2812_frame_buffer[WS2812_BLUE_BIT(led_number, bit)] = ((b >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0; - } -} - -// Setleds for standard RGB -void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { - static bool s_init = false; - if (!s_init) { - ws2812_init(); - s_init = true; - } - - for (uint16_t i = 0; i < leds; i++) { - ws2812_write_led(i, ledarray[i].r, ledarray[i].g, ledarray[i].b); - } -} diff --git a/drivers/arm/ws2812_spi.c b/drivers/arm/ws2812_spi.c deleted file mode 100644 index 36e08e39ed..0000000000 --- a/drivers/arm/ws2812_spi.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "quantum.h" -#include "ws2812.h" - -/* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */ - -#ifdef RGBW -# error "RGBW not supported" -#endif - -// Define the spi your LEDs are plugged to here -#ifndef WS2812_SPI -# define WS2812_SPI SPID1 -#endif - -#ifndef WS2812_SPI_MOSI_PAL_MODE -# define WS2812_SPI_MOSI_PAL_MODE 5 -#endif - -#define BYTES_FOR_LED_BYTE 4 -#define NB_COLORS 3 -#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS) -#define DATA_SIZE (BYTES_FOR_LED * RGBLED_NUM) -#define RESET_SIZE 200 -#define PREAMBLE_SIZE 4 - -static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0}; - -/* - * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to - * the ws2812b protocol, we use this helper function to translate bytes into - * 0s and 1s for the LED (with the appropriate timing). - */ -static uint8_t get_protocol_eq(uint8_t data, int pos) { - uint8_t eq = 0; - if (data & (1 << (2 * (3 - pos)))) - eq = 0b1110; - else - eq = 0b1000; - if (data & (2 << (2 * (3 - pos)))) - eq += 0b11100000; - else - eq += 0b10000000; - return eq; -} - -static void set_led_color_rgb(LED_TYPE color, int pos) { - uint8_t* tx_start = &txbuf[PREAMBLE_SIZE]; - - for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.g, j); - for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.r, j); - for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j); -} - -void ws2812_init(void) { -#if defined(USE_GPIOV1) - palSetLineMode(RGB_DI_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); -#else - palSetLineMode(RGB_DI_PIN, PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL); -#endif - - // TODO: more dynamic baudrate - static const SPIConfig spicfg = { - 0, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), - SPI_CR1_BR_1 | SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us (2.25 MHz) - }; - - spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */ - spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */ - spiSelect(&WS2812_SPI); /* Slave Select assertion. */ -} - -void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { - static bool s_init = false; - if (!s_init) { - ws2812_init(); - s_init = true; - } - - for (uint8_t i = 0; i < leds; i++) { - set_led_color_rgb(ledarray[i], i); - } - - // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues. - // Instead spiSend can be used to send synchronously (or the thread logic can be added back). -#ifdef WS2812_SPI_SYNC - spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); -#else - spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); -#endif -} diff --git a/drivers/chibios/analog.c b/drivers/chibios/analog.c new file mode 100644 index 0000000000..6f6db64010 --- /dev/null +++ b/drivers/chibios/analog.c @@ -0,0 +1,276 @@ +/* Copyright 2019 Drew Mills + * + * 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 . + */ + +#include "quantum.h" +#include "analog.h" +#include "ch.h" +#include + +#if !HAL_USE_ADC +# error "You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC." +#endif + +#if !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4 +# error "You need to set one of the 'STM32_ADC_USE_ADCx' settings to TRUE in your mcuconf.h to use the ADC." +#endif + +#if STM32_ADC_DUAL_MODE +# error "STM32 ADC Dual Mode is not supported at this time." +#endif + +#if STM32_ADCV3_OVERSAMPLING +# error "STM32 ADCV3 Oversampling is not supported at this time." +#endif + +// Otherwise assume V3 +#if defined(STM32F0XX) || defined(STM32L0XX) +# define USE_ADCV1 +#elif defined(STM32F1XX) || defined(STM32F2XX) || defined(STM32F4XX) +# define USE_ADCV2 +#endif + +// BODGE to make v2 look like v1,3 and 4 +#ifdef USE_ADCV2 +# if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_3) +# define ADC_SMPR_SMP_1P5 ADC_SAMPLE_3 +# define ADC_SMPR_SMP_7P5 ADC_SAMPLE_15 +# define ADC_SMPR_SMP_13P5 ADC_SAMPLE_28 +# define ADC_SMPR_SMP_28P5 ADC_SAMPLE_56 +# define ADC_SMPR_SMP_41P5 ADC_SAMPLE_84 +# define ADC_SMPR_SMP_55P5 ADC_SAMPLE_112 +# define ADC_SMPR_SMP_71P5 ADC_SAMPLE_144 +# define ADC_SMPR_SMP_239P5 ADC_SAMPLE_480 +# endif + +# if !defined(ADC_SMPR_SMP_1P5) && defined(ADC_SAMPLE_1P5) +# define ADC_SMPR_SMP_1P5 ADC_SAMPLE_1P5 +# define ADC_SMPR_SMP_7P5 ADC_SAMPLE_7P5 +# define ADC_SMPR_SMP_13P5 ADC_SAMPLE_13P5 +# define ADC_SMPR_SMP_28P5 ADC_SAMPLE_28P5 +# define ADC_SMPR_SMP_41P5 ADC_SAMPLE_41P5 +# define ADC_SMPR_SMP_55P5 ADC_SAMPLE_55P5 +# define ADC_SMPR_SMP_71P5 ADC_SAMPLE_71P5 +# define ADC_SMPR_SMP_239P5 ADC_SAMPLE_239P5 +# endif + +// we still sample at 12bit, but scale down to the requested bit range +# define ADC_CFGR1_RES_12BIT 12 +# define ADC_CFGR1_RES_10BIT 10 +# define ADC_CFGR1_RES_8BIT 8 +# define ADC_CFGR1_RES_6BIT 6 +#endif + +/* User configurable ADC options */ +#ifndef ADC_COUNT +# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F4XX) +# define ADC_COUNT 1 +# elif defined(STM32F3XX) +# define ADC_COUNT 4 +# else +# error "ADC_COUNT has not been set for this ARM microcontroller." +# endif +#endif + +#ifndef ADC_NUM_CHANNELS +# define ADC_NUM_CHANNELS 1 +#elif ADC_NUM_CHANNELS != 1 +# error "The ARM ADC implementation currently only supports reading one channel at a time." +#endif + +#ifndef ADC_BUFFER_DEPTH +# define ADC_BUFFER_DEPTH 1 +#endif + +// For more sampling rate options, look at hal_adc_lld.h in ChibiOS +#ifndef ADC_SAMPLING_RATE +# define ADC_SAMPLING_RATE ADC_SMPR_SMP_1P5 +#endif + +// Options are 12, 10, 8, and 6 bit. +#ifndef ADC_RESOLUTION +# define ADC_RESOLUTION ADC_CFGR1_RES_10BIT +#endif + +static ADCConfig adcCfg = {}; +static adcsample_t sampleBuffer[ADC_NUM_CHANNELS * ADC_BUFFER_DEPTH]; + +// Initialize to max number of ADCs, set to empty object to initialize all to false. +static bool adcInitialized[ADC_COUNT] = {}; + +// TODO: add back TR handling??? +static ADCConversionGroup adcConversionGroup = { + .circular = FALSE, + .num_channels = (uint16_t)(ADC_NUM_CHANNELS), +#if defined(USE_ADCV1) + .cfgr1 = ADC_CFGR1_CONT | ADC_RESOLUTION, + .smpr = ADC_SAMPLING_RATE, +#elif defined(USE_ADCV2) +# if !defined(STM32F1XX) + .cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without... +# endif + .smpr2 = ADC_SMPR2_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN9(ADC_SAMPLING_RATE), + .smpr1 = ADC_SMPR1_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN15(ADC_SAMPLING_RATE), +#else + .cfgr = ADC_CFGR_CONT | ADC_RESOLUTION, + .smpr = {ADC_SMPR1_SMP_AN0(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN1(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN2(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN3(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN4(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN5(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN6(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN7(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN8(ADC_SAMPLING_RATE) | ADC_SMPR1_SMP_AN9(ADC_SAMPLING_RATE), ADC_SMPR2_SMP_AN10(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN11(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN12(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN13(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN14(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN15(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN16(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN17(ADC_SAMPLING_RATE) | ADC_SMPR2_SMP_AN18(ADC_SAMPLING_RATE)}, +#endif +}; + +// clang-format off +__attribute__((weak)) adc_mux pinToMux(pin_t pin) { + switch (pin) { +#if defined(STM32F0XX) + case A0: return TO_MUX( ADC_CHSELR_CHSEL0, 0 ); + case A1: return TO_MUX( ADC_CHSELR_CHSEL1, 0 ); + case A2: return TO_MUX( ADC_CHSELR_CHSEL2, 0 ); + case A3: return TO_MUX( ADC_CHSELR_CHSEL3, 0 ); + case A4: return TO_MUX( ADC_CHSELR_CHSEL4, 0 ); + case A5: return TO_MUX( ADC_CHSELR_CHSEL5, 0 ); + case A6: return TO_MUX( ADC_CHSELR_CHSEL6, 0 ); + case A7: return TO_MUX( ADC_CHSELR_CHSEL7, 0 ); + case B0: return TO_MUX( ADC_CHSELR_CHSEL8, 0 ); + case B1: return TO_MUX( ADC_CHSELR_CHSEL9, 0 ); + case C0: return TO_MUX( ADC_CHSELR_CHSEL10, 0 ); + case C1: return TO_MUX( ADC_CHSELR_CHSEL11, 0 ); + case C2: return TO_MUX( ADC_CHSELR_CHSEL12, 0 ); + case C3: return TO_MUX( ADC_CHSELR_CHSEL13, 0 ); + case C4: return TO_MUX( ADC_CHSELR_CHSEL14, 0 ); + case C5: return TO_MUX( ADC_CHSELR_CHSEL15, 0 ); +#elif defined(STM32F3XX) + case A0: return TO_MUX( ADC_CHANNEL_IN1, 0 ); + case A1: return TO_MUX( ADC_CHANNEL_IN2, 0 ); + case A2: return TO_MUX( ADC_CHANNEL_IN3, 0 ); + case A3: return TO_MUX( ADC_CHANNEL_IN4, 0 ); + case A4: return TO_MUX( ADC_CHANNEL_IN1, 1 ); + case A5: return TO_MUX( ADC_CHANNEL_IN2, 1 ); + case A6: return TO_MUX( ADC_CHANNEL_IN3, 1 ); + case A7: return TO_MUX( ADC_CHANNEL_IN4, 1 ); + case B0: return TO_MUX( ADC_CHANNEL_IN12, 2 ); + case B1: return TO_MUX( ADC_CHANNEL_IN1, 2 ); + case B2: return TO_MUX( ADC_CHANNEL_IN12, 1 ); + case B12: return TO_MUX( ADC_CHANNEL_IN2, 3 ); + case B13: return TO_MUX( ADC_CHANNEL_IN3, 3 ); + case B14: return TO_MUX( ADC_CHANNEL_IN4, 3 ); + case B15: return TO_MUX( ADC_CHANNEL_IN5, 3 ); + case C0: return TO_MUX( ADC_CHANNEL_IN6, 0 ); // Can also be ADC2 + case C1: return TO_MUX( ADC_CHANNEL_IN7, 0 ); // Can also be ADC2 + case C2: return TO_MUX( ADC_CHANNEL_IN8, 0 ); // Can also be ADC2 + case C3: return TO_MUX( ADC_CHANNEL_IN9, 0 ); // Can also be ADC2 + case C4: return TO_MUX( ADC_CHANNEL_IN5, 1 ); + case C5: return TO_MUX( ADC_CHANNEL_IN11, 1 ); + case D8: return TO_MUX( ADC_CHANNEL_IN12, 3 ); + case D9: return TO_MUX( ADC_CHANNEL_IN13, 3 ); + case D10: return TO_MUX( ADC_CHANNEL_IN7, 2 ); // Can also be ADC4 + case D11: return TO_MUX( ADC_CHANNEL_IN8, 2 ); // Can also be ADC4 + case D12: return TO_MUX( ADC_CHANNEL_IN9, 2 ); // Can also be ADC4 + case D13: return TO_MUX( ADC_CHANNEL_IN10, 2 ); // Can also be ADC4 + case D14: return TO_MUX( ADC_CHANNEL_IN11, 2 ); // Can also be ADC4 + case E7: return TO_MUX( ADC_CHANNEL_IN13, 2 ); + case E8: return TO_MUX( ADC_CHANNEL_IN6, 2 ); // Can also be ADC4 + case E9: return TO_MUX( ADC_CHANNEL_IN2, 2 ); + case E10: return TO_MUX( ADC_CHANNEL_IN14, 2 ); + case E11: return TO_MUX( ADC_CHANNEL_IN15, 2 ); + case E12: return TO_MUX( ADC_CHANNEL_IN16, 2 ); + case E13: return TO_MUX( ADC_CHANNEL_IN3, 2 ); + case E14: return TO_MUX( ADC_CHANNEL_IN1, 3 ); + case E15: return TO_MUX( ADC_CHANNEL_IN2, 3 ); + case F2: return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2 + case F4: return TO_MUX( ADC_CHANNEL_IN5, 0 ); +#elif defined(STM32F4XX) // TODO: add all pins + case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); + //case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 ); +#elif defined(STM32F1XX) // TODO: add all pins + case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); +#endif + } + + // return an adc that would never be used so intToADCDriver will bail out + return TO_MUX(0, 0xFF); +} +// clang-format on + +static inline ADCDriver* intToADCDriver(uint8_t adcInt) { + switch (adcInt) { +#if STM32_ADC_USE_ADC1 + case 0: + return &ADCD1; +#endif +#if STM32_ADC_USE_ADC2 + case 1: + return &ADCD2; +#endif +#if STM32_ADC_USE_ADC3 + case 2: + return &ADCD3; +#endif +#if STM32_ADC_USE_ADC4 + case 3: + return &ADCD4; +#endif + } + + return NULL; +} + +static inline void manageAdcInitializationDriver(uint8_t adc, ADCDriver* adcDriver) { + if (!adcInitialized[adc]) { + adcStart(adcDriver, &adcCfg); + adcInitialized[adc] = true; + } +} + +int16_t analogReadPin(pin_t pin) { + palSetLineMode(pin, PAL_MODE_INPUT_ANALOG); + + return adc_read(pinToMux(pin)); +} + +int16_t analogReadPinAdc(pin_t pin, uint8_t adc) { + palSetLineMode(pin, PAL_MODE_INPUT_ANALOG); + + adc_mux target = pinToMux(pin); + target.adc = adc; + return adc_read(target); +} + +int16_t adc_read(adc_mux mux) { +#if defined(USE_ADCV1) + // TODO: fix previous assumption of only 1 input... + adcConversionGroup.chselr = 1 << mux.input; /*no macro to convert N to ADC_CHSELR_CHSEL1*/ +#elif defined(USE_ADCV2) + adcConversionGroup.sqr3 = ADC_SQR3_SQ1_N(mux.input); +#else + adcConversionGroup.sqr[0] = ADC_SQR1_SQ1_N(mux.input); +#endif + + ADCDriver* targetDriver = intToADCDriver(mux.adc); + if (!targetDriver) { + return 0; + } + + manageAdcInitializationDriver(mux.adc, targetDriver); + if (adcConvert(targetDriver, &adcConversionGroup, &sampleBuffer[0], ADC_BUFFER_DEPTH) != MSG_OK) { + return 0; + } + +#ifdef USE_ADCV2 + // fake 12-bit -> N-bit scale + return (*sampleBuffer) >> (12 - ADC_RESOLUTION); +#else + // already handled as part of adcConvert + return *sampleBuffer; +#endif +} diff --git a/drivers/chibios/analog.h b/drivers/chibios/analog.h new file mode 100644 index 0000000000..e61c394265 --- /dev/null +++ b/drivers/chibios/analog.h @@ -0,0 +1,41 @@ +/* Copyright 2019 Drew Mills + * + * 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 . + */ + +#pragma once + +#include +#include "quantum.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + uint16_t input; + uint8_t adc; +} adc_mux; +#define TO_MUX(i, a) \ + (adc_mux) { i, a } + +int16_t analogReadPin(pin_t pin); +int16_t analogReadPinAdc(pin_t pin, uint8_t adc); +adc_mux pinToMux(pin_t pin); + +int16_t adc_read(adc_mux mux); + +#ifdef __cplusplus +} +#endif diff --git a/drivers/chibios/i2c_master.c b/drivers/chibios/i2c_master.c new file mode 100644 index 0000000000..ede915fa4a --- /dev/null +++ b/drivers/chibios/i2c_master.c @@ -0,0 +1,114 @@ +/* Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * + * 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 . + */ + +/* This library is only valid for STM32 processors. + * This library follows the convention of the AVR i2c_master library. + * As a result addresses are expected to be already shifted (addr << 1). + * I2CD1 is the default driver which corresponds to pins B6 and B7. This + * can be changed. + * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that + * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. Pins B6 and B7 are used + * but using any other I2C pins should be trivial. + */ +#include "quantum.h" +#include "i2c_master.h" +#include +#include + +static uint8_t i2c_address; + +static const I2CConfig i2cconfig = { +#ifdef USE_I2CV1 + I2C1_OPMODE, + I2C1_CLOCK_SPEED, + I2C1_DUTY_CYCLE, +#else + // This configures the I2C clock to 400khz assuming a 72Mhz clock + // For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html + STM32_TIMINGR_PRESC(I2C1_TIMINGR_PRESC) | STM32_TIMINGR_SCLDEL(I2C1_TIMINGR_SCLDEL) | STM32_TIMINGR_SDADEL(I2C1_TIMINGR_SDADEL) | STM32_TIMINGR_SCLH(I2C1_TIMINGR_SCLH) | STM32_TIMINGR_SCLL(I2C1_TIMINGR_SCLL), 0, 0 +#endif +}; + +static i2c_status_t chibios_to_qmk(const msg_t* status) { + switch (*status) { + case I2C_NO_ERROR: + return I2C_STATUS_SUCCESS; + case I2C_TIMEOUT: + return I2C_STATUS_TIMEOUT; + // I2C_BUS_ERROR, I2C_ARBITRATION_LOST, I2C_ACK_FAILURE, I2C_OVERRUN, I2C_PEC_ERROR, I2C_SMB_ALERT + default: + return I2C_STATUS_ERROR; + } +} + +__attribute__((weak)) void i2c_init(void) { + // Try releasing special pins for a short time + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_INPUT); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_INPUT); + + chThdSleepMilliseconds(10); +#if defined(USE_GPIOV1) + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_STM32_ALTERNATE_OPENDRAIN); +#else + palSetPadMode(I2C1_SCL_BANK, I2C1_SCL, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); + palSetPadMode(I2C1_SDA_BANK, I2C1_SDA, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); +#endif +} + +i2c_status_t i2c_start(uint8_t address) { + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + return I2C_STATUS_SUCCESS; +} + +i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, TIME_MS2I(timeout)); + return chibios_to_qmk(&status); +} + +i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = address; + i2cStart(&I2C_DRIVER, &i2cconfig); + msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, TIME_MS2I(timeout)); + return chibios_to_qmk(&status); +} + +i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = devaddr; + i2cStart(&I2C_DRIVER, &i2cconfig); + + uint8_t complete_packet[length + 1]; + for (uint8_t i = 0; i < length; i++) { + complete_packet[i + 1] = data[i]; + } + complete_packet[0] = regaddr; + + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, TIME_MS2I(timeout)); + return chibios_to_qmk(&status); +} + +i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { + i2c_address = devaddr; + i2cStart(&I2C_DRIVER, &i2cconfig); + msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®addr, 1, data, length, TIME_MS2I(timeout)); + return chibios_to_qmk(&status); +} + +void i2c_stop(void) { i2cStop(&I2C_DRIVER); } diff --git a/drivers/chibios/i2c_master.h b/drivers/chibios/i2c_master.h new file mode 100644 index 0000000000..3d3891289f --- /dev/null +++ b/drivers/chibios/i2c_master.h @@ -0,0 +1,106 @@ +/* Copyright 2018 Jack Humbert + * Copyright 2018 Yiancar + * + * 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 . + */ + +/* This library follows the convention of the AVR i2c_master library. + * As a result addresses are expected to be already shifted (addr << 1). + * I2CD1 is the default driver which corresponds to pins B6 and B7. This + * can be changed. + * Please ensure that HAL_USE_I2C is TRUE in the halconf.h file and that + * STM32_I2C_USE_I2C1 is TRUE in the mcuconf.h file. + */ +#pragma once + +#include "ch.h" +#include + +#ifdef I2C1_BANK +# define I2C1_SCL_BANK I2C1_BANK +# define I2C1_SDA_BANK I2C1_BANK +#endif + +#ifndef I2C1_SCL_BANK +# define I2C1_SCL_BANK GPIOB +#endif + +#ifndef I2C1_SDA_BANK +# define I2C1_SDA_BANK GPIOB +#endif + +#ifndef I2C1_SCL +# define I2C1_SCL 6 +#endif +#ifndef I2C1_SDA +# define I2C1_SDA 7 +#endif + +#ifdef USE_I2CV1 +# ifndef I2C1_OPMODE +# define I2C1_OPMODE OPMODE_I2C +# endif +# ifndef I2C1_CLOCK_SPEED +# define I2C1_CLOCK_SPEED 100000 /* 400000 */ +# endif +# ifndef I2C1_DUTY_CYCLE +# define I2C1_DUTY_CYCLE STD_DUTY_CYCLE /* FAST_DUTY_CYCLE_2 */ +# endif +#else +// The default timing values below configures the I2C clock to 400khz assuming a 72Mhz clock +// For more info : https://www.st.com/en/embedded-software/stsw-stm32126.html +# ifndef I2C1_TIMINGR_PRESC +# define I2C1_TIMINGR_PRESC 0U +# endif +# ifndef I2C1_TIMINGR_SCLDEL +# define I2C1_TIMINGR_SCLDEL 7U +# endif +# ifndef I2C1_TIMINGR_SDADEL +# define I2C1_TIMINGR_SDADEL 0U +# endif +# ifndef I2C1_TIMINGR_SCLH +# define I2C1_TIMINGR_SCLH 38U +# endif +# ifndef I2C1_TIMINGR_SCLL +# define I2C1_TIMINGR_SCLL 129U +# endif +#endif + +#ifndef I2C_DRIVER +# define I2C_DRIVER I2CD1 +#endif + +#ifndef USE_GPIOV1 +// The default PAL alternate modes are used to signal that the pins are used for I2C +# ifndef I2C1_SCL_PAL_MODE +# define I2C1_SCL_PAL_MODE 4 +# endif +# ifndef I2C1_SDA_PAL_MODE +# define I2C1_SDA_PAL_MODE 4 +# endif +#endif + +typedef int16_t i2c_status_t; + +#define I2C_STATUS_SUCCESS (0) +#define I2C_STATUS_ERROR (-1) +#define I2C_STATUS_TIMEOUT (-2) + +void i2c_init(void); +i2c_status_t i2c_start(uint8_t address); +i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout); +i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); +void i2c_stop(void); diff --git a/drivers/chibios/ws2812.c b/drivers/chibios/ws2812.c new file mode 100644 index 0000000000..bdca565d88 --- /dev/null +++ b/drivers/chibios/ws2812.c @@ -0,0 +1,95 @@ +#include "quantum.h" +#include "ws2812.h" +#include "ch.h" +#include "hal.h" + +/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */ + +#ifndef NOP_FUDGE +# if defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) +# define NOP_FUDGE 0.4 +# else +# error("NOP_FUDGE configuration required") +# define NOP_FUDGE 1 // this just pleases the compile so the above error is easier to spot +# endif +#endif + +#define NUMBER_NOPS 6 +#define CYCLES_PER_SEC (STM32_SYSCLK / NUMBER_NOPS * NOP_FUDGE) +#define NS_PER_SEC (1000000000L) // Note that this has to be SIGNED since we want to be able to check for negative values of derivatives +#define NS_PER_CYCLE (NS_PER_SEC / CYCLES_PER_SEC) +#define NS_TO_CYCLES(n) ((n) / NS_PER_CYCLE) + +#define wait_ns(x) \ + do { \ + for (int i = 0; i < NS_TO_CYCLES(x); i++) { \ + __asm__ volatile("nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t" \ + "nop\n\t"); \ + } \ + } while (0) + +// These are the timing constraints taken mostly from the WS2812 datasheets +// These are chosen to be conservative and avoid problems rather than for maximum throughput + +#define T1H 900 // Width of a 1 bit in ns +#define T1L (1250 - T1H) // Width of a 1 bit in ns + +#define T0H 350 // Width of a 0 bit in ns +#define T0L (1250 - T0H) // Width of a 0 bit in ns + +// The reset gap can be 6000 ns, but depending on the LED strip it may have to be increased +// to values like 600000 ns. If it is too small, the pixels will show nothing most of the time. +#define RES 10000 // Width of the low gap between bits to cause a frame to latch + +void sendByte(uint8_t byte) { + // WS2812 protocol wants most significant bits first + for (unsigned char bit = 0; bit < 8; bit++) { + bool is_one = byte & (1 << (7 - bit)); + // using something like wait_ns(is_one ? T1L : T0L) here throws off timings + if (is_one) { + // 1 + writePinHigh(RGB_DI_PIN); + wait_ns(T1H); + writePinLow(RGB_DI_PIN); + wait_ns(T1L); + } else { + // 0 + writePinHigh(RGB_DI_PIN); + wait_ns(T0H); + writePinLow(RGB_DI_PIN); + wait_ns(T0L); + } + } +} + +void ws2812_init(void) { setPinOutput(RGB_DI_PIN); } + +// Setleds for standard RGB +void ws2812_setleds(LED_TYPE *ledarray, uint16_t leds) { + static bool s_init = false; + if (!s_init) { + ws2812_init(); + s_init = true; + } + + // this code is very time dependent, so we need to disable interrupts + chSysLock(); + + for (uint8_t i = 0; i < leds; i++) { + // WS2812 protocol dictates grb order + sendByte(ledarray[i].g); + sendByte(ledarray[i].r); + sendByte(ledarray[i].b); +#ifdef RGBW + sendByte(ledarray[i].w); +#endif + } + + wait_ns(RES); + + chSysUnlock(); +} diff --git a/drivers/chibios/ws2812.h b/drivers/chibios/ws2812.h new file mode 100644 index 0000000000..41c22a00b8 --- /dev/null +++ b/drivers/chibios/ws2812.h @@ -0,0 +1,16 @@ +#pragma once + +#include "quantum/color.h" + +/* User Interface + * + * Input: + * ledarray: An array of GRB data describing the LED colors + * number_of_leds: The number of LEDs to write + * + * The functions will perform the following actions: + * - Set the data-out pin as output + * - Send out the LED data + * - Wait 50us to reset the LEDs + */ +void ws2812_setleds(LED_TYPE *ledarray, uint16_t number_of_leds); diff --git a/drivers/chibios/ws2812_pwm.c b/drivers/chibios/ws2812_pwm.c new file mode 100644 index 0000000000..1a17210298 --- /dev/null +++ b/drivers/chibios/ws2812_pwm.c @@ -0,0 +1,207 @@ +#include "ws2812.h" +#include "quantum.h" +#include "hal.h" + +/* Adapted from https://github.com/joewa/WS2812-LED-Driver_ChibiOS/ */ + +#ifdef RGBW +# error "RGBW not supported" +#endif + +#ifndef WS2812_PWM_DRIVER +# define WS2812_PWM_DRIVER PWMD2 // TIMx +#endif +#ifndef WS2812_PWM_CHANNEL +# define WS2812_PWM_CHANNEL 2 // Channel +#endif +#ifndef WS2812_PWM_PAL_MODE +# define WS2812_PWM_PAL_MODE 2 // DI Pin's alternate function value +#endif +#ifndef WS2812_DMA_STREAM +# define WS2812_DMA_STREAM STM32_DMA1_STREAM2 // DMA Stream for TIMx_UP +#endif +#ifndef WS2812_DMA_CHANNEL +# define WS2812_DMA_CHANNEL 2 // DMA Channel for TIMx_UP +#endif + +#ifndef WS2812_PWM_TARGET_PERIOD +//# define WS2812_PWM_TARGET_PERIOD 800000 // Original code is 800k...? +# define WS2812_PWM_TARGET_PERIOD 80000 // TODO: work out why 10x less on f303/f4x1 +#endif + +/* --- PRIVATE CONSTANTS ---------------------------------------------------- */ + +#define WS2812_PWM_FREQUENCY (STM32_SYSCLK / 2) /**< Clock frequency of PWM, must be valid with respect to system clock! */ +#define WS2812_PWM_PERIOD (WS2812_PWM_FREQUENCY / WS2812_PWM_TARGET_PERIOD) /**< Clock period in ticks. 1 / 800kHz = 1.25 uS (as per datasheet) */ + +/** + * @brief Number of bit-periods to hold the data line low at the end of a frame + * + * The reset period for each frame must be at least 50 uS; so we add in 50 bit-times + * of zeroes at the end. (50 bits)*(1.25 uS/bit) = 62.5 uS, which gives us some + * slack in the timing requirements + */ +#define WS2812_RESET_BIT_N (50) +#define WS2812_COLOR_BIT_N (RGBLED_NUM * 24) /**< Number of data bits */ +#define WS2812_BIT_N (WS2812_COLOR_BIT_N + WS2812_RESET_BIT_N) /**< Total number of bits in a frame */ + +/** + * @brief High period for a zero, in ticks + * + * Per the datasheet: + * WS2812: + * - T0H: 200 nS to 500 nS, inclusive + * - T0L: 650 nS to 950 nS, inclusive + * WS2812B: + * - T0H: 200 nS to 500 nS, inclusive + * - T0L: 750 nS to 1050 nS, inclusive + * + * The duty cycle is calculated for a high period of 350 nS. + */ +#define WS2812_DUTYCYCLE_0 (WS2812_PWM_FREQUENCY / (1000000000 / 350)) + +/** + * @brief High period for a one, in ticks + * + * Per the datasheet: + * WS2812: + * - T1H: 550 nS to 850 nS, inclusive + * - T1L: 450 nS to 750 nS, inclusive + * WS2812B: + * - T1H: 750 nS to 1050 nS, inclusive + * - T1L: 200 nS to 500 nS, inclusive + * + * The duty cycle is calculated for a high period of 800 nS. + * This is in the middle of the specifications of the WS2812 and WS2812B. + */ +#define WS2812_DUTYCYCLE_1 (WS2812_PWM_FREQUENCY / (1000000000 / 800)) + +/* --- PRIVATE MACROS ------------------------------------------------------- */ + +/** + * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given bit + * + * @param[in] led: The led index [0, @ref RGBLED_NUM) + * @param[in] byte: The byte number [0, 2] + * @param[in] bit: The bit number [0, 7] + * + * @return The bit index + */ +#define WS2812_BIT(led, byte, bit) (24 * (led) + 8 * (byte) + (7 - (bit))) + +/** + * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given red bit + * + * @note The red byte is the middle byte in the color packet + * + * @param[in] led: The led index [0, @ref RGBLED_NUM) + * @param[in] bit: The bit number [0, 7] + * + * @return The bit index + */ +#define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 1, (bit)) + +/** + * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given green bit + * + * @note The red byte is the first byte in the color packet + * + * @param[in] led: The led index [0, @ref RGBLED_NUM) + * @param[in] bit: The bit number [0, 7] + * + * @return The bit index + */ +#define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 0, (bit)) + +/** + * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given blue bit + * + * @note The red byte is the last byte in the color packet + * + * @param[in] led: The led index [0, @ref RGBLED_NUM) + * @param[in] bit: The bit index [0, 7] + * + * @return The bit index + */ +#define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 2, (bit)) + +/* --- PRIVATE VARIABLES ---------------------------------------------------- */ + +static uint32_t ws2812_frame_buffer[WS2812_BIT_N + 1]; /**< Buffer for a frame */ + +/* --- PUBLIC FUNCTIONS ----------------------------------------------------- */ +/* + * Gedanke: Double-buffer type transactions: double buffer transfers using two memory pointers for +the memory (while the DMA is reading/writing from/to a buffer, the application can +write/read to/from the other buffer). + */ + +void ws2812_init(void) { + // Initialize led frame buffer + uint32_t i; + for (i = 0; i < WS2812_COLOR_BIT_N; i++) ws2812_frame_buffer[i] = WS2812_DUTYCYCLE_0; // All color bits are zero duty cycle + for (i = 0; i < WS2812_RESET_BIT_N; i++) ws2812_frame_buffer[i + WS2812_COLOR_BIT_N] = 0; // All reset bits are zero + +#if defined(USE_GPIOV1) + palSetLineMode(RGB_DI_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); +#else + palSetLineMode(RGB_DI_PIN, PAL_MODE_ALTERNATE(WS2812_PWM_PAL_MODE) | PAL_STM32_OSPEED_HIGHEST | PAL_STM32_PUPDR_FLOATING); +#endif + + // PWM Configuration + //#pragma GCC diagnostic ignored "-Woverride-init" // Turn off override-init warning for this struct. We use the overriding ability to set a "default" channel config + static const PWMConfig ws2812_pwm_config = { + .frequency = WS2812_PWM_FREQUENCY, + .period = WS2812_PWM_PERIOD, // Mit dieser Periode wird UDE-Event erzeugt und ein neuer Wert (Länge WS2812_BIT_N) vom DMA ins CCR geschrieben + .callback = NULL, + .channels = + { + [0 ... 3] = {.mode = PWM_OUTPUT_DISABLED, .callback = NULL}, // Channels default to disabled + [WS2812_PWM_CHANNEL - 1] = {.mode = PWM_OUTPUT_ACTIVE_HIGH, .callback = NULL}, // Turn on the channel we care about + }, + .cr2 = 0, + .dier = TIM_DIER_UDE, // DMA on update event for next period + }; + //#pragma GCC diagnostic pop // Restore command-line warning options + + // Configure DMA + // dmaInit(); // Joe added this + dmaStreamAlloc(WS2812_DMA_STREAM - STM32_DMA1_STREAM1, 10, NULL, NULL); + dmaStreamSetPeripheral(WS2812_DMA_STREAM, &(WS2812_PWM_DRIVER.tim->CCR[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register + dmaStreamSetMemory0(WS2812_DMA_STREAM, ws2812_frame_buffer); + dmaStreamSetTransactionSize(WS2812_DMA_STREAM, WS2812_BIT_N); + dmaStreamSetMode(WS2812_DMA_STREAM, STM32_DMA_CR_CHSEL(WS2812_DMA_CHANNEL) | STM32_DMA_CR_DIR_M2P | STM32_DMA_CR_PSIZE_WORD | STM32_DMA_CR_MSIZE_WORD | STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3)); + // M2P: Memory 2 Periph; PL: Priority Level + + // Start DMA + dmaStreamEnable(WS2812_DMA_STREAM); + + // Configure PWM + // NOTE: It's required that preload be enabled on the timer channel CCR register. This is currently enabled in the + // ChibiOS driver code, so we don't have to do anything special to the timer. If we did, we'd have to start the timer, + // disable counting, enable the channel, and then make whatever configuration changes we need. + pwmStart(&WS2812_PWM_DRIVER, &ws2812_pwm_config); + pwmEnableChannel(&WS2812_PWM_DRIVER, WS2812_PWM_CHANNEL - 1, 0); // Initial period is 0; output will be low until first duty cycle is DMA'd in +} + +void ws2812_write_led(uint16_t led_number, uint8_t r, uint8_t g, uint8_t b) { + // Write color to frame buffer + for (uint8_t bit = 0; bit < 8; bit++) { + ws2812_frame_buffer[WS2812_RED_BIT(led_number, bit)] = ((r >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0; + ws2812_frame_buffer[WS2812_GREEN_BIT(led_number, bit)] = ((g >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0; + ws2812_frame_buffer[WS2812_BLUE_BIT(led_number, bit)] = ((b >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0; + } +} + +// Setleds for standard RGB +void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { + static bool s_init = false; + if (!s_init) { + ws2812_init(); + s_init = true; + } + + for (uint16_t i = 0; i < leds; i++) { + ws2812_write_led(i, ledarray[i].r, ledarray[i].g, ledarray[i].b); + } +} diff --git a/drivers/chibios/ws2812_spi.c b/drivers/chibios/ws2812_spi.c new file mode 100644 index 0000000000..36e08e39ed --- /dev/null +++ b/drivers/chibios/ws2812_spi.c @@ -0,0 +1,90 @@ +#include "quantum.h" +#include "ws2812.h" + +/* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */ + +#ifdef RGBW +# error "RGBW not supported" +#endif + +// Define the spi your LEDs are plugged to here +#ifndef WS2812_SPI +# define WS2812_SPI SPID1 +#endif + +#ifndef WS2812_SPI_MOSI_PAL_MODE +# define WS2812_SPI_MOSI_PAL_MODE 5 +#endif + +#define BYTES_FOR_LED_BYTE 4 +#define NB_COLORS 3 +#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS) +#define DATA_SIZE (BYTES_FOR_LED * RGBLED_NUM) +#define RESET_SIZE 200 +#define PREAMBLE_SIZE 4 + +static uint8_t txbuf[PREAMBLE_SIZE + DATA_SIZE + RESET_SIZE] = {0}; + +/* + * As the trick here is to use the SPI to send a huge pattern of 0 and 1 to + * the ws2812b protocol, we use this helper function to translate bytes into + * 0s and 1s for the LED (with the appropriate timing). + */ +static uint8_t get_protocol_eq(uint8_t data, int pos) { + uint8_t eq = 0; + if (data & (1 << (2 * (3 - pos)))) + eq = 0b1110; + else + eq = 0b1000; + if (data & (2 << (2 * (3 - pos)))) + eq += 0b11100000; + else + eq += 0b10000000; + return eq; +} + +static void set_led_color_rgb(LED_TYPE color, int pos) { + uint8_t* tx_start = &txbuf[PREAMBLE_SIZE]; + + for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.g, j); + for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.r, j); + for (int j = 0; j < 4; j++) tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.b, j); +} + +void ws2812_init(void) { +#if defined(USE_GPIOV1) + palSetLineMode(RGB_DI_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL); +#else + palSetLineMode(RGB_DI_PIN, PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL); +#endif + + // TODO: more dynamic baudrate + static const SPIConfig spicfg = { + 0, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), + SPI_CR1_BR_1 | SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us (2.25 MHz) + }; + + spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */ + spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */ + spiSelect(&WS2812_SPI); /* Slave Select assertion. */ +} + +void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { + static bool s_init = false; + if (!s_init) { + ws2812_init(); + s_init = true; + } + + for (uint8_t i = 0; i < leds; i++) { + set_led_color_rgb(ledarray[i], i); + } + + // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues. + // Instead spiSend can be used to send synchronously (or the thread logic can be added back). +#ifdef WS2812_SPI_SYNC + spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); +#else + spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); +#endif +} diff --git a/keyboards/hs60/v2/ansi/rules.mk b/keyboards/hs60/v2/ansi/rules.mk index d013775c11..8efabcccbf 100644 --- a/keyboards/hs60/v2/ansi/rules.mk +++ b/keyboards/hs60/v2/ansi/rules.mk @@ -30,4 +30,4 @@ SRC = keyboards/wilba_tech/wt_main.c \ keyboards/wilba_tech/wt_rgb_backlight.c \ drivers/issi/is31fl3733.c \ quantum/color.c \ - drivers/arm/i2c_master.c + drivers/chibios/i2c_master.c diff --git a/keyboards/hs60/v2/hhkb/rules.mk b/keyboards/hs60/v2/hhkb/rules.mk index 44399851f0..14951cca70 100644 --- a/keyboards/hs60/v2/hhkb/rules.mk +++ b/keyboards/hs60/v2/hhkb/rules.mk @@ -28,4 +28,4 @@ SRC = keyboards/wilba_tech/wt_main.c \ keyboards/wilba_tech/wt_rgb_backlight.c \ drivers/issi/is31fl3733.c \ quantum/color.c \ - drivers/arm/i2c_master.c + drivers/chibios/i2c_master.c diff --git a/keyboards/hs60/v2/iso/rules.mk b/keyboards/hs60/v2/iso/rules.mk index 582ed4e817..96bfbce056 100644 --- a/keyboards/hs60/v2/iso/rules.mk +++ b/keyboards/hs60/v2/iso/rules.mk @@ -30,4 +30,4 @@ SRC = keyboards/wilba_tech/wt_main.c \ keyboards/wilba_tech/wt_rgb_backlight.c \ drivers/issi/is31fl3733.c \ quantum/color.c \ - drivers/arm/i2c_master.c + drivers/chibios/i2c_master.c diff --git a/keyboards/nk65/rules.mk b/keyboards/nk65/rules.mk index 1b734161fc..f7db412a24 100755 --- a/keyboards/nk65/rules.mk +++ b/keyboards/nk65/rules.mk @@ -30,4 +30,4 @@ SRC = keyboards/wilba_tech/wt_main.c \ keyboards/wilba_tech/wt_rgb_backlight.c \ drivers/issi/is31fl3733.c \ quantum/color.c \ - drivers/arm/i2c_master.c + drivers/chibios/i2c_master.c diff --git a/keyboards/wilba_tech/wt_rgb_backlight.c b/keyboards/wilba_tech/wt_rgb_backlight.c index 9db4478efb..1a8bd8981a 100644 --- a/keyboards/wilba_tech/wt_rgb_backlight.c +++ b/keyboards/wilba_tech/wt_rgb_backlight.c @@ -49,7 +49,7 @@ #else #include "ch.h" #include "hal.h" -#include "drivers/arm/i2c_master.h" +#include "drivers/chibios/i2c_master.h" #endif #if defined(RGB_BACKLIGHT_DAWN60) diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c deleted file mode 100644 index 781378788c..0000000000 --- a/quantum/audio/audio.c +++ /dev/null @@ -1,805 +0,0 @@ -/* Copyright 2016 Jack Humbert - * - * 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 . - */ - -#include -#include -//#include -#if defined(__AVR__) -# include -# include -# include -#endif -#include "print.h" -#include "audio.h" -#include "keymap.h" -#include "wait.h" - -#include "eeconfig.h" - -#define CPU_PRESCALER 8 - -// ----------------------------------------------------------------------------- -// Timer Abstractions -// ----------------------------------------------------------------------------- - -// Currently we support timers 1 and 3 used at the sime time, channels A-C, -// pins PB5, PB6, PB7, PC4, PC5, and PC6 -#if defined(C6_AUDIO) -# define CPIN_AUDIO -# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC6); -# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); -# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) -# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) -# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); -# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); -# define TIMER_3_PERIOD ICR3 -# define TIMER_3_DUTY_CYCLE OCR3A -# define TIMER3_AUDIO_vect TIMER3_COMPA_vect -#endif -#if defined(C5_AUDIO) -# define CPIN_AUDIO -# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC5); -# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3B1) | (0 << COM3B0) | (1 << WGM31) | (0 << WGM30); -# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3B) -# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3B) -# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3B1); -# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3B1) | _BV(COM3B0)); -# define TIMER_3_PERIOD ICR3 -# define TIMER_3_DUTY_CYCLE OCR3B -# define TIMER3_AUDIO_vect TIMER3_COMPB_vect -#endif -#if defined(C4_AUDIO) -# define CPIN_AUDIO -# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC4); -# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3C1) | (0 << COM3C0) | (1 << WGM31) | (0 << WGM30); -# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3C) -# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3C) -# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3C1); -# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3C1) | _BV(COM3C0)); -# define TIMER_3_PERIOD ICR3 -# define TIMER_3_DUTY_CYCLE OCR3C -# define TIMER3_AUDIO_vect TIMER3_COMPC_vect -#endif - -#if defined(B5_AUDIO) -# define BPIN_AUDIO -# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB5); -# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10); -# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A) -# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A) -# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1); -# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0)); -# define TIMER_1_PERIOD ICR1 -# define TIMER_1_DUTY_CYCLE OCR1A -# define TIMER1_AUDIO_vect TIMER1_COMPA_vect -#endif -#if defined(B6_AUDIO) -# define BPIN_AUDIO -# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB6); -# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10); -# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1B) -# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1B) -# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1B1); -# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1B1) | _BV(COM1B0)); -# define TIMER_1_PERIOD ICR1 -# define TIMER_1_DUTY_CYCLE OCR1B -# define TIMER1_AUDIO_vect TIMER1_COMPB_vect -#endif -#if defined(B7_AUDIO) -# define BPIN_AUDIO -# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB7); -# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1C1) | (0 << COM1C0) | (1 << WGM11) | (0 << WGM10); -# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1C) -# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1C) -# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1C1); -# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0)); -# define TIMER_1_PERIOD ICR1 -# define TIMER_1_DUTY_CYCLE OCR1C -# define TIMER1_AUDIO_vect TIMER1_COMPC_vect -#endif -// ----------------------------------------------------------------------------- - -int voices = 0; -int voice_place = 0; -float frequency = 0; -float frequency_alt = 0; -int volume = 0; -long position = 0; - -float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; -int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; -bool sliding = false; - -float place = 0; - -uint8_t* sample; -uint16_t sample_length = 0; - -bool playing_notes = false; -bool playing_note = false; -float note_frequency = 0; -float note_length = 0; -uint8_t note_tempo = TEMPO_DEFAULT; -float note_timbre = TIMBRE_DEFAULT; -uint16_t note_position = 0; -float (*notes_pointer)[][2]; -uint16_t notes_count; -bool notes_repeat; -bool note_resting = false; - -uint16_t current_note = 0; -uint8_t rest_counter = 0; - -#ifdef VIBRATO_ENABLE -float vibrato_counter = 0; -float vibrato_strength = .5; -float vibrato_rate = 0.125; -#endif - -float polyphony_rate = 0; - -static bool audio_initialized = false; - -audio_config_t audio_config; - -uint16_t envelope_index = 0; -bool glissando = true; - -#ifndef STARTUP_SONG -# define STARTUP_SONG SONG(STARTUP_SOUND) -#endif -#ifndef AUDIO_ON_SONG -# define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND) -#endif -#ifndef AUDIO_OFF_SONG -# define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND) -#endif -float startup_song[][2] = STARTUP_SONG; -float audio_on_song[][2] = AUDIO_ON_SONG; -float audio_off_song[][2] = AUDIO_OFF_SONG; - -void audio_init() { - // Check EEPROM - if (!eeconfig_is_enabled()) { - eeconfig_init(); - } - audio_config.raw = eeconfig_read_audio(); - - if (!audio_initialized) { -// Set audio ports as output -#ifdef CPIN_AUDIO - CPIN_SET_DIRECTION - DISABLE_AUDIO_COUNTER_3_ISR; -#endif -#ifdef BPIN_AUDIO - BPIN_SET_DIRECTION - DISABLE_AUDIO_COUNTER_1_ISR; -#endif - -// TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B -// Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation -// OC3A -- PC6 -// OC3B -- PC5 -// OC3C -- PC4 -// OC1A -- PB5 -// OC1B -- PB6 -// OC1C -- PB7 - -// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A) -// OCR3A - PC6 -// OCR3B - PC5 -// OCR3C - PC4 -// OCR1A - PB5 -// OCR1B - PB6 -// OCR1C - PB7 - -// Clock Select (CS3n) = 0b010 = Clock / 8 -#ifdef CPIN_AUDIO - INIT_AUDIO_COUNTER_3 - TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); - TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER)); - TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre); -#endif -#ifdef BPIN_AUDIO - INIT_AUDIO_COUNTER_1 - TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10); - TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER)); - TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre); -#endif - - audio_initialized = true; - } - - if (audio_config.enable) { - PLAY_SONG(startup_song); - } -} - -void stop_all_notes() { - dprintf("audio stop all notes"); - - if (!audio_initialized) { - audio_init(); - } - voices = 0; - -#ifdef CPIN_AUDIO - DISABLE_AUDIO_COUNTER_3_ISR; - DISABLE_AUDIO_COUNTER_3_OUTPUT; -#endif - -#ifdef BPIN_AUDIO - DISABLE_AUDIO_COUNTER_1_ISR; - DISABLE_AUDIO_COUNTER_1_OUTPUT; -#endif - - playing_notes = false; - playing_note = false; - frequency = 0; - frequency_alt = 0; - volume = 0; - - for (uint8_t i = 0; i < 8; i++) { - frequencies[i] = 0; - volumes[i] = 0; - } -} - -void stop_note(float freq) { - dprintf("audio stop note freq=%d", (int)freq); - - if (playing_note) { - if (!audio_initialized) { - audio_init(); - } - for (int i = 7; i >= 0; i--) { - if (frequencies[i] == freq) { - frequencies[i] = 0; - volumes[i] = 0; - for (int j = i; (j < 7); j++) { - frequencies[j] = frequencies[j + 1]; - frequencies[j + 1] = 0; - volumes[j] = volumes[j + 1]; - volumes[j + 1] = 0; - } - break; - } - } - voices--; - if (voices < 0) voices = 0; - if (voice_place >= voices) { - voice_place = 0; - } - if (voices == 0) { -#ifdef CPIN_AUDIO - DISABLE_AUDIO_COUNTER_3_ISR; - DISABLE_AUDIO_COUNTER_3_OUTPUT; -#endif -#ifdef BPIN_AUDIO - DISABLE_AUDIO_COUNTER_1_ISR; - DISABLE_AUDIO_COUNTER_1_OUTPUT; -#endif - frequency = 0; - frequency_alt = 0; - volume = 0; - playing_note = false; - } - } -} - -#ifdef VIBRATO_ENABLE - -float mod(float a, int b) { - float r = fmod(a, b); - return r < 0 ? r + b : r; -} - -float vibrato(float average_freq) { -# ifdef VIBRATO_STRENGTH_ENABLE - float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); -# else - float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; -# endif - vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); - return vibrated_freq; -} - -#endif - -#ifdef CPIN_AUDIO -ISR(TIMER3_AUDIO_vect) { - float freq; - - if (playing_note) { - if (voices > 0) { -# ifdef BPIN_AUDIO - float freq_alt = 0; - if (voices > 1) { - if (polyphony_rate == 0) { - if (glissando) { - if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) { - frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2); - } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) { - frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2); - } else { - frequency_alt = frequencies[voices - 2]; - } - } else { - frequency_alt = frequencies[voices - 2]; - } - -# ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq_alt = vibrato(frequency_alt); - } else { - freq_alt = frequency_alt; - } -# else - freq_alt = frequency_alt; -# endif - } - - if (envelope_index < 65535) { - envelope_index++; - } - - freq_alt = voice_envelope(freq_alt); - - if (freq_alt < 30.517578125) { - freq_alt = 30.52; - } - - TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq_alt * CPU_PRESCALER)); - TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq_alt * CPU_PRESCALER)) * note_timbre); - } -# endif - - if (polyphony_rate > 0) { - if (voices > 1) { - voice_place %= voices; - if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { - voice_place = (voice_place + 1) % voices; - place = 0.0; - } - } - -# ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(frequencies[voice_place]); - } else { - freq = frequencies[voice_place]; - } -# else - freq = frequencies[voice_place]; -# endif - } else { - if (glissando) { - if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { - frequency = frequency * pow(2, 440 / frequency / 12 / 2); - } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { - frequency = frequency * pow(2, -440 / frequency / 12 / 2); - } else { - frequency = frequencies[voices - 1]; - } - } else { - frequency = frequencies[voices - 1]; - } - -# ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(frequency); - } else { - freq = frequency; - } -# else - freq = frequency; -# endif - } - - if (envelope_index < 65535) { - envelope_index++; - } - - freq = voice_envelope(freq); - - if (freq < 30.517578125) { - freq = 30.52; - } - - TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); - TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); - } - } - - if (playing_notes) { - if (note_frequency > 0) { -# ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(note_frequency); - } else { - freq = note_frequency; - } -# else - freq = note_frequency; -# endif - - if (envelope_index < 65535) { - envelope_index++; - } - freq = voice_envelope(freq); - - TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); - TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); - } else { - TIMER_3_PERIOD = 0; - TIMER_3_DUTY_CYCLE = 0; - } - - note_position++; - bool end_of_note = false; - if (TIMER_3_PERIOD > 0) { - if (!note_resting) - end_of_note = (note_position >= (note_length / TIMER_3_PERIOD * 0xFFFF - 1)); - else - end_of_note = (note_position >= (note_length)); - } else { - end_of_note = (note_position >= (note_length)); - } - - if (end_of_note) { - current_note++; - if (current_note >= notes_count) { - if (notes_repeat) { - current_note = 0; - } else { - DISABLE_AUDIO_COUNTER_3_ISR; - DISABLE_AUDIO_COUNTER_3_OUTPUT; - playing_notes = false; - return; - } - } - if (!note_resting) { - note_resting = true; - current_note--; - if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { - note_frequency = 0; - note_length = 1; - } else { - note_frequency = (*notes_pointer)[current_note][0]; - note_length = 1; - } - } else { - note_resting = false; - envelope_index = 0; - note_frequency = (*notes_pointer)[current_note][0]; - note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); - } - - note_position = 0; - } - } - - if (!audio_config.enable) { - playing_notes = false; - playing_note = false; - } -} -#endif - -#ifdef BPIN_AUDIO -ISR(TIMER1_AUDIO_vect) { -# if defined(BPIN_AUDIO) && !defined(CPIN_AUDIO) - float freq = 0; - - if (playing_note) { - if (voices > 0) { - if (polyphony_rate > 0) { - if (voices > 1) { - voice_place %= voices; - if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { - voice_place = (voice_place + 1) % voices; - place = 0.0; - } - } - -# ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(frequencies[voice_place]); - } else { - freq = frequencies[voice_place]; - } -# else - freq = frequencies[voice_place]; -# endif - } else { - if (glissando) { - if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { - frequency = frequency * pow(2, 440 / frequency / 12 / 2); - } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { - frequency = frequency * pow(2, -440 / frequency / 12 / 2); - } else { - frequency = frequencies[voices - 1]; - } - } else { - frequency = frequencies[voices - 1]; - } - -# ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(frequency); - } else { - freq = frequency; - } -# else - freq = frequency; -# endif - } - - if (envelope_index < 65535) { - envelope_index++; - } - - freq = voice_envelope(freq); - - if (freq < 30.517578125) { - freq = 30.52; - } - - TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); - TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); - } - } - - if (playing_notes) { - if (note_frequency > 0) { -# ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(note_frequency); - } else { - freq = note_frequency; - } -# else - freq = note_frequency; -# endif - - if (envelope_index < 65535) { - envelope_index++; - } - freq = voice_envelope(freq); - - TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); - TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); - } else { - TIMER_1_PERIOD = 0; - TIMER_1_DUTY_CYCLE = 0; - } - - note_position++; - bool end_of_note = false; - if (TIMER_1_PERIOD > 0) { - if (!note_resting) - end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF - 1)); - else - end_of_note = (note_position >= (note_length)); - } else { - end_of_note = (note_position >= (note_length)); - } - - if (end_of_note) { - current_note++; - if (current_note >= notes_count) { - if (notes_repeat) { - current_note = 0; - } else { - DISABLE_AUDIO_COUNTER_1_ISR; - DISABLE_AUDIO_COUNTER_1_OUTPUT; - playing_notes = false; - return; - } - } - if (!note_resting) { - note_resting = true; - current_note--; - if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { - note_frequency = 0; - note_length = 1; - } else { - note_frequency = (*notes_pointer)[current_note][0]; - note_length = 1; - } - } else { - note_resting = false; - envelope_index = 0; - note_frequency = (*notes_pointer)[current_note][0]; - note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); - } - - note_position = 0; - } - } - - if (!audio_config.enable) { - playing_notes = false; - playing_note = false; - } -# endif -} -#endif - -void play_note(float freq, int vol) { - dprintf("audio play note freq=%d vol=%d", (int)freq, vol); - - if (!audio_initialized) { - audio_init(); - } - - if (audio_config.enable && voices < 8) { -#ifdef CPIN_AUDIO - DISABLE_AUDIO_COUNTER_3_ISR; -#endif -#ifdef BPIN_AUDIO - DISABLE_AUDIO_COUNTER_1_ISR; -#endif - - // Cancel notes if notes are playing - if (playing_notes) stop_all_notes(); - - playing_note = true; - - envelope_index = 0; - - if (freq > 0) { - frequencies[voices] = freq; - volumes[voices] = vol; - voices++; - } - -#ifdef CPIN_AUDIO - ENABLE_AUDIO_COUNTER_3_ISR; - ENABLE_AUDIO_COUNTER_3_OUTPUT; -#endif -#ifdef BPIN_AUDIO -# ifdef CPIN_AUDIO - if (voices > 1) { - ENABLE_AUDIO_COUNTER_1_ISR; - ENABLE_AUDIO_COUNTER_1_OUTPUT; - } -# else - ENABLE_AUDIO_COUNTER_1_ISR; - ENABLE_AUDIO_COUNTER_1_OUTPUT; -# endif -#endif - } -} - -void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) { - if (!audio_initialized) { - audio_init(); - } - - if (audio_config.enable) { -#ifdef CPIN_AUDIO - DISABLE_AUDIO_COUNTER_3_ISR; -#endif -#ifdef BPIN_AUDIO - DISABLE_AUDIO_COUNTER_1_ISR; -#endif - - // Cancel note if a note is playing - if (playing_note) stop_all_notes(); - - playing_notes = true; - - notes_pointer = np; - notes_count = n_count; - notes_repeat = n_repeat; - - place = 0; - current_note = 0; - - note_frequency = (*notes_pointer)[current_note][0]; - note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); - note_position = 0; - -#ifdef CPIN_AUDIO - ENABLE_AUDIO_COUNTER_3_ISR; - ENABLE_AUDIO_COUNTER_3_OUTPUT; -#endif -#ifdef BPIN_AUDIO -# ifndef CPIN_AUDIO - ENABLE_AUDIO_COUNTER_1_ISR; - ENABLE_AUDIO_COUNTER_1_OUTPUT; -# endif -#endif - } -} - -bool is_playing_notes(void) { return playing_notes; } - -bool is_audio_on(void) { return (audio_config.enable != 0); } - -void audio_toggle(void) { - audio_config.enable ^= 1; - eeconfig_update_audio(audio_config.raw); - if (audio_config.enable) audio_on_user(); -} - -void audio_on(void) { - audio_config.enable = 1; - eeconfig_update_audio(audio_config.raw); - audio_on_user(); - PLAY_SONG(audio_on_song); -} - -void audio_off(void) { - PLAY_SONG(audio_off_song); - wait_ms(100); - stop_all_notes(); - audio_config.enable = 0; - eeconfig_update_audio(audio_config.raw); -} - -#ifdef VIBRATO_ENABLE - -// Vibrato rate functions - -void set_vibrato_rate(float rate) { vibrato_rate = rate; } - -void increase_vibrato_rate(float change) { vibrato_rate *= change; } - -void decrease_vibrato_rate(float change) { vibrato_rate /= change; } - -# ifdef VIBRATO_STRENGTH_ENABLE - -void set_vibrato_strength(float strength) { vibrato_strength = strength; } - -void increase_vibrato_strength(float change) { vibrato_strength *= change; } - -void decrease_vibrato_strength(float change) { vibrato_strength /= change; } - -# endif /* VIBRATO_STRENGTH_ENABLE */ - -#endif /* VIBRATO_ENABLE */ - -// Polyphony functions - -void set_polyphony_rate(float rate) { polyphony_rate = rate; } - -void enable_polyphony() { polyphony_rate = 5; } - -void disable_polyphony() { polyphony_rate = 0; } - -void increase_polyphony_rate(float change) { polyphony_rate *= change; } - -void decrease_polyphony_rate(float change) { polyphony_rate /= change; } - -// Timbre function - -void set_timbre(float timbre) { note_timbre = timbre; } - -// Tempo functions - -void set_tempo(uint8_t tempo) { note_tempo = tempo; } - -void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } - -void increase_tempo(uint8_t tempo_change) { - if (note_tempo - tempo_change < 10) { - note_tempo = 10; - } else { - note_tempo -= tempo_change; - } -} diff --git a/quantum/audio/audio_arm.c b/quantum/audio/audio_arm.c deleted file mode 100644 index fba7c59873..0000000000 --- a/quantum/audio/audio_arm.c +++ /dev/null @@ -1,702 +0,0 @@ -/* Copyright 2016 Jack Humbert - * - * 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 . - */ - -#include "audio.h" -#include "ch.h" -#include "hal.h" - -#include -#include "print.h" -#include "keymap.h" - -#include "eeconfig.h" - -// ----------------------------------------------------------------------------- - -int voices = 0; -int voice_place = 0; -float frequency = 0; -float frequency_alt = 0; -int volume = 0; -long position = 0; - -float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; -int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; -bool sliding = false; - -float place = 0; - -uint8_t *sample; -uint16_t sample_length = 0; - -bool playing_notes = false; -bool playing_note = false; -float note_frequency = 0; -float note_length = 0; -uint8_t note_tempo = TEMPO_DEFAULT; -float note_timbre = TIMBRE_DEFAULT; -uint16_t note_position = 0; -float (*notes_pointer)[][2]; -uint16_t notes_count; -bool notes_repeat; -bool note_resting = false; - -uint16_t current_note = 0; -uint8_t rest_counter = 0; - -#ifdef VIBRATO_ENABLE -float vibrato_counter = 0; -float vibrato_strength = .5; -float vibrato_rate = 0.125; -#endif - -float polyphony_rate = 0; - -static bool audio_initialized = false; - -audio_config_t audio_config; - -uint16_t envelope_index = 0; -bool glissando = true; - -#ifndef STARTUP_SONG -# define STARTUP_SONG SONG(STARTUP_SOUND) -#endif -float startup_song[][2] = STARTUP_SONG; - -static void gpt_cb8(GPTDriver *gptp); - -#define DAC_BUFFER_SIZE 100 -#ifndef DAC_SAMPLE_MAX -# define DAC_SAMPLE_MAX 65535U -#endif - -#define START_CHANNEL_1() \ - gptStart(&GPTD6, &gpt6cfg1); \ - gptStartContinuous(&GPTD6, 2U) -#define START_CHANNEL_2() \ - gptStart(&GPTD7, &gpt7cfg1); \ - gptStartContinuous(&GPTD7, 2U) -#define STOP_CHANNEL_1() gptStopTimer(&GPTD6) -#define STOP_CHANNEL_2() gptStopTimer(&GPTD7) -#define RESTART_CHANNEL_1() \ - STOP_CHANNEL_1(); \ - START_CHANNEL_1() -#define RESTART_CHANNEL_2() \ - STOP_CHANNEL_2(); \ - START_CHANNEL_2() -#define UPDATE_CHANNEL_1_FREQ(freq) \ - gpt6cfg1.frequency = freq * DAC_BUFFER_SIZE; \ - RESTART_CHANNEL_1() -#define UPDATE_CHANNEL_2_FREQ(freq) \ - gpt7cfg1.frequency = freq * DAC_BUFFER_SIZE; \ - RESTART_CHANNEL_2() -#define GET_CHANNEL_1_FREQ (uint16_t)(gpt6cfg1.frequency * DAC_BUFFER_SIZE) -#define GET_CHANNEL_2_FREQ (uint16_t)(gpt7cfg1.frequency * DAC_BUFFER_SIZE) - -/* - * GPT6 configuration. - */ -// static const GPTConfig gpt6cfg1 = { -// .frequency = 1000000U, -// .callback = NULL, -// .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ -// .dier = 0U -// }; - -GPTConfig gpt6cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE, - .callback = NULL, - .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ - .dier = 0U}; - -GPTConfig gpt7cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE, - .callback = NULL, - .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ - .dier = 0U}; - -GPTConfig gpt8cfg1 = {.frequency = 10, - .callback = gpt_cb8, - .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ - .dier = 0U}; - -/* - * DAC test buffer (sine wave). - */ -// static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = { -// 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437, -// 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846, -// 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221, -// 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544, -// 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801, -// 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982, -// 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078, -// 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086, -// 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004, -// 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837, -// 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591, -// 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278, -// 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912, -// 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507, -// 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082, -// 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657, -// 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248, -// 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873, -// 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550, -// 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293, -// 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112, -// 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16, -// 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8, -// 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90, -// 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257, -// 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503, -// 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816, -// 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182, -// 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587, -// 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012 -// }; - -// static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = { -// 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8, -// 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90, -// 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257, -// 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503, -// 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816, -// 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182, -// 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587, -// 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012, -// 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437, -// 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846, -// 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221, -// 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544, -// 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801, -// 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982, -// 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078, -// 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086, -// 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004, -// 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837, -// 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591, -// 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278, -// 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912, -// 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507, -// 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082, -// 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657, -// 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248, -// 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873, -// 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550, -// 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293, -// 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112, -// 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16 -// }; - -// squarewave -static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = { - // First half is max, second half is 0 - [0 ... DAC_BUFFER_SIZE / 2 - 1] = DAC_SAMPLE_MAX, - [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = 0, -}; - -// squarewave -static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = { - // opposite of dac_buffer above - [0 ... DAC_BUFFER_SIZE / 2 - 1] = 0, - [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = DAC_SAMPLE_MAX, -}; - -/* - * DAC streaming callback. - */ -size_t nz = 0; -static void end_cb1(DACDriver *dacp) { - (void)dacp; - - nz++; - if ((nz % 1000) == 0) { - // palTogglePad(GPIOD, GPIOD_LED3); - } -} - -/* - * DAC error callback. - */ -static void error_cb1(DACDriver *dacp, dacerror_t err) { - (void)dacp; - (void)err; - - chSysHalt("DAC failure"); -} - -static const DACConfig dac1cfg1 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT}; - -static const DACConversionGroup dacgrpcfg1 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)}; - -static const DACConfig dac1cfg2 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT}; - -static const DACConversionGroup dacgrpcfg2 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)}; - -void audio_init() { - if (audio_initialized) { - return; - } - -// Check EEPROM -#ifdef EEPROM_ENABLE - if (!eeconfig_is_enabled()) { - eeconfig_init(); - } - audio_config.raw = eeconfig_read_audio(); -#else // ARM EEPROM - audio_config.enable = true; -# ifdef AUDIO_CLICKY_ON - audio_config.clicky_enable = true; -# endif -#endif // ARM EEPROM - - /* - * Starting DAC1 driver, setting up the output pin as analog as suggested - * by the Reference Manual. - */ - palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG); - palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); - dacStart(&DACD1, &dac1cfg1); - dacStart(&DACD2, &dac1cfg2); - - /* - * Starting GPT6/7 driver, it is used for triggering the DAC. - */ - START_CHANNEL_1(); - START_CHANNEL_2(); - - /* - * Starting a continuous conversion. - */ - dacStartConversion(&DACD1, &dacgrpcfg1, (dacsample_t *)dac_buffer, DAC_BUFFER_SIZE); - dacStartConversion(&DACD2, &dacgrpcfg2, (dacsample_t *)dac_buffer_2, DAC_BUFFER_SIZE); - - audio_initialized = true; - - if (audio_config.enable) { - PLAY_SONG(startup_song); - } else { - stop_all_notes(); - } -} - -void stop_all_notes() { - dprintf("audio stop all notes"); - - if (!audio_initialized) { - audio_init(); - } - voices = 0; - - gptStopTimer(&GPTD6); - gptStopTimer(&GPTD7); - gptStopTimer(&GPTD8); - - playing_notes = false; - playing_note = false; - frequency = 0; - frequency_alt = 0; - volume = 0; - - for (uint8_t i = 0; i < 8; i++) { - frequencies[i] = 0; - volumes[i] = 0; - } -} - -void stop_note(float freq) { - dprintf("audio stop note freq=%d", (int)freq); - - if (playing_note) { - if (!audio_initialized) { - audio_init(); - } - for (int i = 7; i >= 0; i--) { - if (frequencies[i] == freq) { - frequencies[i] = 0; - volumes[i] = 0; - for (int j = i; (j < 7); j++) { - frequencies[j] = frequencies[j + 1]; - frequencies[j + 1] = 0; - volumes[j] = volumes[j + 1]; - volumes[j + 1] = 0; - } - break; - } - } - voices--; - if (voices < 0) { - voices = 0; - } - if (voice_place >= voices) { - voice_place = 0; - } - if (voices == 0) { - STOP_CHANNEL_1(); - STOP_CHANNEL_2(); - gptStopTimer(&GPTD8); - frequency = 0; - frequency_alt = 0; - volume = 0; - playing_note = false; - } - } -} - -#ifdef VIBRATO_ENABLE - -float mod(float a, int b) { - float r = fmod(a, b); - return r < 0 ? r + b : r; -} - -float vibrato(float average_freq) { -# ifdef VIBRATO_STRENGTH_ENABLE - float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); -# else - float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; -# endif - vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); - return vibrated_freq; -} - -#endif - -static void gpt_cb8(GPTDriver *gptp) { - float freq; - - if (playing_note) { - if (voices > 0) { - float freq_alt = 0; - if (voices > 1) { - if (polyphony_rate == 0) { - if (glissando) { - if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) { - frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2); - } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) { - frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2); - } else { - frequency_alt = frequencies[voices - 2]; - } - } else { - frequency_alt = frequencies[voices - 2]; - } - -#ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq_alt = vibrato(frequency_alt); - } else { - freq_alt = frequency_alt; - } -#else - freq_alt = frequency_alt; -#endif - } - - if (envelope_index < 65535) { - envelope_index++; - } - - freq_alt = voice_envelope(freq_alt); - - if (freq_alt < 30.517578125) { - freq_alt = 30.52; - } - - if (GET_CHANNEL_2_FREQ != (uint16_t)freq_alt) { - UPDATE_CHANNEL_2_FREQ(freq_alt); - } else { - RESTART_CHANNEL_2(); - } - // note_timbre; - } - - if (polyphony_rate > 0) { - if (voices > 1) { - voice_place %= voices; - if (place++ > (frequencies[voice_place] / polyphony_rate)) { - voice_place = (voice_place + 1) % voices; - place = 0.0; - } - } - -#ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(frequencies[voice_place]); - } else { - freq = frequencies[voice_place]; - } -#else - freq = frequencies[voice_place]; -#endif - } else { - if (glissando) { - if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { - frequency = frequency * pow(2, 440 / frequency / 12 / 2); - } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { - frequency = frequency * pow(2, -440 / frequency / 12 / 2); - } else { - frequency = frequencies[voices - 1]; - } - } else { - frequency = frequencies[voices - 1]; - } - -#ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(frequency); - } else { - freq = frequency; - } -#else - freq = frequency; -#endif - } - - if (envelope_index < 65535) { - envelope_index++; - } - - freq = voice_envelope(freq); - - if (freq < 30.517578125) { - freq = 30.52; - } - - if (GET_CHANNEL_1_FREQ != (uint16_t)freq) { - UPDATE_CHANNEL_1_FREQ(freq); - } else { - RESTART_CHANNEL_1(); - } - // note_timbre; - } - } - - if (playing_notes) { - if (note_frequency > 0) { -#ifdef VIBRATO_ENABLE - if (vibrato_strength > 0) { - freq = vibrato(note_frequency); - } else { - freq = note_frequency; - } -#else - freq = note_frequency; -#endif - - if (envelope_index < 65535) { - envelope_index++; - } - freq = voice_envelope(freq); - - if (GET_CHANNEL_1_FREQ != (uint16_t)freq) { - UPDATE_CHANNEL_1_FREQ(freq); - UPDATE_CHANNEL_2_FREQ(freq); - } - // note_timbre; - } else { - // gptStopTimer(&GPTD6); - // gptStopTimer(&GPTD7); - } - - note_position++; - bool end_of_note = false; - if (GET_CHANNEL_1_FREQ > 0) { - if (!note_resting) - end_of_note = (note_position >= (note_length * 8 - 1)); - else - end_of_note = (note_position >= (note_length * 8)); - } else { - end_of_note = (note_position >= (note_length * 8)); - } - - if (end_of_note) { - current_note++; - if (current_note >= notes_count) { - if (notes_repeat) { - current_note = 0; - } else { - STOP_CHANNEL_1(); - STOP_CHANNEL_2(); - // gptStopTimer(&GPTD8); - playing_notes = false; - return; - } - } - if (!note_resting) { - note_resting = true; - current_note--; - if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { - note_frequency = 0; - note_length = 1; - } else { - note_frequency = (*notes_pointer)[current_note][0]; - note_length = 1; - } - } else { - note_resting = false; - envelope_index = 0; - note_frequency = (*notes_pointer)[current_note][0]; - note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); - } - - note_position = 0; - } - } - - if (!audio_config.enable) { - playing_notes = false; - playing_note = false; - } -} - -void play_note(float freq, int vol) { - dprintf("audio play note freq=%d vol=%d", (int)freq, vol); - - if (!audio_initialized) { - audio_init(); - } - - if (audio_config.enable && voices < 8) { - // Cancel notes if notes are playing - if (playing_notes) { - stop_all_notes(); - } - - playing_note = true; - - envelope_index = 0; - - if (freq > 0) { - frequencies[voices] = freq; - volumes[voices] = vol; - voices++; - } - - gptStart(&GPTD8, &gpt8cfg1); - gptStartContinuous(&GPTD8, 2U); - RESTART_CHANNEL_1(); - RESTART_CHANNEL_2(); - } -} - -void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) { - if (!audio_initialized) { - audio_init(); - } - - if (audio_config.enable) { - // Cancel note if a note is playing - if (playing_note) { - stop_all_notes(); - } - - playing_notes = true; - - notes_pointer = np; - notes_count = n_count; - notes_repeat = n_repeat; - - place = 0; - current_note = 0; - - note_frequency = (*notes_pointer)[current_note][0]; - note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); - note_position = 0; - - gptStart(&GPTD8, &gpt8cfg1); - gptStartContinuous(&GPTD8, 2U); - RESTART_CHANNEL_1(); - RESTART_CHANNEL_2(); - } -} - -bool is_playing_notes(void) { return playing_notes; } - -bool is_audio_on(void) { return (audio_config.enable != 0); } - -void audio_toggle(void) { - audio_config.enable ^= 1; - eeconfig_update_audio(audio_config.raw); - if (audio_config.enable) { - audio_on_user(); - } -} - -void audio_on(void) { - audio_config.enable = 1; - eeconfig_update_audio(audio_config.raw); - audio_on_user(); -} - -void audio_off(void) { - stop_all_notes(); - audio_config.enable = 0; - eeconfig_update_audio(audio_config.raw); -} - -#ifdef VIBRATO_ENABLE - -// Vibrato rate functions - -void set_vibrato_rate(float rate) { vibrato_rate = rate; } - -void increase_vibrato_rate(float change) { vibrato_rate *= change; } - -void decrease_vibrato_rate(float change) { vibrato_rate /= change; } - -# ifdef VIBRATO_STRENGTH_ENABLE - -void set_vibrato_strength(float strength) { vibrato_strength = strength; } - -void increase_vibrato_strength(float change) { vibrato_strength *= change; } - -void decrease_vibrato_strength(float change) { vibrato_strength /= change; } - -# endif /* VIBRATO_STRENGTH_ENABLE */ - -#endif /* VIBRATO_ENABLE */ - -// Polyphony functions - -void set_polyphony_rate(float rate) { polyphony_rate = rate; } - -void enable_polyphony() { polyphony_rate = 5; } - -void disable_polyphony() { polyphony_rate = 0; } - -void increase_polyphony_rate(float change) { polyphony_rate *= change; } - -void decrease_polyphony_rate(float change) { polyphony_rate /= change; } - -// Timbre function - -void set_timbre(float timbre) { note_timbre = timbre; } - -// Tempo functions - -void set_tempo(uint8_t tempo) { note_tempo = tempo; } - -void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } - -void increase_tempo(uint8_t tempo_change) { - if (note_tempo - tempo_change < 10) { - note_tempo = 10; - } else { - note_tempo -= tempo_change; - } -} diff --git a/quantum/audio/audio_avr.c b/quantum/audio/audio_avr.c new file mode 100644 index 0000000000..781378788c --- /dev/null +++ b/quantum/audio/audio_avr.c @@ -0,0 +1,805 @@ +/* Copyright 2016 Jack Humbert + * + * 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 . + */ + +#include +#include +//#include +#if defined(__AVR__) +# include +# include +# include +#endif +#include "print.h" +#include "audio.h" +#include "keymap.h" +#include "wait.h" + +#include "eeconfig.h" + +#define CPU_PRESCALER 8 + +// ----------------------------------------------------------------------------- +// Timer Abstractions +// ----------------------------------------------------------------------------- + +// Currently we support timers 1 and 3 used at the sime time, channels A-C, +// pins PB5, PB6, PB7, PC4, PC5, and PC6 +#if defined(C6_AUDIO) +# define CPIN_AUDIO +# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC6); +# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3A1) | (0 << COM3A0) | (1 << WGM31) | (0 << WGM30); +# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3A) +# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3A) +# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3A1); +# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3A1) | _BV(COM3A0)); +# define TIMER_3_PERIOD ICR3 +# define TIMER_3_DUTY_CYCLE OCR3A +# define TIMER3_AUDIO_vect TIMER3_COMPA_vect +#endif +#if defined(C5_AUDIO) +# define CPIN_AUDIO +# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC5); +# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3B1) | (0 << COM3B0) | (1 << WGM31) | (0 << WGM30); +# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3B) +# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3B) +# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3B1); +# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3B1) | _BV(COM3B0)); +# define TIMER_3_PERIOD ICR3 +# define TIMER_3_DUTY_CYCLE OCR3B +# define TIMER3_AUDIO_vect TIMER3_COMPB_vect +#endif +#if defined(C4_AUDIO) +# define CPIN_AUDIO +# define CPIN_SET_DIRECTION DDRC |= _BV(PORTC4); +# define INIT_AUDIO_COUNTER_3 TCCR3A = (0 << COM3C1) | (0 << COM3C0) | (1 << WGM31) | (0 << WGM30); +# define ENABLE_AUDIO_COUNTER_3_ISR TIMSK3 |= _BV(OCIE3C) +# define DISABLE_AUDIO_COUNTER_3_ISR TIMSK3 &= ~_BV(OCIE3C) +# define ENABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A |= _BV(COM3C1); +# define DISABLE_AUDIO_COUNTER_3_OUTPUT TCCR3A &= ~(_BV(COM3C1) | _BV(COM3C0)); +# define TIMER_3_PERIOD ICR3 +# define TIMER_3_DUTY_CYCLE OCR3C +# define TIMER3_AUDIO_vect TIMER3_COMPC_vect +#endif + +#if defined(B5_AUDIO) +# define BPIN_AUDIO +# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB5); +# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << WGM11) | (0 << WGM10); +# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1A) +# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1A) +# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1A1); +# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1A1) | _BV(COM1A0)); +# define TIMER_1_PERIOD ICR1 +# define TIMER_1_DUTY_CYCLE OCR1A +# define TIMER1_AUDIO_vect TIMER1_COMPA_vect +#endif +#if defined(B6_AUDIO) +# define BPIN_AUDIO +# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB6); +# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10); +# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1B) +# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1B) +# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1B1); +# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1B1) | _BV(COM1B0)); +# define TIMER_1_PERIOD ICR1 +# define TIMER_1_DUTY_CYCLE OCR1B +# define TIMER1_AUDIO_vect TIMER1_COMPB_vect +#endif +#if defined(B7_AUDIO) +# define BPIN_AUDIO +# define BPIN_SET_DIRECTION DDRB |= _BV(PORTB7); +# define INIT_AUDIO_COUNTER_1 TCCR1A = (0 << COM1C1) | (0 << COM1C0) | (1 << WGM11) | (0 << WGM10); +# define ENABLE_AUDIO_COUNTER_1_ISR TIMSK1 |= _BV(OCIE1C) +# define DISABLE_AUDIO_COUNTER_1_ISR TIMSK1 &= ~_BV(OCIE1C) +# define ENABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A |= _BV(COM1C1); +# define DISABLE_AUDIO_COUNTER_1_OUTPUT TCCR1A &= ~(_BV(COM1C1) | _BV(COM1C0)); +# define TIMER_1_PERIOD ICR1 +# define TIMER_1_DUTY_CYCLE OCR1C +# define TIMER1_AUDIO_vect TIMER1_COMPC_vect +#endif +// ----------------------------------------------------------------------------- + +int voices = 0; +int voice_place = 0; +float frequency = 0; +float frequency_alt = 0; +int volume = 0; +long position = 0; + +float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +bool sliding = false; + +float place = 0; + +uint8_t* sample; +uint16_t sample_length = 0; + +bool playing_notes = false; +bool playing_note = false; +float note_frequency = 0; +float note_length = 0; +uint8_t note_tempo = TEMPO_DEFAULT; +float note_timbre = TIMBRE_DEFAULT; +uint16_t note_position = 0; +float (*notes_pointer)[][2]; +uint16_t notes_count; +bool notes_repeat; +bool note_resting = false; + +uint16_t current_note = 0; +uint8_t rest_counter = 0; + +#ifdef VIBRATO_ENABLE +float vibrato_counter = 0; +float vibrato_strength = .5; +float vibrato_rate = 0.125; +#endif + +float polyphony_rate = 0; + +static bool audio_initialized = false; + +audio_config_t audio_config; + +uint16_t envelope_index = 0; +bool glissando = true; + +#ifndef STARTUP_SONG +# define STARTUP_SONG SONG(STARTUP_SOUND) +#endif +#ifndef AUDIO_ON_SONG +# define AUDIO_ON_SONG SONG(AUDIO_ON_SOUND) +#endif +#ifndef AUDIO_OFF_SONG +# define AUDIO_OFF_SONG SONG(AUDIO_OFF_SOUND) +#endif +float startup_song[][2] = STARTUP_SONG; +float audio_on_song[][2] = AUDIO_ON_SONG; +float audio_off_song[][2] = AUDIO_OFF_SONG; + +void audio_init() { + // Check EEPROM + if (!eeconfig_is_enabled()) { + eeconfig_init(); + } + audio_config.raw = eeconfig_read_audio(); + + if (!audio_initialized) { +// Set audio ports as output +#ifdef CPIN_AUDIO + CPIN_SET_DIRECTION + DISABLE_AUDIO_COUNTER_3_ISR; +#endif +#ifdef BPIN_AUDIO + BPIN_SET_DIRECTION + DISABLE_AUDIO_COUNTER_1_ISR; +#endif + +// TCCR3A / TCCR3B: Timer/Counter #3 Control Registers TCCR3A/TCCR3B, TCCR1A/TCCR1B +// Compare Output Mode (COM3An and COM1An) = 0b00 = Normal port operation +// OC3A -- PC6 +// OC3B -- PC5 +// OC3C -- PC4 +// OC1A -- PB5 +// OC1B -- PB6 +// OC1C -- PB7 + +// Waveform Generation Mode (WGM3n) = 0b1110 = Fast PWM Mode 14. Period = ICR3, Duty Cycle OCR3A) +// OCR3A - PC6 +// OCR3B - PC5 +// OCR3C - PC4 +// OCR1A - PB5 +// OCR1B - PB6 +// OCR1C - PB7 + +// Clock Select (CS3n) = 0b010 = Clock / 8 +#ifdef CPIN_AUDIO + INIT_AUDIO_COUNTER_3 + TCCR3B = (1 << WGM33) | (1 << WGM32) | (0 << CS32) | (1 << CS31) | (0 << CS30); + TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER)); + TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre); +#endif +#ifdef BPIN_AUDIO + INIT_AUDIO_COUNTER_1 + TCCR1B = (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10); + TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER)); + TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre); +#endif + + audio_initialized = true; + } + + if (audio_config.enable) { + PLAY_SONG(startup_song); + } +} + +void stop_all_notes() { + dprintf("audio stop all notes"); + + if (!audio_initialized) { + audio_init(); + } + voices = 0; + +#ifdef CPIN_AUDIO + DISABLE_AUDIO_COUNTER_3_ISR; + DISABLE_AUDIO_COUNTER_3_OUTPUT; +#endif + +#ifdef BPIN_AUDIO + DISABLE_AUDIO_COUNTER_1_ISR; + DISABLE_AUDIO_COUNTER_1_OUTPUT; +#endif + + playing_notes = false; + playing_note = false; + frequency = 0; + frequency_alt = 0; + volume = 0; + + for (uint8_t i = 0; i < 8; i++) { + frequencies[i] = 0; + volumes[i] = 0; + } +} + +void stop_note(float freq) { + dprintf("audio stop note freq=%d", (int)freq); + + if (playing_note) { + if (!audio_initialized) { + audio_init(); + } + for (int i = 7; i >= 0; i--) { + if (frequencies[i] == freq) { + frequencies[i] = 0; + volumes[i] = 0; + for (int j = i; (j < 7); j++) { + frequencies[j] = frequencies[j + 1]; + frequencies[j + 1] = 0; + volumes[j] = volumes[j + 1]; + volumes[j + 1] = 0; + } + break; + } + } + voices--; + if (voices < 0) voices = 0; + if (voice_place >= voices) { + voice_place = 0; + } + if (voices == 0) { +#ifdef CPIN_AUDIO + DISABLE_AUDIO_COUNTER_3_ISR; + DISABLE_AUDIO_COUNTER_3_OUTPUT; +#endif +#ifdef BPIN_AUDIO + DISABLE_AUDIO_COUNTER_1_ISR; + DISABLE_AUDIO_COUNTER_1_OUTPUT; +#endif + frequency = 0; + frequency_alt = 0; + volume = 0; + playing_note = false; + } + } +} + +#ifdef VIBRATO_ENABLE + +float mod(float a, int b) { + float r = fmod(a, b); + return r < 0 ? r + b : r; +} + +float vibrato(float average_freq) { +# ifdef VIBRATO_STRENGTH_ENABLE + float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); +# else + float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; +# endif + vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); + return vibrated_freq; +} + +#endif + +#ifdef CPIN_AUDIO +ISR(TIMER3_AUDIO_vect) { + float freq; + + if (playing_note) { + if (voices > 0) { +# ifdef BPIN_AUDIO + float freq_alt = 0; + if (voices > 1) { + if (polyphony_rate == 0) { + if (glissando) { + if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) { + frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2); + } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) { + frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2); + } else { + frequency_alt = frequencies[voices - 2]; + } + } else { + frequency_alt = frequencies[voices - 2]; + } + +# ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq_alt = vibrato(frequency_alt); + } else { + freq_alt = frequency_alt; + } +# else + freq_alt = frequency_alt; +# endif + } + + if (envelope_index < 65535) { + envelope_index++; + } + + freq_alt = voice_envelope(freq_alt); + + if (freq_alt < 30.517578125) { + freq_alt = 30.52; + } + + TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq_alt * CPU_PRESCALER)); + TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq_alt * CPU_PRESCALER)) * note_timbre); + } +# endif + + if (polyphony_rate > 0) { + if (voices > 1) { + voice_place %= voices; + if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { + voice_place = (voice_place + 1) % voices; + place = 0.0; + } + } + +# ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(frequencies[voice_place]); + } else { + freq = frequencies[voice_place]; + } +# else + freq = frequencies[voice_place]; +# endif + } else { + if (glissando) { + if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { + frequency = frequency * pow(2, 440 / frequency / 12 / 2); + } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { + frequency = frequency * pow(2, -440 / frequency / 12 / 2); + } else { + frequency = frequencies[voices - 1]; + } + } else { + frequency = frequencies[voices - 1]; + } + +# ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(frequency); + } else { + freq = frequency; + } +# else + freq = frequency; +# endif + } + + if (envelope_index < 65535) { + envelope_index++; + } + + freq = voice_envelope(freq); + + if (freq < 30.517578125) { + freq = 30.52; + } + + TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); + TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); + } + } + + if (playing_notes) { + if (note_frequency > 0) { +# ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(note_frequency); + } else { + freq = note_frequency; + } +# else + freq = note_frequency; +# endif + + if (envelope_index < 65535) { + envelope_index++; + } + freq = voice_envelope(freq); + + TIMER_3_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); + TIMER_3_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); + } else { + TIMER_3_PERIOD = 0; + TIMER_3_DUTY_CYCLE = 0; + } + + note_position++; + bool end_of_note = false; + if (TIMER_3_PERIOD > 0) { + if (!note_resting) + end_of_note = (note_position >= (note_length / TIMER_3_PERIOD * 0xFFFF - 1)); + else + end_of_note = (note_position >= (note_length)); + } else { + end_of_note = (note_position >= (note_length)); + } + + if (end_of_note) { + current_note++; + if (current_note >= notes_count) { + if (notes_repeat) { + current_note = 0; + } else { + DISABLE_AUDIO_COUNTER_3_ISR; + DISABLE_AUDIO_COUNTER_3_OUTPUT; + playing_notes = false; + return; + } + } + if (!note_resting) { + note_resting = true; + current_note--; + if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { + note_frequency = 0; + note_length = 1; + } else { + note_frequency = (*notes_pointer)[current_note][0]; + note_length = 1; + } + } else { + note_resting = false; + envelope_index = 0; + note_frequency = (*notes_pointer)[current_note][0]; + note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); + } + + note_position = 0; + } + } + + if (!audio_config.enable) { + playing_notes = false; + playing_note = false; + } +} +#endif + +#ifdef BPIN_AUDIO +ISR(TIMER1_AUDIO_vect) { +# if defined(BPIN_AUDIO) && !defined(CPIN_AUDIO) + float freq = 0; + + if (playing_note) { + if (voices > 0) { + if (polyphony_rate > 0) { + if (voices > 1) { + voice_place %= voices; + if (place++ > (frequencies[voice_place] / polyphony_rate / CPU_PRESCALER)) { + voice_place = (voice_place + 1) % voices; + place = 0.0; + } + } + +# ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(frequencies[voice_place]); + } else { + freq = frequencies[voice_place]; + } +# else + freq = frequencies[voice_place]; +# endif + } else { + if (glissando) { + if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { + frequency = frequency * pow(2, 440 / frequency / 12 / 2); + } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { + frequency = frequency * pow(2, -440 / frequency / 12 / 2); + } else { + frequency = frequencies[voices - 1]; + } + } else { + frequency = frequencies[voices - 1]; + } + +# ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(frequency); + } else { + freq = frequency; + } +# else + freq = frequency; +# endif + } + + if (envelope_index < 65535) { + envelope_index++; + } + + freq = voice_envelope(freq); + + if (freq < 30.517578125) { + freq = 30.52; + } + + TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); + TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); + } + } + + if (playing_notes) { + if (note_frequency > 0) { +# ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(note_frequency); + } else { + freq = note_frequency; + } +# else + freq = note_frequency; +# endif + + if (envelope_index < 65535) { + envelope_index++; + } + freq = voice_envelope(freq); + + TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (freq * CPU_PRESCALER)); + TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (freq * CPU_PRESCALER)) * note_timbre); + } else { + TIMER_1_PERIOD = 0; + TIMER_1_DUTY_CYCLE = 0; + } + + note_position++; + bool end_of_note = false; + if (TIMER_1_PERIOD > 0) { + if (!note_resting) + end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF - 1)); + else + end_of_note = (note_position >= (note_length)); + } else { + end_of_note = (note_position >= (note_length)); + } + + if (end_of_note) { + current_note++; + if (current_note >= notes_count) { + if (notes_repeat) { + current_note = 0; + } else { + DISABLE_AUDIO_COUNTER_1_ISR; + DISABLE_AUDIO_COUNTER_1_OUTPUT; + playing_notes = false; + return; + } + } + if (!note_resting) { + note_resting = true; + current_note--; + if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { + note_frequency = 0; + note_length = 1; + } else { + note_frequency = (*notes_pointer)[current_note][0]; + note_length = 1; + } + } else { + note_resting = false; + envelope_index = 0; + note_frequency = (*notes_pointer)[current_note][0]; + note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); + } + + note_position = 0; + } + } + + if (!audio_config.enable) { + playing_notes = false; + playing_note = false; + } +# endif +} +#endif + +void play_note(float freq, int vol) { + dprintf("audio play note freq=%d vol=%d", (int)freq, vol); + + if (!audio_initialized) { + audio_init(); + } + + if (audio_config.enable && voices < 8) { +#ifdef CPIN_AUDIO + DISABLE_AUDIO_COUNTER_3_ISR; +#endif +#ifdef BPIN_AUDIO + DISABLE_AUDIO_COUNTER_1_ISR; +#endif + + // Cancel notes if notes are playing + if (playing_notes) stop_all_notes(); + + playing_note = true; + + envelope_index = 0; + + if (freq > 0) { + frequencies[voices] = freq; + volumes[voices] = vol; + voices++; + } + +#ifdef CPIN_AUDIO + ENABLE_AUDIO_COUNTER_3_ISR; + ENABLE_AUDIO_COUNTER_3_OUTPUT; +#endif +#ifdef BPIN_AUDIO +# ifdef CPIN_AUDIO + if (voices > 1) { + ENABLE_AUDIO_COUNTER_1_ISR; + ENABLE_AUDIO_COUNTER_1_OUTPUT; + } +# else + ENABLE_AUDIO_COUNTER_1_ISR; + ENABLE_AUDIO_COUNTER_1_OUTPUT; +# endif +#endif + } +} + +void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) { + if (!audio_initialized) { + audio_init(); + } + + if (audio_config.enable) { +#ifdef CPIN_AUDIO + DISABLE_AUDIO_COUNTER_3_ISR; +#endif +#ifdef BPIN_AUDIO + DISABLE_AUDIO_COUNTER_1_ISR; +#endif + + // Cancel note if a note is playing + if (playing_note) stop_all_notes(); + + playing_notes = true; + + notes_pointer = np; + notes_count = n_count; + notes_repeat = n_repeat; + + place = 0; + current_note = 0; + + note_frequency = (*notes_pointer)[current_note][0]; + note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); + note_position = 0; + +#ifdef CPIN_AUDIO + ENABLE_AUDIO_COUNTER_3_ISR; + ENABLE_AUDIO_COUNTER_3_OUTPUT; +#endif +#ifdef BPIN_AUDIO +# ifndef CPIN_AUDIO + ENABLE_AUDIO_COUNTER_1_ISR; + ENABLE_AUDIO_COUNTER_1_OUTPUT; +# endif +#endif + } +} + +bool is_playing_notes(void) { return playing_notes; } + +bool is_audio_on(void) { return (audio_config.enable != 0); } + +void audio_toggle(void) { + audio_config.enable ^= 1; + eeconfig_update_audio(audio_config.raw); + if (audio_config.enable) audio_on_user(); +} + +void audio_on(void) { + audio_config.enable = 1; + eeconfig_update_audio(audio_config.raw); + audio_on_user(); + PLAY_SONG(audio_on_song); +} + +void audio_off(void) { + PLAY_SONG(audio_off_song); + wait_ms(100); + stop_all_notes(); + audio_config.enable = 0; + eeconfig_update_audio(audio_config.raw); +} + +#ifdef VIBRATO_ENABLE + +// Vibrato rate functions + +void set_vibrato_rate(float rate) { vibrato_rate = rate; } + +void increase_vibrato_rate(float change) { vibrato_rate *= change; } + +void decrease_vibrato_rate(float change) { vibrato_rate /= change; } + +# ifdef VIBRATO_STRENGTH_ENABLE + +void set_vibrato_strength(float strength) { vibrato_strength = strength; } + +void increase_vibrato_strength(float change) { vibrato_strength *= change; } + +void decrease_vibrato_strength(float change) { vibrato_strength /= change; } + +# endif /* VIBRATO_STRENGTH_ENABLE */ + +#endif /* VIBRATO_ENABLE */ + +// Polyphony functions + +void set_polyphony_rate(float rate) { polyphony_rate = rate; } + +void enable_polyphony() { polyphony_rate = 5; } + +void disable_polyphony() { polyphony_rate = 0; } + +void increase_polyphony_rate(float change) { polyphony_rate *= change; } + +void decrease_polyphony_rate(float change) { polyphony_rate /= change; } + +// Timbre function + +void set_timbre(float timbre) { note_timbre = timbre; } + +// Tempo functions + +void set_tempo(uint8_t tempo) { note_tempo = tempo; } + +void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } + +void increase_tempo(uint8_t tempo_change) { + if (note_tempo - tempo_change < 10) { + note_tempo = 10; + } else { + note_tempo -= tempo_change; + } +} diff --git a/quantum/audio/audio_chibios.c b/quantum/audio/audio_chibios.c new file mode 100644 index 0000000000..fba7c59873 --- /dev/null +++ b/quantum/audio/audio_chibios.c @@ -0,0 +1,702 @@ +/* Copyright 2016 Jack Humbert + * + * 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 . + */ + +#include "audio.h" +#include "ch.h" +#include "hal.h" + +#include +#include "print.h" +#include "keymap.h" + +#include "eeconfig.h" + +// ----------------------------------------------------------------------------- + +int voices = 0; +int voice_place = 0; +float frequency = 0; +float frequency_alt = 0; +int volume = 0; +long position = 0; + +float frequencies[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +int volumes[8] = {0, 0, 0, 0, 0, 0, 0, 0}; +bool sliding = false; + +float place = 0; + +uint8_t *sample; +uint16_t sample_length = 0; + +bool playing_notes = false; +bool playing_note = false; +float note_frequency = 0; +float note_length = 0; +uint8_t note_tempo = TEMPO_DEFAULT; +float note_timbre = TIMBRE_DEFAULT; +uint16_t note_position = 0; +float (*notes_pointer)[][2]; +uint16_t notes_count; +bool notes_repeat; +bool note_resting = false; + +uint16_t current_note = 0; +uint8_t rest_counter = 0; + +#ifdef VIBRATO_ENABLE +float vibrato_counter = 0; +float vibrato_strength = .5; +float vibrato_rate = 0.125; +#endif + +float polyphony_rate = 0; + +static bool audio_initialized = false; + +audio_config_t audio_config; + +uint16_t envelope_index = 0; +bool glissando = true; + +#ifndef STARTUP_SONG +# define STARTUP_SONG SONG(STARTUP_SOUND) +#endif +float startup_song[][2] = STARTUP_SONG; + +static void gpt_cb8(GPTDriver *gptp); + +#define DAC_BUFFER_SIZE 100 +#ifndef DAC_SAMPLE_MAX +# define DAC_SAMPLE_MAX 65535U +#endif + +#define START_CHANNEL_1() \ + gptStart(&GPTD6, &gpt6cfg1); \ + gptStartContinuous(&GPTD6, 2U) +#define START_CHANNEL_2() \ + gptStart(&GPTD7, &gpt7cfg1); \ + gptStartContinuous(&GPTD7, 2U) +#define STOP_CHANNEL_1() gptStopTimer(&GPTD6) +#define STOP_CHANNEL_2() gptStopTimer(&GPTD7) +#define RESTART_CHANNEL_1() \ + STOP_CHANNEL_1(); \ + START_CHANNEL_1() +#define RESTART_CHANNEL_2() \ + STOP_CHANNEL_2(); \ + START_CHANNEL_2() +#define UPDATE_CHANNEL_1_FREQ(freq) \ + gpt6cfg1.frequency = freq * DAC_BUFFER_SIZE; \ + RESTART_CHANNEL_1() +#define UPDATE_CHANNEL_2_FREQ(freq) \ + gpt7cfg1.frequency = freq * DAC_BUFFER_SIZE; \ + RESTART_CHANNEL_2() +#define GET_CHANNEL_1_FREQ (uint16_t)(gpt6cfg1.frequency * DAC_BUFFER_SIZE) +#define GET_CHANNEL_2_FREQ (uint16_t)(gpt7cfg1.frequency * DAC_BUFFER_SIZE) + +/* + * GPT6 configuration. + */ +// static const GPTConfig gpt6cfg1 = { +// .frequency = 1000000U, +// .callback = NULL, +// .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ +// .dier = 0U +// }; + +GPTConfig gpt6cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE, + .callback = NULL, + .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ + .dier = 0U}; + +GPTConfig gpt7cfg1 = {.frequency = 440U * DAC_BUFFER_SIZE, + .callback = NULL, + .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ + .dier = 0U}; + +GPTConfig gpt8cfg1 = {.frequency = 10, + .callback = gpt_cb8, + .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ + .dier = 0U}; + +/* + * DAC test buffer (sine wave). + */ +// static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = { +// 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437, +// 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846, +// 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221, +// 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544, +// 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801, +// 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982, +// 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078, +// 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086, +// 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004, +// 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837, +// 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591, +// 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278, +// 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912, +// 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507, +// 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082, +// 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657, +// 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248, +// 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873, +// 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550, +// 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293, +// 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112, +// 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16, +// 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8, +// 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90, +// 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257, +// 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503, +// 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816, +// 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182, +// 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587, +// 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012 +// }; + +// static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = { +// 12, 8, 5, 3, 2, 1, 0, 1, 2, 3, 5, 8, +// 12, 16, 20, 26, 32, 38, 45, 53, 61, 70, 80, 90, +// 101, 112, 124, 136, 150, 163, 177, 192, 208, 224, 240, 257, +// 275, 293, 312, 331, 350, 371, 391, 413, 434, 457, 479, 503, +// 526, 550, 575, 600, 626, 651, 678, 705, 732, 759, 787, 816, +// 844, 873, 903, 933, 963, 993, 1024, 1055, 1086, 1118, 1150, 1182, +// 1215, 1248, 1281, 1314, 1347, 1381, 1415, 1449, 1483, 1518, 1552, 1587, +// 1622, 1657, 1692, 1727, 1763, 1798, 1834, 1869, 1905, 1940, 1976, 2012, +// 2047, 2082, 2118, 2154, 2189, 2225, 2260, 2296, 2331, 2367, 2402, 2437, +// 2472, 2507, 2542, 2576, 2611, 2645, 2679, 2713, 2747, 2780, 2813, 2846, +// 2879, 2912, 2944, 2976, 3008, 3039, 3070, 3101, 3131, 3161, 3191, 3221, +// 3250, 3278, 3307, 3335, 3362, 3389, 3416, 3443, 3468, 3494, 3519, 3544, +// 3568, 3591, 3615, 3637, 3660, 3681, 3703, 3723, 3744, 3763, 3782, 3801, +// 3819, 3837, 3854, 3870, 3886, 3902, 3917, 3931, 3944, 3958, 3970, 3982, +// 3993, 4004, 4014, 4024, 4033, 4041, 4049, 4056, 4062, 4068, 4074, 4078, +// 4082, 4086, 4089, 4091, 4092, 4093, 4094, 4093, 4092, 4091, 4089, 4086, +// 4082, 4078, 4074, 4068, 4062, 4056, 4049, 4041, 4033, 4024, 4014, 4004, +// 3993, 3982, 3970, 3958, 3944, 3931, 3917, 3902, 3886, 3870, 3854, 3837, +// 3819, 3801, 3782, 3763, 3744, 3723, 3703, 3681, 3660, 3637, 3615, 3591, +// 3568, 3544, 3519, 3494, 3468, 3443, 3416, 3389, 3362, 3335, 3307, 3278, +// 3250, 3221, 3191, 3161, 3131, 3101, 3070, 3039, 3008, 2976, 2944, 2912, +// 2879, 2846, 2813, 2780, 2747, 2713, 2679, 2645, 2611, 2576, 2542, 2507, +// 2472, 2437, 2402, 2367, 2331, 2296, 2260, 2225, 2189, 2154, 2118, 2082, +// 2047, 2012, 1976, 1940, 1905, 1869, 1834, 1798, 1763, 1727, 1692, 1657, +// 1622, 1587, 1552, 1518, 1483, 1449, 1415, 1381, 1347, 1314, 1281, 1248, +// 1215, 1182, 1150, 1118, 1086, 1055, 1024, 993, 963, 933, 903, 873, +// 844, 816, 787, 759, 732, 705, 678, 651, 626, 600, 575, 550, +// 526, 503, 479, 457, 434, 413, 391, 371, 350, 331, 312, 293, +// 275, 257, 240, 224, 208, 192, 177, 163, 150, 136, 124, 112, +// 101, 90, 80, 70, 61, 53, 45, 38, 32, 26, 20, 16 +// }; + +// squarewave +static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = { + // First half is max, second half is 0 + [0 ... DAC_BUFFER_SIZE / 2 - 1] = DAC_SAMPLE_MAX, + [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = 0, +}; + +// squarewave +static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = { + // opposite of dac_buffer above + [0 ... DAC_BUFFER_SIZE / 2 - 1] = 0, + [DAC_BUFFER_SIZE / 2 ... DAC_BUFFER_SIZE - 1] = DAC_SAMPLE_MAX, +}; + +/* + * DAC streaming callback. + */ +size_t nz = 0; +static void end_cb1(DACDriver *dacp) { + (void)dacp; + + nz++; + if ((nz % 1000) == 0) { + // palTogglePad(GPIOD, GPIOD_LED3); + } +} + +/* + * DAC error callback. + */ +static void error_cb1(DACDriver *dacp, dacerror_t err) { + (void)dacp; + (void)err; + + chSysHalt("DAC failure"); +} + +static const DACConfig dac1cfg1 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT}; + +static const DACConversionGroup dacgrpcfg1 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)}; + +static const DACConfig dac1cfg2 = {.init = DAC_SAMPLE_MAX, .datamode = DAC_DHRM_12BIT_RIGHT}; + +static const DACConversionGroup dacgrpcfg2 = {.num_channels = 1U, .end_cb = end_cb1, .error_cb = error_cb1, .trigger = DAC_TRG(0)}; + +void audio_init() { + if (audio_initialized) { + return; + } + +// Check EEPROM +#ifdef EEPROM_ENABLE + if (!eeconfig_is_enabled()) { + eeconfig_init(); + } + audio_config.raw = eeconfig_read_audio(); +#else // ARM EEPROM + audio_config.enable = true; +# ifdef AUDIO_CLICKY_ON + audio_config.clicky_enable = true; +# endif +#endif // ARM EEPROM + + /* + * Starting DAC1 driver, setting up the output pin as analog as suggested + * by the Reference Manual. + */ + palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG); + palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); + dacStart(&DACD1, &dac1cfg1); + dacStart(&DACD2, &dac1cfg2); + + /* + * Starting GPT6/7 driver, it is used for triggering the DAC. + */ + START_CHANNEL_1(); + START_CHANNEL_2(); + + /* + * Starting a continuous conversion. + */ + dacStartConversion(&DACD1, &dacgrpcfg1, (dacsample_t *)dac_buffer, DAC_BUFFER_SIZE); + dacStartConversion(&DACD2, &dacgrpcfg2, (dacsample_t *)dac_buffer_2, DAC_BUFFER_SIZE); + + audio_initialized = true; + + if (audio_config.enable) { + PLAY_SONG(startup_song); + } else { + stop_all_notes(); + } +} + +void stop_all_notes() { + dprintf("audio stop all notes"); + + if (!audio_initialized) { + audio_init(); + } + voices = 0; + + gptStopTimer(&GPTD6); + gptStopTimer(&GPTD7); + gptStopTimer(&GPTD8); + + playing_notes = false; + playing_note = false; + frequency = 0; + frequency_alt = 0; + volume = 0; + + for (uint8_t i = 0; i < 8; i++) { + frequencies[i] = 0; + volumes[i] = 0; + } +} + +void stop_note(float freq) { + dprintf("audio stop note freq=%d", (int)freq); + + if (playing_note) { + if (!audio_initialized) { + audio_init(); + } + for (int i = 7; i >= 0; i--) { + if (frequencies[i] == freq) { + frequencies[i] = 0; + volumes[i] = 0; + for (int j = i; (j < 7); j++) { + frequencies[j] = frequencies[j + 1]; + frequencies[j + 1] = 0; + volumes[j] = volumes[j + 1]; + volumes[j + 1] = 0; + } + break; + } + } + voices--; + if (voices < 0) { + voices = 0; + } + if (voice_place >= voices) { + voice_place = 0; + } + if (voices == 0) { + STOP_CHANNEL_1(); + STOP_CHANNEL_2(); + gptStopTimer(&GPTD8); + frequency = 0; + frequency_alt = 0; + volume = 0; + playing_note = false; + } + } +} + +#ifdef VIBRATO_ENABLE + +float mod(float a, int b) { + float r = fmod(a, b); + return r < 0 ? r + b : r; +} + +float vibrato(float average_freq) { +# ifdef VIBRATO_STRENGTH_ENABLE + float vibrated_freq = average_freq * pow(vibrato_lut[(int)vibrato_counter], vibrato_strength); +# else + float vibrated_freq = average_freq * vibrato_lut[(int)vibrato_counter]; +# endif + vibrato_counter = mod((vibrato_counter + vibrato_rate * (1.0 + 440.0 / average_freq)), VIBRATO_LUT_LENGTH); + return vibrated_freq; +} + +#endif + +static void gpt_cb8(GPTDriver *gptp) { + float freq; + + if (playing_note) { + if (voices > 0) { + float freq_alt = 0; + if (voices > 1) { + if (polyphony_rate == 0) { + if (glissando) { + if (frequency_alt != 0 && frequency_alt < frequencies[voices - 2] && frequency_alt < frequencies[voices - 2] * pow(2, -440 / frequencies[voices - 2] / 12 / 2)) { + frequency_alt = frequency_alt * pow(2, 440 / frequency_alt / 12 / 2); + } else if (frequency_alt != 0 && frequency_alt > frequencies[voices - 2] && frequency_alt > frequencies[voices - 2] * pow(2, 440 / frequencies[voices - 2] / 12 / 2)) { + frequency_alt = frequency_alt * pow(2, -440 / frequency_alt / 12 / 2); + } else { + frequency_alt = frequencies[voices - 2]; + } + } else { + frequency_alt = frequencies[voices - 2]; + } + +#ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq_alt = vibrato(frequency_alt); + } else { + freq_alt = frequency_alt; + } +#else + freq_alt = frequency_alt; +#endif + } + + if (envelope_index < 65535) { + envelope_index++; + } + + freq_alt = voice_envelope(freq_alt); + + if (freq_alt < 30.517578125) { + freq_alt = 30.52; + } + + if (GET_CHANNEL_2_FREQ != (uint16_t)freq_alt) { + UPDATE_CHANNEL_2_FREQ(freq_alt); + } else { + RESTART_CHANNEL_2(); + } + // note_timbre; + } + + if (polyphony_rate > 0) { + if (voices > 1) { + voice_place %= voices; + if (place++ > (frequencies[voice_place] / polyphony_rate)) { + voice_place = (voice_place + 1) % voices; + place = 0.0; + } + } + +#ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(frequencies[voice_place]); + } else { + freq = frequencies[voice_place]; + } +#else + freq = frequencies[voice_place]; +#endif + } else { + if (glissando) { + if (frequency != 0 && frequency < frequencies[voices - 1] && frequency < frequencies[voices - 1] * pow(2, -440 / frequencies[voices - 1] / 12 / 2)) { + frequency = frequency * pow(2, 440 / frequency / 12 / 2); + } else if (frequency != 0 && frequency > frequencies[voices - 1] && frequency > frequencies[voices - 1] * pow(2, 440 / frequencies[voices - 1] / 12 / 2)) { + frequency = frequency * pow(2, -440 / frequency / 12 / 2); + } else { + frequency = frequencies[voices - 1]; + } + } else { + frequency = frequencies[voices - 1]; + } + +#ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(frequency); + } else { + freq = frequency; + } +#else + freq = frequency; +#endif + } + + if (envelope_index < 65535) { + envelope_index++; + } + + freq = voice_envelope(freq); + + if (freq < 30.517578125) { + freq = 30.52; + } + + if (GET_CHANNEL_1_FREQ != (uint16_t)freq) { + UPDATE_CHANNEL_1_FREQ(freq); + } else { + RESTART_CHANNEL_1(); + } + // note_timbre; + } + } + + if (playing_notes) { + if (note_frequency > 0) { +#ifdef VIBRATO_ENABLE + if (vibrato_strength > 0) { + freq = vibrato(note_frequency); + } else { + freq = note_frequency; + } +#else + freq = note_frequency; +#endif + + if (envelope_index < 65535) { + envelope_index++; + } + freq = voice_envelope(freq); + + if (GET_CHANNEL_1_FREQ != (uint16_t)freq) { + UPDATE_CHANNEL_1_FREQ(freq); + UPDATE_CHANNEL_2_FREQ(freq); + } + // note_timbre; + } else { + // gptStopTimer(&GPTD6); + // gptStopTimer(&GPTD7); + } + + note_position++; + bool end_of_note = false; + if (GET_CHANNEL_1_FREQ > 0) { + if (!note_resting) + end_of_note = (note_position >= (note_length * 8 - 1)); + else + end_of_note = (note_position >= (note_length * 8)); + } else { + end_of_note = (note_position >= (note_length * 8)); + } + + if (end_of_note) { + current_note++; + if (current_note >= notes_count) { + if (notes_repeat) { + current_note = 0; + } else { + STOP_CHANNEL_1(); + STOP_CHANNEL_2(); + // gptStopTimer(&GPTD8); + playing_notes = false; + return; + } + } + if (!note_resting) { + note_resting = true; + current_note--; + if ((*notes_pointer)[current_note][0] == (*notes_pointer)[current_note + 1][0]) { + note_frequency = 0; + note_length = 1; + } else { + note_frequency = (*notes_pointer)[current_note][0]; + note_length = 1; + } + } else { + note_resting = false; + envelope_index = 0; + note_frequency = (*notes_pointer)[current_note][0]; + note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); + } + + note_position = 0; + } + } + + if (!audio_config.enable) { + playing_notes = false; + playing_note = false; + } +} + +void play_note(float freq, int vol) { + dprintf("audio play note freq=%d vol=%d", (int)freq, vol); + + if (!audio_initialized) { + audio_init(); + } + + if (audio_config.enable && voices < 8) { + // Cancel notes if notes are playing + if (playing_notes) { + stop_all_notes(); + } + + playing_note = true; + + envelope_index = 0; + + if (freq > 0) { + frequencies[voices] = freq; + volumes[voices] = vol; + voices++; + } + + gptStart(&GPTD8, &gpt8cfg1); + gptStartContinuous(&GPTD8, 2U); + RESTART_CHANNEL_1(); + RESTART_CHANNEL_2(); + } +} + +void play_notes(float (*np)[][2], uint16_t n_count, bool n_repeat) { + if (!audio_initialized) { + audio_init(); + } + + if (audio_config.enable) { + // Cancel note if a note is playing + if (playing_note) { + stop_all_notes(); + } + + playing_notes = true; + + notes_pointer = np; + notes_count = n_count; + notes_repeat = n_repeat; + + place = 0; + current_note = 0; + + note_frequency = (*notes_pointer)[current_note][0]; + note_length = ((*notes_pointer)[current_note][1] / 4) * (((float)note_tempo) / 100); + note_position = 0; + + gptStart(&GPTD8, &gpt8cfg1); + gptStartContinuous(&GPTD8, 2U); + RESTART_CHANNEL_1(); + RESTART_CHANNEL_2(); + } +} + +bool is_playing_notes(void) { return playing_notes; } + +bool is_audio_on(void) { return (audio_config.enable != 0); } + +void audio_toggle(void) { + audio_config.enable ^= 1; + eeconfig_update_audio(audio_config.raw); + if (audio_config.enable) { + audio_on_user(); + } +} + +void audio_on(void) { + audio_config.enable = 1; + eeconfig_update_audio(audio_config.raw); + audio_on_user(); +} + +void audio_off(void) { + stop_all_notes(); + audio_config.enable = 0; + eeconfig_update_audio(audio_config.raw); +} + +#ifdef VIBRATO_ENABLE + +// Vibrato rate functions + +void set_vibrato_rate(float rate) { vibrato_rate = rate; } + +void increase_vibrato_rate(float change) { vibrato_rate *= change; } + +void decrease_vibrato_rate(float change) { vibrato_rate /= change; } + +# ifdef VIBRATO_STRENGTH_ENABLE + +void set_vibrato_strength(float strength) { vibrato_strength = strength; } + +void increase_vibrato_strength(float change) { vibrato_strength *= change; } + +void decrease_vibrato_strength(float change) { vibrato_strength /= change; } + +# endif /* VIBRATO_STRENGTH_ENABLE */ + +#endif /* VIBRATO_ENABLE */ + +// Polyphony functions + +void set_polyphony_rate(float rate) { polyphony_rate = rate; } + +void enable_polyphony() { polyphony_rate = 5; } + +void disable_polyphony() { polyphony_rate = 0; } + +void increase_polyphony_rate(float change) { polyphony_rate *= change; } + +void decrease_polyphony_rate(float change) { polyphony_rate /= change; } + +// Timbre function + +void set_timbre(float timbre) { note_timbre = timbre; } + +// Tempo functions + +void set_tempo(uint8_t tempo) { note_tempo = tempo; } + +void decrease_tempo(uint8_t tempo_change) { note_tempo += tempo_change; } + +void increase_tempo(uint8_t tempo_change) { + if (note_tempo - tempo_change < 10) { + note_tempo = 10; + } else { + note_tempo -= tempo_change; + } +} diff --git a/quantum/backlight/backlight_arm.c b/quantum/backlight/backlight_arm.c deleted file mode 100644 index 723544adb9..0000000000 --- a/quantum/backlight/backlight_arm.c +++ /dev/null @@ -1,184 +0,0 @@ -#include "quantum.h" -#include "backlight.h" -#include -#include "debug.h" - -// TODO: remove short term bodge when refactoring BACKLIGHT_CUSTOM_DRIVER out -#ifdef BACKLIGHT_PIN - -// GPIOV2 && GPIOV3 -# ifndef BACKLIGHT_PAL_MODE -# define BACKLIGHT_PAL_MODE 2 -# endif - -// GENERIC -# ifndef BACKLIGHT_PWM_DRIVER -# define BACKLIGHT_PWM_DRIVER PWMD4 -# endif -# ifndef BACKLIGHT_PWM_CHANNEL -# define BACKLIGHT_PWM_CHANNEL 3 -# endif - -static void breathing_callback(PWMDriver *pwmp); - -static PWMConfig pwmCFG = {0xFFFF, /* PWM clock frequency */ - 256, /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ - NULL, /* No Callback */ - { /* Default all channels to disabled - Channels will be configured durring init */ - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL}}, - 0, /* HW dependent part.*/ - 0}; - -static PWMConfig pwmCFG_breathing = {0xFFFF, /** PWM clock frequency */ - 256, /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ - breathing_callback, /* Breathing Callback */ - { /* Default all channels to disabled - Channels will be configured durring init */ - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL}, - {PWM_OUTPUT_DISABLED, NULL}}, - 0, /* HW dependent part.*/ - 0}; - -// See http://jared.geek.nz/2013/feb/linear-led-pwm -static uint16_t cie_lightness(uint16_t v) { - if (v <= 5243) // if below 8% of max - return v / 9; // same as dividing by 900% - else { - uint32_t y = (((uint32_t)v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare - // to get a useful result with integer division, we shift left in the expression above - // and revert what we've done again after squaring. - y = y * y * y >> 8; - if (y > 0xFFFFUL) // prevent overflow - return 0xFFFFU; - else - return (uint16_t)y; - } -} - -void backlight_init_ports(void) { - // printf("backlight_init_ports()\n"); - -# ifdef USE_GPIOV1 - palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); -# else - palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_ALTERNATE(BACKLIGHT_PAL_MODE)); -# endif - - pwmCFG.channels[BACKLIGHT_PWM_CHANNEL - 1].mode = PWM_OUTPUT_ACTIVE_HIGH; - pwmCFG_breathing.channels[BACKLIGHT_PWM_CHANNEL - 1].mode = PWM_OUTPUT_ACTIVE_HIGH; - pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG); - - backlight_set(get_backlight_level()); - if (is_backlight_breathing()) { - breathing_enable(); - } -} - -void backlight_set(uint8_t level) { - // printf("backlight_set(%d)\n", level); - if (level == 0) { - // Turn backlight off - pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1); - } else { - // Turn backlight on - if (!is_breathing()) { - uint32_t duty = (uint32_t)(cie_lightness(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS)); - // printf("duty: (%d)\n", duty); - pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); - } - } -} - -uint8_t backlight_tick = 0; - -void backlight_task(void) {} - -# define BREATHING_NO_HALT 0 -# define BREATHING_HALT_OFF 1 -# define BREATHING_HALT_ON 2 -# define BREATHING_STEPS 128 - -static uint8_t breathing_halt = BREATHING_NO_HALT; -static uint16_t breathing_counter = 0; - -bool is_breathing(void) { return BACKLIGHT_PWM_DRIVER.config == &pwmCFG_breathing; } - -static inline void breathing_min(void) { breathing_counter = 0; } - -static inline void breathing_max(void) { breathing_counter = get_breathing_period() * 256 / 2; } - -void breathing_interrupt_enable(void) { - pwmStop(&BACKLIGHT_PWM_DRIVER); - pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG_breathing); - chSysLockFromISR(); - pwmEnablePeriodicNotification(&BACKLIGHT_PWM_DRIVER); - pwmEnableChannelI(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, 0xFFFF)); - chSysUnlockFromISR(); -} - -void breathing_interrupt_disable(void) { - pwmStop(&BACKLIGHT_PWM_DRIVER); - pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG); -} - -void breathing_enable(void) { - breathing_counter = 0; - breathing_halt = BREATHING_NO_HALT; - breathing_interrupt_enable(); -} - -void breathing_pulse(void) { - if (get_backlight_level() == 0) - breathing_min(); - else - breathing_max(); - breathing_halt = BREATHING_HALT_ON; - breathing_interrupt_enable(); -} - -void breathing_disable(void) { - // printf("breathing_disable()\n"); - breathing_interrupt_disable(); - // Restore backlight level - backlight_set(get_backlight_level()); -} - -void breathing_self_disable(void) { - if (get_backlight_level() == 0) - breathing_halt = BREATHING_HALT_OFF; - else - breathing_halt = BREATHING_HALT_ON; -} - -/* To generate breathing curve in python: - * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)] - */ -static const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - -// Use this before the cie_lightness function. -static inline uint16_t scale_backlight(uint16_t v) { return v / BACKLIGHT_LEVELS * get_backlight_level(); } - -static void breathing_callback(PWMDriver *pwmp) { - (void)pwmp; - uint8_t breathing_period = get_breathing_period(); - uint16_t interval = (uint16_t)breathing_period * 256 / BREATHING_STEPS; - // resetting after one period to prevent ugly reset at overflow. - breathing_counter = (breathing_counter + 1) % (breathing_period * 256); - uint8_t index = breathing_counter / interval % BREATHING_STEPS; - - if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) { - breathing_interrupt_disable(); - } - - uint32_t duty = cie_lightness(scale_backlight(breathing_table[index] * 256)); - - chSysLockFromISR(); - pwmEnableChannelI(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); - chSysUnlockFromISR(); -} - -#endif diff --git a/quantum/backlight/backlight_chibios.c b/quantum/backlight/backlight_chibios.c new file mode 100644 index 0000000000..723544adb9 --- /dev/null +++ b/quantum/backlight/backlight_chibios.c @@ -0,0 +1,184 @@ +#include "quantum.h" +#include "backlight.h" +#include +#include "debug.h" + +// TODO: remove short term bodge when refactoring BACKLIGHT_CUSTOM_DRIVER out +#ifdef BACKLIGHT_PIN + +// GPIOV2 && GPIOV3 +# ifndef BACKLIGHT_PAL_MODE +# define BACKLIGHT_PAL_MODE 2 +# endif + +// GENERIC +# ifndef BACKLIGHT_PWM_DRIVER +# define BACKLIGHT_PWM_DRIVER PWMD4 +# endif +# ifndef BACKLIGHT_PWM_CHANNEL +# define BACKLIGHT_PWM_CHANNEL 3 +# endif + +static void breathing_callback(PWMDriver *pwmp); + +static PWMConfig pwmCFG = {0xFFFF, /* PWM clock frequency */ + 256, /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ + NULL, /* No Callback */ + { /* Default all channels to disabled - Channels will be configured durring init */ + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL}}, + 0, /* HW dependent part.*/ + 0}; + +static PWMConfig pwmCFG_breathing = {0xFFFF, /** PWM clock frequency */ + 256, /* PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ + breathing_callback, /* Breathing Callback */ + { /* Default all channels to disabled - Channels will be configured durring init */ + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL}, + {PWM_OUTPUT_DISABLED, NULL}}, + 0, /* HW dependent part.*/ + 0}; + +// See http://jared.geek.nz/2013/feb/linear-led-pwm +static uint16_t cie_lightness(uint16_t v) { + if (v <= 5243) // if below 8% of max + return v / 9; // same as dividing by 900% + else { + uint32_t y = (((uint32_t)v + 10486) << 8) / (10486 + 0xFFFFUL); // add 16% of max and compare + // to get a useful result with integer division, we shift left in the expression above + // and revert what we've done again after squaring. + y = y * y * y >> 8; + if (y > 0xFFFFUL) // prevent overflow + return 0xFFFFU; + else + return (uint16_t)y; + } +} + +void backlight_init_ports(void) { + // printf("backlight_init_ports()\n"); + +# ifdef USE_GPIOV1 + palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_STM32_ALTERNATE_PUSHPULL); +# else + palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), PAL_MODE_ALTERNATE(BACKLIGHT_PAL_MODE)); +# endif + + pwmCFG.channels[BACKLIGHT_PWM_CHANNEL - 1].mode = PWM_OUTPUT_ACTIVE_HIGH; + pwmCFG_breathing.channels[BACKLIGHT_PWM_CHANNEL - 1].mode = PWM_OUTPUT_ACTIVE_HIGH; + pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG); + + backlight_set(get_backlight_level()); + if (is_backlight_breathing()) { + breathing_enable(); + } +} + +void backlight_set(uint8_t level) { + // printf("backlight_set(%d)\n", level); + if (level == 0) { + // Turn backlight off + pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1); + } else { + // Turn backlight on + if (!is_breathing()) { + uint32_t duty = (uint32_t)(cie_lightness(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS)); + // printf("duty: (%d)\n", duty); + pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); + } + } +} + +uint8_t backlight_tick = 0; + +void backlight_task(void) {} + +# define BREATHING_NO_HALT 0 +# define BREATHING_HALT_OFF 1 +# define BREATHING_HALT_ON 2 +# define BREATHING_STEPS 128 + +static uint8_t breathing_halt = BREATHING_NO_HALT; +static uint16_t breathing_counter = 0; + +bool is_breathing(void) { return BACKLIGHT_PWM_DRIVER.config == &pwmCFG_breathing; } + +static inline void breathing_min(void) { breathing_counter = 0; } + +static inline void breathing_max(void) { breathing_counter = get_breathing_period() * 256 / 2; } + +void breathing_interrupt_enable(void) { + pwmStop(&BACKLIGHT_PWM_DRIVER); + pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG_breathing); + chSysLockFromISR(); + pwmEnablePeriodicNotification(&BACKLIGHT_PWM_DRIVER); + pwmEnableChannelI(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, 0xFFFF)); + chSysUnlockFromISR(); +} + +void breathing_interrupt_disable(void) { + pwmStop(&BACKLIGHT_PWM_DRIVER); + pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG); +} + +void breathing_enable(void) { + breathing_counter = 0; + breathing_halt = BREATHING_NO_HALT; + breathing_interrupt_enable(); +} + +void breathing_pulse(void) { + if (get_backlight_level() == 0) + breathing_min(); + else + breathing_max(); + breathing_halt = BREATHING_HALT_ON; + breathing_interrupt_enable(); +} + +void breathing_disable(void) { + // printf("breathing_disable()\n"); + breathing_interrupt_disable(); + // Restore backlight level + backlight_set(get_backlight_level()); +} + +void breathing_self_disable(void) { + if (get_backlight_level() == 0) + breathing_halt = BREATHING_HALT_OFF; + else + breathing_halt = BREATHING_HALT_ON; +} + +/* To generate breathing curve in python: + * from math import sin, pi; [int(sin(x/128.0*pi)**4*255) for x in range(128)] + */ +static const uint8_t breathing_table[BREATHING_STEPS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, 4, 5, 6, 8, 10, 12, 15, 17, 20, 24, 28, 32, 36, 41, 46, 51, 57, 63, 70, 76, 83, 91, 98, 106, 113, 121, 129, 138, 146, 154, 162, 170, 178, 185, 193, 200, 207, 213, 220, 225, 231, 235, 240, 244, 247, 250, 252, 253, 254, 255, 254, 253, 252, 250, 247, 244, 240, 235, 231, 225, 220, 213, 207, 200, 193, 185, 178, 170, 162, 154, 146, 138, 129, 121, 113, 106, 98, 91, 83, 76, 70, 63, 57, 51, 46, 41, 36, 32, 28, 24, 20, 17, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +// Use this before the cie_lightness function. +static inline uint16_t scale_backlight(uint16_t v) { return v / BACKLIGHT_LEVELS * get_backlight_level(); } + +static void breathing_callback(PWMDriver *pwmp) { + (void)pwmp; + uint8_t breathing_period = get_breathing_period(); + uint16_t interval = (uint16_t)breathing_period * 256 / BREATHING_STEPS; + // resetting after one period to prevent ugly reset at overflow. + breathing_counter = (breathing_counter + 1) % (breathing_period * 256); + uint8_t index = breathing_counter / interval % BREATHING_STEPS; + + if (((breathing_halt == BREATHING_HALT_ON) && (index == BREATHING_STEPS / 2)) || ((breathing_halt == BREATHING_HALT_OFF) && (index == BREATHING_STEPS - 1))) { + breathing_interrupt_disable(); + } + + uint32_t duty = cie_lightness(scale_backlight(breathing_table[index] * 256)); + + chSysLockFromISR(); + pwmEnableChannelI(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); + chSysUnlockFromISR(); +} + +#endif diff --git a/tmk_core/chibios.mk b/tmk_core/chibios.mk index 014019ef04..b400e9e0f1 100644 --- a/tmk_core/chibios.mk +++ b/tmk_core/chibios.mk @@ -179,7 +179,7 @@ HEX = $(OBJCOPY) -O $(FORMAT) EEP = BIN = $(OBJCOPY) -O binary -COMMON_VPATH += $(DRIVER_PATH)/arm +COMMON_VPATH += $(DRIVER_PATH)/chibios THUMBFLAGS = -DTHUMB_PRESENT -mno-thumb-interwork -DTHUMB_NO_INTERWORKING -mthumb -DTHUMB diff --git a/tmk_core/common.mk b/tmk_core/common.mk index 6863929cee..b766ebe97e 100644 --- a/tmk_core/common.mk +++ b/tmk_core/common.mk @@ -1,13 +1,5 @@ COMMON_DIR = common -ifeq ($(PLATFORM),AVR) - PLATFORM_COMMON_DIR = $(COMMON_DIR)/avr -else ifeq ($(PLATFORM),CHIBIOS) - PLATFORM_COMMON_DIR = $(COMMON_DIR)/chibios -else ifeq ($(PLATFORM),ARM_ATSAM) - PLATFORM_COMMON_DIR = $(COMMON_DIR)/arm_atsam -else - PLATFORM_COMMON_DIR = $(COMMON_DIR)/test -endif +PLATFORM_COMMON_DIR = $(COMMON_DIR)/$(PLATFORM_KEY) TMK_COMMON_SRC += $(COMMON_DIR)/host.c \ $(COMMON_DIR)/keyboard.c \ @@ -179,6 +171,4 @@ endif # Search Path VPATH += $(TMK_PATH)/$(COMMON_DIR) -ifeq ($(PLATFORM),CHIBIOS) -VPATH += $(TMK_PATH)/$(COMMON_DIR)/chibios -endif +VPATH += $(TMK_PATH)/$(PLATFORM_COMMON_DIR) -- cgit v1.2.3