diff options
author | vin <git@vineetk.net> | 2023-10-09 10:43:46 -0400 |
---|---|---|
committer | vin <git@vineetk.net> | 2023-10-09 12:54:54 -0400 |
commit | 797953ea2c49e3f16ca560729f6bdbb15a59d580 (patch) | |
tree | 50d66a320a8886d07cd9de0ec03cf05c6685064f /platforms/chibios/drivers | |
parent | 52db763c718733eada69e81b587377e3a720d390 (diff) |
remove unused libraries
Diffstat (limited to 'platforms/chibios/drivers')
44 files changed, 0 insertions, 7650 deletions
diff --git a/platforms/chibios/drivers/analog.c b/platforms/chibios/drivers/analog.c deleted file mode 100644 index bf84ce8f76..0000000000 --- a/platforms/chibios/drivers/analog.c +++ /dev/null @@ -1,329 +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 <http://www.gnu.org/licenses/>. - */ - -#include "analog.h" -#include <ch.h> -#include <hal.h> - -#if !HAL_USE_ADC -# error "You need to set HAL_USE_ADC to TRUE in your halconf.h to use the ADC." -#endif - -#if !RP_ADC_USE_ADC1 && !STM32_ADC_USE_ADC1 && !STM32_ADC_USE_ADC2 && !STM32_ADC_USE_ADC3 && !STM32_ADC_USE_ADC4 && !WB32_ADC_USE_ADC1 -# error "You need to set one of the 'xxx_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) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) -# define USE_ADCV2 -#endif - -// BODGE to make v2 look like v1,3 and 4 -#if defined(USE_ADCV2) || defined(RP2040) -# 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(RP2040) || defined(STM32F0XX) || defined(STM32F1XX) || defined(STM32F4XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) -# 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 -# ifdef ADC_CFGR_RES_10BITS // ADCv3, ADCv4 -# define ADC_RESOLUTION ADC_CFGR_RES_10BITS -# else // ADCv1, ADCv5, or the bodge for ADCv2 above -# define ADC_RESOLUTION ADC_CFGR1_RES_10BIT -# endif -#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) && !defined(GD32VF103) && !defined(WB32F3G71xx) && !defined(WB32FQ95xx) - .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), -#elif defined(RP2040) -// RP2040 does not have any extra config here -#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( 0, 0 ); - case A1: return TO_MUX( 1, 0 ); - case A2: return TO_MUX( 2, 0 ); - case A3: return TO_MUX( 3, 0 ); - case A4: return TO_MUX( 4, 0 ); - case A5: return TO_MUX( 5, 0 ); - case A6: return TO_MUX( 6, 0 ); - case A7: return TO_MUX( 7, 0 ); - case B0: return TO_MUX( 8, 0 ); - case B1: return TO_MUX( 9, 0 ); - case C0: return TO_MUX( 10, 0 ); - case C1: return TO_MUX( 11, 0 ); - case C2: return TO_MUX( 12, 0 ); - case C3: return TO_MUX( 13, 0 ); - case C4: return TO_MUX( 14, 0 ); - case C5: return TO_MUX( 15, 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_IN3, 3 ); - case B13: return TO_MUX( ADC_CHANNEL_IN5, 2 ); - 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) - case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); - case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 ); - case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 ); - case A3: return TO_MUX( ADC_CHANNEL_IN3, 0 ); - case A4: return TO_MUX( ADC_CHANNEL_IN4, 0 ); - case A5: return TO_MUX( ADC_CHANNEL_IN5, 0 ); - case A6: return TO_MUX( ADC_CHANNEL_IN6, 0 ); - case A7: return TO_MUX( ADC_CHANNEL_IN7, 0 ); - case B0: return TO_MUX( ADC_CHANNEL_IN8, 0 ); - case B1: return TO_MUX( ADC_CHANNEL_IN9, 0 ); - case C0: return TO_MUX( ADC_CHANNEL_IN10, 0 ); - case C1: return TO_MUX( ADC_CHANNEL_IN11, 0 ); - case C2: return TO_MUX( ADC_CHANNEL_IN12, 0 ); - case C3: return TO_MUX( ADC_CHANNEL_IN13, 0 ); - case C4: return TO_MUX( ADC_CHANNEL_IN14, 0 ); - case C5: return TO_MUX( ADC_CHANNEL_IN15, 0 ); -# if STM32_ADC_USE_ADC3 - case F3: return TO_MUX( ADC_CHANNEL_IN9, 2 ); - case F4: return TO_MUX( ADC_CHANNEL_IN14, 2 ); - case F5: return TO_MUX( ADC_CHANNEL_IN15, 2 ); - case F6: return TO_MUX( ADC_CHANNEL_IN4, 2 ); - case F7: return TO_MUX( ADC_CHANNEL_IN5, 2 ); - case F8: return TO_MUX( ADC_CHANNEL_IN6, 2 ); - case F9: return TO_MUX( ADC_CHANNEL_IN7, 2 ); - case F10: return TO_MUX( ADC_CHANNEL_IN8, 2 ); -# endif -#elif defined(STM32F1XX) || defined(GD32VF103) || defined(WB32F3G71xx) || defined(WB32FQ95xx) - case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); - case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 ); - case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 ); - case A3: return TO_MUX( ADC_CHANNEL_IN3, 0 ); - case A4: return TO_MUX( ADC_CHANNEL_IN4, 0 ); - case A5: return TO_MUX( ADC_CHANNEL_IN5, 0 ); - case A6: return TO_MUX( ADC_CHANNEL_IN6, 0 ); - case A7: return TO_MUX( ADC_CHANNEL_IN7, 0 ); - case B0: return TO_MUX( ADC_CHANNEL_IN8, 0 ); - case B1: return TO_MUX( ADC_CHANNEL_IN9, 0 ); - case C0: return TO_MUX( ADC_CHANNEL_IN10, 0 ); - case C1: return TO_MUX( ADC_CHANNEL_IN11, 0 ); - case C2: return TO_MUX( ADC_CHANNEL_IN12, 0 ); - case C3: return TO_MUX( ADC_CHANNEL_IN13, 0 ); - case C4: return TO_MUX( ADC_CHANNEL_IN14, 0 ); - case C5: return TO_MUX( ADC_CHANNEL_IN15, 0 ); - // STM32F103x[C-G] in 144-pin packages also have analog inputs on F6...F10, but they are on ADC3, and the - // ChibiOS ADC driver for STM32F1xx currently supports only ADC1, therefore these pins are not usable. -#elif defined(RP2040) - case 26U: return TO_MUX(0, 0); - case 27U: return TO_MUX(1, 0); - case 28U: return TO_MUX(2, 0); - case 29U: return TO_MUX(3, 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 RP_ADC_USE_ADC1 || STM32_ADC_USE_ADC1 || WB32_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); -#elif defined(RP2040) - adcConversionGroup.channel_mask = 1 << 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; - } - -#if defined(USE_ADCV2) || defined(RP2040) - // fake 12-bit -> N-bit scale - return (*sampleBuffer) >> (12 - ADC_RESOLUTION); -#else - // already handled as part of adcConvert - return *sampleBuffer; -#endif -} diff --git a/platforms/chibios/drivers/analog.h b/platforms/chibios/drivers/analog.h deleted file mode 100644 index 67a7b466a5..0000000000 --- a/platforms/chibios/drivers/analog.h +++ /dev/null @@ -1,43 +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 <http://www.gnu.org/licenses/>. - */ - -#pragma once - -#include <stdint.h> -#include "gpio.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/platforms/chibios/drivers/audio_dac.h b/platforms/chibios/drivers/audio_dac.h deleted file mode 100644 index 07cd622ead..0000000000 --- a/platforms/chibios/drivers/audio_dac.h +++ /dev/null @@ -1,126 +0,0 @@ -/* Copyright 2019 Jack Humbert - * Copyright 2020 JohSchneider - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -#pragma once - -#ifndef A4 -# define A4 PAL_LINE(GPIOA, 4) -#endif -#ifndef A5 -# define A5 PAL_LINE(GPIOA, 5) -#endif - -/** - * Size of the dac_buffer arrays. All must be the same size. - */ -#define AUDIO_DAC_BUFFER_SIZE 256U - -/** - * Highest value allowed sample value. - - * since the DAC is limited to 12 bit, the absolute max is 0xfff = 4095U; - * lower values adjust the peak-voltage aka volume down. - * adjusting this value has only an effect on a sample-buffer whose values are - * are NOT pregenerated - see square-wave - */ -#ifndef AUDIO_DAC_SAMPLE_MAX -# define AUDIO_DAC_SAMPLE_MAX 4095U -#endif - -#if !defined(AUDIO_DAC_SAMPLE_RATE) && !defined(AUDIO_MAX_SIMULTANEOUS_TONES) && !defined(AUDIO_DAC_QUALITY_VERY_LOW) && !defined(AUDIO_DAC_QUALITY_LOW) && !defined(AUDIO_DAC_QUALITY_HIGH) && !defined(AUDIO_DAC_QUALITY_VERY_HIGH) -# define AUDIO_DAC_QUALITY_SANE_MINIMUM -#endif - -/** - * These presets allow you to quickly switch between quality settings for - * the DAC. The sample rate and maximum number of simultaneous tones roughly - * has an inverse relationship - slightly higher sample rates may be possible. - * - * NOTE: a high sample-rate results in a higher cpu-load, which might lead to - * (audible) discontinuities and/or starve other processes of cpu-time - * (like RGB-led back-lighting, ...) - */ -#ifdef AUDIO_DAC_QUALITY_VERY_LOW -# define AUDIO_DAC_SAMPLE_RATE 11025U -# define AUDIO_MAX_SIMULTANEOUS_TONES 8 -#endif - -#ifdef AUDIO_DAC_QUALITY_LOW -# define AUDIO_DAC_SAMPLE_RATE 22050U -# define AUDIO_MAX_SIMULTANEOUS_TONES 4 -#endif - -#ifdef AUDIO_DAC_QUALITY_HIGH -# define AUDIO_DAC_SAMPLE_RATE 44100U -# define AUDIO_MAX_SIMULTANEOUS_TONES 2 -#endif - -#ifdef AUDIO_DAC_QUALITY_VERY_HIGH -# define AUDIO_DAC_SAMPLE_RATE 88200U -# define AUDIO_MAX_SIMULTANEOUS_TONES 1 -#endif - -#ifdef AUDIO_DAC_QUALITY_SANE_MINIMUM -/* a sane-minimum config: with a trade-off between cpu-load and tone-range - * - * the (currently) highest defined note is NOTE_B8 with 7902Hz; if we now - * aim for an even even multiple of the buffer-size, we end up with: - * ( roundUptoPow2(highest note / AUDIO_DAC_BUFFER_SIZE) * nyquist-rate * AUDIO_DAC_BUFFER_SIZE) - * 7902/256 = 30.867 * 2 * 256 ~= 16384 - * which works out (but the 'scope shows some sampling artifacts with lower harmonics :-P) - */ -# define AUDIO_DAC_SAMPLE_RATE 16384U -# define AUDIO_MAX_SIMULTANEOUS_TONES 8 -#endif - -/** - * Effective bit-rate of the DAC. 44.1khz is the standard for most audio - any - * lower will sacrifice perceptible audio quality. Any higher will limit the - * number of simultaneous tones. In most situations, a tenth (1/10) of the - * sample rate is where notes become unbearable. - */ -#ifndef AUDIO_DAC_SAMPLE_RATE -# define AUDIO_DAC_SAMPLE_RATE 44100U -#endif - -/** - * The number of tones that can be played simultaneously. If too high a value - * is used here, the keyboard will freeze and glitch-out when that many tones - * are being played. - */ -#ifndef AUDIO_MAX_SIMULTANEOUS_TONES -# define AUDIO_MAX_SIMULTANEOUS_TONES 2 -#endif - -/** - * The default value of the DAC when not playing anything. Certain hardware - * setups may require a high (AUDIO_DAC_SAMPLE_MAX) or low (0) value here. - * Since multiple added sine waves tend to oscillate around the midpoint, - * and possibly never/rarely reach either 0 of MAX, 1/2 MAX can be a - * reasonable default value. - */ -#ifndef AUDIO_DAC_OFF_VALUE -# define AUDIO_DAC_OFF_VALUE AUDIO_DAC_SAMPLE_MAX / 2 -#endif - -#if AUDIO_DAC_OFF_VALUE > AUDIO_DAC_SAMPLE_MAX -# error "AUDIO_DAC: OFF_VALUE may not be larger than SAMPLE_MAX" -#endif - -/** - *user overridable sample generation/processing - */ -uint16_t dac_value_generate(void); diff --git a/platforms/chibios/drivers/audio_dac_additive.c b/platforms/chibios/drivers/audio_dac_additive.c deleted file mode 100644 index 22e4fa2608..0000000000 --- a/platforms/chibios/drivers/audio_dac_additive.c +++ /dev/null @@ -1,344 +0,0 @@ -/* Copyright 2016-2019 Jack Humbert - * Copyright 2020 JohSchneider - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "audio.h" -#include "gpio.h" -#include <math.h> -#include "util.h" - -// Need to disable GCC's "tautological-compare" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`. Corresponding pop at the end of the file. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtautological-compare" - -/* - Audio Driver: DAC - - which utilizes the dac unit many STM32 are equipped with, to output a modulated waveform from samples stored in the dac_buffer_* array who are passed to the hardware through DMA - - it is also possible to have a custom sample-LUT by implementing/overriding 'dac_value_generate' - - this driver allows for multiple simultaneous tones to be played through one single channel by doing additive wave-synthesis -*/ - -#if !defined(AUDIO_PIN) -# error "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC additive)' for available options." -#endif -#if defined(AUDIO_PIN_ALT) && !defined(AUDIO_PIN_ALT_AS_NEGATIVE) -# pragma message "Audio feature: AUDIO_PIN_ALT set, but not AUDIO_PIN_ALT_AS_NEGATIVE - pin will be left unused; audio might still work though." -#endif - -#if !defined(AUDIO_PIN_ALT) -// no ALT pin defined is valid, but the c-ifs below need some value set -# define AUDIO_PIN_ALT PAL_NOLINE -#endif - -#if !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) && !defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID) -# define AUDIO_DAC_SAMPLE_WAVEFORM_SINE -#endif - -#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SINE -/* one full sine wave over [0,2*pi], but shifted up one amplitude and left pi/4; for the samples to start at 0 - */ -static const dacsample_t dac_buffer_sine[AUDIO_DAC_BUFFER_SIZE] = { - // 256 values, max 4095 - 0x0, 0x1, 0x2, 0x6, 0xa, 0xf, 0x16, 0x1e, 0x27, 0x32, 0x3d, 0x4a, 0x58, 0x67, 0x78, 0x89, 0x9c, 0xb0, 0xc5, 0xdb, 0xf2, 0x10a, 0x123, 0x13e, 0x159, 0x175, 0x193, 0x1b1, 0x1d1, 0x1f1, 0x212, 0x235, 0x258, 0x27c, 0x2a0, 0x2c6, 0x2ed, 0x314, 0x33c, 0x365, 0x38e, 0x3b8, 0x3e3, 0x40e, 0x43a, 0x467, 0x494, 0x4c2, 0x4f0, 0x51f, 0x54e, 0x57d, 0x5ad, 0x5dd, 0x60e, 0x63f, 0x670, 0x6a1, 0x6d3, 0x705, 0x737, 0x769, 0x79b, 0x7cd, 0x800, 0x832, 0x864, 0x896, 0x8c8, 0x8fa, 0x92c, 0x95e, 0x98f, 0x9c0, 0x9f1, 0xa22, 0xa52, 0xa82, 0xab1, 0xae0, 0xb0f, 0xb3d, 0xb6b, 0xb98, 0xbc5, 0xbf1, 0xc1c, 0xc47, 0xc71, 0xc9a, 0xcc3, 0xceb, 0xd12, 0xd39, 0xd5f, 0xd83, 0xda7, 0xdca, 0xded, 0xe0e, 0xe2e, 0xe4e, 0xe6c, 0xe8a, 0xea6, 0xec1, 0xedc, 0xef5, 0xf0d, 0xf24, 0xf3a, 0xf4f, 0xf63, 0xf76, 0xf87, 0xf98, 0xfa7, 0xfb5, 0xfc2, 0xfcd, 0xfd8, 0xfe1, 0xfe9, 0xff0, 0xff5, 0xff9, 0xffd, 0xffe, - 0xfff, 0xffe, 0xffd, 0xff9, 0xff5, 0xff0, 0xfe9, 0xfe1, 0xfd8, 0xfcd, 0xfc2, 0xfb5, 0xfa7, 0xf98, 0xf87, 0xf76, 0xf63, 0xf4f, 0xf3a, 0xf24, 0xf0d, 0xef5, 0xedc, 0xec1, 0xea6, 0xe8a, 0xe6c, 0xe4e, 0xe2e, 0xe0e, 0xded, 0xdca, 0xda7, 0xd83, 0xd5f, 0xd39, 0xd12, 0xceb, 0xcc3, 0xc9a, 0xc71, 0xc47, 0xc1c, 0xbf1, 0xbc5, 0xb98, 0xb6b, 0xb3d, 0xb0f, 0xae0, 0xab1, 0xa82, 0xa52, 0xa22, 0x9f1, 0x9c0, 0x98f, 0x95e, 0x92c, 0x8fa, 0x8c8, 0x896, 0x864, 0x832, 0x800, 0x7cd, 0x79b, 0x769, 0x737, 0x705, 0x6d3, 0x6a1, 0x670, 0x63f, 0x60e, 0x5dd, 0x5ad, 0x57d, 0x54e, 0x51f, 0x4f0, 0x4c2, 0x494, 0x467, 0x43a, 0x40e, 0x3e3, 0x3b8, 0x38e, 0x365, 0x33c, 0x314, 0x2ed, 0x2c6, 0x2a0, 0x27c, 0x258, 0x235, 0x212, 0x1f1, 0x1d1, 0x1b1, 0x193, 0x175, 0x159, 0x13e, 0x123, 0x10a, 0xf2, 0xdb, 0xc5, 0xb0, 0x9c, 0x89, 0x78, 0x67, 0x58, 0x4a, 0x3d, 0x32, 0x27, 0x1e, 0x16, 0xf, 0xa, 0x6, 0x2, 0x1}; -#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SINE -#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE -static const dacsample_t dac_buffer_triangle[AUDIO_DAC_BUFFER_SIZE] = { - // 256 values, max 4095 - 0x0, 0x20, 0x40, 0x60, 0x80, 0xa0, 0xc0, 0xe0, 0x100, 0x120, 0x140, 0x160, 0x180, 0x1a0, 0x1c0, 0x1e0, 0x200, 0x220, 0x240, 0x260, 0x280, 0x2a0, 0x2c0, 0x2e0, 0x300, 0x320, 0x340, 0x360, 0x380, 0x3a0, 0x3c0, 0x3e0, 0x400, 0x420, 0x440, 0x460, 0x480, 0x4a0, 0x4c0, 0x4e0, 0x500, 0x520, 0x540, 0x560, 0x580, 0x5a0, 0x5c0, 0x5e0, 0x600, 0x620, 0x640, 0x660, 0x680, 0x6a0, 0x6c0, 0x6e0, 0x700, 0x720, 0x740, 0x760, 0x780, 0x7a0, 0x7c0, 0x7e0, 0x800, 0x81f, 0x83f, 0x85f, 0x87f, 0x89f, 0x8bf, 0x8df, 0x8ff, 0x91f, 0x93f, 0x95f, 0x97f, 0x99f, 0x9bf, 0x9df, 0x9ff, 0xa1f, 0xa3f, 0xa5f, 0xa7f, 0xa9f, 0xabf, 0xadf, 0xaff, 0xb1f, 0xb3f, 0xb5f, 0xb7f, 0xb9f, 0xbbf, 0xbdf, 0xbff, 0xc1f, 0xc3f, 0xc5f, 0xc7f, 0xc9f, 0xcbf, 0xcdf, 0xcff, 0xd1f, 0xd3f, 0xd5f, 0xd7f, 0xd9f, 0xdbf, 0xddf, 0xdff, 0xe1f, 0xe3f, 0xe5f, 0xe7f, 0xe9f, 0xebf, 0xedf, 0xeff, 0xf1f, 0xf3f, 0xf5f, 0xf7f, 0xf9f, 0xfbf, 0xfdf, - 0xfff, 0xfdf, 0xfbf, 0xf9f, 0xf7f, 0xf5f, 0xf3f, 0xf1f, 0xeff, 0xedf, 0xebf, 0xe9f, 0xe7f, 0xe5f, 0xe3f, 0xe1f, 0xdff, 0xddf, 0xdbf, 0xd9f, 0xd7f, 0xd5f, 0xd3f, 0xd1f, 0xcff, 0xcdf, 0xcbf, 0xc9f, 0xc7f, 0xc5f, 0xc3f, 0xc1f, 0xbff, 0xbdf, 0xbbf, 0xb9f, 0xb7f, 0xb5f, 0xb3f, 0xb1f, 0xaff, 0xadf, 0xabf, 0xa9f, 0xa7f, 0xa5f, 0xa3f, 0xa1f, 0x9ff, 0x9df, 0x9bf, 0x99f, 0x97f, 0x95f, 0x93f, 0x91f, 0x8ff, 0x8df, 0x8bf, 0x89f, 0x87f, 0x85f, 0x83f, 0x81f, 0x800, 0x7e0, 0x7c0, 0x7a0, 0x780, 0x760, 0x740, 0x720, 0x700, 0x6e0, 0x6c0, 0x6a0, 0x680, 0x660, 0x640, 0x620, 0x600, 0x5e0, 0x5c0, 0x5a0, 0x580, 0x560, 0x540, 0x520, 0x500, 0x4e0, 0x4c0, 0x4a0, 0x480, 0x460, 0x440, 0x420, 0x400, 0x3e0, 0x3c0, 0x3a0, 0x380, 0x360, 0x340, 0x320, 0x300, 0x2e0, 0x2c0, 0x2a0, 0x280, 0x260, 0x240, 0x220, 0x200, 0x1e0, 0x1c0, 0x1a0, 0x180, 0x160, 0x140, 0x120, 0x100, 0xe0, 0xc0, 0xa0, 0x80, 0x60, 0x40, 0x20}; -#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE -#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE -static const dacsample_t dac_buffer_square[AUDIO_DAC_BUFFER_SIZE] = { - [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = AUDIO_DAC_OFF_VALUE, // first and - [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX, // second half -}; -#endif // AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE -/* -// four steps: 0, 1/3, 2/3 and 1 -static const dacsample_t dac_buffer_staircase[AUDIO_DAC_BUFFER_SIZE] = { - [0 ... AUDIO_DAC_BUFFER_SIZE/3 -1 ] = 0, - [AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE / 2 -1 ] = AUDIO_DAC_SAMPLE_MAX / 3, - [AUDIO_DAC_BUFFER_SIZE / 2 ... 3 * AUDIO_DAC_BUFFER_SIZE / 4 -1 ] = 2 * AUDIO_DAC_SAMPLE_MAX / 3, - [3 * AUDIO_DAC_BUFFER_SIZE / 4 ... AUDIO_DAC_BUFFER_SIZE -1 ] = AUDIO_DAC_SAMPLE_MAX, -} -*/ -#ifdef AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID -static const dacsample_t dac_buffer_trapezoid[AUDIO_DAC_BUFFER_SIZE] = {0x0, 0x1f, 0x7f, 0xdf, 0x13f, 0x19f, 0x1ff, 0x25f, 0x2bf, 0x31f, 0x37f, 0x3df, 0x43f, 0x49f, 0x4ff, 0x55f, 0x5bf, 0x61f, 0x67f, 0x6df, 0x73f, 0x79f, 0x7ff, 0x85f, 0x8bf, 0x91f, 0x97f, 0x9df, 0xa3f, 0xa9f, 0xaff, 0xb5f, 0xbbf, 0xc1f, 0xc7f, 0xcdf, 0xd3f, 0xd9f, 0xdff, 0xe5f, 0xebf, 0xf1f, 0xf7f, 0xfdf, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, 0xfff, - 0xfff, 0xfdf, 0xf7f, 0xf1f, 0xebf, 0xe5f, 0xdff, 0xd9f, 0xd3f, 0xcdf, 0xc7f, 0xc1f, 0xbbf, 0xb5f, 0xaff, 0xa9f, 0xa3f, 0x9df, 0x97f, 0x91f, 0x8bf, 0x85f, 0x7ff, 0x79f, 0x73f, 0x6df, 0x67f, 0x61f, 0x5bf, 0x55f, 0x4ff, 0x49f, 0x43f, 0x3df, 0x37f, 0x31f, 0x2bf, 0x25f, 0x1ff, 0x19f, 0x13f, 0xdf, 0x7f, 0x1f, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; -#endif // AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID - -static dacsample_t dac_buffer_empty[AUDIO_DAC_BUFFER_SIZE] = {AUDIO_DAC_OFF_VALUE}; - -/* keep track of the sample position for for each frequency */ -static float dac_if[AUDIO_MAX_SIMULTANEOUS_TONES] = {0.0}; - -static float active_tones_snapshot[AUDIO_MAX_SIMULTANEOUS_TONES] = {0}; -static uint8_t active_tones_snapshot_length = 0; - -typedef enum { - OUTPUT_SHOULD_START, - OUTPUT_RUN_NORMALLY, - // path 1: wait for zero, then change/update active tones - OUTPUT_TONES_CHANGED, - OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE, - // path 2: hardware should stop, wait for zero then turn output off = stop the timer - OUTPUT_SHOULD_STOP, - OUTPUT_REACHED_ZERO_BEFORE_OFF, - OUTPUT_OFF, - OUTPUT_OFF_1, - OUTPUT_OFF_2, // trailing off: giving the DAC two more conversion cycles until the AUDIO_DAC_OFF_VALUE reaches the output, then turn the timer off, which leaves the output at that level - number_of_output_states -} output_states_t; -output_states_t state = OUTPUT_OFF_2; - -/** - * Generation of the waveform being passed to the callback. Declared weak so users - * can override it with their own wave-forms/noises. - */ -__attribute__((weak)) uint16_t dac_value_generate(void) { - // DAC is running/asking for values but snapshot length is zero -> must be playing a pause - if (active_tones_snapshot_length == 0) { - return AUDIO_DAC_OFF_VALUE; - } - - /* doing additive wave synthesis over all currently playing tones = adding up - * sine-wave-samples for each frequency, scaled by the number of active tones - */ - uint16_t value = 0; - float frequency = 0.0f; - - for (uint8_t i = 0; i < active_tones_snapshot_length; i++) { - /* Note: a user implementation does not have to rely on the active_tones_snapshot, but - * could directly query the active frequencies through audio_get_processed_frequency */ - frequency = active_tones_snapshot[i]; - - dac_if[i] = dac_if[i] + ((frequency * AUDIO_DAC_BUFFER_SIZE) / AUDIO_DAC_SAMPLE_RATE) * 2 / 3; - /*Note: the 2/3 are necessary to get the correct frequencies on the - * DAC output (as measured with an oscilloscope), since the gpt - * timer runs with 3*AUDIO_DAC_SAMPLE_RATE; and the DAC callback - * is called twice per conversion.*/ - - dac_if[i] = fmodf(dac_if[i], AUDIO_DAC_BUFFER_SIZE); - - // Wavetable generation/lookup - uint16_t dac_i = (uint16_t)dac_if[i]; - -#if defined(AUDIO_DAC_SAMPLE_WAVEFORM_SINE) - value += dac_buffer_sine[dac_i] / active_tones_snapshot_length; -#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRIANGLE) - value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length; -#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_TRAPEZOID) - value += dac_buffer_trapezoid[dac_i] / active_tones_snapshot_length; -#elif defined(AUDIO_DAC_SAMPLE_WAVEFORM_SQUARE) - value += dac_buffer_square[dac_i] / active_tones_snapshot_length; -#endif - /* - // SINE - value += dac_buffer_sine[dac_i] / active_tones_snapshot_length / 3; - // TRIANGLE - value += dac_buffer_triangle[dac_i] / active_tones_snapshot_length / 3; - // SQUARE - value += dac_buffer_square[dac_i] / active_tones_snapshot_length / 3; - //NOTE: combination of these three wave-forms is more exemplary - and doesn't sound particularly good :-P - */ - - // STAIRS (mostly usefully as test-pattern) - // value_avg = dac_buffer_staircase[dac_i] / active_tones_snapshot_length; - } - - return value; -} - -/** - * DAC streaming callback. Does all of the main computing for playing songs. - * - * Note: chibios calls this CB twice: during the 'half buffer event', and the 'full buffer event'. - */ -static void dac_end(DACDriver *dacp) { - dacsample_t *sample_p = (dacp)->samples; - - // work on the other half of the buffer - if (dacIsBufferComplete(dacp)) { - sample_p += AUDIO_DAC_BUFFER_SIZE / 2; // 'half_index' - } - - for (uint8_t s = 0; s < AUDIO_DAC_BUFFER_SIZE / 2; s++) { - if (OUTPUT_OFF <= state) { - sample_p[s] = AUDIO_DAC_OFF_VALUE; - continue; - } else { - sample_p[s] = dac_value_generate(); - } - - /* zero crossing (or approach, whereas zero == DAC_OFF_VALUE, which can be configured to anything from 0 to DAC_SAMPLE_MAX) - * ============================*=*========================== AUDIO_DAC_SAMPLE_MAX - * * * - * * * - * --------------------------------------------------------- - * * * } AUDIO_DAC_SAMPLE_MAX/100 - * --------------------------------------------------------- AUDIO_DAC_OFF_VALUE - * * * } AUDIO_DAC_SAMPLE_MAX/100 - * --------------------------------------------------------- - * * - * * * - * * * - * =====*=*================================================= 0x0 - */ - if (((sample_p[s] + (AUDIO_DAC_SAMPLE_MAX / 100)) > AUDIO_DAC_OFF_VALUE) && // value approaches from below - (sample_p[s] < (AUDIO_DAC_OFF_VALUE + (AUDIO_DAC_SAMPLE_MAX / 100))) // or above - ) { - if ((OUTPUT_SHOULD_START == state) && (active_tones_snapshot_length > 0)) { - state = OUTPUT_RUN_NORMALLY; - } else if (OUTPUT_TONES_CHANGED == state) { - state = OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE; - } else if (OUTPUT_SHOULD_STOP == state) { - state = OUTPUT_REACHED_ZERO_BEFORE_OFF; - } - } - - // still 'ramping up', reset the output to OFF_VALUE until the generated values reach that value, to do a smooth handover - if (OUTPUT_SHOULD_START == state) { - sample_p[s] = AUDIO_DAC_OFF_VALUE; - } - - if ((OUTPUT_SHOULD_START == state) || (OUTPUT_REACHED_ZERO_BEFORE_OFF == state) || (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state)) { - uint8_t active_tones = MIN(AUDIO_MAX_SIMULTANEOUS_TONES, audio_get_number_of_active_tones()); - active_tones_snapshot_length = 0; - // update the snapshot - once, and only on occasion that something changed; - // -> saves cpu cycles (?) - for (uint8_t i = 0; i < active_tones; i++) { - float freq = audio_get_processed_frequency(i); - if (freq > 0) { // disregard 'rest' notes, with valid frequency 0.0f; which would only lower the resulting waveform volume during the additive synthesis step - active_tones_snapshot[active_tones_snapshot_length++] = freq; - } - } - - if ((0 == active_tones_snapshot_length) && (OUTPUT_REACHED_ZERO_BEFORE_OFF == state)) { - state = OUTPUT_OFF; - } - if (OUTPUT_REACHED_ZERO_BEFORE_TONE_CHANGE == state) { - state = OUTPUT_RUN_NORMALLY; - } - } - } - - // update audio internal state (note position, current_note, ...) - if (audio_update_state()) { - if (OUTPUT_SHOULD_STOP != state) { - state = OUTPUT_TONES_CHANGED; - } - } - - if (OUTPUT_OFF <= state) { - if (OUTPUT_OFF_2 == state) { - // stopping timer6 = stopping the DAC at whatever value it is currently pushing to the output = AUDIO_DAC_OFF_VALUE - gptStopTimer(&GPTD6); - } else { - state++; - } - } -} - -static void dac_error(DACDriver *dacp, dacerror_t err) { - (void)dacp; - (void)err; - - chSysHalt("DAC failure. halp"); -} - -static const GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE * 3, - .callback = NULL, - .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ - .dier = 0U}; - -static const DACConfig dac_conf = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT}; - -/** - * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered - * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency - * to be a third of what we expect. - * - * Here are all the values for DAC_TRG (TSEL in the ref manual) - * TIM15_TRGO 0b011 - * TIM2_TRGO 0b100 - * TIM3_TRGO 0b001 - * TIM6_TRGO 0b000 - * TIM7_TRGO 0b010 - * EXTI9 0b110 - * SWTRIG 0b111 - */ -static const DACConversionGroup dac_conv_cfg = {.num_channels = 1U, .end_cb = dac_end, .error_cb = dac_error, .trigger = DAC_TRG(0b000)}; - -void audio_driver_initialize(void) { - if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { - palSetLineMode(A4, PAL_MODE_INPUT_ANALOG); - dacStart(&DACD1, &dac_conf); - } - if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { - palSetLineMode(A5, PAL_MODE_INPUT_ANALOG); - dacStart(&DACD2, &dac_conf); - } - - /* enable the output buffer, to directly drive external loads with no additional circuitry - * - * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers - * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer - * Note: enabling the output buffer imparts an additional dc-offset of a couple mV - * - * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet - * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.' - */ - DACD1.params->dac->CR &= ~DAC_CR_BOFF1; - DACD2.params->dac->CR &= ~DAC_CR_BOFF2; - - if (AUDIO_PIN == A4) { - dacStartConversion(&DACD1, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE); - } else if (AUDIO_PIN == A5) { - dacStartConversion(&DACD2, &dac_conv_cfg, dac_buffer_empty, AUDIO_DAC_BUFFER_SIZE); - } - - // no inverted/out-of-phase waveform (yet?), only pulling AUDIO_PIN_ALT to AUDIO_DAC_OFF_VALUE -#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) - if (AUDIO_PIN_ALT == A4) { - dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE); - } else if (AUDIO_PIN_ALT == A5) { - dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE); - } -#endif - - gptStart(&GPTD6, &gpt6cfg1); -} - -void audio_driver_stop(void) { - state = OUTPUT_SHOULD_STOP; -} - -void audio_driver_start(void) { - gptStartContinuous(&GPTD6, 2U); - - for (uint8_t i = 0; i < AUDIO_MAX_SIMULTANEOUS_TONES; i++) { - dac_if[i] = 0.0f; - active_tones_snapshot[i] = 0.0f; - } - active_tones_snapshot_length = 0; - state = OUTPUT_SHOULD_START; -} - -#pragma GCC diagnostic pop diff --git a/platforms/chibios/drivers/audio_dac_basic.c b/platforms/chibios/drivers/audio_dac_basic.c deleted file mode 100644 index 9a3f3fea1f..0000000000 --- a/platforms/chibios/drivers/audio_dac_basic.c +++ /dev/null @@ -1,254 +0,0 @@ -/* Copyright 2016-2020 Jack Humbert - * Copyright 2020 JohSchneider - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "audio.h" -#include "gpio.h" - -// Need to disable GCC's "tautological-compare" warning for this file, as it causes issues when running `KEEP_INTERMEDIATES=yes`. Corresponding pop at the end of the file. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtautological-compare" - -/* - Audio Driver: DAC - - which utilizes both channels of the DAC unit many STM32 are equipped with to output a modulated square-wave, from precomputed samples stored in a buffer, which is passed to the hardware through DMA - - this driver can either be used to drive to separate speakers, wired to A4+Gnd and A5+Gnd, which allows two tones to be played simultaneously - OR - one speaker wired to A4+A5 with the AUDIO_PIN_ALT_AS_NEGATIVE define set - see docs/feature_audio - -*/ - -#if !defined(AUDIO_PIN) -# pragma message "Audio feature enabled, but no suitable pin selected as AUDIO_PIN - see docs/feature_audio under 'ARM (DAC basic)' for available options." -// TODO: make this an 'error' instead; go through a breaking change, and add AUDIO_PIN A5 to all keyboards currently using AUDIO on STM32 based boards? - for now: set the define here -# define AUDIO_PIN A5 -#endif -// check configuration for ONE speaker, connected to both DAC pins -#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) && !defined(AUDIO_PIN_ALT) -# error "Audio feature: AUDIO_PIN_ALT_AS_NEGATIVE set, but no pin configured as AUDIO_PIN_ALT" -#endif - -#ifndef AUDIO_PIN_ALT -// no ALT pin defined is valid, but the c-ifs below need some value set -# define AUDIO_PIN_ALT -1 -#endif - -#if !defined(AUDIO_STATE_TIMER) -# define AUDIO_STATE_TIMER GPTD8 -#endif - -// square-wave -static const dacsample_t dac_buffer_1[AUDIO_DAC_BUFFER_SIZE] = { - // First half is max, second half is 0 - [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = AUDIO_DAC_SAMPLE_MAX, - [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = 0, -}; - -// square-wave -static const dacsample_t dac_buffer_2[AUDIO_DAC_BUFFER_SIZE] = { - // opposite of dac_buffer above - [0 ... AUDIO_DAC_BUFFER_SIZE / 2 - 1] = 0, - [AUDIO_DAC_BUFFER_SIZE / 2 ... AUDIO_DAC_BUFFER_SIZE - 1] = AUDIO_DAC_SAMPLE_MAX, -}; - -GPTConfig gpt6cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE, - .callback = NULL, - .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ - .dier = 0U}; -GPTConfig gpt7cfg1 = {.frequency = AUDIO_DAC_SAMPLE_RATE, - .callback = NULL, - .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ - .dier = 0U}; - -static void gpt_audio_state_cb(GPTDriver *gptp); -GPTConfig gptStateUpdateCfg = {.frequency = 10, - .callback = gpt_audio_state_cb, - .cr2 = TIM_CR2_MMS_1, /* MMS = 010 = TRGO on Update Event. */ - .dier = 0U}; - -static const DACConfig dac_conf_ch1 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT}; -static const DACConfig dac_conf_ch2 = {.init = AUDIO_DAC_OFF_VALUE, .datamode = DAC_DHRM_12BIT_RIGHT}; - -/** - * @note The DAC_TRG(0) here selects the Timer 6 TRGO event, which is triggered - * on the rising edge after 3 APB1 clock cycles, causing our gpt6cfg1.frequency - * to be a third of what we expect. - * - * Here are all the values for DAC_TRG (TSEL in the ref manual) - * TIM15_TRGO 0b011 - * TIM2_TRGO 0b100 - * TIM3_TRGO 0b001 - * TIM6_TRGO 0b000 - * TIM7_TRGO 0b010 - * EXTI9 0b110 - * SWTRIG 0b111 - */ -static const DACConversionGroup dac_conv_grp_ch1 = {.num_channels = 1U, .trigger = DAC_TRG(0b000)}; -static const DACConversionGroup dac_conv_grp_ch2 = {.num_channels = 1U, .trigger = DAC_TRG(0b010)}; - -void channel_1_start(void) { - gptStart(&GPTD6, &gpt6cfg1); - gptStartContinuous(&GPTD6, 2U); - palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG); -} - -void channel_1_stop(void) { - gptStopTimer(&GPTD6); - palSetPadMode(GPIOA, 4, PAL_MODE_OUTPUT_PUSHPULL); - palSetPad(GPIOA, 4); -} - -static float channel_1_frequency = 0.0f; -void channel_1_set_frequency(float freq) { - channel_1_frequency = freq; - - channel_1_stop(); - if (freq <= 0.0) // a pause/rest has freq=0 - return; - - gpt6cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE; - channel_1_start(); -} -float channel_1_get_frequency(void) { - return channel_1_frequency; -} - -void channel_2_start(void) { - gptStart(&GPTD7, &gpt7cfg1); - gptStartContinuous(&GPTD7, 2U); - palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); -} - -void channel_2_stop(void) { - gptStopTimer(&GPTD7); - palSetPadMode(GPIOA, 5, PAL_MODE_OUTPUT_PUSHPULL); - palSetPad(GPIOA, 5); -} - -static float channel_2_frequency = 0.0f; -void channel_2_set_frequency(float freq) { - channel_2_frequency = freq; - - channel_2_stop(); - if (freq <= 0.0) // a pause/rest has freq=0 - return; - - gpt7cfg1.frequency = 2 * freq * AUDIO_DAC_BUFFER_SIZE; - channel_2_start(); -} -float channel_2_get_frequency(void) { - return channel_2_frequency; -} - -static void gpt_audio_state_cb(GPTDriver *gptp) { - if (audio_update_state()) { -#if defined(AUDIO_PIN_ALT_AS_NEGATIVE) - // one piezo/speaker connected to both audio pins, the generated square-waves are inverted - channel_1_set_frequency(audio_get_processed_frequency(0)); - channel_2_set_frequency(audio_get_processed_frequency(0)); - -#else // two separate audio outputs/speakers - // primary speaker on A4, optional secondary on A5 - if (AUDIO_PIN == A4) { - channel_1_set_frequency(audio_get_processed_frequency(0)); - if (AUDIO_PIN_ALT == A5) { - if (audio_get_number_of_active_tones() > 1) { - channel_2_set_frequency(audio_get_processed_frequency(1)); - } else { - channel_2_stop(); - } - } - } - - // primary speaker on A5, optional secondary on A4 - if (AUDIO_PIN == A5) { - channel_2_set_frequency(audio_get_processed_frequency(0)); - if (AUDIO_PIN_ALT == A4) { - if (audio_get_number_of_active_tones() > 1) { - channel_1_set_frequency(audio_get_processed_frequency(1)); - } else { - channel_1_stop(); - } - } - } -#endif - } -} - -void audio_driver_initialize(void) { - if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { - palSetPadMode(GPIOA, 4, PAL_MODE_INPUT_ANALOG); - dacStart(&DACD1, &dac_conf_ch1); - - // initial setup of the dac-triggering timer is still required, even - // though it gets reconfigured and restarted later on - gptStart(&GPTD6, &gpt6cfg1); - } - - if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { - palSetPadMode(GPIOA, 5, PAL_MODE_INPUT_ANALOG); - dacStart(&DACD2, &dac_conf_ch2); - - gptStart(&GPTD7, &gpt7cfg1); - } - - /* enable the output buffer, to directly drive external loads with no additional circuitry - * - * see: AN4566 Application note: Extending the DAC performance of STM32 microcontrollers - * Note: Buffer-Off bit -> has to be set 0 to enable the output buffer - * Note: enabling the output buffer imparts an additional dc-offset of a couple mV - * - * this is done here, reaching directly into the stm32 registers since chibios has not implemented BOFF handling yet - * (see: chibios/os/hal/ports/STM32/todo.txt '- BOFF handling in DACv1.' - */ - DACD1.params->dac->CR &= ~DAC_CR_BOFF1; - DACD2.params->dac->CR &= ~DAC_CR_BOFF2; - - // start state-updater - gptStart(&AUDIO_STATE_TIMER, &gptStateUpdateCfg); -} - -void audio_driver_stop(void) { - if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { - gptStopTimer(&GPTD6); - - // stop the ongoing conversion and put the output in a known state - dacStopConversion(&DACD1); - dacPutChannelX(&DACD1, 0, AUDIO_DAC_OFF_VALUE); - } - - if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { - gptStopTimer(&GPTD7); - - dacStopConversion(&DACD2); - dacPutChannelX(&DACD2, 0, AUDIO_DAC_OFF_VALUE); - } - gptStopTimer(&AUDIO_STATE_TIMER); -} - -void audio_driver_start(void) { - if ((AUDIO_PIN == A4) || (AUDIO_PIN_ALT == A4)) { - dacStartConversion(&DACD1, &dac_conv_grp_ch1, (dacsample_t *)dac_buffer_1, AUDIO_DAC_BUFFER_SIZE); - } - if ((AUDIO_PIN == A5) || (AUDIO_PIN_ALT == A5)) { - dacStartConversion(&DACD2, &dac_conv_grp_ch2, (dacsample_t *)dac_buffer_2, AUDIO_DAC_BUFFER_SIZE); - } - gptStartContinuous(&AUDIO_STATE_TIMER, 2U); -} - -#pragma GCC diagnostic pop diff --git a/platforms/chibios/drivers/audio_pwm.h b/platforms/chibios/drivers/audio_pwm.h deleted file mode 100644 index 86cab916e1..0000000000 --- a/platforms/chibios/drivers/audio_pwm.h +++ /dev/null @@ -1,40 +0,0 @@ -/* Copyright 2020 Jack Humbert - * Copyright 2020 JohSchneider - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -#pragma once - -#if !defined(AUDIO_PWM_DRIVER) -// NOTE: Timer2 seems to be used otherwise in QMK, otherwise we could default to A5 (= TIM2_CH1, with PWMD2 and alternate-function(1)) -# define AUDIO_PWM_DRIVER PWMD1 -#endif - -#if !defined(AUDIO_PWM_CHANNEL) -// NOTE: sticking to the STM data-sheet numbering: TIMxCH1 to TIMxCH4 -// default: STM32F303CC PA8+TIM1_CH1 -> 1 -# define AUDIO_PWM_CHANNEL 1 -#endif - -#if !defined(AUDIO_PWM_PAL_MODE) -// pin-alternate function: see the data-sheet for which pin needs what AF to connect to TIMx_CHy -// default: STM32F303CC PA8+TIM1_CH1 -> 6 -# define AUDIO_PWM_PAL_MODE 6 -#endif - -#if !defined(AUDIO_STATE_TIMER) -// timer used to trigger updates in the audio-system, configured/enabled in chibios mcuconf. -// Tim6 is the default for "larger" STMs, smaller ones might not have this one (enabled) and need to switch to a different one (e.g.: STM32F103 has only Tim1-Tim4) -# define AUDIO_STATE_TIMER GPTD6 -#endif diff --git a/platforms/chibios/drivers/audio_pwm_hardware.c b/platforms/chibios/drivers/audio_pwm_hardware.c deleted file mode 100644 index 40d891326f..0000000000 --- a/platforms/chibios/drivers/audio_pwm_hardware.c +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2022 Stefan Kerkmann -// Copyright 2020 Jack Humbert -// Copyright 2020 JohSchneider -// SPDX-License-Identifier: GPL-2.0-or-later - -// Audio Driver: PWM the duty-cycle is always kept at 50%, and the pwm-period is -// adjusted to match the frequency of a note to be played back. This driver uses -// the chibios-PWM system to produce a square-wave on specific output pins that -// are connected to the PWM hardware. The hardware directly toggles the pin via -// its alternate function. see your MCUs data-sheet for which pin can be driven -// by what timer - looking for TIMx_CHy and the corresponding alternate -// function. - -#include "audio.h" -#include "gpio.h" - -#if !defined(AUDIO_PIN) -# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings" -#endif - -#if !defined(AUDIO_PWM_COUNTER_FREQUENCY) -# define AUDIO_PWM_COUNTER_FREQUENCY 100000 -#endif - -extern bool playing_note; -extern bool playing_melody; -extern uint8_t note_timbre; - -static PWMConfig pwmCFG = {.frequency = AUDIO_PWM_COUNTER_FREQUENCY, /* PWM clock frequency */ - .period = 2, - .callback = NULL, - .channels = {[(AUDIO_PWM_CHANNEL - 1)] = {.mode = PWM_OUTPUT_ACTIVE_HIGH, .callback = NULL}}}; - -static float channel_1_frequency = 0.0f; - -void channel_1_set_frequency(float freq) { - channel_1_frequency = freq; - - if (freq <= 0.0) { - // a pause/rest has freq=0 - return; - } - - pwmcnt_t period = (pwmCFG.frequency / freq); - chSysLockFromISR(); - pwmChangePeriodI(&AUDIO_PWM_DRIVER, period); - pwmEnableChannelI(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, - // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH - PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100)); - chSysUnlockFromISR(); -} - -float channel_1_get_frequency(void) { - return channel_1_frequency; -} - -void channel_1_start(void) { - pwmStop(&AUDIO_PWM_DRIVER); - pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); -} - -void channel_1_stop(void) { - pwmStop(&AUDIO_PWM_DRIVER); -} - -static virtual_timer_t audio_vt; -static void audio_callback(virtual_timer_t *vtp, void *p); - -// a regular timer task, that checks the note to be currently played and updates -// the pwm to output that frequency. -static void audio_callback(virtual_timer_t *vtp, void *p) { - float freq; // TODO: freq_alt - - if (audio_update_state()) { - freq = audio_get_processed_frequency(0); // freq_alt would be index=1 - channel_1_set_frequency(freq); - } - - chSysLockFromISR(); - chVTSetI(&audio_vt, TIME_MS2I(16), audio_callback, NULL); - chSysUnlockFromISR(); -} - -void audio_driver_initialize(void) { - pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); - - // connect the AUDIO_PIN to the PWM hardware -#if defined(USE_GPIOV1) // STM32F103C8, RP2040 - palSetLineMode(AUDIO_PIN, AUDIO_PWM_PAL_MODE); -#else // GPIOv2 (or GPIOv3 for f4xx, which is the same/compatible at this command) - palSetLineMode(AUDIO_PIN, PAL_MODE_ALTERNATE(AUDIO_PWM_PAL_MODE)); -#endif - - chVTObjectInit(&audio_vt); -} - -void audio_driver_start(void) { - channel_1_stop(); - channel_1_start(); - - if ((playing_note || playing_melody) && !chVTIsArmed(&audio_vt)) { - // a whole note is one beat, which is - per definition in - // musical_notes.h - set to 64 the longest note is - // BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4 the tempo (which - // might vary!) is in bpm (beats per minute) therefore: if the timer - // ticks away at 64Hz (~16.6ms) audio_update_state is called just often - // enough to not miss any notes - chVTSet(&audio_vt, TIME_MS2I(16), audio_callback, NULL); - } -} - -void audio_driver_stop(void) { - channel_1_stop(); - chVTReset(&audio_vt); -} diff --git a/platforms/chibios/drivers/audio_pwm_software.c b/platforms/chibios/drivers/audio_pwm_software.c deleted file mode 100644 index 663a9eca16..0000000000 --- a/platforms/chibios/drivers/audio_pwm_software.c +++ /dev/null @@ -1,165 +0,0 @@ -/* Copyright 2020 Jack Humbert - * Copyright 2020 JohSchneider - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -/* -Audio Driver: PWM - -the duty-cycle is always kept at 50%, and the pwm-period is adjusted to match the frequency of a note to be played back. - -this driver uses the chibios-PWM system to produce a square-wave on any given output pin in software -- a pwm callback is used to set/clear the configured pin. - - */ -#include "audio.h" -#include "gpio.h" - -#if !defined(AUDIO_PIN) -# error "Audio feature enabled, but no pin selected - see docs/feature_audio under the ARM PWM settings" -#endif -extern bool playing_note; -extern bool playing_melody; -extern uint8_t note_timbre; - -static void pwm_audio_period_callback(PWMDriver *pwmp); -static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp); - -static PWMConfig pwmCFG = { - .frequency = 100000, /* PWM clock frequency */ - // CHIBIOS-BUG? can't set the initial period to <2, or the pwm (hard or software) takes ~130ms with .frequency=500000 for a pwmChangePeriod to take effect; with no output=silence in the meantime - .period = 2, /* initial PWM period (in ticks) 1S (1/10kHz=0.1mS 0.1ms*10000 ticks=1S) */ - .callback = pwm_audio_period_callback, - .channels = - { - // software-PWM just needs another callback on any channel - {PWM_OUTPUT_ACTIVE_HIGH, pwm_audio_channel_interrupt_callback}, /* channel 0 -> TIMx_CH1 */ - {PWM_OUTPUT_DISABLED, NULL}, /* channel 1 -> TIMx_CH2 */ - {PWM_OUTPUT_DISABLED, NULL}, /* channel 2 -> TIMx_CH3 */ - {PWM_OUTPUT_DISABLED, NULL} /* channel 3 -> TIMx_CH4 */ - }, -}; - -static float channel_1_frequency = 0.0f; -void channel_1_set_frequency(float freq) { - channel_1_frequency = freq; - - if (freq <= 0.0) // a pause/rest has freq=0 - return; - - pwmcnt_t period = (pwmCFG.frequency / freq); - pwmChangePeriod(&AUDIO_PWM_DRIVER, period); - - pwmEnableChannel(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1, - // adjust the duty-cycle so that the output is for 'note_timbre' duration HIGH - PWM_PERCENTAGE_TO_WIDTH(&AUDIO_PWM_DRIVER, (100 - note_timbre) * 100)); -} - -float channel_1_get_frequency(void) { - return channel_1_frequency; -} - -void channel_1_start(void) { - pwmStop(&AUDIO_PWM_DRIVER); - pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); - - pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); - pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1); -} - -void channel_1_stop(void) { - pwmStop(&AUDIO_PWM_DRIVER); - - palClearLine(AUDIO_PIN); // leave the line low, after last note was played - -#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) - palClearLine(AUDIO_PIN_ALT); // leave the line low, after last note was played -#endif -} - -// generate a PWM signal on any pin, not necessarily the one connected to the timer -static void pwm_audio_period_callback(PWMDriver *pwmp) { - (void)pwmp; - palClearLine(AUDIO_PIN); - -#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) - palSetLine(AUDIO_PIN_ALT); -#endif -} -static void pwm_audio_channel_interrupt_callback(PWMDriver *pwmp) { - (void)pwmp; - if (channel_1_frequency > 0) { - palSetLine(AUDIO_PIN); // generate a PWM signal on any pin, not necessarily the one connected to the timer -#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) - palClearLine(AUDIO_PIN_ALT); -#endif - } -} - -static void gpt_callback(GPTDriver *gptp); -GPTConfig gptCFG = { - /* a whole note is one beat, which is - per definition in musical_notes.h - set to 64 - the longest note is BREAVE_DOT=128+64=192, the shortest SIXTEENTH=4 - the tempo (which might vary!) is in bpm (beats per minute) - therefore: if the timer ticks away at .frequency = (60*64)Hz, - and the .interval counts from 64 downwards - audio_update_state is - called just often enough to not miss anything - */ - .frequency = 60 * 64, - .callback = gpt_callback, -}; - -void audio_driver_initialize(void) { - pwmStart(&AUDIO_PWM_DRIVER, &pwmCFG); - - palSetLineMode(AUDIO_PIN, PAL_MODE_OUTPUT_PUSHPULL); - palClearLine(AUDIO_PIN); - -#if defined(AUDIO_PIN_ALT) && defined(AUDIO_PIN_ALT_AS_NEGATIVE) - palSetLineMode(AUDIO_PIN_ALT, PAL_MODE_OUTPUT_PUSHPULL); - palClearLine(AUDIO_PIN_ALT); -#endif - - pwmEnablePeriodicNotification(&AUDIO_PWM_DRIVER); // enable pwm callbacks - pwmEnableChannelNotification(&AUDIO_PWM_DRIVER, AUDIO_PWM_CHANNEL - 1); - - gptStart(&AUDIO_STATE_TIMER, &gptCFG); -} - -void audio_driver_start(void) { - channel_1_stop(); - channel_1_start(); - - if (playing_note || playing_melody) { - gptStartContinuous(&AUDIO_STATE_TIMER, 64); - } -} - -void audio_driver_stop(void) { - channel_1_stop(); - gptStopTimer(&AUDIO_STATE_TIMER); -} - -/* a regular timer task, that checks the note to be currently played - * and updates the pwm to output that frequency - */ -static void gpt_callback(GPTDriver *gptp) { - float freq; // TODO: freq_alt - - if (audio_update_state()) { - freq = audio_get_processed_frequency(0); // freq_alt would be index=1 - channel_1_set_frequency(freq); - } -} diff --git a/platforms/chibios/drivers/backlight_pwm.c b/platforms/chibios/drivers/backlight_pwm.c deleted file mode 100644 index 01e6f71307..0000000000 --- a/platforms/chibios/drivers/backlight_pwm.c +++ /dev/null @@ -1,173 +0,0 @@ -#include "backlight.h" -#include "gpio.h" -#include "wait.h" -#include <hal.h> - -// Maximum duty cycle limit -#ifndef BACKLIGHT_LIMIT_VAL -# define BACKLIGHT_LIMIT_VAL 255 -#endif - -#ifndef BACKLIGHT_PAL_MODE -# if defined(USE_GPIOV1) -# define BACKLIGHT_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL -# else -// GPIOV2 && GPIOV3 -# define BACKLIGHT_PAL_MODE 2 -# endif -#endif - -// GENERIC -#ifndef BACKLIGHT_PWM_DRIVER -# define BACKLIGHT_PWM_DRIVER PWMD4 -#endif -#ifndef BACKLIGHT_PWM_CHANNEL -# define BACKLIGHT_PWM_CHANNEL 3 -#endif - -// Support for pins which are on TIM1_CH1N - requires STM32_PWM_USE_ADVANCED -#ifdef BACKLIGHT_PWM_COMPLEMENTARY_OUTPUT -# if BACKLIGHT_ON_STATE == 1 -# define PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_LOW; -# else -# define PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH; -# endif -#else -# if BACKLIGHT_ON_STATE == 1 -# define PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH; -# else -# define PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_LOW; -# endif -#endif - -#ifndef BACKLIGHT_PWM_COUNTER_FREQUENCY -# define BACKLIGHT_PWM_COUNTER_FREQUENCY 0xFFFF -#endif - -#ifndef BACKLIGHT_PWM_PERIOD -# define BACKLIGHT_PWM_PERIOD 256 -#endif - -static PWMConfig pwmCFG = { - .frequency = BACKLIGHT_PWM_COUNTER_FREQUENCY, /* PWM clock frequency */ - .period = BACKLIGHT_PWM_PERIOD, /* PWM period in counter ticks. e.g. clock frequency is 10KHz, period is 256 ticks then t_period is 25.6ms */ -}; - -#ifdef BACKLIGHT_BREATHING -static virtual_timer_t breathing_vt; -#endif - -// 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; - } - } -} - -static uint32_t rescale_limit_val(uint32_t val) { - // rescale the supplied backlight value to be in terms of the value limit - return (val * (BACKLIGHT_LIMIT_VAL + 1)) / 256; -} - -void backlight_init_ports(void) { -#ifdef USE_GPIOV1 - palSetPadMode(PAL_PORT(BACKLIGHT_PIN), PAL_PAD(BACKLIGHT_PIN), BACKLIGHT_PAL_MODE); -#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_MODE; - pwmStart(&BACKLIGHT_PWM_DRIVER, &pwmCFG); - - backlight_set(get_backlight_level()); - -#ifdef BACKLIGHT_BREATHING - chVTObjectInit(&breathing_vt); - if (is_backlight_breathing()) { - breathing_enable(); - } -#endif -} - -void backlight_set(uint8_t level) { - if (level > BACKLIGHT_LEVELS) { - level = BACKLIGHT_LEVELS; - } - - if (level == 0) { - // Turn backlight off - pwmDisableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1); - } else { - // Turn backlight on - uint32_t duty = (uint32_t)(cie_lightness(rescale_limit_val(0xFFFF * (uint32_t)level / BACKLIGHT_LEVELS))); - pwmEnableChannel(&BACKLIGHT_PWM_DRIVER, BACKLIGHT_PWM_CHANNEL - 1, PWM_FRACTION_TO_WIDTH(&BACKLIGHT_PWM_DRIVER, 0xFFFF, duty)); - } -} - -void backlight_task(void) {} - -#ifdef BACKLIGHT_BREATHING - -# define BREATHING_STEPS 128 - -/* 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}; - -static void breathing_callback(virtual_timer_t *vtp, void *p); - -bool is_breathing(void) { - return chVTIsArmed(&breathing_vt); -} - -void breathing_enable(void) { - /* Update frequency is 256Hz -> 3906us intervals */ - chVTSetContinuous(&breathing_vt, TIME_US2I(3906), breathing_callback, NULL); -} - -void breathing_disable(void) { - chVTReset(&breathing_vt); - - // Restore backlight level - backlight_set(get_backlight_level()); -} - -// 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(virtual_timer_t *vtp, void *p) { - 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. - static uint16_t breathing_counter = 0; - breathing_counter = (breathing_counter + 1) % (breathing_period * 256); - uint8_t index = breathing_counter / interval % BREATHING_STEPS; - uint32_t duty = cie_lightness(rescale_limit_val(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(); -} - -// TODO: integrate generic pulse solution -void breathing_pulse(void) { - backlight_set(is_backlight_enabled() ? 0 : BACKLIGHT_LEVELS); - wait_ms(10); - backlight_set(is_backlight_enabled() ? get_backlight_level() : 0); -} - -#endif diff --git a/platforms/chibios/drivers/backlight_timer.c b/platforms/chibios/drivers/backlight_timer.c deleted file mode 100644 index 0fabee93b1..0000000000 --- a/platforms/chibios/drivers/backlight_timer.c +++ /dev/null @@ -1,178 +0,0 @@ -#include "backlight.h" -#include "backlight_driver_common.h" -#include "wait.h" - -#ifndef BACKLIGHT_GPT_DRIVER -# define BACKLIGHT_GPT_DRIVER GPTD15 -#endif - -// Platform specific implementations -static void backlight_timer_configure(bool enable); -static void backlight_timer_set_duty(uint16_t duty); -static uint16_t backlight_timer_get_duty(void); - -// 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) { - backlight_pins_init(); - - backlight_set(get_backlight_level()); - -#ifdef BACKLIGHT_BREATHING - if (is_backlight_breathing()) { - breathing_enable(); - } -#endif -} - -void backlight_set(uint8_t level) { - if (level > BACKLIGHT_LEVELS) level = BACKLIGHT_LEVELS; - - backlight_pins_off(); - - backlight_timer_set_duty(cie_lightness(0xFFFFU / BACKLIGHT_LEVELS * level)); - backlight_timer_configure(level != 0); -} - -static void backlight_timer_top(void) { -#ifdef BACKLIGHT_BREATHING - if (is_breathing()) { - breathing_task(); - } -#endif - - if (backlight_timer_get_duty() > 256) { - backlight_pins_on(); - } -} - -static void backlight_timer_cmp(void) { - backlight_pins_off(); -} - -void backlight_task(void) {} - -#ifdef BACKLIGHT_BREATHING -# define BREATHING_STEPS 128 - -static bool breathing = false; -static uint16_t breathing_counter = 0; - -/* 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(); -} - -void breathing_task(void) { - 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; - - // printf("index:%u\n", index); - - backlight_timer_set_duty(cie_lightness(scale_backlight((uint16_t)breathing_table[index] * 256))); -} - -bool is_breathing(void) { - return breathing; -} - -void breathing_enable(void) { - breathing_counter = 0; - breathing = true; -} -void breathing_disable(void) { - breathing = false; -} - -void breathing_pulse(void) { - backlight_set(is_backlight_enabled() ? 0 : BACKLIGHT_LEVELS); - wait_ms(10); - backlight_set(is_backlight_enabled() ? get_backlight_level() : 0); -} -#endif - -#ifdef PROTOCOL_CHIBIOS -// On Platforms where timers fire every tick and have no capture/top events -// - fake event in the normal timer callback -uint16_t s_duty = 0; - -static void timerCallback(void) { - /* Software PWM - * timer:1111 1111 1111 1111 - * \______/| \_______/____ count(0-255) - * \ \______________ unused(1) - * \__________________ index of step table(0-127) - */ - - // this works for cca 65536 irqs/sec - static union { - uint16_t raw; - struct { - uint16_t count : 8; - uint8_t dummy : 1; - uint8_t index : 7; - } pwm; - } timer = {.raw = 0}; - - timer.raw++; - - if (timer.pwm.count == 0) { - // LED on - backlight_timer_top(); - } else if (timer.pwm.count == (s_duty / 256)) { - // LED off - backlight_timer_cmp(); - } -} - -static void backlight_timer_set_duty(uint16_t duty) { - s_duty = duty; -} -static uint16_t backlight_timer_get_duty(void) { - return s_duty; -} - -// ChibiOS - Map GPT timer onto Software PWM -static void gptTimerCallback(GPTDriver *gptp) { - (void)gptp; - timerCallback(); -} - -static void backlight_timer_configure(bool enable) { - static const GPTConfig gptcfg = {1000000, gptTimerCallback, 0, 0}; - - static bool s_init = false; - if (!s_init) { - gptStart(&BACKLIGHT_GPT_DRIVER, &gptcfg); - s_init = true; - } - - if (enable) { - gptStartContinuous(&BACKLIGHT_GPT_DRIVER, gptcfg.frequency / 0xFFFF); - } else { - gptStopTimer(&BACKLIGHT_GPT_DRIVER); - } -} -#endif diff --git a/platforms/chibios/drivers/eeprom/eeprom_kinetis_flexram.c b/platforms/chibios/drivers/eeprom/eeprom_kinetis_flexram.c deleted file mode 100644 index 6468cbf3fa..0000000000 --- a/platforms/chibios/drivers/eeprom/eeprom_kinetis_flexram.c +++ /dev/null @@ -1,546 +0,0 @@ -#include <ch.h> -#include <hal.h> - -#include "eeprom_kinetis_flexram.h" -#include "eeconfig.h" - -/*************************************/ -/* Hardware backend */ -/* */ -/* Code from PJRC/Teensyduino */ -/*************************************/ - -/* Teensyduino Core Library - * http://www.pjrc.com/teensy/ - * Copyright (c) 2013 PJRC.COM, LLC. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * 1. The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * 2. If the Software is incorporated into a build system that allows - * selection among a list of target devices, then similar target - * devices manufactured by PJRC.COM must be included in the list of - * target devices and selectable in the same manner. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#if defined(K20x) /* chip selection */ -/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ - -/* - ^^^ Here be dragons: - NXP AppNote AN4282 section 3.1 states that partitioning must only be done once. - Once EEPROM partitioning is done, the size is locked to this initial configuration. - Attempts to modify the EEPROM_SIZE setting may brick your board. -*/ - -// Writing unaligned 16 or 32 bit data is handled automatically when -// this is defined, but at a cost of extra code size. Without this, -// any unaligned write will cause a hard fault exception! If you're -// absolutely sure all 16 and 32 bit writes will be aligned, you can -// remove the extra unnecessary code. -// -# define HANDLE_UNALIGNED_WRITES - -// Minimum EEPROM Endurance -// ------------------------ -# if (EEPROM_SIZE == 2048) // 35000 writes/byte or 70000 writes/word -# define EEESIZE 0x33 -# elif (EEPROM_SIZE == 1024) // 75000 writes/byte or 150000 writes/word -# define EEESIZE 0x34 -# elif (EEPROM_SIZE == 512) // 155000 writes/byte or 310000 writes/word -# define EEESIZE 0x35 -# elif (EEPROM_SIZE == 256) // 315000 writes/byte or 630000 writes/word -# define EEESIZE 0x36 -# elif (EEPROM_SIZE == 128) // 635000 writes/byte or 1270000 writes/word -# define EEESIZE 0x37 -# elif (EEPROM_SIZE == 64) // 1275000 writes/byte or 2550000 writes/word -# define EEESIZE 0x38 -# elif (EEPROM_SIZE == 32) // 2555000 writes/byte or 5110000 writes/word -# define EEESIZE 0x39 -# endif - -/** \brief eeprom initialization - * - * FIXME: needs doc - */ -void eeprom_initialize(void) { - uint32_t count = 0; - uint16_t do_flash_cmd[] = {0xf06f, 0x037f, 0x7003, 0x7803, 0xf013, 0x0f80, 0xd0fb, 0x4770}; - uint8_t status; - - if (FTFL->FCNFG & FTFL_FCNFG_RAMRDY) { - // FlexRAM is configured as traditional RAM - // We need to reconfigure for EEPROM usage - FTFL->FCCOB0 = 0x80; // PGMPART = Program Partition Command - FTFL->FCCOB4 = EEESIZE; // EEPROM Size - FTFL->FCCOB5 = 0x03; // 0K for Dataflash, 32K for EEPROM backup - __disable_irq(); - // do_flash_cmd() must execute from RAM. Luckily the C syntax is simple... - (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFL->FSTAT)); - __enable_irq(); - status = FTFL->FSTAT; - if (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)) { - FTFL->FSTAT = (status & (FTFL_FSTAT_RDCOLERR | FTFL_FSTAT_ACCERR | FTFL_FSTAT_FPVIOL)); - return; // error - } - } - // wait for eeprom to become ready (is this really necessary?) - while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { - if (++count > 20000) break; - } -} - -# define FlexRAM ((uint8_t *)0x14000000) - -/** \brief eeprom read byte - * - * FIXME: needs doc - */ -uint8_t eeprom_read_byte(const uint8_t *addr) { - uint32_t offset = (uint32_t)addr; - if (offset >= EEPROM_SIZE) return 0; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - return FlexRAM[offset]; -} - -/** \brief eeprom read word - * - * FIXME: needs doc - */ -uint16_t eeprom_read_word(const uint16_t *addr) { - uint32_t offset = (uint32_t)addr; - if (offset >= EEPROM_SIZE - 1) return 0; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - return *(uint16_t *)(&FlexRAM[offset]); -} - -/** \brief eeprom read dword - * - * FIXME: needs doc - */ -uint32_t eeprom_read_dword(const uint32_t *addr) { - uint32_t offset = (uint32_t)addr; - if (offset >= EEPROM_SIZE - 3) return 0; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - return *(uint32_t *)(&FlexRAM[offset]); -} - -/** \brief eeprom read block - * - * FIXME: needs doc - */ -void eeprom_read_block(void *buf, const void *addr, uint32_t len) { - uint32_t offset = (uint32_t)addr; - uint8_t *dest = (uint8_t *)buf; - uint32_t end = offset + len; - - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - if (end > EEPROM_SIZE) end = EEPROM_SIZE; - while (offset < end) { - *dest++ = FlexRAM[offset++]; - } -} - -/** \brief eeprom is ready - * - * FIXME: needs doc - */ -int eeprom_is_ready(void) { - return (FTFL->FCNFG & FTFL_FCNFG_EEERDY) ? 1 : 0; -} - -/** \brief flexram wait - * - * FIXME: needs doc - */ -static void flexram_wait(void) { - while (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) { - // TODO: timeout - } -} - -/** \brief eeprom_write_byte - * - * FIXME: needs doc - */ -void eeprom_write_byte(uint8_t *addr, uint8_t value) { - uint32_t offset = (uint32_t)addr; - - if (offset >= EEPROM_SIZE) return; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - if (FlexRAM[offset] != value) { - FlexRAM[offset] = value; - flexram_wait(); - } -} - -/** \brief eeprom write word - * - * FIXME: needs doc - */ -void eeprom_write_word(uint16_t *addr, uint16_t value) { - uint32_t offset = (uint32_t)addr; - - if (offset >= EEPROM_SIZE - 1) return; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); -# ifdef HANDLE_UNALIGNED_WRITES - if ((offset & 1) == 0) { -# endif - if (*(uint16_t *)(&FlexRAM[offset]) != value) { - *(uint16_t *)(&FlexRAM[offset]) = value; - flexram_wait(); - } -# ifdef HANDLE_UNALIGNED_WRITES - } else { - if (FlexRAM[offset] != value) { - FlexRAM[offset] = value; - flexram_wait(); - } - if (FlexRAM[offset + 1] != (value >> 8)) { - FlexRAM[offset + 1] = value >> 8; - flexram_wait(); - } - } -# endif -} - -/** \brief eeprom write dword - * - * FIXME: needs doc - */ -void eeprom_write_dword(uint32_t *addr, uint32_t value) { - uint32_t offset = (uint32_t)addr; - - if (offset >= EEPROM_SIZE - 3) return; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); -# ifdef HANDLE_UNALIGNED_WRITES - switch (offset & 3) { - case 0: -# endif - if (*(uint32_t *)(&FlexRAM[offset]) != value) { - *(uint32_t *)(&FlexRAM[offset]) = value; - flexram_wait(); - } - return; -# ifdef HANDLE_UNALIGNED_WRITES - case 2: - if (*(uint16_t *)(&FlexRAM[offset]) != value) { - *(uint16_t *)(&FlexRAM[offset]) = value; - flexram_wait(); - } - if (*(uint16_t *)(&FlexRAM[offset + 2]) != (value >> 16)) { - *(uint16_t *)(&FlexRAM[offset + 2]) = value >> 16; - flexram_wait(); - } - return; - default: - if (FlexRAM[offset] != value) { - FlexRAM[offset] = value; - flexram_wait(); - } - if (*(uint16_t *)(&FlexRAM[offset + 1]) != (value >> 8)) { - *(uint16_t *)(&FlexRAM[offset + 1]) = value >> 8; - flexram_wait(); - } - if (FlexRAM[offset + 3] != (value >> 24)) { - FlexRAM[offset + 3] = value >> 24; - flexram_wait(); - } - } -# endif -} - -/** \brief eeprom write block - * - * FIXME: needs doc - */ -void eeprom_write_block(const void *buf, void *addr, uint32_t len) { - uint32_t offset = (uint32_t)addr; - const uint8_t *src = (const uint8_t *)buf; - - if (offset >= EEPROM_SIZE) return; - if (!(FTFL->FCNFG & FTFL_FCNFG_EEERDY)) eeprom_initialize(); - if (len >= EEPROM_SIZE) len = EEPROM_SIZE; - if (offset + len >= EEPROM_SIZE) len = EEPROM_SIZE - offset; - while (len > 0) { - uint32_t lsb = offset & 3; - if (lsb == 0 && len >= 4) { - // write aligned 32 bits - uint32_t val32; - val32 = *src++; - val32 |= (*src++ << 8); - val32 |= (*src++ << 16); - val32 |= (*src++ << 24); - if (*(uint32_t *)(&FlexRAM[offset]) != val32) { - *(uint32_t *)(&FlexRAM[offset]) = val32; - flexram_wait(); - } - offset += 4; - len -= 4; - } else if ((lsb == 0 || lsb == 2) && len >= 2) { - // write aligned 16 bits - uint16_t val16; - val16 = *src++; - val16 |= (*src++ << 8); - if (*(uint16_t *)(&FlexRAM[offset]) != val16) { - *(uint16_t *)(&FlexRAM[offset]) = val16; - flexram_wait(); - } - offset += 2; - len -= 2; - } else { - // write 8 bits - uint8_t val8 = *src++; - if (FlexRAM[offset] != val8) { - FlexRAM[offset] = val8; - flexram_wait(); - } - offset++; - len--; - } - } -} - -/* -void do_flash_cmd(volatile uint8_t *fstat) -{ - *fstat = 0x80; - while ((*fstat & 0x80) == 0) ; // wait -} -00000000 <do_flash_cmd>: - 0: f06f 037f mvn.w r3, #127 ; 0x7f - 4: 7003 strb r3, [r0, #0] - 6: 7803 ldrb r3, [r0, #0] - 8: f013 0f80 tst.w r3, #128 ; 0x80 - c: d0fb beq.n 6 <do_flash_cmd+0x6> - e: 4770 bx lr -*/ - -#elif defined(KL2x) /* chip selection */ -/* Teensy LC (emulated) */ - -# define SYMVAL(sym) (uint32_t)(((uint8_t *)&(sym)) - ((uint8_t *)0)) - -extern uint32_t __eeprom_workarea_start__; -extern uint32_t __eeprom_workarea_end__; - -static uint32_t flashend = 0; - -void eeprom_initialize(void) { - const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); - - do { - if (*p++ == 0xFFFF) { - flashend = (uint32_t)(p - 2); - return; - } - } while (p < (uint16_t *)SYMVAL(__eeprom_workarea_end__)); - flashend = (uint32_t)(p - 1); -} - -uint8_t eeprom_read_byte(const uint8_t *addr) { - uint32_t offset = (uint32_t)addr; - const uint16_t *p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); - const uint16_t *end = (const uint16_t *)((uint32_t)flashend); - uint16_t val; - uint8_t data = 0xFF; - - if (!end) { - eeprom_initialize(); - end = (const uint16_t *)((uint32_t)flashend); - } - if (offset < EEPROM_SIZE) { - while (p <= end) { - val = *p++; - if ((val & 255) == offset) data = val >> 8; - } - } - return data; -} - -static void flash_write(const uint16_t *code, uint32_t addr, uint32_t data) { - // with great power comes great responsibility.... - uint32_t stat; - *(uint32_t *)&(FTFA->FCCOB3) = 0x06000000 | (addr & 0x00FFFFFC); - *(uint32_t *)&(FTFA->FCCOB7) = data; - __disable_irq(); - (*((void (*)(volatile uint8_t *))((uint32_t)code | 1)))(&(FTFA->FSTAT)); - __enable_irq(); - stat = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL); - if (stat) { - FTFA->FSTAT = stat; - } - MCM->PLACR |= MCM_PLACR_CFCC; -} - -void eeprom_write_byte(uint8_t *addr, uint8_t data) { - uint32_t offset = (uint32_t)addr; - const uint16_t *p, *end = (const uint16_t *)((uint32_t)flashend); - uint32_t i, val, flashaddr; - uint16_t do_flash_cmd[] = {0x2380, 0x7003, 0x7803, 0xb25b, 0x2b00, 0xdafb, 0x4770}; - uint8_t buf[EEPROM_SIZE]; - - if (offset >= EEPROM_SIZE) return; - if (!end) { - eeprom_initialize(); - end = (const uint16_t *)((uint32_t)flashend); - } - if (++end < (uint16_t *)SYMVAL(__eeprom_workarea_end__)) { - val = (data << 8) | offset; - flashaddr = (uint32_t)end; - flashend = flashaddr; - if ((flashaddr & 2) == 0) { - val |= 0xFFFF0000; - } else { - val <<= 16; - val |= 0x0000FFFF; - } - flash_write(do_flash_cmd, flashaddr, val); - } else { - for (i = 0; i < EEPROM_SIZE; i++) { - buf[i] = 0xFF; - } - val = 0; - for (p = (uint16_t *)SYMVAL(__eeprom_workarea_start__); p < (uint16_t *)SYMVAL(__eeprom_workarea_end__); p++) { - val = *p; - if ((val & 255) < EEPROM_SIZE) { - buf[val & 255] = val >> 8; - } - } - buf[offset] = data; - for (flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); flashaddr < (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_end__); flashaddr += 1024) { - *(uint32_t *)&(FTFA->FCCOB3) = 0x09000000 | flashaddr; - __disable_irq(); - (*((void (*)(volatile uint8_t *))((uint32_t)do_flash_cmd | 1)))(&(FTFA->FSTAT)); - __enable_irq(); - val = FTFA->FSTAT & (FTFA_FSTAT_RDCOLERR | FTFA_FSTAT_ACCERR | FTFA_FSTAT_FPVIOL); - ; - if (val) FTFA->FSTAT = val; - MCM->PLACR |= MCM_PLACR_CFCC; - } - flashaddr = (uint32_t)(uint16_t *)SYMVAL(__eeprom_workarea_start__); - for (i = 0; i < EEPROM_SIZE; i++) { - if (buf[i] == 0xFF) continue; - if ((flashaddr & 2) == 0) { - val = (buf[i] << 8) | i; - } else { - val = val | (buf[i] << 24) | (i << 16); - flash_write(do_flash_cmd, flashaddr, val); - } - flashaddr += 2; - } - flashend = flashaddr; - if ((flashaddr & 2)) { - val |= 0xFFFF0000; - flash_write(do_flash_cmd, flashaddr, val); - } - } -} - -/* -void do_flash_cmd(volatile uint8_t *fstat) -{ - *fstat = 0x80; - while ((*fstat & 0x80) == 0) ; // wait -} -00000000 <do_flash_cmd>: - 0: 2380 movs r3, #128 ; 0x80 - 2: 7003 strb r3, [r0, #0] - 4: 7803 ldrb r3, [r0, #0] - 6: b25b sxtb r3, r3 - 8: 2b00 cmp r3, #0 - a: dafb bge.n 4 <do_flash_cmd+0x4> - c: 4770 bx lr -*/ - -uint16_t eeprom_read_word(const uint16_t *addr) { - const uint8_t *p = (const uint8_t *)addr; - return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8); -} - -uint32_t eeprom_read_dword(const uint32_t *addr) { - const uint8_t *p = (const uint8_t *)addr; - return eeprom_read_byte(p) | (eeprom_read_byte(p + 1) << 8) | (eeprom_read_byte(p + 2) << 16) | (eeprom_read_byte(p + 3) << 24); -} - -void eeprom_read_block(void *buf, const void *addr, uint32_t len) { - const uint8_t *p = (const uint8_t *)addr; - uint8_t * dest = (uint8_t *)buf; - while (len--) { - *dest++ = eeprom_read_byte(p++); - } -} - -int eeprom_is_ready(void) { - return 1; -} - -void eeprom_write_word(uint16_t *addr, uint16_t value) { - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p, value >> 8); -} - -void eeprom_write_dword(uint32_t *addr, uint32_t value) { - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p++, value >> 8); - eeprom_write_byte(p++, value >> 16); - eeprom_write_byte(p, value >> 24); -} - -void eeprom_write_block(const void *buf, void *addr, uint32_t len) { - uint8_t * p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); - } -} - -#else -# error Unsupported Teensy EEPROM. -#endif /* chip selection */ -// The update functions just calls write for now, but could probably be optimized - -void eeprom_update_byte(uint8_t *addr, uint8_t value) { - eeprom_write_byte(addr, value); -} - -void eeprom_update_word(uint16_t *addr, uint16_t value) { - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p, value >> 8); -} - -void eeprom_update_dword(uint32_t *addr, uint32_t value) { - uint8_t *p = (uint8_t *)addr; - eeprom_write_byte(p++, value); - eeprom_write_byte(p++, value >> 8); - eeprom_write_byte(p++, value >> 16); - eeprom_write_byte(p, value >> 24); -} - -void eeprom_update_block(const void *buf, void *addr, size_t len) { - uint8_t * p = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - while (len--) { - eeprom_write_byte(p++, *src++); - } -} diff --git a/platforms/chibios/drivers/eeprom/eeprom_kinetis_flexram.h b/platforms/chibios/drivers/eeprom/eeprom_kinetis_flexram.h deleted file mode 100755 index 9a14a1fa79..0000000000 --- a/platforms/chibios/drivers/eeprom/eeprom_kinetis_flexram.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2022 Nick Brassel (@tzarc) -// SPDX-License-Identifier: GPL-2.0-or-later -#pragma once - -#include <ch.h> -#include <hal.h> - -#if defined(K20x) -/* Teensy 3.0, 3.1, 3.2; mchck; infinity keyboard */ -// The EEPROM is really RAM with a hardware-based backup system to -// flash memory. Selecting a smaller size EEPROM allows more wear -// leveling, for higher write endurance. If you edit this file, -// set this to the smallest size your application can use. Also, -// due to Freescale's implementation, writing 16 or 32 bit words -// (aligned to 2 or 4 byte boundaries) has twice the endurance -// compared to writing 8 bit bytes. -// -# ifndef EEPROM_SIZE -# define EEPROM_SIZE 32 -# endif -#elif defined(KL2x) /* Teensy LC (emulated) */ -# define EEPROM_SIZE 128 -#else -# error Unsupported Teensy EEPROM. -#endif diff --git a/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.c b/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.c deleted file mode 100644 index a81fe3353c..0000000000 --- a/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.c +++ /dev/null @@ -1,629 +0,0 @@ -/* - * This software is experimental and a work in progress. - * Under no circumstances should these files be used in relation to any critical system(s). - * Use of these files is at your own risk. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by - * Artur F. - * - * Modifications for QMK and STM32F303 by Yiancar - * Modifications to add flash wear leveling by Ilya Zhuravlev - * Modifications to increase flash density by Don Kjer - */ - -#include <stdio.h> -#include <stdbool.h> -#include "util.h" -#include "debug.h" -#include "eeprom_legacy_emulated_flash.h" -#include "legacy_flash_ops.h" - -/* - * We emulate eeprom by writing a snapshot compacted view of eeprom contents, - * followed by a write log of any change since that snapshot: - * - * === SIMULATED EEPROM CONTENTS === - * - * ┌─ Compacted ┬ Write Log ─┐ - * │............│[BYTE][BYTE]│ - * │FFFF....FFFF│[WRD0][WRD1]│ - * │FFFFFFFFFFFF│[WORD][NEXT]│ - * │....FFFFFFFF│[BYTE][WRD0]│ - * ├────────────┼────────────┤ - * └──PAGE_BASE │ │ - * PAGE_LAST─┴─WRITE_BASE │ - * WRITE_LAST ┘ - * - * Compacted contents are the 1's complement of the actual EEPROM contents. - * e.g. An 'FFFF' represents a '0000' value. - * - * The size of the 'compacted' area is equal to the size of the 'emulated' eeprom. - * The size of the compacted-area and write log are configurable, and the combined - * size of Compacted + WriteLog is a multiple FEE_PAGE_SIZE, which is MCU dependent. - * Simulated Eeprom contents are located at the end of available flash space. - * - * The following configuration defines can be set: - * - * FEE_PAGE_COUNT # Total number of pages to use for eeprom simulation (Compact + Write log) - * FEE_DENSITY_BYTES # Size of simulated eeprom. (Defaults to half the space allocated by FEE_PAGE_COUNT) - * NOTE: The current implementation does not include page swapping, - * and FEE_DENSITY_BYTES will consume that amount of RAM as a cached view of actual EEPROM contents. - * - * The maximum size of FEE_DENSITY_BYTES is currently 16384. The write log size equals - * FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES. - * The larger the write log, the less frequently the compacted area needs to be rewritten. - * - * - * *** General Algorithm *** - * - * During initialization: - * The contents of the Compacted-flash area are loaded and the 1's complement value - * is cached into memory (e.g. 0xFFFF in Flash represents 0x0000 in cache). - * Write log entries are processed until a 0xFFFF is reached. - * Each log entry updates a byte or word in the cache. - * - * During reads: - * EEPROM contents are given back directly from the cache in memory. - * - * During writes: - * The contents of the cache is updated first. - * If the Compacted-flash area corresponding to the write address is unprogrammed, the 1's complement of the value is written directly into Compacted-flash - * Otherwise: - * If the write log is full, erase both the Compacted-flash area and the Write log, then write cached contents to the Compacted-flash area. - * Otherwise a Write log entry is constructed and appended to the next free position in the Write log. - * - * - * *** Write Log Structure *** - * - * Write log entries allow for optimized byte writes to addresses below 128. Writing 0 or 1 words are also optimized when word-aligned. - * - * === WRITE LOG ENTRY FORMATS === - * - * ╔═══ Byte-Entry ══╗ - * ║0XXXXXXX║YYYYYYYY║ - * ║ └──┬──┘║└──┬───┘║ - * ║ Address║ Value ║ - * ╚════════╩════════╝ - * 0 <= Address < 0x80 (128) - * - * ╔ Word-Encoded 0 ╗ - * ║100XXXXXXXXXXXXX║ - * ║ │└─────┬─────┘║ - * ║ │Address >> 1 ║ - * ║ └── Value: 0 ║ - * ╚════════════════╝ - * 0 <= Address <= 0x3FFE (16382) - * - * ╔ Word-Encoded 1 ╗ - * ║101XXXXXXXXXXXXX║ - * ║ │└─────┬─────┘║ - * ║ │Address >> 1 ║ - * ║ └── Value: 1 ║ - * ╚════════════════╝ - * 0 <= Address <= 0x3FFE (16382) - * - * ╔═══ Reserved ═══╗ - * ║110XXXXXXXXXXXXX║ - * ╚════════════════╝ - * - * ╔═══════════ Word-Next ═══════════╗ - * ║111XXXXXXXXXXXXX║YYYYYYYYYYYYYYYY║ - * ║ └─────┬─────┘║└───────┬──────┘║ - * ║(Address-128)>>1║ ~Value ║ - * ╚════════════════╩════════════════╝ - * ( 0 <= Address < 0x0080 (128): Reserved) - * 0x80 <= Address <= 0x3FFE (16382) - * - * Write Log entry ranges: - * 0x0000 ... 0x7FFF - Byte-Entry; address is (Entry & 0x7F00) >> 4; value is (Entry & 0xFF) - * 0x8000 ... 0x9FFF - Word-Encoded 0; address is (Entry & 0x1FFF) << 1; value is 0 - * 0xA000 ... 0xBFFF - Word-Encoded 1; address is (Entry & 0x1FFF) << 1; value is 1 - * 0xC000 ... 0xDFFF - Reserved - * 0xE000 ... 0xFFBF - Word-Next; address is (Entry & 0x1FFF) << 1 + 0x80; value is ~(Next_Entry) - * 0xFFC0 ... 0xFFFE - Reserved - * 0xFFFF - Unprogrammed - * - */ - -#include "eeprom_legacy_emulated_flash_defs.h" -/* These bits are used for optimizing encoding of bytes, 0 and 1 */ -#define FEE_WORD_ENCODING 0x8000 -#define FEE_VALUE_NEXT 0x6000 -#define FEE_VALUE_RESERVED 0x4000 -#define FEE_VALUE_ENCODED 0x2000 -#define FEE_BYTE_RANGE 0x80 - -/* Flash word value after erase */ -#define FEE_EMPTY_WORD ((uint16_t)0xFFFF) - -#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) || !defined(FEE_MCU_FLASH_SIZE) || !defined(FEE_PAGE_BASE_ADDRESS) -# error "not implemented." -#endif - -/* In-memory contents of emulated eeprom for faster access */ -/* *TODO: Implement page swapping */ -static uint16_t WordBuf[FEE_DENSITY_BYTES / 2]; -static uint8_t *DataBuf = (uint8_t *)WordBuf; - -/* Pointer to the first available slot within the write log */ -static uint16_t *empty_slot; - -// #define DEBUG_EEPROM_OUTPUT - -/* - * Debug print utils - */ - -#if defined(DEBUG_EEPROM_OUTPUT) - -# define debug_eeprom debug_enable -# define eeprom_println(s) println(s) -# define eeprom_printf(fmt, ...) xprintf(fmt, ##__VA_ARGS__); - -#else /* NO_DEBUG */ - -# define debug_eeprom false -# define eeprom_println(s) -# define eeprom_printf(fmt, ...) - -#endif /* NO_DEBUG */ - -void print_eeprom(void) { -#ifndef NO_DEBUG - int empty_rows = 0; - for (uint16_t i = 0; i < FEE_DENSITY_BYTES; i++) { - if (i % 16 == 0) { - if (i >= FEE_DENSITY_BYTES - 16) { - /* Make sure we display the last row */ - empty_rows = 0; - } - /* Check if this row is uninitialized */ - ++empty_rows; - for (uint16_t j = 0; j < 16; j++) { - if (DataBuf[i + j]) { - empty_rows = 0; - break; - } - } - if (empty_rows > 1) { - /* Repeat empty row */ - if (empty_rows == 2) { - /* Only display the first repeat empty row */ - println("*"); - } - i += 15; - continue; - } - xprintf("%04x", i); - } - if (i % 8 == 0) print(" "); - - xprintf(" %02x", DataBuf[i]); - if ((i + 1) % 16 == 0) { - println(""); - } - } -#endif -} - -uint16_t EEPROM_Init(void) { - /* Load emulated eeprom contents from compacted flash into memory */ - uint16_t *src = (uint16_t *)FEE_COMPACTED_BASE_ADDRESS; - uint16_t *dest = (uint16_t *)DataBuf; - for (; src < (uint16_t *)FEE_COMPACTED_LAST_ADDRESS; ++src, ++dest) { - *dest = ~*src; - } - - if (debug_eeprom) { - println("EEPROM_Init Compacted Pages:"); - print_eeprom(); - println("EEPROM_Init Write Log:"); - } - - /* Replay write log */ - uint16_t *log_addr; - for (log_addr = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; log_addr < (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS; ++log_addr) { - uint16_t address = *log_addr; - if (address == FEE_EMPTY_WORD) { - break; - } - /* Check for lowest 128-bytes optimization */ - if (!(address & FEE_WORD_ENCODING)) { - uint8_t bvalue = (uint8_t)address; - address >>= 8; - DataBuf[address] = bvalue; - eeprom_printf("DataBuf[0x%02x] = 0x%02x;\n", address, bvalue); - } else { - uint16_t wvalue; - /* Check if value is in next word */ - if ((address & FEE_VALUE_NEXT) == FEE_VALUE_NEXT) { - /* Read value from next word */ - if (++log_addr >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { - break; - } - wvalue = ~*log_addr; - if (!wvalue) { - eeprom_printf("Incomplete write at log_addr: 0x%04lx;\n", (uint32_t)log_addr); - /* Possibly incomplete write. Ignore and continue */ - continue; - } - address &= 0x1FFF; - address <<= 1; - /* Writes to addresses less than 128 are byte log entries */ - address += FEE_BYTE_RANGE; - } else { - /* Reserved for future use */ - if (address & FEE_VALUE_RESERVED) { - eeprom_printf("Reserved encoded value at log_addr: 0x%04lx;\n", (uint32_t)log_addr); - continue; - } - /* Optimization for 0 or 1 values. */ - wvalue = (address & FEE_VALUE_ENCODED) >> 13; - address &= 0x1FFF; - address <<= 1; - } - if (address < FEE_DENSITY_BYTES) { - eeprom_printf("DataBuf[0x%04x] = 0x%04x;\n", address, wvalue); - *(uint16_t *)(&DataBuf[address]) = wvalue; - } else { - eeprom_printf("DataBuf[0x%04x] cannot be set to 0x%04x [BAD ADDRESS]\n", address, wvalue); - } - } - } - - empty_slot = log_addr; - - if (debug_eeprom) { - println("EEPROM_Init Final DataBuf:"); - print_eeprom(); - } - - return FEE_DENSITY_BYTES; -} - -/* Clear flash contents (doesn't touch in-memory DataBuf) */ -static void eeprom_clear(void) { - FLASH_Unlock(); - - for (uint16_t page_num = 0; page_num < FEE_PAGE_COUNT; ++page_num) { - eeprom_printf("FLASH_ErasePage(0x%04lx)\n", (uint32_t)(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE))); - FLASH_ErasePage(FEE_PAGE_BASE_ADDRESS + (page_num * FEE_PAGE_SIZE)); - } - - FLASH_Lock(); - - empty_slot = (uint16_t *)FEE_WRITE_LOG_BASE_ADDRESS; - eeprom_printf("eeprom_clear empty_slot: 0x%08lx\n", (uint32_t)empty_slot); -} - -/* Erase emulated eeprom */ -void EEPROM_Erase(void) { - eeprom_println("EEPROM_Erase"); - /* Erase compacted pages and write log */ - eeprom_clear(); - /* re-initialize to reset DataBuf */ - EEPROM_Init(); -} - -/* Compact write log */ -static uint8_t eeprom_compact(void) { - /* Erase compacted pages and write log */ - eeprom_clear(); - - FLASH_Unlock(); - - FLASH_Status final_status = FLASH_COMPLETE; - - /* Write emulated eeprom contents from memory to compacted flash */ - uint16_t *src = (uint16_t *)DataBuf; - uintptr_t dest = FEE_COMPACTED_BASE_ADDRESS; - uint16_t value; - for (; dest < FEE_COMPACTED_LAST_ADDRESS; ++src, dest += 2) { - value = *src; - if (value) { - eeprom_printf("FLASH_ProgramHalfWord(0x%04lx, 0x%04x)\n", (uint32_t)dest, ~value); - FLASH_Status status = FLASH_ProgramHalfWord(dest, ~value); - if (status != FLASH_COMPLETE) final_status = status; - } - } - - FLASH_Lock(); - - if (debug_eeprom) { - println("eeprom_compacted:"); - print_eeprom(); - } - - return final_status; -} - -static uint8_t eeprom_write_direct_entry(uint16_t Address) { - /* Check if we can just write this directly to the compacted flash area */ - uintptr_t directAddress = FEE_COMPACTED_BASE_ADDRESS + (Address & 0xFFFE); - if (*(uint16_t *)directAddress == FEE_EMPTY_WORD) { - /* Write the value directly to the compacted area without a log entry */ - uint16_t value = ~*(uint16_t *)(&DataBuf[Address & 0xFFFE]); - /* Early exit if a write isn't needed */ - if (value == FEE_EMPTY_WORD) return FLASH_COMPLETE; - - FLASH_Unlock(); - - eeprom_printf("FLASH_ProgramHalfWord(0x%08lx, 0x%04x) [DIRECT]\n", (uint32_t)directAddress, value); - FLASH_Status status = FLASH_ProgramHalfWord(directAddress, value); - - FLASH_Lock(); - return status; - } - return 0; -} - -static uint8_t eeprom_write_log_word_entry(uint16_t Address) { - FLASH_Status final_status = FLASH_COMPLETE; - - uint16_t value = *(uint16_t *)(&DataBuf[Address]); - eeprom_printf("eeprom_write_log_word_entry(0x%04x): 0x%04x\n", Address, value); - - /* MSB signifies the lowest 128-byte optimization is not in effect */ - uint16_t encoding = FEE_WORD_ENCODING; - uint8_t entry_size; - if (value <= 1) { - encoding |= value << 13; - entry_size = 2; - } else { - encoding |= FEE_VALUE_NEXT; - entry_size = 4; - /* Writes to addresses less than 128 are byte log entries */ - Address -= FEE_BYTE_RANGE; - } - - /* if we can't find an empty spot, we must compact emulated eeprom */ - if (empty_slot > (uint16_t *)(FEE_WRITE_LOG_LAST_ADDRESS - entry_size)) { - /* compact the write log into the compacted flash area */ - return eeprom_compact(); - } - - /* Word log writes should be word-aligned. Take back a bit */ - Address >>= 1; - Address |= encoding; - - /* ok we found a place let's write our data */ - FLASH_Unlock(); - - /* address */ - eeprom_printf("FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\n", (uint32_t)empty_slot, Address); - final_status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, Address); - - /* value */ - if (encoding == (FEE_WORD_ENCODING | FEE_VALUE_NEXT)) { - eeprom_printf("FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\n", (uint32_t)empty_slot, ~value); - FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, ~value); - if (status != FLASH_COMPLETE) final_status = status; - } - - FLASH_Lock(); - - return final_status; -} - -static uint8_t eeprom_write_log_byte_entry(uint16_t Address) { - eeprom_printf("eeprom_write_log_byte_entry(0x%04x): 0x%02x\n", Address, DataBuf[Address]); - - /* if couldn't find an empty spot, we must compact emulated eeprom */ - if (empty_slot >= (uint16_t *)FEE_WRITE_LOG_LAST_ADDRESS) { - /* compact the write log into the compacted flash area */ - return eeprom_compact(); - } - - /* ok we found a place let's write our data */ - FLASH_Unlock(); - - /* Pack address and value into the same word */ - uint16_t value = (Address << 8) | DataBuf[Address]; - - /* write to flash */ - eeprom_printf("FLASH_ProgramHalfWord(0x%08lx, 0x%04x)\n", (uint32_t)empty_slot, value); - FLASH_Status status = FLASH_ProgramHalfWord((uintptr_t)empty_slot++, value); - - FLASH_Lock(); - - return status; -} - -uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte) { - /* if the address is out-of-bounds, do nothing */ - if (Address >= FEE_DENSITY_BYTES) { - eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [BAD ADDRESS]\n", Address, DataByte); - return FLASH_BAD_ADDRESS; - } - - /* if the value is the same, don't bother writing it */ - if (DataBuf[Address] == DataByte) { - eeprom_printf("EEPROM_WriteDataByte(0x%04x, 0x%02x) [SKIP SAME]\n", Address, DataByte); - return 0; - } - - /* keep DataBuf cache in sync */ - DataBuf[Address] = DataByte; - eeprom_printf("EEPROM_WriteDataByte DataBuf[0x%04x] = 0x%02x\n", Address, DataBuf[Address]); - - /* perform the write into flash memory */ - /* First, attempt to write directly into the compacted flash area */ - FLASH_Status status = eeprom_write_direct_entry(Address); - if (!status) { - /* Otherwise append to the write log */ - if (Address < FEE_BYTE_RANGE) { - status = eeprom_write_log_byte_entry(Address); - } else { - status = eeprom_write_log_word_entry(Address & 0xFFFE); - } - } - if (status != 0 && status != FLASH_COMPLETE) { - eeprom_printf("EEPROM_WriteDataByte [STATUS == %d]\n", status); - } - return status; -} - -uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord) { - /* if the address is out-of-bounds, do nothing */ - if (Address >= FEE_DENSITY_BYTES) { - eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [BAD ADDRESS]\n", Address, DataWord); - return FLASH_BAD_ADDRESS; - } - - /* Check for word alignment */ - FLASH_Status final_status = FLASH_COMPLETE; - if (Address % 2) { - final_status = EEPROM_WriteDataByte(Address, DataWord); - FLASH_Status status = EEPROM_WriteDataByte(Address + 1, DataWord >> 8); - if (status != FLASH_COMPLETE) final_status = status; - if (final_status != 0 && final_status != FLASH_COMPLETE) { - eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); - } - return final_status; - } - - /* if the value is the same, don't bother writing it */ - uint16_t oldValue = *(uint16_t *)(&DataBuf[Address]); - if (oldValue == DataWord) { - eeprom_printf("EEPROM_WriteDataWord(0x%04x, 0x%04x) [SKIP SAME]\n", Address, DataWord); - return 0; - } - - /* keep DataBuf cache in sync */ - *(uint16_t *)(&DataBuf[Address]) = DataWord; - eeprom_printf("EEPROM_WriteDataWord DataBuf[0x%04x] = 0x%04x\n", Address, *(uint16_t *)(&DataBuf[Address])); - - /* perform the write into flash memory */ - /* First, attempt to write directly into the compacted flash area */ - final_status = eeprom_write_direct_entry(Address); - if (!final_status) { - /* Otherwise append to the write log */ - /* Check if we need to fall back to byte write */ - if (Address < FEE_BYTE_RANGE) { - final_status = FLASH_COMPLETE; - /* Only write a byte if it has changed */ - if ((uint8_t)oldValue != (uint8_t)DataWord) { - final_status = eeprom_write_log_byte_entry(Address); - } - FLASH_Status status = FLASH_COMPLETE; - /* Only write a byte if it has changed */ - if ((oldValue >> 8) != (DataWord >> 8)) { - status = eeprom_write_log_byte_entry(Address + 1); - } - if (status != FLASH_COMPLETE) final_status = status; - } else { - final_status = eeprom_write_log_word_entry(Address); - } - } - if (final_status != 0 && final_status != FLASH_COMPLETE) { - eeprom_printf("EEPROM_WriteDataWord [STATUS == %d]\n", final_status); - } - return final_status; -} - -uint8_t EEPROM_ReadDataByte(uint16_t Address) { - uint8_t DataByte = 0xFF; - - if (Address < FEE_DENSITY_BYTES) { - DataByte = DataBuf[Address]; - } - - eeprom_printf("EEPROM_ReadDataByte(0x%04x): 0x%02x\n", Address, DataByte); - - return DataByte; -} - -uint16_t EEPROM_ReadDataWord(uint16_t Address) { - uint16_t DataWord = 0xFFFF; - - if (Address < FEE_DENSITY_BYTES - 1) { - /* Check word alignment */ - if (Address % 2) { - DataWord = DataBuf[Address] | (DataBuf[Address + 1] << 8); - } else { - DataWord = *(uint16_t *)(&DataBuf[Address]); - } - } - - eeprom_printf("EEPROM_ReadDataWord(0x%04x): 0x%04x\n", Address, DataWord); - - return DataWord; -} - -/***************************************************************************** - * Bind to eeprom_driver.c - *******************************************************************************/ -void eeprom_driver_init(void) { - EEPROM_Init(); -} - -void eeprom_driver_erase(void) { - EEPROM_Erase(); -} - -void eeprom_read_block(void *buf, const void *addr, size_t len) { - const uint8_t *src = (const uint8_t *)addr; - uint8_t * dest = (uint8_t *)buf; - - /* Check word alignment */ - if (len && (uintptr_t)src % 2) { - /* Read the unaligned first byte */ - *dest++ = EEPROM_ReadDataByte((const uintptr_t)src++); - --len; - } - - uint16_t value; - bool aligned = ((uintptr_t)dest % 2 == 0); - while (len > 1) { - value = EEPROM_ReadDataWord((const uintptr_t)((uint16_t *)src)); - if (aligned) { - *(uint16_t *)dest = value; - dest += 2; - } else { - *dest++ = value; - *dest++ = value >> 8; - } - src += 2; - len -= 2; - } - if (len) { - *dest = EEPROM_ReadDataByte((const uintptr_t)src); - } -} - -void eeprom_write_block(const void *buf, void *addr, size_t len) { - uint8_t * dest = (uint8_t *)addr; - const uint8_t *src = (const uint8_t *)buf; - - /* Check word alignment */ - if (len && (uintptr_t)dest % 2) { - /* Write the unaligned first byte */ - EEPROM_WriteDataByte((uintptr_t)dest++, *src++); - --len; - } - - uint16_t value; - bool aligned = ((uintptr_t)src % 2 == 0); - while (len > 1) { - if (aligned) { - value = *(uint16_t *)src; - } else { - value = *(uint8_t *)src | (*(uint8_t *)(src + 1) << 8); - } - EEPROM_WriteDataWord((uintptr_t)((uint16_t *)dest), value); - dest += 2; - src += 2; - len -= 2; - } - - if (len) { - EEPROM_WriteDataByte((uintptr_t)dest, *src); - } -} diff --git a/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.h b/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.h deleted file mode 100644 index 8fcfb556b8..0000000000 --- a/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * This software is experimental and a work in progress. - * Under no circumstances should these files be used in relation to any critical system(s). - * Use of these files is at your own risk. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * This files are free to use from http://engsta.com/stm32-flash-memory-eeprom-emulator/ by - * Artur F. - * - * Modifications for QMK and STM32F303 by Yiancar - * - * This library assumes 8-bit data locations. To add a new MCU, please provide the flash - * page size and the total flash size in Kb. The number of available pages must be a multiple - * of 2. Only half of the pages account for the total EEPROM size. - * This library also assumes that the pages are not used by the firmware. - */ - -#pragma once - -uint16_t EEPROM_Init(void); -void EEPROM_Erase(void); -uint8_t EEPROM_WriteDataByte(uint16_t Address, uint8_t DataByte); -uint8_t EEPROM_WriteDataWord(uint16_t Address, uint16_t DataWord); -uint8_t EEPROM_ReadDataByte(uint16_t Address); -uint16_t EEPROM_ReadDataWord(uint16_t Address); - -void print_eeprom(void); diff --git a/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash_defs.h b/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash_defs.h deleted file mode 100644 index 57d0440330..0000000000 --- a/platforms/chibios/drivers/eeprom/eeprom_legacy_emulated_flash_defs.h +++ /dev/null @@ -1,136 +0,0 @@ -/* Copyright 2021 QMK - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ -#pragma once - -#include <hal.h> - -#if !defined(FEE_PAGE_SIZE) || !defined(FEE_PAGE_COUNT) -# if defined(STM32F103xB) || defined(STM32F042x6) || defined(GD32VF103C8) || defined(GD32VF103CB) -# ifndef FEE_PAGE_SIZE -# define FEE_PAGE_SIZE 0x400 // Page size = 1KByte -# endif -# ifndef FEE_PAGE_COUNT -# define FEE_PAGE_COUNT 2 // How many pages are used -# endif -# elif defined(STM32F103xE) || defined(STM32F303xC) || defined(STM32F303xE) || defined(STM32F072xB) || defined(STM32F070xB) -# ifndef FEE_PAGE_SIZE -# define FEE_PAGE_SIZE 0x800 // Page size = 2KByte -# endif -# ifndef FEE_PAGE_COUNT -# define FEE_PAGE_COUNT 4 // How many pages are used -# endif -# elif defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE) -# ifndef FEE_PAGE_SIZE -# define FEE_PAGE_SIZE 0x4000 // Page size = 16KByte -# endif -# ifndef FEE_PAGE_COUNT -# define FEE_PAGE_COUNT 1 // How many pages are used -# endif -# endif -#endif - -#if !defined(FEE_MCU_FLASH_SIZE) -# if defined(STM32F042x6) -# define FEE_MCU_FLASH_SIZE 32 // Size in Kb -# elif defined(GD32VF103C8) -# define FEE_MCU_FLASH_SIZE 64 // Size in Kb -# elif defined(STM32F103xB) || defined(STM32F072xB) || defined(STM32F070xB) || defined(GD32VF103CB) -# define FEE_MCU_FLASH_SIZE 128 // Size in Kb -# elif defined(STM32F303xC) || defined(STM32F401xC) -# define FEE_MCU_FLASH_SIZE 256 // Size in Kb -# elif defined(STM32F103xE) || defined(STM32F303xE) || defined(STM32F401xE) || defined(STM32F411xE) -# define FEE_MCU_FLASH_SIZE 512 // Size in Kb -# elif defined(STM32F405xG) -# define FEE_MCU_FLASH_SIZE 1024 // Size in Kb -# endif -#endif - -/* Start of the emulated eeprom */ -#if !defined(FEE_PAGE_BASE_ADDRESS) -# if defined(STM32F401xC) || defined(STM32F401xE) || defined(STM32F405xG) || defined(STM32F411xE) -# ifndef FEE_PAGE_BASE_ADDRESS -# define FEE_PAGE_BASE_ADDRESS 0x08004000 // bodge to force 2nd 16k page -# endif -# else -# ifndef FEE_FLASH_BASE -# define FEE_FLASH_BASE 0x8000000 -# endif -/* Default to end of flash */ -# define FEE_PAGE_BASE_ADDRESS ((uintptr_t)(FEE_FLASH_BASE) + FEE_MCU_FLASH_SIZE * 1024 - (FEE_PAGE_COUNT * FEE_PAGE_SIZE)) -# endif -#endif - -/* Addressable range 16KByte: 0 <-> (0x1FFF << 1) */ -#define FEE_ADDRESS_MAX_SIZE 0x4000 - -/* Size of combined compacted eeprom and write log pages */ -#define FEE_DENSITY_MAX_SIZE (FEE_PAGE_COUNT * FEE_PAGE_SIZE) - -#ifndef FEE_MCU_FLASH_SIZE_IGNORE_CHECK /* *TODO: Get rid of this check */ -# if FEE_DENSITY_MAX_SIZE > (FEE_MCU_FLASH_SIZE * 1024) -# pragma message STR(FEE_DENSITY_MAX_SIZE) " > " STR(FEE_MCU_FLASH_SIZE * 1024) -# error emulated eeprom: FEE_DENSITY_MAX_SIZE is greater than available flash size -# endif -#endif - -/* Size of emulated eeprom */ -#ifdef FEE_DENSITY_BYTES -# if (FEE_DENSITY_BYTES > FEE_DENSITY_MAX_SIZE) -# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE) -# error emulated eeprom: FEE_DENSITY_BYTES exceeds FEE_DENSITY_MAX_SIZE -# endif -# if (FEE_DENSITY_BYTES == FEE_DENSITY_MAX_SIZE) -# pragma message STR(FEE_DENSITY_BYTES) " == " STR(FEE_DENSITY_MAX_SIZE) -# warning emulated eeprom: FEE_DENSITY_BYTES leaves no room for a write log. This will greatly increase the flash wear rate! -# endif -# if FEE_DENSITY_BYTES > FEE_ADDRESS_MAX_SIZE -# pragma message STR(FEE_DENSITY_BYTES) " > " STR(FEE_ADDRESS_MAX_SIZE) -# error emulated eeprom: FEE_DENSITY_BYTES is greater than FEE_ADDRESS_MAX_SIZE allows -# endif -# if ((FEE_DENSITY_BYTES) % 2) == 1 -# error emulated eeprom: FEE_DENSITY_BYTES must be even -# endif -#else -/* Default to half of allocated space used for emulated eeprom, half for write log */ -# define FEE_DENSITY_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE / 2) -#endif - -/* Size of write log */ -#ifdef FEE_WRITE_LOG_BYTES -# if ((FEE_DENSITY_BYTES + FEE_WRITE_LOG_BYTES) > FEE_DENSITY_MAX_SIZE) -# pragma message STR(FEE_DENSITY_BYTES) " + " STR(FEE_WRITE_LOG_BYTES) " > " STR(FEE_DENSITY_MAX_SIZE) -# error emulated eeprom: FEE_WRITE_LOG_BYTES exceeds remaining FEE_DENSITY_MAX_SIZE -# endif -# if ((FEE_WRITE_LOG_BYTES) % 2) == 1 -# error emulated eeprom: FEE_WRITE_LOG_BYTES must be even -# endif -#else -/* Default to use all remaining space */ -# define FEE_WRITE_LOG_BYTES (FEE_PAGE_COUNT * FEE_PAGE_SIZE - FEE_DENSITY_BYTES) -#endif - -/* Start of the emulated eeprom compacted flash area */ -#define FEE_COMPACTED_BASE_ADDRESS FEE_PAGE_BASE_ADDRESS -/* End of the emulated eeprom compacted flash area */ -#define FEE_COMPACTED_LAST_ADDRESS (FEE_COMPACTED_BASE_ADDRESS + FEE_DENSITY_BYTES) -/* Start of the emulated eeprom write log */ -#define FEE_WRITE_LOG_BASE_ADDRESS FEE_COMPACTED_LAST_ADDRESS -/* End of the emulated eeprom write log */ -#define FEE_WRITE_LOG_LAST_ADDRESS (FEE_WRITE_LOG_BASE_ADDRESS + FEE_WRITE_LOG_BYTES) - -#if defined(DYNAMIC_KEYMAP_EEPROM_MAX_ADDR) && (DYNAMIC_KEYMAP_EEPROM_MAX_ADDR >= FEE_DENSITY_BYTES) -# error emulated eeprom: DYNAMIC_KEYMAP_EEPROM_MAX_ADDR is greater than the FEE_DENSITY_BYTES available -#endif diff --git a/platforms/chibios/drivers/eeprom/eeprom_stm32_L0_L1.c b/platforms/chibios/drivers/eeprom/eeprom_stm32_L0_L1.c deleted file mode 100644 index ed26cc7145..0000000000 --- a/platforms/chibios/drivers/eeprom/eeprom_stm32_L0_L1.c +++ /dev/null @@ -1,96 +0,0 @@ -/* Copyright 2020 Nick Brassel (tzarc) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <stdint.h> -#include <string.h> - -#include <hal.h> -#include "eeprom_driver.h" -#include "eeprom_stm32_L0_L1.h" - -#define EEPROM_BASE_ADDR 0x08080000 -#define EEPROM_ADDR(offset) (EEPROM_BASE_ADDR + (offset)) -#define EEPROM_PTR(offset) ((__IO uint8_t *)EEPROM_ADDR(offset)) -#define EEPROM_BYTE(location, offset) (*(EEPROM_PTR(((uint32_t)location) + ((uint32_t)offset)))) - -#define BUFFER_BYTE(buffer, offset) (*(((uint8_t *)buffer) + offset)) - -#define FLASH_PEKEY1 0x89ABCDEF -#define FLASH_PEKEY2 0x02030405 - -static inline void STM32_L0_L1_EEPROM_WaitNotBusy(void) { - while (FLASH->SR & FLASH_SR_BSY) { - __WFI(); - } -} - -static inline void STM32_L0_L1_EEPROM_Unlock(void) { - STM32_L0_L1_EEPROM_WaitNotBusy(); - if (FLASH->PECR & FLASH_PECR_PELOCK) { - FLASH->PEKEYR = FLASH_PEKEY1; - FLASH->PEKEYR = FLASH_PEKEY2; - } -} - -static inline void STM32_L0_L1_EEPROM_Lock(void) { - STM32_L0_L1_EEPROM_WaitNotBusy(); - FLASH->PECR |= FLASH_PECR_PELOCK; -} - -void eeprom_driver_init(void) {} - -void eeprom_driver_erase(void) { - STM32_L0_L1_EEPROM_Unlock(); - - for (size_t offset = 0; offset < STM32_ONBOARD_EEPROM_SIZE; offset += sizeof(uint32_t)) { - FLASH->PECR |= FLASH_PECR_ERASE | FLASH_PECR_DATA; - - *(__IO uint32_t *)EEPROM_ADDR(offset) = (uint32_t)0; - - STM32_L0_L1_EEPROM_WaitNotBusy(); - FLASH->PECR &= ~(FLASH_PECR_ERASE | FLASH_PECR_DATA); - } - - STM32_L0_L1_EEPROM_Lock(); -} - -void eeprom_read_block(void *buf, const void *addr, size_t len) { - for (size_t offset = 0; offset < len; ++offset) { - // Drop out if we've hit the limit of the EEPROM - if ((((uint32_t)addr) + offset) >= STM32_ONBOARD_EEPROM_SIZE) { - break; - } - - STM32_L0_L1_EEPROM_WaitNotBusy(); - BUFFER_BYTE(buf, offset) = EEPROM_BYTE(addr, offset); - } -} - -void eeprom_write_block(const void *buf, void *addr, size_t len) { - STM32_L0_L1_EEPROM_Unlock(); - - for (size_t offset = 0; offset < len; ++offset) { - // Drop out if we've hit the limit of the EEPROM - if ((((uint32_t)addr) + offset) >= STM32_ONBOARD_EEPROM_SIZE) { - break; - } - - STM32_L0_L1_EEPROM_WaitNotBusy(); - EEPROM_BYTE(addr, offset) = BUFFER_BYTE(buf, offset); - } - - STM32_L0_L1_EEPROM_Lock(); -} diff --git a/platforms/chibios/drivers/eeprom/eeprom_stm32_L0_L1.h b/platforms/chibios/drivers/eeprom/eeprom_stm32_L0_L1.h deleted file mode 100644 index 616d7ccbee..0000000000 --- a/platforms/chibios/drivers/eeprom/eeprom_stm32_L0_L1.h +++ /dev/null @@ -1,33 +0,0 @@ -/* Copyright 2020 Nick Brassel (tzarc) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#pragma once - -/* - The size used by the STM32 L0/L1 EEPROM driver. -*/ -#ifndef STM32_ONBOARD_EEPROM_SIZE -# ifdef VIA_ENABLE -# define STM32_ONBOARD_EEPROM_SIZE 1024 -# else -# include "eeconfig.h" -# define STM32_ONBOARD_EEPROM_SIZE (((EECONFIG_SIZE + 3) / 4) * 4) // based off eeconfig's current usage, aligned to 4-byte sizes, to deal with LTO and EEPROM page sizing -# endif -#endif - -#if STM32_ONBOARD_EEPROM_SIZE > 128 -# pragma message("Please note: resetting EEPROM using an STM32L0/L1 device takes up to 1 second for every 1kB of internal EEPROM used.") -#endif diff --git a/platforms/chibios/drivers/flash/legacy_flash_ops.c b/platforms/chibios/drivers/flash/legacy_flash_ops.c deleted file mode 100644 index fe5ad64764..0000000000 --- a/platforms/chibios/drivers/flash/legacy_flash_ops.c +++ /dev/null @@ -1,208 +0,0 @@ -/* - * This software is experimental and a work in progress. - * Under no circumstances should these files be used in relation to any critical system(s). - * Use of these files is at your own risk. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and - * https://github.com/leaflabs/libmaple - * - * Modifications for QMK and STM32F303 by Yiancar - */ - -#include <hal.h> -#include "legacy_flash_ops.h" - -#if defined(STM32F1XX) -# define FLASH_SR_WRPERR FLASH_SR_WRPRTERR -#endif - -#if defined(MCU_GD32V) -/* GigaDevice GD32VF103 is a STM32F103 clone at heart. */ -# include "gd32v_compatibility.h" -#endif - -#if defined(STM32F4XX) -# define FLASH_SR_PGERR (FLASH_SR_PGSERR | FLASH_SR_PGPERR | FLASH_SR_PGAERR) - -# define FLASH_KEY1 0x45670123U -# define FLASH_KEY2 0xCDEF89ABU - -static uint8_t ADDR2PAGE(uint32_t Page_Address) { - switch (Page_Address) { - case 0x08000000 ... 0x08003FFF: - return 0; - case 0x08004000 ... 0x08007FFF: - return 1; - case 0x08008000 ... 0x0800BFFF: - return 2; - case 0x0800C000 ... 0x0800FFFF: - return 3; - } - - // TODO: bad times... - return 7; -} -#endif - -/* Delay definition */ -#define EraseTimeout ((uint32_t)0x00000FFF) -#define ProgramTimeout ((uint32_t)0x0000001F) - -#define ASSERT(exp) (void)((0)) - -/** - * @brief Inserts a time delay. - * @param None - * @retval None - */ -static void delay(void) { - __IO uint32_t i = 0; - for (i = 0xFF; i != 0; i--) { - } -} - -/** - * @brief Returns the FLASH Status. - * @param None - * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, - * FLASH_ERROR_WRP or FLASH_COMPLETE - */ -FLASH_Status FLASH_GetStatus(void) { - if ((FLASH->SR & FLASH_SR_BSY) == FLASH_SR_BSY) return FLASH_BUSY; - - if ((FLASH->SR & FLASH_SR_PGERR) != 0) return FLASH_ERROR_PG; - - if ((FLASH->SR & FLASH_SR_WRPERR) != 0) return FLASH_ERROR_WRP; - -#if defined(FLASH_OBR_OPTERR) - if ((FLASH->SR & FLASH_OBR_OPTERR) != 0) return FLASH_ERROR_OPT; -#endif - - return FLASH_COMPLETE; -} - -/** - * @brief Waits for a Flash operation to complete or a TIMEOUT to occur. - * @param Timeout: FLASH progamming Timeout - * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, - * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. - */ -FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout) { - FLASH_Status status; - - /* Check for the Flash Status */ - status = FLASH_GetStatus(); - /* Wait for a Flash operation to complete or a TIMEOUT to occur */ - while ((status == FLASH_BUSY) && (Timeout != 0x00)) { - delay(); - status = FLASH_GetStatus(); - Timeout--; - } - if (Timeout == 0) status = FLASH_TIMEOUT; - /* Return the operation status */ - return status; -} - -/** - * @brief Erases a specified FLASH page. - * @param Page_Address: The page address to be erased. - * @retval FLASH Status: The returned value can be: FLASH_BUSY, FLASH_ERROR_PG, - * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. - */ -FLASH_Status FLASH_ErasePage(uint32_t Page_Address) { - FLASH_Status status = FLASH_COMPLETE; - /* Check the parameters */ - ASSERT(IS_FLASH_ADDRESS(Page_Address)); - /* Wait for last operation to be completed */ - status = FLASH_WaitForLastOperation(EraseTimeout); - - if (status == FLASH_COMPLETE) { - /* if the previous operation is completed, proceed to erase the page */ -#if defined(FLASH_CR_SNB) - FLASH->CR &= ~FLASH_CR_SNB; - FLASH->CR |= FLASH_CR_SER | (ADDR2PAGE(Page_Address) << FLASH_CR_SNB_Pos); -#else - FLASH->CR |= FLASH_CR_PER; - FLASH->AR = Page_Address; -#endif - FLASH->CR |= FLASH_CR_STRT; - - /* Wait for last operation to be completed */ - status = FLASH_WaitForLastOperation(EraseTimeout); - if (status != FLASH_TIMEOUT) { - /* if the erase operation is completed, disable the configured Bits */ -#if defined(FLASH_CR_SNB) - FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_SNB); -#else - FLASH->CR &= ~FLASH_CR_PER; -#endif - } - FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR); - } - /* Return the Erase Status */ - return status; -} - -/** - * @brief Programs a half word at a specified address. - * @param Address: specifies the address to be programmed. - * @param Data: specifies the data to be programmed. - * @retval FLASH Status: The returned value can be: FLASH_ERROR_PG, - * FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT. - */ -FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) { - FLASH_Status status = FLASH_BAD_ADDRESS; - - if (IS_FLASH_ADDRESS(Address)) { - /* Wait for last operation to be completed */ - status = FLASH_WaitForLastOperation(ProgramTimeout); - if (status == FLASH_COMPLETE) { - /* if the previous operation is completed, proceed to program the new data */ - -#if defined(FLASH_CR_PSIZE) - FLASH->CR &= ~FLASH_CR_PSIZE; - FLASH->CR |= FLASH_CR_PSIZE_0; -#endif - FLASH->CR |= FLASH_CR_PG; - *(__IO uint16_t*)Address = Data; - /* Wait for last operation to be completed */ - status = FLASH_WaitForLastOperation(ProgramTimeout); - if (status != FLASH_TIMEOUT) { - /* if the program operation is completed, disable the PG Bit */ - FLASH->CR &= ~FLASH_CR_PG; - } - FLASH->SR = (FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPERR); - } - } - return status; -} - -/** - * @brief Unlocks the FLASH Program Erase Controller. - * @param None - * @retval None - */ -void FLASH_Unlock(void) { - if (FLASH->CR & FLASH_CR_LOCK) { - /* Authorize the FPEC Access */ - FLASH->KEYR = FLASH_KEY1; - FLASH->KEYR = FLASH_KEY2; - } -} - -/** - * @brief Locks the FLASH Program Erase Controller. - * @param None - * @retval None - */ -void FLASH_Lock(void) { - /* Set the Lock Bit to lock the FPEC and the FCR */ - FLASH->CR |= FLASH_CR_LOCK; -} diff --git a/platforms/chibios/drivers/flash/legacy_flash_ops.h b/platforms/chibios/drivers/flash/legacy_flash_ops.h deleted file mode 100644 index ef80764055..0000000000 --- a/platforms/chibios/drivers/flash/legacy_flash_ops.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This software is experimental and a work in progress. - * Under no circumstances should these files be used in relation to any critical system(s). - * Use of these files is at your own risk. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, - * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR - * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * This files are free to use from https://github.com/rogerclarkmelbourne/Arduino_STM32 and - * https://github.com/leaflabs/libmaple - * - * Modifications for QMK and STM32F303 by Yiancar - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include <stdint.h> - -#ifdef LEGACY_FLASH_OPS_MOCKED -extern uint8_t FlashBuf[MOCK_FLASH_SIZE]; -#endif - -typedef enum { FLASH_BUSY = 1, FLASH_ERROR_PG, FLASH_ERROR_WRP, FLASH_ERROR_OPT, FLASH_COMPLETE, FLASH_TIMEOUT, FLASH_BAD_ADDRESS } FLASH_Status; - -#define IS_FLASH_ADDRESS(ADDRESS) (((ADDRESS) >= 0x08000000) && ((ADDRESS) < 0x0807FFFF)) - -FLASH_Status FLASH_WaitForLastOperation(uint32_t Timeout); -FLASH_Status FLASH_ErasePage(uint32_t Page_Address); -FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data); - -void FLASH_Unlock(void); -void FLASH_Lock(void); - -#ifdef __cplusplus -} -#endif diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c deleted file mode 100644 index 7c49f9d005..0000000000 --- a/platforms/chibios/drivers/i2c_master.c +++ /dev/null @@ -1,219 +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 <http://www.gnu.org/licenses/>. - */ - -/* 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 "i2c_master.h" -#include "gpio.h" -#include "chibios_config.h" -#include <string.h> -#include <ch.h> -#include <hal.h> - -#ifndef I2C1_SCL_PIN -# define I2C1_SCL_PIN B6 -#endif -#ifndef I2C1_SDA_PIN -# define I2C1_SDA_PIN B7 -#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 - -#ifdef USE_GPIOV1 -# ifndef I2C1_SCL_PAL_MODE -# define I2C1_SCL_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN -# endif -# ifndef I2C1_SDA_PAL_MODE -# define I2C1_SDA_PAL_MODE PAL_MODE_ALTERNATE_OPENDRAIN -# endif -#else -// 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 - -static uint8_t i2c_address; - -static const I2CConfig i2cconfig = { -#if defined(USE_I2CV1_CONTRIB) - I2C1_CLOCK_SPEED, -#elif defined(USE_I2CV1) - I2C1_OPMODE, - I2C1_CLOCK_SPEED, - I2C1_DUTY_CYCLE, -#elif defined(WB32F3G71xx) || defined(WB32FQ95xx) - I2C1_OPMODE, - I2C1_CLOCK_SPEED, -#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 -}; - -/** - * @brief Handles any I2C error condition by stopping the I2C peripheral and - * aborting any ongoing transactions. Furthermore ChibiOS status codes are - * converted into QMK codes. - * - * @param status ChibiOS specific I2C status code - * @return i2c_status_t QMK specific I2C status code - */ -static i2c_status_t i2c_epilogue(const msg_t status) { - if (status == MSG_OK) { - return I2C_STATUS_SUCCESS; - } - - // From ChibiOS HAL: "After a timeout the driver must be stopped and - // restarted because the bus is in an uncertain state." We also issue that - // hard stop in case of any error. - i2c_stop(); - - return status == MSG_TIMEOUT ? I2C_STATUS_TIMEOUT : I2C_STATUS_ERROR; -} - -__attribute__((weak)) void i2c_init(void) { - static bool is_initialised = false; - if (!is_initialised) { - is_initialised = true; - - // Try releasing special pins for a short time - palSetLineMode(I2C1_SCL_PIN, PAL_MODE_INPUT); - palSetLineMode(I2C1_SDA_PIN, PAL_MODE_INPUT); - - chThdSleepMilliseconds(10); -#if defined(USE_GPIOV1) - palSetLineMode(I2C1_SCL_PIN, I2C1_SCL_PAL_MODE); - palSetLineMode(I2C1_SDA_PIN, I2C1_SDA_PAL_MODE); -#else - palSetLineMode(I2C1_SCL_PIN, PAL_MODE_ALTERNATE(I2C1_SCL_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN); - palSetLineMode(I2C1_SDA_PIN, PAL_MODE_ALTERNATE(I2C1_SDA_PAL_MODE) | PAL_OUTPUT_TYPE_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 i2c_epilogue(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 i2c_epilogue(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 (uint16_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 i2c_epilogue(status); -} - -i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { - i2c_address = devaddr; - i2cStart(&I2C_DRIVER, &i2cconfig); - - uint8_t complete_packet[length + 2]; - for (uint16_t i = 0; i < length; i++) { - complete_packet[i + 2] = data[i]; - } - complete_packet[0] = regaddr >> 8; - complete_packet[1] = regaddr & 0xFF; - - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 2, 0, 0, TIME_MS2I(timeout)); - return i2c_epilogue(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 i2c_epilogue(status); -} - -i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { - i2c_address = devaddr; - i2cStart(&I2C_DRIVER, &i2cconfig); - uint8_t register_packet[2] = {regaddr >> 8, regaddr & 0xFF}; - msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), register_packet, 2, data, length, TIME_MS2I(timeout)); - return i2c_epilogue(status); -} - -void i2c_stop(void) { - i2cStop(&I2C_DRIVER); -} diff --git a/platforms/chibios/drivers/i2c_master.h b/platforms/chibios/drivers/i2c_master.h deleted file mode 100644 index deee7ecc08..0000000000 --- a/platforms/chibios/drivers/i2c_master.h +++ /dev/null @@ -1,43 +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 <http://www.gnu.org/licenses/>. - */ - -/* 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 <stdint.h> - -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_writeReg16(uint8_t devaddr, uint16_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); -i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout); -void i2c_stop(void); diff --git a/platforms/chibios/drivers/ps2/ps2_io.c b/platforms/chibios/drivers/ps2/ps2_io.c deleted file mode 100644 index 9eb56d63da..0000000000 --- a/platforms/chibios/drivers/ps2/ps2_io.c +++ /dev/null @@ -1,56 +0,0 @@ -#include <stdbool.h> -#include "ps2_io.h" - -// chibiOS headers -#include "ch.h" -#include "hal.h" -#include "gpio.h" - -/* Check port settings for clock and data line */ -#if !(defined(PS2_CLOCK_PIN)) -# error "PS/2 clock setting is required in config.h" -#endif - -#if !(defined(PS2_DATA_PIN)) -# error "PS/2 data setting is required in config.h" -#endif - -/* - * Clock - */ -void clock_init(void) {} - -void clock_lo(void) { - palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_OUTPUT_OPENDRAIN); - palWriteLine(PS2_CLOCK_PIN, PAL_LOW); -} - -void clock_hi(void) { - palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_OUTPUT_OPENDRAIN); - palWriteLine(PS2_CLOCK_PIN, PAL_HIGH); -} - -bool clock_in(void) { - palSetLineMode(PS2_CLOCK_PIN, PAL_MODE_INPUT); - return palReadLine(PS2_CLOCK_PIN); -} - -/* - * Data - */ -void data_init(void) {} - -void data_lo(void) { - palSetLineMode(PS2_DATA_PIN, PAL_MODE_OUTPUT_OPENDRAIN); - palWriteLine(PS2_DATA_PIN, PAL_LOW); -} - -void data_hi(void) { - palSetLineMode(PS2_DATA_PIN, PAL_MODE_OUTPUT_OPENDRAIN); - palWriteLine(PS2_DATA_PIN, PAL_HIGH); -} - -bool data_in(void) { - palSetLineMode(PS2_DATA_PIN, PAL_MODE_INPUT); - return palReadLine(PS2_DATA_PIN); -} diff --git a/platforms/chibios/drivers/serial.c b/platforms/chibios/drivers/serial.c deleted file mode 100644 index f087d0c2ed..0000000000 --- a/platforms/chibios/drivers/serial.c +++ /dev/null @@ -1,297 +0,0 @@ -/* - * WARNING: be careful changing this code, it is very timing dependent - */ - -#include "serial.h" -#include "gpio.h" -#include "wait.h" -#include "synchronization_util.h" - -#include <hal.h> - -// TODO: resolve/remove build warnings -#if defined(RGBLIGHT_ENABLE) && defined(RGBLED_SPLIT) && defined(PROTOCOL_CHIBIOS) && defined(WS2812_DRIVER_BITBANG) -# warning "RGBLED_SPLIT not supported with bitbang WS2812 driver" -#endif - -// default wait implementation cannot be called within interrupt -// this method seems to be more accurate than GPT timers -#if PORT_SUPPORTS_RT == FALSE -# error "chSysPolledDelayX method not supported on this platform" -#else -# undef wait_us -// Force usage of polled waiting - in case WAIT_US_TIMER is activated -# define wait_us(us) chSysPolledDelayX(US2RTC(REALTIME_COUNTER_CLOCK, us)) -#endif - -#ifndef SELECT_SOFT_SERIAL_SPEED -# define SELECT_SOFT_SERIAL_SPEED 1 -// TODO: correct speeds... -// 0: about 189kbps (Experimental only) -// 1: about 137kbps (default) -// 2: about 75kbps -// 3: about 39kbps -// 4: about 26kbps -// 5: about 20kbps -#endif - -// Serial pulse period in microseconds. At the moment, going lower than 12 causes communication failure -#if SELECT_SOFT_SERIAL_SPEED == 0 -# define SERIAL_DELAY 12 -#elif SELECT_SOFT_SERIAL_SPEED == 1 -# define SERIAL_DELAY 16 -#elif SELECT_SOFT_SERIAL_SPEED == 2 -# define SERIAL_DELAY 24 -#elif SELECT_SOFT_SERIAL_SPEED == 3 -# define SERIAL_DELAY 32 -#elif SELECT_SOFT_SERIAL_SPEED == 4 -# define SERIAL_DELAY 48 -#elif SELECT_SOFT_SERIAL_SPEED == 5 -# define SERIAL_DELAY 64 -#else -# error invalid SELECT_SOFT_SERIAL_SPEED value -#endif - -inline static void serial_delay(void) { - wait_us(SERIAL_DELAY); -} -inline static void serial_delay_half(void) { - wait_us(SERIAL_DELAY / 2); -} -inline static void serial_delay_blip(void) { - wait_us(1); -} -inline static void serial_output(void) { - setPinOutput(SOFT_SERIAL_PIN); -} -inline static void serial_input(void) { - setPinInputHigh(SOFT_SERIAL_PIN); -} -inline static bool serial_read_pin(void) { - return !!readPin(SOFT_SERIAL_PIN); -} -inline static void serial_low(void) { - writePinLow(SOFT_SERIAL_PIN); -} -inline static void serial_high(void) { - writePinHigh(SOFT_SERIAL_PIN); -} - -void interrupt_handler(void *arg); - -// Use thread + palWaitLineTimeout instead of palSetLineCallback -// - Methods like setPinOutput and palEnableLineEvent/palDisableLineEvent -// cause the interrupt to lock up, which would limit to only receiving data... -static THD_WORKING_AREA(waThread1, 128); -static THD_FUNCTION(Thread1, arg) { - (void)arg; - chRegSetThreadName("blinker"); - while (true) { - palWaitLineTimeout(SOFT_SERIAL_PIN, TIME_INFINITE); - interrupt_handler(NULL); - } -} - -void soft_serial_initiator_init(void) { - serial_output(); - serial_high(); -} - -void soft_serial_target_init(void) { - serial_input(); - - palEnablePadEvent(PAL_PORT(SOFT_SERIAL_PIN), PAL_PAD(SOFT_SERIAL_PIN), PAL_EVENT_MODE_FALLING_EDGE); - chThdCreateStatic(waThread1, sizeof(waThread1), HIGHPRIO, Thread1, NULL); -} - -// Used by the master to synchronize timing with the slave. -static void __attribute__((noinline)) sync_recv(void) { - serial_input(); - // This shouldn't hang if the slave disconnects because the - // serial line will float to high if the slave does disconnect. - while (!serial_read_pin()) { - } - - serial_delay(); -} - -// Used by the slave to send a synchronization signal to the master. -static void __attribute__((noinline)) sync_send(void) { - serial_output(); - - serial_low(); - serial_delay(); - - serial_high(); -} - -// Reads a byte from the serial line -static uint8_t __attribute__((noinline)) serial_read_byte(void) { - uint8_t byte = 0; - serial_input(); - for (uint8_t i = 0; i < 8; ++i) { - byte = (byte << 1) | serial_read_pin(); - serial_delay(); - } - - return byte; -} - -// Sends a byte with MSB ordering -static void __attribute__((noinline)) serial_write_byte(uint8_t data) { - uint8_t b = 8; - serial_output(); - while (b--) { - if (data & (1 << b)) { - serial_high(); - } else { - serial_low(); - } - serial_delay(); - } -} - -// interrupt handle to be used by the slave device -void interrupt_handler(void *arg) { - split_shared_memory_lock_autounlock(); - chSysLockFromISR(); - - sync_send(); - - // read mid pulses - serial_delay_blip(); - - uint8_t checksum_computed = 0; - int sstd_index = 0; - - sstd_index = serial_read_byte(); - sync_send(); - - split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; - for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { - split_trans_initiator2target_buffer(trans)[i] = serial_read_byte(); - sync_send(); - checksum_computed += split_trans_initiator2target_buffer(trans)[i]; - } - checksum_computed ^= 7; - - serial_read_byte(); - sync_send(); - - // wait for the sync to finish sending - serial_delay(); - - // Allow any slave processing to occur - if (trans->slave_callback) { - trans->slave_callback(trans->initiator2target_buffer_size, split_trans_initiator2target_buffer(trans), trans->target2initiator_buffer_size, split_trans_target2initiator_buffer(trans)); - } - - uint8_t checksum = 0; - for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { - serial_write_byte(split_trans_target2initiator_buffer(trans)[i]); - sync_send(); - serial_delay_half(); - checksum += split_trans_target2initiator_buffer(trans)[i]; - } - serial_write_byte(checksum ^ 7); - sync_send(); - - // wait for the sync to finish sending - serial_delay(); - - // end transaction - serial_input(); - - // TODO: remove extra delay between transactions - serial_delay(); - - chSysUnlockFromISR(); -} - -static inline bool initiate_transaction(uint8_t sstd_index) { - if (sstd_index > NUM_TOTAL_TRANSACTIONS) return false; - - split_shared_memory_lock_autounlock(); - - split_transaction_desc_t *trans = &split_transaction_table[sstd_index]; - - // TODO: remove extra delay between transactions - serial_delay(); - - // this code is very time dependent, so we need to disable interrupts - chSysLock(); - - // signal to the slave that we want to start a transaction - serial_output(); - serial_low(); - serial_delay_blip(); - - // wait for the slaves response - serial_input(); - serial_high(); - serial_delay(); - - // check if the slave is present - if (serial_read_pin()) { - // slave failed to pull the line low, assume not present - serial_dprintf("serial::NO_RESPONSE\n"); - chSysUnlock(); - return false; - } - - // if the slave is present synchronize with it - uint8_t checksum = 0; - // send data to the slave - serial_write_byte(sstd_index); // first chunk is transaction id - sync_recv(); - - for (int i = 0; i < trans->initiator2target_buffer_size; ++i) { - serial_write_byte(split_trans_initiator2target_buffer(trans)[i]); - sync_recv(); - checksum += split_trans_initiator2target_buffer(trans)[i]; - } - serial_write_byte(checksum ^ 7); - sync_recv(); - - serial_delay(); - serial_delay(); // read mid pulses - - // receive data from the slave - uint8_t checksum_computed = 0; - for (int i = 0; i < trans->target2initiator_buffer_size; ++i) { - split_trans_target2initiator_buffer(trans)[i] = serial_read_byte(); - sync_recv(); - checksum_computed += split_trans_target2initiator_buffer(trans)[i]; - } - checksum_computed ^= 7; - uint8_t checksum_received = serial_read_byte(); - - sync_recv(); - serial_delay(); - - if ((checksum_computed) != (checksum_received)) { - serial_dprintf("serial::FAIL[%u,%u,%u]\n", checksum_computed, checksum_received, sstd_index); - serial_output(); - serial_high(); - - chSysUnlock(); - return false; - } - - // always, release the line when not in use - serial_high(); - serial_output(); - - chSysUnlock(); - return true; -} - -///////// -// start transaction by initiator -// -// bool soft_serial_transaction(int sstd_index) -// -// this code is very time dependent, so we need to disable interrupts -bool soft_serial_transaction(int sstd_index) { - return initiate_transaction((uint8_t)sstd_index); -} diff --git a/platforms/chibios/drivers/serial_protocol.c b/platforms/chibios/drivers/serial_protocol.c deleted file mode 100644 index e0f583ccde..0000000000 --- a/platforms/chibios/drivers/serial_protocol.c +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2022 Stefan Kerkmann -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <ch.h> - -#include "serial.h" -#include "serial_protocol.h" -#include "synchronization_util.h" - -static inline bool initiate_transaction(uint8_t transaction_id); -static inline bool react_to_transaction(void); - -/** - * @brief This thread runs on the slave and responds to transactions initiated - * by the master. - */ -static THD_WORKING_AREA(waSlaveThread, 1024); -static THD_FUNCTION(SlaveThread, arg) { - (void)arg; - chRegSetThreadName("split_protocol_tx_rx"); - - while (true) { - if (unlikely(!react_to_transaction())) { - /* Clear the receive queue, to start with a clean slate. - * Parts of failed transactions or spurious bytes could still be in it. */ - serial_transport_driver_clear(); - } - } -} - -/** - * @brief Slave specific initializations. - */ -void soft_serial_target_init(void) { - serial_transport_driver_slave_init(); - - /* Start transport thread. */ - chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL); -} - -/** - * @brief Master specific initializations. - */ -void soft_serial_initiator_init(void) { - serial_transport_driver_master_init(); -} - -/** - * @brief React to transactions started by the master. - */ -static inline bool react_to_transaction(void) { - uint8_t transaction_id = 0; - /* Wait until there is a transaction for us. */ - if (unlikely(!serial_transport_receive_blocking(&transaction_id, sizeof(transaction_id)))) { - return false; - } - - /* Sanity check that we are actually responding to a valid transaction. */ - if (unlikely(transaction_id >= NUM_TOTAL_TRANSACTIONS)) { - return false; - } - - split_shared_memory_lock_autounlock(); - - split_transaction_desc_t* transaction = &split_transaction_table[transaction_id]; - - /* Send back the handshake which is XORed as a simple checksum, - to signal that the slave is ready to receive possible transaction buffers */ - transaction_id ^= NUM_TOTAL_TRANSACTIONS; - if (unlikely(!serial_transport_send(&transaction_id, sizeof(transaction_id)))) { - return false; - } - - /* Receive transaction buffer from the master. If this transaction requires it.*/ - if (transaction->initiator2target_buffer_size) { - if (unlikely(!serial_transport_receive(split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size))) { - return false; - } - } - - /* Allow any slave processing to occur. */ - if (transaction->slave_callback) { - transaction->slave_callback(transaction->initiator2target_buffer_size, split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size, split_trans_target2initiator_buffer(transaction)); - } - - /* Send transaction buffer to the master. If this transaction requires it. */ - if (transaction->target2initiator_buffer_size) { - if (unlikely(!serial_transport_send(split_trans_target2initiator_buffer(transaction), transaction->target2initiator_buffer_size))) { - return false; - } - } - - return true; -} - -/** - * @brief Start transaction from the master half to the slave half. - * - * @param index Transaction Table index of the transaction to start. - * @return bool Indicates success of transaction. - */ -bool soft_serial_transaction(int index) { - /* Clear the receive queue, to start with a clean slate. - * Parts of failed transactions or spurious bytes could still be in it. */ - serial_transport_driver_clear(); - - return initiate_transaction((uint8_t)index); -} - -/** - * @brief Initiate transaction to slave half. - */ -static inline bool initiate_transaction(uint8_t transaction_id) { - /* Sanity check that we are actually starting a valid transaction. */ - if (unlikely(transaction_id >= NUM_TOTAL_TRANSACTIONS)) { - serial_dprintf("SPLIT: illegal transaction id\n"); - return false; - } - - split_shared_memory_lock_autounlock(); - - split_transaction_desc_t* transaction = &split_transaction_table[transaction_id]; - - /* Send transaction table index to the slave, which doubles as basic handshake token. */ - if (unlikely(!serial_transport_send(&transaction_id, sizeof(transaction_id)))) { - serial_dprintf("SPLIT: sending handshake failed\n"); - return false; - } - - uint8_t transaction_id_shake = 0xFF; - - /* Which we always read back first so that we can error out correctly. - * - due to the half duplex limitations on return codes, we always have to read *something*. - * - without the read, write only transactions *always* succeed, even during the boot process where the slave is not ready. - */ - if (unlikely(!serial_transport_receive(&transaction_id_shake, sizeof(transaction_id_shake)) || (transaction_id_shake != (transaction_id ^ NUM_TOTAL_TRANSACTIONS)))) { - serial_dprintf("SPLIT: receiving handshake failed\n"); - return false; - } - - /* Send transaction buffer to the slave. If this transaction requires it. */ - if (transaction->initiator2target_buffer_size) { - if (unlikely(!serial_transport_send(split_trans_initiator2target_buffer(transaction), transaction->initiator2target_buffer_size))) { - serial_dprintf("SPLIT: sending buffer failed\n"); - return false; - } - } - - /* Receive transaction buffer from the slave. If this transaction requires it. */ - if (transaction->target2initiator_buffer_size) { - if (unlikely(!serial_transport_receive(split_trans_target2initiator_buffer(transaction), transaction->target2initiator_buffer_size))) { - serial_dprintf("SPLIT: receiving buffer failed\n"); - return false; - } - } - - return true; -} diff --git a/platforms/chibios/drivers/serial_protocol.h b/platforms/chibios/drivers/serial_protocol.h deleted file mode 100644 index 4275a7f8d8..0000000000 --- a/platforms/chibios/drivers/serial_protocol.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2022 Stefan Kerkmann -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <stddef.h> -#include <stdint.h> -#include <stdbool.h> - -#pragma once - -/** - * @brief Clears any intermediate sending or receiving state of the driver to a known good - * state. This happens after errors in the middle of transactions, to start with - * a clean slate. - */ -void serial_transport_driver_clear(void); - -/** - * @brief Driver specific initialization on the slave half. - */ -void serial_transport_driver_slave_init(void); - -/** - * @brief Driver specific specific initialization on the master half. - */ -void serial_transport_driver_master_init(void); - -/** - * @brief Blocking receive of size * bytes. - * - * @return true Receive success. - * @return false Receive failed, e.g. by bit errors. - */ -bool __attribute__((nonnull, hot)) serial_transport_receive(uint8_t* destination, const size_t size); - -/** - * @brief Blocking receive of size * bytes with an implicitly defined timeout. - * - * @return true Receive success. - * @return false Receive failed, e.g. by timeout or bit errors. - */ -bool __attribute__((nonnull, hot)) serial_transport_receive_blocking(uint8_t* destination, const size_t size); - -/** - * @brief Blocking send of buffer with timeout. - * - * @return true Send success. - * @return false Send failed, e.g. by timeout or bit errors. - */ -bool __attribute__((nonnull, hot)) serial_transport_send(const uint8_t* source, const size_t size); diff --git a/platforms/chibios/drivers/serial_usart.c b/platforms/chibios/drivers/serial_usart.c deleted file mode 100644 index 767ef8726f..0000000000 --- a/platforms/chibios/drivers/serial_usart.c +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2021 QMK -// Copyright 2022 Stefan Kerkmann -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "serial_usart.h" -#include "serial_protocol.h" -#include "synchronization_util.h" -#include "chibios_config.h" - -#if defined(SERIAL_USART_CONFIG) -static QMKSerialConfig serial_config = SERIAL_USART_CONFIG; -#elif defined(MCU_STM32) /* STM32 MCUs */ -static QMKSerialConfig serial_config = { -# if HAL_USE_SERIAL - .speed = (SERIAL_USART_SPEED), -# else - .baud = (SERIAL_USART_SPEED), -# endif - .cr1 = (SERIAL_USART_CR1), - .cr2 = (SERIAL_USART_CR2), -# if !defined(SERIAL_USART_FULL_DUPLEX) - .cr3 = ((SERIAL_USART_CR3) | USART_CR3_HDSEL) /* activate half-duplex mode */ -# else - .cr3 = (SERIAL_USART_CR3) -# endif -}; -#elif defined(MCU_RP) /* Raspberry Pi MCUs */ -/* USART in 8E2 config with RX and TX FIFOs enabled. */ -// clang-format off -static QMKSerialConfig serial_config = { - .baud = (SERIAL_USART_SPEED), - .UARTLCR_H = UART_UARTLCR_H_WLEN_8BITS | UART_UARTLCR_H_PEN | UART_UARTLCR_H_STP2 | UART_UARTLCR_H_FEN, - .UARTCR = 0U, - .UARTIFLS = UART_UARTIFLS_RXIFLSEL_1_8F | UART_UARTIFLS_TXIFLSEL_1_8E, - .UARTDMACR = 0U -}; -// clang-format on -#else -# error MCU Familiy not supported by default, supply your own serial_config by defining SERIAL_USART_CONFIG in your keyboard files. -#endif - -static QMKSerialDriver* serial_driver = (QMKSerialDriver*)&SERIAL_USART_DRIVER; - -#if HAL_USE_SERIAL - -/** - * @brief SERIAL Driver startup routine. - */ -static inline void usart_driver_start(void) { - sdStart(serial_driver, &serial_config); -} - -inline void serial_transport_driver_clear(void) { - osalSysLock(); - bool volatile queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue); - osalSysUnlock(); - - while (queue_not_empty) { - osalSysLock(); - /* Hard reset the input queue. */ - iqResetI(&serial_driver->iqueue); - osalSysUnlock(); - /* Allow pending interrupts to preempt. - * Do not merge the lock/unlock blocks into one - * or the code will not work properly. - * The empty read adds a tiny amount of delay. */ - (void)queue_not_empty; - osalSysLock(); - queue_not_empty = !iqIsEmptyI(&serial_driver->iqueue); - osalSysUnlock(); - } -} - -#elif HAL_USE_SIO - -/** - * @brief SIO Driver startup routine. - */ -static inline void usart_driver_start(void) { - sioStart(serial_driver, &serial_config); -} - -inline void serial_transport_driver_clear(void) { - if (sioHasRXErrorsX(serial_driver)) { - sioGetAndClearErrors(serial_driver); - } - osalSysLock(); - while (!sioIsRXEmptyX(serial_driver)) { - (void)sioGetX(serial_driver); - } - osalSysUnlock(); -} - -#else - -# error Either the SERIAL or SIO driver has to be activated to use the usart driver for split keyboards. - -#endif - -inline bool serial_transport_send(const uint8_t* source, const size_t size) { - bool success = (size_t)chnWriteTimeout(serial_driver, source, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size; - -#if !defined(SERIAL_USART_FULL_DUPLEX) - /* Half duplex fills the input queue with the data we wrote - just throw it away. */ - if (likely(success)) { - size_t bytes_left = size; -# if HAL_USE_SERIAL - /* The SERIAL driver uses large soft FIFOs that are filled from an IRQ - * context, so there is a delay between receiving the data and it - * becoming actually available, therefore we have to apply a timeout - * mechanism. Under the right circumstances (e.g. bad cables paired with - * high baud rates) less bytes can be present in the input queue as - * well. */ - uint8_t dump[64]; - - while (unlikely(bytes_left >= 64)) { - if (unlikely(!serial_transport_receive(dump, 64))) { - return false; - } - bytes_left -= 64; - } - - return serial_transport_receive(dump, bytes_left); -# else - /* The SIO driver directly accesses the hardware FIFOs of the USART - * peripheral. As these are limited in depth, the RX FIFO might have - * been overflowed by a large transaction that we just send. Therefore - * we attempt to read back all the data we send or until the FIFO runs - * empty in case it overflowed and data was truncated. */ - if (unlikely(sioSynchronizeTXEnd(serial_driver, TIME_MS2I(SERIAL_USART_TIMEOUT)) < MSG_OK)) { - return false; - } - - osalSysLock(); - while (bytes_left > 0 && !sioIsRXEmptyX(serial_driver)) { - (void)sioGetX(serial_driver); - bytes_left--; - } - osalSysUnlock(); -# endif - } -#endif - - return success; -} - -inline bool serial_transport_receive(uint8_t* destination, const size_t size) { - bool success = (size_t)chnReadTimeout(serial_driver, destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)) == size; - return success; -} - -inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) { - bool success = (size_t)chnRead(serial_driver, destination, size) == size; - return success; -} - -#if !defined(SERIAL_USART_FULL_DUPLEX) - -/** - * @brief Initiate pins for USART peripheral. Half-duplex configuration. - */ -__attribute__((weak)) void usart_init(void) { -# if defined(MCU_STM32) /* STM32 MCUs */ -# if defined(USE_GPIOV1) - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_OPENDRAIN); -# else - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN); -# endif - -# if defined(USART_REMAP) - USART_REMAP; -# endif -# elif defined(MCU_RP) /* Raspberry Pi MCUs */ -# error Half-duplex with the SIO driver is not supported due to hardware limitations on the RP2040, switch to the PIO driver which has half-duplex support. -# else -# pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files." -# endif -} - -#else - -/** - * @brief Initiate pins for USART peripheral. Full-duplex configuration. - */ -__attribute__((weak)) void usart_init(void) { -# if defined(MCU_STM32) /* STM32 MCUs */ -# if defined(USE_GPIOV1) - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_PUSHPULL); - palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT); -# else - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST); - palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST); -# endif - -# if defined(USART_REMAP) - USART_REMAP; -# endif -# elif defined(MCU_RP) /* Raspberry Pi MCUs */ - palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE_UART); - palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE_UART); -# else -# pragma message "usart_init: MCU Familiy not supported by default, please supply your own init code by implementing usart_init() in your keyboard files." -# endif -} - -#endif - -/** - * @brief Overridable master specific initializations. - */ -__attribute__((weak, nonnull)) void usart_master_init(QMKSerialDriver** driver) { - (void)driver; - usart_init(); -} - -/** - * @brief Overridable slave specific initializations. - */ -__attribute__((weak, nonnull)) void usart_slave_init(QMKSerialDriver** driver) { - (void)driver; - usart_init(); -} - -void serial_transport_driver_slave_init(void) { - usart_slave_init(&serial_driver); - usart_driver_start(); -} - -void serial_transport_driver_master_init(void) { - usart_master_init(&serial_driver); - -#if defined(MCU_STM32) && defined(SERIAL_USART_PIN_SWAP) - serial_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins -#endif - - usart_driver_start(); -} diff --git a/platforms/chibios/drivers/serial_usart.h b/platforms/chibios/drivers/serial_usart.h deleted file mode 100644 index dec8a292e9..0000000000 --- a/platforms/chibios/drivers/serial_usart.h +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2021 QMK -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "serial.h" -#include <hal.h> - -#if defined(SOFT_SERIAL_PIN) -# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN -#endif - -#if !defined(SERIAL_USART_TX_PIN) -# define SERIAL_USART_TX_PIN A9 -#endif - -#if !defined(SERIAL_USART_RX_PIN) -# define SERIAL_USART_RX_PIN A10 -#endif - -#if !defined(SELECT_SOFT_SERIAL_SPEED) -# define SELECT_SOFT_SERIAL_SPEED 1 -#endif - -#if defined(SERIAL_USART_SPEED) -// Allow advanced users to directly set SERIAL_USART_SPEED -#elif SELECT_SOFT_SERIAL_SPEED == 0 -# define SERIAL_USART_SPEED 460800 -#elif SELECT_SOFT_SERIAL_SPEED == 1 -# define SERIAL_USART_SPEED 230400 -#elif SELECT_SOFT_SERIAL_SPEED == 2 -# define SERIAL_USART_SPEED 115200 -#elif SELECT_SOFT_SERIAL_SPEED == 3 -# define SERIAL_USART_SPEED 57600 -#elif SELECT_SOFT_SERIAL_SPEED == 4 -# define SERIAL_USART_SPEED 38400 -#elif SELECT_SOFT_SERIAL_SPEED == 5 -# define SERIAL_USART_SPEED 19200 -#else -# error invalid SELECT_SOFT_SERIAL_SPEED value -#endif - -#if !defined(SERIAL_USART_TIMEOUT) -# define SERIAL_USART_TIMEOUT 20 -#endif - -#if HAL_USE_SERIAL - -typedef SerialDriver QMKSerialDriver; -typedef SerialConfig QMKSerialConfig; - -# if !defined(SERIAL_USART_DRIVER) -# define SERIAL_USART_DRIVER SD1 -# endif - -#elif HAL_USE_SIO - -typedef SIODriver QMKSerialDriver; -typedef SIOConfig QMKSerialConfig; - -# if !defined(SERIAL_USART_DRIVER) -# define SERIAL_USART_DRIVER SIOD1 -# endif - -#endif - -#if !defined(USE_GPIOV1) -/* The default PAL alternate modes are used to signal that the pins are used for USART. */ -# if !defined(SERIAL_USART_TX_PAL_MODE) -# define SERIAL_USART_TX_PAL_MODE 7 -# endif -# if !defined(SERIAL_USART_RX_PAL_MODE) -# define SERIAL_USART_RX_PAL_MODE 7 -# endif -#endif - -#if !defined(USART_CR1_M0) -# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so -#endif - -#if !defined(SERIAL_USART_CR1) -# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length -#endif - -#if !defined(SERIAL_USART_CR2) -# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits -#endif - -#if !defined(SERIAL_USART_CR3) -# define SERIAL_USART_CR3 0 -#endif - -#if defined(USART1_REMAP) -# define USART_REMAP \ - do { \ - (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); \ - } while (0) -#elif defined(USART2_REMAP) -# define USART_REMAP \ - do { \ - (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); \ - } while (0) -#elif defined(USART3_PARTIALREMAP) -# define USART_REMAP \ - do { \ - (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); \ - } while (0) -#elif defined(USART3_FULLREMAP) -# define USART_REMAP \ - do { \ - (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); \ - } while (0) -#endif diff --git a/platforms/chibios/drivers/spi_master.c b/platforms/chibios/drivers/spi_master.c deleted file mode 100644 index c3ab0623f0..0000000000 --- a/platforms/chibios/drivers/spi_master.c +++ /dev/null @@ -1,291 +0,0 @@ -/* Copyright 2020 Nick Brassel (tzarc) - * - * 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 3 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 <https://www.gnu.org/licenses/>. - */ - -#include "spi_master.h" - -#include "timer.h" - -static pin_t currentSlavePin = NO_PIN; - -#if defined(K20x) || defined(KL2x) || defined(RP2040) -static SPIConfig spiConfig = {NULL, 0, 0, 0}; -#else -static SPIConfig spiConfig = {false, NULL, 0, 0, 0, 0}; -#endif - -__attribute__((weak)) void spi_init(void) { - static bool is_initialised = false; - if (!is_initialised) { - is_initialised = true; - - // Try releasing special pins for a short time - setPinInput(SPI_SCK_PIN); - setPinInput(SPI_MOSI_PIN); - setPinInput(SPI_MISO_PIN); - - chThdSleepMilliseconds(10); -#if defined(USE_GPIOV1) - palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), SPI_SCK_PAL_MODE); - palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), SPI_MOSI_PAL_MODE); - palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), SPI_MISO_PAL_MODE); -#else - palSetPadMode(PAL_PORT(SPI_SCK_PIN), PAL_PAD(SPI_SCK_PIN), SPI_SCK_FLAGS); - palSetPadMode(PAL_PORT(SPI_MOSI_PIN), PAL_PAD(SPI_MOSI_PIN), SPI_MOSI_FLAGS); - palSetPadMode(PAL_PORT(SPI_MISO_PIN), PAL_PAD(SPI_MISO_PIN), SPI_MISO_FLAGS); -#endif - spiStop(&SPI_DRIVER); - currentSlavePin = NO_PIN; - } -} - -bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor) { - if (currentSlavePin != NO_PIN || slavePin == NO_PIN) { - return false; - } - -#if !(defined(WB32F3G71xx) || defined(WB32FQ95xx)) - uint16_t roundedDivisor = 2; - while (roundedDivisor < divisor) { - roundedDivisor <<= 1; - } - - if (roundedDivisor < 2 || roundedDivisor > 256) { - return false; - } -#endif - -#if defined(K20x) || defined(KL2x) - spiConfig.tar0 = SPIx_CTARn_FMSZ(7) | SPIx_CTARn_ASC(1); - - if (lsbFirst) { - spiConfig.tar0 |= SPIx_CTARn_LSBFE; - } - - switch (mode) { - case 0: - break; - case 1: - spiConfig.tar0 |= SPIx_CTARn_CPHA; - break; - case 2: - spiConfig.tar0 |= SPIx_CTARn_CPOL; - break; - case 3: - spiConfig.tar0 |= SPIx_CTARn_CPHA | SPIx_CTARn_CPOL; - break; - } - - switch (roundedDivisor) { - case 2: - spiConfig.tar0 |= SPIx_CTARn_BR(0); - break; - case 4: - spiConfig.tar0 |= SPIx_CTARn_BR(1); - break; - case 8: - spiConfig.tar0 |= SPIx_CTARn_BR(3); - break; - case 16: - spiConfig.tar0 |= SPIx_CTARn_BR(4); - break; - case 32: - spiConfig.tar0 |= SPIx_CTARn_BR(5); - break; - case 64: - spiConfig.tar0 |= SPIx_CTARn_BR(6); - break; - case 128: - spiConfig.tar0 |= SPIx_CTARn_BR(7); - break; - case 256: - spiConfig.tar0 |= SPIx_CTARn_BR(8); - break; - } - -#elif defined(HT32) - spiConfig.cr0 = SPI_CR0_SELOEN; - spiConfig.cr1 = SPI_CR1_MODE | 8; // 8 bits and in master mode - - if (lsbFirst) { - spiConfig.cr1 |= SPI_CR1_FIRSTBIT; - } - - switch (mode) { - case 0: - spiConfig.cr1 |= SPI_CR1_FORMAT_MODE0; - break; - case 1: - spiConfig.cr1 |= SPI_CR1_FORMAT_MODE1; - break; - case 2: - spiConfig.cr1 |= SPI_CR1_FORMAT_MODE2; - break; - case 3: - spiConfig.cr1 |= SPI_CR1_FORMAT_MODE3; - break; - } - - spiConfig.cpr = (roundedDivisor - 1) >> 1; - -#elif defined(WB32F3G71xx) || defined(WB32FQ95xx) - if (!lsbFirst) { - osalDbgAssert(lsbFirst != FALSE, "unsupported lsbFirst"); - } - - if (divisor < 1) { - return false; - } - - spiConfig.SPI_BaudRatePrescaler = (divisor << 2); - - switch (mode) { - case 0: - spiConfig.SPI_CPHA = SPI_CPHA_1Edge; - spiConfig.SPI_CPOL = SPI_CPOL_Low; - break; - case 1: - spiConfig.SPI_CPHA = SPI_CPHA_2Edge; - spiConfig.SPI_CPOL = SPI_CPOL_Low; - break; - case 2: - spiConfig.SPI_CPHA = SPI_CPHA_1Edge; - spiConfig.SPI_CPOL = SPI_CPOL_High; - break; - case 3: - spiConfig.SPI_CPHA = SPI_CPHA_2Edge; - spiConfig.SPI_CPOL = SPI_CPOL_High; - break; - } -#elif defined(MCU_RP) - if (lsbFirst) { - osalDbgAssert(lsbFirst == false, "RP2040s PrimeCell SPI implementation does not support sending LSB first."); - } - - // Motorola frame format and 8bit transfer data size. - spiConfig.SSPCR0 = SPI_SSPCR0_FRF_MOTOROLA | SPI_SSPCR0_DSS_8BIT; - // Serial output clock = (ck_sys or ck_peri) / (SSPCPSR->CPSDVSR * (1 + - // SSPCR0->SCR)). SCR is always set to zero, as QMK SPI API expects the - // passed divisor to be the only value to divide the input clock by. - spiConfig.SSPCPSR = roundedDivisor; // Even number from 2 to 254 - - switch (mode) { - case 0: - spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low - spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge - break; - case 1: - spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPO; // Clock polarity: low - spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition - break; - case 2: - spiConfig.SSPCR0 |= SPI_SSPCR0_SPO; // Clock polarity: high - spiConfig.SSPCR0 &= ~SPI_SSPCR0_SPH; // Clock phase: sample on first edge - break; - case 3: - spiConfig.SSPCR0 |= SPI_SSPCR0_SPO; // Clock polarity: high - spiConfig.SSPCR0 |= SPI_SSPCR0_SPH; // Clock phase: sample on second edge transition - break; - } -#else - spiConfig.cr1 = 0; - - if (lsbFirst) { - spiConfig.cr1 |= SPI_CR1_LSBFIRST; - } - - switch (mode) { - case 0: - break; - case 1: - spiConfig.cr1 |= SPI_CR1_CPHA; - break; - case 2: - spiConfig.cr1 |= SPI_CR1_CPOL; - break; - case 3: - spiConfig.cr1 |= SPI_CR1_CPHA | SPI_CR1_CPOL; - break; - } - - switch (roundedDivisor) { - case 2: - break; - case 4: - spiConfig.cr1 |= SPI_CR1_BR_0; - break; - case 8: - spiConfig.cr1 |= SPI_CR1_BR_1; - break; - case 16: - spiConfig.cr1 |= SPI_CR1_BR_1 | SPI_CR1_BR_0; - break; - case 32: - spiConfig.cr1 |= SPI_CR1_BR_2; - break; - case 64: - spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_0; - break; - case 128: - spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1; - break; - case 256: - spiConfig.cr1 |= SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0; - break; - } -#endif - - currentSlavePin = slavePin; - spiConfig.ssport = PAL_PORT(slavePin); - spiConfig.sspad = PAL_PAD(slavePin); - - setPinOutput(slavePin); - spiStart(&SPI_DRIVER, &spiConfig); - spiSelect(&SPI_DRIVER); - - return true; -} - -spi_status_t spi_write(uint8_t data) { - uint8_t rxData; - spiExchange(&SPI_DRIVER, 1, &data, &rxData); - - return rxData; -} - -spi_status_t spi_read(void) { - uint8_t data = 0; - spiReceive(&SPI_DRIVER, 1, &data); - - return data; -} - -spi_status_t spi_transmit(const uint8_t *data, uint16_t length) { - spiSend(&SPI_DRIVER, length, data); - return SPI_STATUS_SUCCESS; -} - -spi_status_t spi_receive(uint8_t *data, uint16_t length) { - spiReceive(&SPI_DRIVER, length, data); - return SPI_STATUS_SUCCESS; -} - -void spi_stop(void) { - if (currentSlavePin != NO_PIN) { - spiUnselect(&SPI_DRIVER); - spiStop(&SPI_DRIVER); - currentSlavePin = NO_PIN; - } -} diff --git a/platforms/chibios/drivers/spi_master.h b/platforms/chibios/drivers/spi_master.h deleted file mode 100644 index 6a3ce481f1..0000000000 --- a/platforms/chibios/drivers/spi_master.h +++ /dev/null @@ -1,93 +0,0 @@ -/* Copyright 2020 Nick Brassel (tzarc) - * - * 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 3 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 <https://www.gnu.org/licenses/>. - */ - -#pragma once - -#include <ch.h> -#include <hal.h> -#include <stdbool.h> - -#include "gpio.h" -#include "chibios_config.h" - -#ifndef SPI_DRIVER -# define SPI_DRIVER SPID2 -#endif - -#ifndef SPI_SCK_PIN -# define SPI_SCK_PIN B13 -#endif - -#ifndef SPI_SCK_PAL_MODE -# if defined(USE_GPIOV1) -# define SPI_SCK_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL -# else -# define SPI_SCK_PAL_MODE 5 -# endif -#endif - -#ifndef SPI_MOSI_PIN -# define SPI_MOSI_PIN B15 -#endif - -#ifndef SPI_MOSI_PAL_MODE -# if defined(USE_GPIOV1) -# define SPI_MOSI_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL -# else -# define SPI_MOSI_PAL_MODE 5 -# endif -#endif - -#ifndef SPI_MISO_PIN -# define SPI_MISO_PIN B14 -#endif - -#ifndef SPI_MISO_PAL_MODE -# if defined(USE_GPIOV1) -# define SPI_MISO_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL -# else -# define SPI_MISO_PAL_MODE 5 -# endif -#endif - -typedef int16_t spi_status_t; - -#define SPI_STATUS_SUCCESS (0) -#define SPI_STATUS_ERROR (-1) -#define SPI_STATUS_TIMEOUT (-2) - -#define SPI_TIMEOUT_IMMEDIATE (0) -#define SPI_TIMEOUT_INFINITE (0xFFFF) - -#ifdef __cplusplus -extern "C" { -#endif -void spi_init(void); - -bool spi_start(pin_t slavePin, bool lsbFirst, uint8_t mode, uint16_t divisor); - -spi_status_t spi_write(uint8_t data); - -spi_status_t spi_read(void); - -spi_status_t spi_transmit(const uint8_t *data, uint16_t length); - -spi_status_t spi_receive(uint8_t *data, uint16_t length); - -void spi_stop(void); -#ifdef __cplusplus -} -#endif diff --git a/platforms/chibios/drivers/uart.c b/platforms/chibios/drivers/uart.c deleted file mode 100644 index 39a59dd445..0000000000 --- a/platforms/chibios/drivers/uart.c +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright 2021 - * - * 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 3 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 <https://www.gnu.org/licenses/>. - */ - -#include "uart.h" - -#if defined(MCU_KINETIS) -static SerialConfig serialConfig = {SERIAL_DEFAULT_BITRATE}; -#elif defined(WB32F3G71xx) || defined(WB32FQ95xx) -static SerialConfig serialConfig = {SERIAL_DEFAULT_BITRATE, SD1_WRDLEN, SD1_STPBIT, SD1_PARITY, SD1_ATFLCT}; -#else -static SerialConfig serialConfig = {SERIAL_DEFAULT_BITRATE, SD1_CR1, SD1_CR2, SD1_CR3}; -#endif - -void uart_init(uint32_t baud) { - static bool is_initialised = false; - - if (!is_initialised) { - is_initialised = true; - -#if defined(MCU_KINETIS) - serialConfig.sc_speed = baud; -#else - serialConfig.speed = baud; -#endif - -#if defined(USE_GPIOV1) - palSetLineMode(SD1_TX_PIN, SD1_TX_PAL_MODE); - palSetLineMode(SD1_RX_PIN, SD1_RX_PAL_MODE); -#else - palSetLineMode(SD1_TX_PIN, PAL_MODE_ALTERNATE(SD1_TX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST); - palSetLineMode(SD1_RX_PIN, PAL_MODE_ALTERNATE(SD1_RX_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST); -#endif - sdStart(&SERIAL_DRIVER, &serialConfig); - } -} - -void uart_write(uint8_t data) { - sdPut(&SERIAL_DRIVER, data); -} - -uint8_t uart_read(void) { - msg_t res = sdGet(&SERIAL_DRIVER); - - return (uint8_t)res; -} - -void uart_transmit(const uint8_t *data, uint16_t length) { - sdWrite(&SERIAL_DRIVER, data, length); -} - -void uart_receive(uint8_t *data, uint16_t length) { - sdRead(&SERIAL_DRIVER, data, length); -} - -bool uart_available(void) { - return !sdGetWouldBlock(&SERIAL_DRIVER); -} diff --git a/platforms/chibios/drivers/uart.h b/platforms/chibios/drivers/uart.h deleted file mode 100644 index 16983072ce..0000000000 --- a/platforms/chibios/drivers/uart.h +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright 2021 - * - * 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 3 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 <https://www.gnu.org/licenses/>. - */ - -#pragma once - -#include <stdint.h> -#include <stdbool.h> - -#include <hal.h> - -#include "gpio.h" -#include "chibios_config.h" - -#ifndef SERIAL_DRIVER -# define SERIAL_DRIVER SD1 -#endif - -#ifndef SD1_TX_PIN -# define SD1_TX_PIN A9 -#endif - -#ifndef SD1_RX_PIN -# define SD1_RX_PIN A10 -#endif - -#ifndef SD1_CTS_PIN -# define SD1_CTS_PIN A11 -#endif - -#ifndef SD1_RTS_PIN -# define SD1_RTS_PIN A12 -#endif - -#ifdef USE_GPIOV1 -# ifndef SD1_TX_PAL_MODE -# define SD1_TX_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL -# endif -# ifndef SD1_RX_PAL_MODE -# define SD1_RX_PAL_MODE PAL_MODE_INPUT -# endif -# ifndef SD1_CTS_PAL_MODE -# define SD1_CTS_PAL_MODE PAL_MODE_INPUT -# endif -# ifndef SD1_RTS_PAL_MODE -# define SD1_RTS_PAL_MODE PAL_MODE_ALTERNATE_PUSHPULL -# endif -#else -# ifndef SD1_TX_PAL_MODE -# define SD1_TX_PAL_MODE 7 -# endif - -# ifndef SD1_RX_PAL_MODE -# define SD1_RX_PAL_MODE 7 -# endif - -# ifndef SD1_CTS_PAL_MODE -# define SD1_CTS_PAL_MODE 7 -# endif - -# ifndef SD1_RTS_PAL_MODE -# define SD1_RTS_PAL_MODE 7 -# endif -#endif - -#ifndef SD1_CR1 -# define SD1_CR1 0 -#endif - -#ifndef SD1_CR2 -# define SD1_CR2 0 -#endif - -#ifndef SD1_CR3 -# define SD1_CR3 0 -#endif - -#ifndef SD1_WRDLEN -# define SD1_WRDLEN 3 -#endif - -#ifndef SD1_STPBIT -# define SD1_STPBIT 0 -#endif - -#ifndef SD1_PARITY -# define SD1_PARITY 0 -#endif - -#ifndef SD1_ATFLCT -# define SD1_ATFLCT 0 -#endif - -void uart_init(uint32_t baud); - -void uart_write(uint8_t data); - -uint8_t uart_read(void); - -void uart_transmit(const uint8_t *data, uint16_t length); - -void uart_receive(uint8_t *data, uint16_t length); - -bool uart_available(void); diff --git a/platforms/chibios/drivers/usbpd_stm32g4.c b/platforms/chibios/drivers/usbpd_stm32g4.c deleted file mode 100644 index 21b8f6db95..0000000000 --- a/platforms/chibios/drivers/usbpd_stm32g4.c +++ /dev/null @@ -1,79 +0,0 @@ -/* Copyright 2021 Nick Brassel (@tzarc) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <quantum.h> - -#ifndef USBPD_UCPD1_CFG1 -# define USBPD_UCPD1_CFG1 (UCPD_CFG1_PSC_UCPDCLK_0 | UCPD_CFG1_TRANSWIN_3 | UCPD_CFG1_IFRGAP_4 | UCPD_CFG1_HBITCLKDIV_4) -#endif // USBPD_UCPD1_CFG1 - -// Initialises the USBPD subsystem -__attribute__((weak)) void usbpd_init(void) { - // Enable the clock for the UCPD1 peripheral - RCC->APB1ENR2 |= RCC_APB1ENR2_UCPD1EN; - - // Copy the existing value - uint32_t CFG1 = UCPD1->CFG1; - // Force-disable UCPD1 before configuring - CFG1 &= ~UCPD_CFG1_UCPDEN; - // Configure UCPD1 - CFG1 = USBPD_UCPD1_CFG1; - // Apply the changes - UCPD1->CFG1 = CFG1; - // Enable UCPD1 - UCPD1->CFG1 |= UCPD_CFG1_UCPDEN; - - // Copy the existing value - uint32_t CR = UCPD1->CR; - // Clear out ANASUBMODE (irrelevant as a sink device) - CR &= ~UCPD_CR_ANASUBMODE_Msk; - // Advertise our capabilities as a sink, with both CC lines enabled - CR |= UCPD_CR_ANAMODE | UCPD_CR_CCENABLE_Msk; - // Apply the changes - UCPD1->CR = CR; - - // Disable dead-battery signals only after UCPD1 is configured to ensure - // that the transition does not go through any intermediate state without - // any pull-down resistance. - PWR->CR3 |= PWR_CR3_UCPD_DBDIS; -} - -// Gets the current state of the USBPD allowance -__attribute__((weak)) usbpd_allowance_t usbpd_get_allowance(void) { - uint32_t CR = UCPD1->CR; - - int ucpd_enabled = (UCPD1->CFG1 & UCPD_CFG1_UCPDEN_Msk) >> UCPD_CFG1_UCPDEN_Pos; - int anamode = (CR & UCPD_CR_ANAMODE_Msk) >> UCPD_CR_ANAMODE_Pos; - int cc_enabled = (CR & UCPD_CR_CCENABLE_Msk) >> UCPD_CR_CCENABLE_Pos; - - if (ucpd_enabled && anamode && cc_enabled) { - uint32_t SR = UCPD1->SR; - int vstate_cc1 = (SR & UCPD_SR_TYPEC_VSTATE_CC1_Msk) >> UCPD_SR_TYPEC_VSTATE_CC1_Pos; - int vstate_cc2 = (SR & UCPD_SR_TYPEC_VSTATE_CC2_Msk) >> UCPD_SR_TYPEC_VSTATE_CC2_Pos; - int vstate_max = vstate_cc1 > vstate_cc2 ? vstate_cc1 : vstate_cc2; - switch (vstate_max) { - case 0: - case 1: - return USBPD_500MA; // Note that this is 500mA (i.e. max USB 2.0), not 900mA, as we're not using USB 3.1 as a sink device. - case 2: - return USBPD_1500MA; - case 3: - return USBPD_3000MA; - } - } - - return USBPD_500MA; -}
\ No newline at end of file diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c deleted file mode 100644 index 1c61f196bd..0000000000 --- a/platforms/chibios/drivers/vendor/RP/RP2040/ps2_vendor.c +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright 2022 Marek Kraus (@gamelaster) -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "gpio.h" -#include "hardware/pio.h" -#include "hardware/clocks.h" -#include "ps2.h" -#include "debug.h" - -#if !defined(MCU_RP) -# error PIO Driver is only available for Raspberry Pi 2040 MCUs! -#endif - -#if defined(PS2_ENABLE) -# if defined(PS2_MOUSE_ENABLE) -# if !defined(PS2_MOUSE_USE_REMOTE_MODE) -# define BUFFERED_MODE_ENABLE -# endif -# else // PS2 Keyboard -# define BUFFERED_MODE_ENABLE -# endif -#endif - -#if PS2_DATA_PIN + 1 != PS2_CLOCK_PIN -# error PS/2 clock pin must be data pin + 1! -#endif - -static inline void pio_serve_interrupt(void); - -#if defined(PS2_PIO_USE_PIO1) -static const PIO pio = pio1; - -OSAL_IRQ_HANDLER(RP_PIO1_IRQ_0_HANDLER) { - OSAL_IRQ_PROLOGUE(); - pio_serve_interrupt(); - OSAL_IRQ_EPILOGUE(); -} -#else -static const PIO pio = pio0; - -OSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) { - OSAL_IRQ_PROLOGUE(); - pio_serve_interrupt(); - OSAL_IRQ_EPILOGUE(); -} -#endif - -#define PS2_WRAP_TARGET 0 -#define PS2_WRAP 20 - -// clang-format off -static const uint16_t ps2_program_instructions[] = { - // .wrap_target - 0x00c7, // 0: jmp pin, 7 - 0xe02a, // 1: set x, 10 - 0x2021, // 2: wait 0 pin, 1 - 0x4001, // 3: in pins, 1 - 0x20a1, // 4: wait 1 pin, 1 - 0x0042, // 5: jmp x--, 2 - 0x0000, // 6: jmp 0 - 0x00e9, // 7: jmp !osre, 9 - 0x0000, // 8: jmp 0 - 0xff81, // 9: set pindirs, 1 [31] - 0xe280, // 10: set pindirs, 0 [2] - 0xe082, // 11: set pindirs, 2 - 0x2021, // 12: wait 0 pin, 1 - 0xe029, // 13: set x, 9 - 0x6081, // 14: out pindirs, 1 - 0x20a1, // 15: wait 1 pin, 1 - 0x2021, // 16: wait 0 pin, 1 - 0x004e, // 17: jmp x--, 14 - 0xe083, // 18: set pindirs, 3 - 0x2021, // 19: wait 0 pin, 1 - 0x20a1, // 20: wait 1 pin, 1 - // .wrap -}; -// clang-format on - -static const struct pio_program ps2_program = { - .instructions = ps2_program_instructions, - .length = 21, - .origin = -1, -}; - -static int state_machine = -1; -static thread_reference_t tx_thread = NULL; - -#define BUFFER_SIZE 32 -static input_buffers_queue_t pio_rx_queue; -static __attribute__((aligned(4))) uint8_t pio_rx_buffer[BQ_BUFFER_SIZE(BUFFER_SIZE, sizeof(uint32_t))]; - -uint8_t ps2_error = PS2_ERR_NONE; - -void pio_serve_interrupt(void) { - uint32_t irqs = pio->ints0; - - if (irqs & (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS << state_machine)) { - osalSysLockFromISR(); - uint32_t* frame_buffer = (uint32_t*)ibqGetEmptyBufferI(&pio_rx_queue); - if (frame_buffer == NULL) { - osalSysUnlockFromISR(); - return; - } - *frame_buffer = pio_sm_get(pio, state_machine); - ibqPostFullBufferI(&pio_rx_queue, sizeof(uint32_t)); - osalSysUnlockFromISR(); - } - - if (irqs & (PIO_IRQ0_INTF_SM0_TXNFULL_BITS << state_machine)) { - pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + state_machine, false); - osalSysLockFromISR(); - osalThreadResumeI(&tx_thread, MSG_OK); - osalSysUnlockFromISR(); - } -} - -void ps2_host_init(void) { - ibqObjectInit(&pio_rx_queue, false, pio_rx_buffer, sizeof(uint32_t), BUFFER_SIZE, NULL, NULL); - uint pio_idx = pio_get_index(pio); - - hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1); - - state_machine = pio_claim_unused_sm(pio, true); - if (state_machine < 0) { - dprintln("ERROR: Failed to acquire state machine for PS/2!"); - ps2_error = PS2_ERR_NODATA; - return; - } - - uint offset = pio_add_program(pio, &ps2_program); - - pio_sm_config c = pio_get_default_sm_config(); - sm_config_set_wrap(&c, offset + PS2_WRAP_TARGET, offset + PS2_WRAP); - - // Set pindirs to input (output enable is inverted below) - pio_sm_set_consecutive_pindirs(pio, state_machine, PS2_DATA_PIN, 2, true); - sm_config_set_clkdiv(&c, (float)clock_get_hz(clk_sys) / (200.0f * KHZ)); - sm_config_set_set_pins(&c, PS2_DATA_PIN, 2); - sm_config_set_out_pins(&c, PS2_DATA_PIN, 1); - sm_config_set_out_shift(&c, true, true, 10); - sm_config_set_in_shift(&c, true, true, 11); - sm_config_set_jmp_pin(&c, PS2_CLOCK_PIN); - sm_config_set_in_pins(&c, PS2_DATA_PIN); - - // clang-format off - iomode_t pin_mode = PAL_RP_PAD_IE | - PAL_RP_GPIO_OE | - PAL_RP_PAD_SLEWFAST | - PAL_RP_PAD_DRIVE12 | - // Invert output enable so that pindirs=1 means input - // and indirs=0 means output. This way, out pindirs - // works correctly with the open-drain PS/2 interface. - // Setting pindirs=1 effectively pulls the line high, - // due to the pull-up resistor, while pindirs=0 pulls - // the line low. - PAL_RP_IOCTRL_OEOVER_DRVINVPERI | - (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1); - // clang-format on - - palSetLineMode(PS2_DATA_PIN, pin_mode); - palSetLineMode(PS2_CLOCK_PIN, pin_mode); - - pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + state_machine, true); - pio_sm_init(pio, state_machine, offset, &c); - -#if defined(PS2_PIO_USE_PIO1) - nvicEnableVector(RP_PIO1_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY); -#else - nvicEnableVector(RP_PIO0_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY); -#endif - - pio_sm_set_enabled(pio, state_machine, true); -} - -static int bit_parity(int x) { - return !__builtin_parity(x); -} - -uint8_t ps2_host_send(uint8_t data) { - uint32_t frame = 0b1000000000; - frame = frame | data; - - if (bit_parity(data)) { - frame = frame | (1 << 8); - } - - pio_sm_put(pio, state_machine, frame); - - msg_t msg = MSG_OK; - osalSysLock(); - while (pio_sm_is_tx_fifo_full(pio, state_machine)) { - pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + state_machine, true); - msg = osalThreadSuspendTimeoutS(&tx_thread, TIME_MS2I(100)); - if (msg < MSG_OK) { - pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + state_machine, false); - ps2_error = PS2_ERR_NODATA; - osalSysUnlock(); - return 0; - } - } - osalSysUnlock(); - - return ps2_host_recv_response(); -} - -static uint8_t ps2_get_data_from_frame(uint32_t frame) { - uint8_t data = (frame >> 22) & 0xFF; - uint32_t start_bit = (frame & 0b00000000001000000000000000000000) ? 1 : 0; - uint32_t parity_bit = (frame & 0b01000000000000000000000000000000) ? 1 : 0; - uint32_t stop_bit = (frame & 0b10000000001000000000000000000000) ? 1 : 0; - - if (start_bit != 0) { - ps2_error = PS2_ERR_STARTBIT1; - return 0; - } - - if (parity_bit != bit_parity(data)) { - ps2_error = PS2_ERR_PARITY; - return 0; - } - - if (stop_bit != 1) { - ps2_error = PS2_ERR_STARTBIT2; - return 0; - } - - return data; -} - -uint8_t ps2_host_recv_response(void) { - uint32_t frame = 0; - msg_t msg = MSG_OK; - - msg = ibqReadTimeout(&pio_rx_queue, (uint8_t*)&frame, sizeof(uint32_t), TIME_MS2I(100)); - if (msg < MSG_OK) { - ps2_error = PS2_ERR_NODATA; - return 0; - } - - return ps2_get_data_from_frame(frame); -} - -#ifdef BUFFERED_MODE_ENABLE - -bool pbuf_has_data(void) { - osalSysLock(); - bool has_data = !ibqIsEmptyI(&pio_rx_queue); - osalSysUnlock(); - return has_data; -} - -uint8_t ps2_host_recv(void) { - uint32_t frame = 0; - msg_t msg = MSG_OK; - - uint8_t has_data = pbuf_has_data(); - if (has_data) { - msg = ibqReadTimeout(&pio_rx_queue, (uint8_t*)&frame, sizeof(uint32_t), TIME_MS2I(100)); - if (msg < MSG_OK) { - ps2_error = PS2_ERR_NODATA; - return 0; - } - } else { - ps2_error = PS2_ERR_NODATA; - } - - return frame != 0 ? ps2_get_data_from_frame(frame) : 0; -} - -#endif diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c deleted file mode 100644 index 3aa8e1165f..0000000000 --- a/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c +++ /dev/null @@ -1,462 +0,0 @@ -// Copyright 2022 Stefan Kerkmann -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "serial_usart.h" -#include "serial_protocol.h" -#include "hardware/pio.h" -#include "hardware/clocks.h" -#include "wait.h" -#include "debug.h" - -#if !defined(MCU_RP) -# error PIO Driver is only available for Raspberry Pi 2040 MCUs! -#endif - -static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout); -static inline bool send_impl(const uint8_t* source, const size_t size); -static inline void pio_serve_interrupt(void); - -#define MSG_PIO_ERROR ((msg_t)(-3)) - -#if defined(SERIAL_PIO_USE_PIO1) -static const PIO pio = pio1; - -OSAL_IRQ_HANDLER(RP_PIO1_IRQ_0_HANDLER) { - OSAL_IRQ_PROLOGUE(); - pio_serve_interrupt(); - OSAL_IRQ_EPILOGUE(); -} -#else -static const PIO pio = pio0; - -OSAL_IRQ_HANDLER(RP_PIO0_IRQ_0_HANDLER) { - OSAL_IRQ_PROLOGUE(); - pio_serve_interrupt(); - OSAL_IRQ_EPILOGUE(); -} -#endif - -#define UART_TX_WRAP_TARGET 0 -#define UART_TX_WRAP 3 - -// clang-format off -#if defined(SERIAL_USART_FULL_DUPLEX) -static const uint16_t uart_tx_program_instructions[] = { - // .wrap_target - 0x9fa0, // 0: pull block side 1 [7] - 0xf727, // 1: set x, 7 side 0 [7] - 0x6001, // 2: out pins, 1 - 0x0642, // 3: jmp x--, 2 [6] - // .wrap -}; -#else -static const uint16_t uart_tx_program_instructions[] = { - // .wrap_target - 0x9fa0, // 0: pull block side 1 [7] - 0xf727, // 1: set x, 7 side 0 [7] - 0x6081, // 2: out pindirs, 1 - 0x0642, // 3: jmp x--, 2 [6] - // .wrap -}; -#endif -// clang-format on - -static const pio_program_t uart_tx_program = { - .instructions = uart_tx_program_instructions, - .length = 4, - .origin = -1, -}; - -#define UART_RX_WRAP_TARGET 0 -#define UART_RX_WRAP 8 - -// clang-format off -static const uint16_t uart_rx_program_instructions[] = { - // .wrap_target - 0x2020, // 0: wait 0 pin, 0 - 0xea27, // 1: set x, 7 [10] - 0x4001, // 2: in pins, 1 - 0x0642, // 3: jmp x--, 2 [6] - 0x00c8, // 4: jmp pin, 8 - 0xc020, // 5: irq wait 0 - 0x20a0, // 6: wait 1 pin, 0 - 0x0000, // 7: jmp 0 - 0x8020, // 8: push block - // .wrap -}; -// clang-format on - -static const pio_program_t uart_rx_program = { - .instructions = uart_rx_program_instructions, - .length = 9, - .origin = -1, -}; - -thread_reference_t rx_thread = NULL; -static int rx_state_machine = -1; - -thread_reference_t tx_thread = NULL; -static int tx_state_machine = -1; - -void pio_serve_interrupt(void) { - uint32_t irqs = pio->ints0; - - // The RX FIFO is not empty any more, therefore wake any sleeping rx thread - if (irqs & (PIO_IRQ0_INTF_SM0_RXNEMPTY_BITS << rx_state_machine)) { - // Disable rx not empty interrupt - pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false); - - osalSysLockFromISR(); - osalThreadResumeI(&rx_thread, MSG_OK); - osalSysUnlockFromISR(); - } - - // The TX FIFO is not full any more, therefore wake any sleeping tx thread - if (irqs & (PIO_IRQ0_INTF_SM0_TXNFULL_BITS << tx_state_machine)) { - // Disable tx not full interrupt - pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false); - osalSysLockFromISR(); - osalThreadResumeI(&tx_thread, MSG_OK); - osalSysUnlockFromISR(); - } - - // IRQ 0 is set on framing or break errors by the rx state machine - if (pio_interrupt_get(pio, 0UL)) { - pio_interrupt_clear(pio, 0UL); - - osalSysLockFromISR(); - osalThreadResumeI(&rx_thread, MSG_PIO_ERROR); - osalSysUnlockFromISR(); - } -} - -#if !defined(SERIAL_USART_FULL_DUPLEX) -// The internal pull-ups of the RP2040 are rather weakish with a range of 50k to -// 80k, which in turn do not provide enough current to guarantee fast signal rise -// times with a parasitic capacitance of greater than 100pf. In real world -// applications, like split keyboards which might have vias in the signal path -// or long PCB traces, this prevents a successful communication. The solution -// is to temporarily augment the weak pull ups from the receiving side by -// driving the tx pin high. On the receiving side the lowest possible drive -// strength is chosen because the transmitting side must still be able to drive -// the signal low. With this configuration the rise times are fast enough and -// the generated low level with 360mV will generate a logical zero. -static void __no_inline_not_in_flash_func(enter_rx_state)(void) { - osalSysLock(); - // Wait for the transmitting state machines FIFO to run empty. At this point - // the last byte has been pulled from the transmitting state machines FIFO - // into the output shift register. We have to wait a tiny bit more until - // this byte is transmitted, before we can turn on the receiving state - // machine again. - while (!pio_sm_is_tx_fifo_empty(pio, tx_state_machine)) { - } - // Wait for ~11 bits, 1 start bit + 8 data bits + 1 stop bit + 1 bit - // headroom. - wait_us(1000000U * 11U / SERIAL_USART_SPEED); - // Disable tx state machine to not interfere with our tx pin manipulation - pio_sm_set_enabled(pio, tx_state_machine, false); - gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_2MA); - pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << SERIAL_USART_TX_PIN, 1U << SERIAL_USART_TX_PIN); - pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, false); - pio_sm_set_enabled(pio, rx_state_machine, true); - osalSysUnlock(); -} - -static void __no_inline_not_in_flash_func(leave_rx_state)(void) { - osalSysLock(); - // In Half-duplex operation the tx pin dual-functions as sender and - // receiver. To not receive the data we will send, we disable the receiving - // state machine. - pio_sm_set_enabled(pio, rx_state_machine, false); - pio_sm_set_consecutive_pindirs(pio, tx_state_machine, SERIAL_USART_TX_PIN, 1U, true); - pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U, 1U << SERIAL_USART_TX_PIN); - gpio_set_drive_strength(SERIAL_USART_TX_PIN, GPIO_DRIVE_STRENGTH_12MA); - pio_sm_restart(pio, tx_state_machine); - pio_sm_set_enabled(pio, tx_state_machine, true); - osalSysUnlock(); -} -#else -// All this trickery is gladly not necessary for full-duplex. -static inline void enter_rx_state(void) {} -static inline void leave_rx_state(void) {} -#endif - -/** - * @brief Clear the FIFO of the RX state machine. - */ -inline void serial_transport_driver_clear(void) { - osalSysLock(); - while (!pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) { - pio_sm_clear_fifos(pio, rx_state_machine); - } - osalSysUnlock(); -} - -static inline msg_t sync_tx(sysinterval_t timeout) { - msg_t msg = MSG_OK; - osalSysLock(); - while (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) { - pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true); - msg = osalThreadSuspendTimeoutS(&tx_thread, timeout); - if (msg < MSG_OK) { - pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, false); - break; - } - } - osalSysUnlock(); - return msg; -} - -static inline bool send_impl(const uint8_t* source, const size_t size) { - size_t send = 0; - msg_t msg; - while (send < size) { - msg = sync_tx(TIME_MS2I(SERIAL_USART_TIMEOUT)); - if (msg < MSG_OK) { - return false; - } - - osalSysLock(); - while (send < size) { - if (pio_sm_is_tx_fifo_full(pio, tx_state_machine)) { - break; - } - if (send >= size) { - break; - } - pio_sm_put(pio, tx_state_machine, (uint32_t)(*source)); - source++; - send++; - } - osalSysUnlock(); - } - - return send == size; -} - -/** - * @brief Blocking send of buffer with timeout. - * - * @return true Send success. - * @return false Send failed. - */ -inline bool serial_transport_send(const uint8_t* source, const size_t size) { - leave_rx_state(); - bool result = send_impl(source, size); - enter_rx_state(); - - return result; -} - -static inline msg_t sync_rx(sysinterval_t timeout) { - msg_t msg = MSG_OK; - osalSysLock(); - while (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) { - pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true); - msg = osalThreadSuspendTimeoutS(&rx_thread, timeout); - if (msg < MSG_OK) { - pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, false); - break; - } - } - osalSysUnlock(); - return msg; -} - -static inline bool receive_impl(uint8_t* destination, const size_t size, sysinterval_t timeout) { - size_t read = 0U; - - while (read < size) { - msg_t msg = sync_rx(timeout); - if (msg < MSG_OK) { - return false; - } - osalSysLock(); - while (true) { - if (pio_sm_is_rx_fifo_empty(pio, rx_state_machine)) { - break; - } - if (read >= size) { - break; - } - *destination++ = *((uint8_t*)&pio->rxf[rx_state_machine] + 3U); - read++; - } - osalSysUnlock(); - } - - return read == size; -} - -/** - * @brief Blocking receive of size * bytes with timeout. - * - * @return true Receive success. - * @return false Receive failed, e.g. by timeout. - */ -inline bool serial_transport_receive(uint8_t* destination, const size_t size) { - return receive_impl(destination, size, TIME_MS2I(SERIAL_USART_TIMEOUT)); -} - -/** - * @brief Blocking receive of size * bytes. - * - * @return true Receive success. - * @return false Receive failed. - */ -inline bool serial_transport_receive_blocking(uint8_t* destination, const size_t size) { - return receive_impl(destination, size, TIME_INFINITE); -} - -static inline void pio_tx_init(pin_t tx_pin) { - uint pio_idx = pio_get_index(pio); - uint offset = pio_add_program(pio, &uart_tx_program); - -#if defined(SERIAL_USART_FULL_DUPLEX) - // clang-format off - iomode_t tx_pin_mode = PAL_RP_GPIO_OE | - PAL_RP_PAD_SLEWFAST | - PAL_RP_PAD_DRIVE4 | - (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1); - // clang-format on - pio_sm_set_pins_with_mask(pio, tx_state_machine, 1U << tx_pin, 1U << tx_pin); - pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true); -#else - // clang-format off - iomode_t tx_pin_mode = PAL_RP_PAD_IE | - PAL_RP_GPIO_OE | - PAL_RP_PAD_SCHMITT | - PAL_RP_PAD_PUE | - PAL_RP_PAD_SLEWFAST | - PAL_RP_PAD_DRIVE12 | - PAL_RP_IOCTRL_OEOVER_DRVINVPERI | - (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1); - // clang-format on - pio_sm_set_pins_with_mask(pio, tx_state_machine, 0U << tx_pin, 1U << tx_pin); - pio_sm_set_consecutive_pindirs(pio, tx_state_machine, tx_pin, 1U, true); -#endif - - palSetLineMode(tx_pin, tx_pin_mode); - - pio_sm_config config = pio_get_default_sm_config(); - sm_config_set_wrap(&config, offset + UART_TX_WRAP_TARGET, offset + UART_TX_WRAP); -#if defined(SERIAL_USART_FULL_DUPLEX) - sm_config_set_sideset(&config, 2, true, false); -#else - sm_config_set_sideset(&config, 2, true, true); -#endif - // OUT shifts to right, no autopull - sm_config_set_out_shift(&config, true, false, 32); - // We are mapping both OUT and side-set to the same pin, because sometimes - // we need to assert user data onto the pin (with OUT) and sometimes - // assert constant values (start/stop bit) - sm_config_set_out_pins(&config, tx_pin, 1); - sm_config_set_sideset_pins(&config, tx_pin); - // We only need TX, so get an 8-deep FIFO! - sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX); - // SM transmits 1 bit per 8 execution cycles. - float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED); - sm_config_set_clkdiv(&config, div); - pio_sm_init(pio, tx_state_machine, offset, &config); - pio_sm_set_enabled(pio, tx_state_machine, true); -} - -static inline void pio_rx_init(pin_t rx_pin) { - uint offset = pio_add_program(pio, &uart_rx_program); - -#if defined(SERIAL_USART_FULL_DUPLEX) - uint pio_idx = pio_get_index(pio); - pio_sm_set_consecutive_pindirs(pio, rx_state_machine, rx_pin, 1, false); - // clang-format off - iomode_t rx_pin_mode = PAL_RP_PAD_IE | - PAL_RP_PAD_SCHMITT | - PAL_RP_PAD_PUE | - (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1); - // clang-format on - palSetLineMode(rx_pin, rx_pin_mode); -#endif - - pio_sm_config config = pio_get_default_sm_config(); - sm_config_set_wrap(&config, offset + UART_RX_WRAP_TARGET, offset + UART_RX_WRAP); - sm_config_set_in_pins(&config, rx_pin); // for WAIT, IN - sm_config_set_jmp_pin(&config, rx_pin); // for JMP - // Shift to right, autopush disabled - sm_config_set_in_shift(&config, true, false, 32); - // Deeper FIFO as we're not doing any TX - sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_RX); - // SM transmits 1 bit per 8 execution cycles. - float div = (float)clock_get_hz(clk_sys) / (8 * SERIAL_USART_SPEED); - sm_config_set_clkdiv(&config, div); - pio_sm_init(pio, rx_state_machine, offset, &config); - pio_sm_set_enabled(pio, rx_state_machine, true); -} - -static inline void pio_init(pin_t tx_pin, pin_t rx_pin) { - uint pio_idx = pio_get_index(pio); - - /* Get PIOx peripheral out of reset state. */ - hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1); - - tx_state_machine = pio_claim_unused_sm(pio, true); - if (tx_state_machine < 0) { - dprintln("ERROR: Failed to acquire state machine for serial transmission!"); - return; - } - pio_tx_init(tx_pin); - - rx_state_machine = pio_claim_unused_sm(pio, true); - if (rx_state_machine < 0) { - dprintln("ERROR: Failed to acquire state machine for serial reception!"); - return; - } - pio_rx_init(rx_pin); - - // Enable error flag IRQ source for rx state machine - pio_set_irq0_source_enabled(pio, pis_sm0_rx_fifo_not_empty + rx_state_machine, true); - pio_set_irq0_source_enabled(pio, pis_sm0_tx_fifo_not_full + tx_state_machine, true); - pio_set_irq0_source_enabled(pio, pis_interrupt0, true); - - // Enable PIO specific interrupt vector, as the pio implementation is timing - // critical we use the highest possible priority. -#if defined(SERIAL_PIO_USE_PIO1) - nvicEnableVector(RP_PIO1_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY); -#else - nvicEnableVector(RP_PIO0_IRQ_0_NUMBER, CORTEX_MAX_KERNEL_PRIORITY); -#endif - - enter_rx_state(); -} - -/** - * @brief PIO driver specific initialization function for the master side. - */ -void serial_transport_driver_master_init(void) { -#if defined(SERIAL_USART_FULL_DUPLEX) - pin_t tx_pin = SERIAL_USART_TX_PIN; - pin_t rx_pin = SERIAL_USART_RX_PIN; -#else - pin_t tx_pin = SERIAL_USART_TX_PIN; - pin_t rx_pin = SERIAL_USART_TX_PIN; -#endif - -#if defined(SERIAL_USART_PIN_SWAP) - pio_init(rx_pin, tx_pin); -#else - pio_init(tx_pin, rx_pin); -#endif -} - -/** - * @brief PIO driver specific initialization function for the slave side. - */ -void serial_transport_driver_slave_init(void) { -#if defined(SERIAL_USART_FULL_DUPLEX) - pin_t tx_pin = SERIAL_USART_TX_PIN; - pin_t rx_pin = SERIAL_USART_RX_PIN; -#else - pin_t tx_pin = SERIAL_USART_TX_PIN; - pin_t rx_pin = SERIAL_USART_TX_PIN; -#endif - - pio_init(tx_pin, rx_pin); -} diff --git a/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c b/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c deleted file mode 100644 index 8d59e13bb2..0000000000 --- a/platforms/chibios/drivers/vendor/RP/RP2040/ws2812_vendor.c +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2022 Stefan Kerkmann (@KarlK90) -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "ws2812.h" - -// Keep this exact include order otherwise we run into naming conflicts between -// pico-sdk and rp2040.h which we don't control. -#include "hardware/timer.h" -#include "hardware/clocks.h" -#include <hal.h> -#include "hardware/pio.h" - -#include "gpio.h" -#include "debug.h" -#include "wait.h" -#include "util.h" - -#if !defined(MCU_RP) -# error PIO Driver is only available for Raspberry Pi 2040 MCUs! -#endif - -#if defined(WS2812_PIO_USE_PIO1) -static const PIO pio = pio1; -#else -static const PIO pio = pio0; -#endif - -#if !defined(RP_DMA_PRIORITY_WS2812) -# define RP_DMA_PRIORITY_WS2812 3 -#endif - -#if defined(WS2812_EXTERNAL_PULLUP) -# pragma message "The GPIOs of the RP2040 are NOT 5V tolerant! Make sure to NOT apply any voltage over 3.3V to the RGB data pin." -#endif - -/*================== WS2812 PIO TIMINGS =================*/ - -// WS2812_T1L rounded to 50ns intervals and split into two wait timings -#define PIO_T1L (WS2812_T1L / 50) -#define PIO_T1L_A (MAX(CEILING(PIO_T1L, 2) - 1, 0)) -#define PIO_T1L_B (MAX(PIO_T1L / 2 - 1, 0)) - -// WS2812_T0L rounded to 50ns intervals -#define PIO_T0L (MAX(WS2812_T0L / 50 - PIO_T1L, 0)) -#define PIO_T0L_A (MAX(PIO_T0L - 1, 0)) - -// WS2812_T0H rounded to 50ns intervals -#define PIO_T0H (WS2812_T0H / 50) -#define PIO_T0H_A MAX(PIO_T0H - 1, 0) - -// WS2812_T1H rounded to 50ns intervals and split into two wait timings -#define PIO_T1H (MAX(WS2812_T1H / 50 - PIO_T0H, 0)) -#define PIO_T1H_A (MAX((CEILING(PIO_T1H, 2) - 1), 0)) -#define PIO_T1H_B (MAX((PIO_T1H / 2) - 1, 0)) - -#if (WS2812_T0L % 50) != 0 -# pragma message "WS2812_T0L is not given in an 50ns interval, it will be rounded to the next 50ns" -#endif - -#if (WS2812_T0H % 50) != 0 -# pragma message "WS2812_T0H is not given in an 50ns interval, it will be rounded to the next 50ns" -#endif - -#if (WS2812_T1L % 50) != 0 -# pragma message "WS2812_T0L is not given in an 50ns interval, it will be rounded to the next 50ns" -#endif - -#if (WS2812_T1H % 50) != 0 -# pragma message "WS2812_T0H is not given in an 50ns interval, it will be rounded to the next 50ns" -#endif - -#if WS2812_T0L < WS2812_T1L -# error WS2812_T0L is shorter than WS2812_T1L, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T1H < WS2812_T0H -# error WS2812_T1H is shorter than WS2812_T0H, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T0L > (850 + WS2812_T1L) -# error WS2812_T0L is longer than 850ns + WS2812_T1L, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T0H > 850 -# error WS2812_T0H is longer than 850ns, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T1H > (1700 + WS2812_T0H) -# error WS2812_T1H is longer than 1700ns + WS2812_T0H, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T1L > 1700 -# error WS2812_T1L is longer than 1700ns, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T0L < (50 + WS2812_T1L) -# error WS2812_T0L is shorter than 50ns + WS2812_T1L, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T0H < 50 -# error WS2812_T0H is shorter than 50ns, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T1H < (100 + WS2812_T0H) -# error WS2812_T1H is longer than 100ns + WS2812_T0H, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -#if WS2812_T1L < 100 -# error WS2812_T1L is longer than 1700ns, this is impossible to express in the RP2040 PIO driver. Please correct your timings. -#endif - -/** - * @brief Helper macro to binary patch the delay part of an per-compiled PIO - * opcode. - */ -#define PIO_DELAY(delay, opcode) (((delay & 0xF) << 8U) | opcode) - -#define WS2812_WRAP_TARGET 0 -#define WS2812_WRAP 5 - -static const uint16_t ws2812_program_instructions[] = { - // .wrap_target - PIO_DELAY(PIO_T1L_A, 0x6021), // 0: out x, 1 side 0 // T1L (max. 1700ns) - PIO_DELAY(PIO_T1L_B, 0xa042), // 1: nop side 0 // T1L - PIO_DELAY(PIO_T0H_A, 0x1025), // 2: jmp !x, 5 side 1 // T0H (max. 850ns) - PIO_DELAY(PIO_T1H_A, 0xb042), // 3: nop side 1 // T1H (max. 1700ns + T0H) - PIO_DELAY(PIO_T1H_B, 0x1000), // 4: jmp 0 side 1 // T1H - PIO_DELAY(PIO_T0L_A, 0xa042), // 5: nop side 0 // T0L (max. 850ns + T1L) - // .wrap -}; - -static const pio_program_t ws2812_program = { - .instructions = ws2812_program_instructions, - .length = ARRAY_SIZE(ws2812_program_instructions), - .origin = -1, -}; - -static uint32_t WS2812_BUFFER[WS2812_LED_COUNT]; -static const rp_dma_channel_t* WS2812_DMA_CHANNEL; -static uint32_t RP_DMA_MODE_WS2812; -static int STATE_MACHINE = -1; - -static SEMAPHORE_DECL(TRANSFER_COUNTER, 1); -static absolute_time_t LAST_TRANSFER; - -/** - * @brief Convert RGBW value into WS2812 compatible 32-bit data word. - */ -__always_inline static uint32_t rgbw8888_to_u32(uint8_t red, uint8_t green, uint8_t blue, uint8_t white) { -#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB) - return ((uint32_t)green << 24) | ((uint32_t)red << 16) | ((uint32_t)blue << 8) | ((uint32_t)white); -#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB) - return ((uint32_t)red << 24) | ((uint32_t)green << 16) | ((uint32_t)blue << 8) | ((uint32_t)white); -#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR) - return ((uint32_t)blue << 24) | ((uint32_t)green << 16) | ((uint32_t)red << 8) | ((uint32_t)white); -#endif -} - -static void ws2812_dma_callback(void* p, uint32_t ct) { - // We assume that there is at least one frame left in the OSR even if the TX - // FIFO is already empty. - rtcnt_t time_to_completion = (pio_sm_get_tx_fifo_level(pio, STATE_MACHINE) + 1) * MAX(WS2812_T1H + WS2812_T1L, WS2812_T0H + WS2812_T0L); - -#if defined(RGBW) - time_to_completion *= 32; -#else - time_to_completion *= 24; -#endif - - // Convert from ns to us - time_to_completion /= 1000; - - update_us_since_boot(&LAST_TRANSFER, time_us_64() + time_to_completion + WS2812_TRST_US); - - osalSysLockFromISR(); - chSemSignalI(&TRANSFER_COUNTER); - osalSysUnlockFromISR(); -} - -bool ws2812_init(void) { - uint pio_idx = pio_get_index(pio); - /* Get PIOx peripheral out of reset state. */ - hal_lld_peripheral_unreset(pio_idx == 0 ? RESETS_ALLREG_PIO0 : RESETS_ALLREG_PIO1); - - // clang-format off - iomode_t rgb_pin_mode = PAL_RP_PAD_SLEWFAST | - PAL_RP_GPIO_OE | -#if defined(WS2812_EXTERNAL_PULLUP) - PAL_RP_IOCTRL_OEOVER_DRVINVPERI | -#endif - (pio_idx == 0 ? PAL_MODE_ALTERNATE_PIO0 : PAL_MODE_ALTERNATE_PIO1); - // clang-format on - - palSetLineMode(WS2812_DI_PIN, rgb_pin_mode); - - STATE_MACHINE = pio_claim_unused_sm(pio, true); - if (STATE_MACHINE < 0) { - dprintln("ERROR: Failed to acquire state machine for WS2812 output!"); - return false; - } - - uint offset = pio_add_program(pio, &ws2812_program); - - pio_sm_set_consecutive_pindirs(pio, STATE_MACHINE, WS2812_DI_PIN, 1, true); - - pio_sm_config config = pio_get_default_sm_config(); - sm_config_set_wrap(&config, offset + WS2812_WRAP_TARGET, offset + WS2812_WRAP); - sm_config_set_sideset_pins(&config, WS2812_DI_PIN); - sm_config_set_fifo_join(&config, PIO_FIFO_JOIN_TX); - -#if defined(WS2812_EXTERNAL_PULLUP) - /* Instruct side-set to change the pin-directions instead of outputting - * a logic level. We generate our levels the following way: - * - * 1: Set RGB data pin to high impedance input and let the pull-up drive the - * signal high. - * - * 0: Set RGB data pin to low impedance output and drive the pin low. - */ - sm_config_set_sideset(&config, 1, false, true); -#else - sm_config_set_sideset(&config, 1, false, false); -#endif - -#if defined(RGBW) - sm_config_set_out_shift(&config, false, true, 32); -#else - sm_config_set_out_shift(&config, false, true, 24); -#endif - - // Every instruction takes 50ns to execute with a clock speed of 20 MHz, - // giving the WS2812 PIO driver its time resolution - float div = clock_get_hz(clk_sys) / (20.0f * MHZ); - sm_config_set_clkdiv(&config, div); - - pio_sm_init(pio, STATE_MACHINE, offset, &config); - pio_sm_set_enabled(pio, STATE_MACHINE, true); - - WS2812_DMA_CHANNEL = dmaChannelAlloc(RP_DMA_CHANNEL_ID_ANY, RP_DMA_PRIORITY_WS2812, (rp_dmaisr_t)ws2812_dma_callback, NULL); - dmaChannelEnableInterruptX(WS2812_DMA_CHANNEL); - dmaChannelSetDestinationX(WS2812_DMA_CHANNEL, (uint32_t)&pio->txf[STATE_MACHINE]); - - // clang-format off - RP_DMA_MODE_WS2812 = DMA_CTRL_TRIG_INCR_READ | - DMA_CTRL_TRIG_DATA_SIZE_WORD | - DMA_CTRL_TRIG_TREQ_SEL(pio == pio0 ? STATE_MACHINE : STATE_MACHINE + 8) | - DMA_CTRL_TRIG_PRIORITY(RP_DMA_PRIORITY_WS2812); - // clang-format on - - return true; -} - -static inline void sync_ws2812_transfer(void) { - if (chSemWaitTimeout(&TRANSFER_COUNTER, TIME_MS2I(WS2812_LED_COUNT)) == MSG_TIMEOUT) { - // Abort the synchronization if we have to wait longer than the total - // count of LEDs in milliseconds. This is safely much longer than it - // would take to push all the data out. - dprintln("ERROR: WS2812 DMA transfer has stalled, aborting!"); - dmaChannelDisableX(WS2812_DMA_CHANNEL); - pio_sm_clear_fifos(pio, STATE_MACHINE); - pio_sm_restart(pio, STATE_MACHINE); - chSemReset(&TRANSFER_COUNTER, 0); - wait_us(WS2812_TRST_US); - return; - } - - // Busy wait until last transfer has finished - busy_wait_until(LAST_TRANSFER); -} - -void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { - static bool is_initialized = false; - if (unlikely(!is_initialized)) { - is_initialized = ws2812_init(); - } - - sync_ws2812_transfer(); - - for (int i = 0; i < leds; i++) { -#if defined(RGBW) - WS2812_BUFFER[i] = rgbw8888_to_u32(ledarray[i].r, ledarray[i].g, ledarray[i].b, ledarray[i].w); -#else - WS2812_BUFFER[i] = rgbw8888_to_u32(ledarray[i].r, ledarray[i].g, ledarray[i].b, 0); -#endif - } - - dmaChannelSetSourceX(WS2812_DMA_CHANNEL, (uint32_t)WS2812_BUFFER); - dmaChannelSetCounterX(WS2812_DMA_CHANNEL, leds); - dmaChannelSetModeX(WS2812_DMA_CHANNEL, RP_DMA_MODE_WS2812); - dmaChannelEnableX(WS2812_DMA_CHANNEL); -} diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c deleted file mode 100644 index 3e4f5ffb89..0000000000 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl.c +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2022 Nick Brassel (@tzarc) -// SPDX-License-Identifier: GPL-2.0-or-later -#include <stdbool.h> -#include <hal.h> -#include "timer.h" -#include "wear_leveling.h" -#include "wear_leveling_internal.h" - -static flash_offset_t base_offset = UINT32_MAX; - -#if defined(WEAR_LEVELING_EFL_FIRST_SECTOR) -static flash_sector_t first_sector = WEAR_LEVELING_EFL_FIRST_SECTOR; -#else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR) -static flash_sector_t first_sector = UINT16_MAX; -#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR) - -static flash_sector_t sector_count = UINT16_MAX; -static BaseFlash * flash; - -// "Automatic" detection of the flash size -- ideally ChibiOS would have this already, but alas, it doesn't. -static inline uint32_t detect_flash_size(void) { -#if defined(WEAR_LEVELING_EFL_FLASH_SIZE) - return WEAR_LEVELING_EFL_FLASH_SIZE; -#elif defined(FLASH_BANK_SIZE) - return FLASH_BANK_SIZE; -#elif defined(FLASH_SIZE) - return FLASH_SIZE; -#elif defined(FLASHSIZE_BASE) -# if defined(QMK_MCU_SERIES_STM32F0XX) || defined(QMK_MCU_SERIES_STM32F1XX) || defined(QMK_MCU_SERIES_STM32F3XX) || defined(QMK_MCU_SERIES_STM32F4XX) || defined(QMK_MCU_SERIES_STM32G4XX) || defined(QMK_MCU_SERIES_STM32L0XX) || defined(QMK_MCU_SERIES_STM32L4XX) || defined(QMK_MCU_SERIES_GD32VF103) - return ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) << 10U; // this register has the flash size in kB, so we convert it to bytes -# elif defined(QMK_MCU_SERIES_STM32L1XX) -# error This MCU family has an uncommon flash size register definition and has not been implemented. Perhaps try using the true EEPROM on the MCU instead? -# endif -#else -# error Unknown flash size definition. - return 0; -#endif -} - -bool backing_store_init(void) { - bs_dprintf("Init\n"); - flash = (BaseFlash *)&EFLD1; - - // Need to re-lock the EFL, as if we've just had the bootloader executing it'll already be unlocked. - backing_store_lock(); - - const flash_descriptor_t *desc = flashGetDescriptor(flash); - uint32_t counter = 0; - uint32_t flash_size = detect_flash_size(); - -#if defined(WEAR_LEVELING_EFL_FIRST_SECTOR) - - // Work out how many sectors we want to use, working forwards from the first sector specified - for (flash_sector_t i = 0; i < desc->sectors_count - first_sector; ++i) { - counter += flashGetSectorSize(flash, first_sector + i); - if (counter >= (WEAR_LEVELING_BACKING_SIZE)) { - sector_count = i + 1; - base_offset = flashGetSectorOffset(flash, first_sector); - break; - } - } - if (sector_count == UINT16_MAX || base_offset >= flash_size) { - // We didn't get the required number of sectors. Can't do anything here. Fault. - chSysHalt("Invalid sector count intended to be used with wear_leveling"); - } - -#else // defined(WEAR_LEVELING_EFL_FIRST_SECTOR) - - // Work out how many sectors we want to use, working backwards from the end of the flash - flash_sector_t last_sector = desc->sectors_count; - for (flash_sector_t i = 0; i < desc->sectors_count; ++i) { - first_sector = desc->sectors_count - i - 1; - if (flashGetSectorOffset(flash, first_sector) >= flash_size) { - last_sector = first_sector; - continue; - } - counter += flashGetSectorSize(flash, first_sector); - if (counter >= (WEAR_LEVELING_BACKING_SIZE)) { - sector_count = last_sector - first_sector; - base_offset = flashGetSectorOffset(flash, first_sector); - break; - } - } - -#endif // defined(WEAR_LEVELING_EFL_FIRST_SECTOR) - - return true; -} - -bool backing_store_unlock(void) { - bs_dprintf("Unlock\n"); - return eflStart(&EFLD1, NULL) == HAL_RET_SUCCESS; -} - -bool backing_store_erase(void) { -#ifdef WEAR_LEVELING_DEBUG_OUTPUT - uint32_t start = timer_read32(); -#endif - - bool ret = true; - flash_error_t status; - for (int i = 0; i < sector_count; ++i) { - // Kick off the sector erase - status = flashStartEraseSector(flash, first_sector + i); - if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) { - ret = false; - } - - // Wait for the erase to complete - status = flashWaitErase(flash); - if (status != FLASH_NO_ERROR && status != FLASH_BUSY_ERASING) { - ret = false; - } - } - - bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start))); - return ret; -} - -bool backing_store_write(uint32_t address, backing_store_int_t value) { - uint32_t offset = (base_offset + address); - bs_dprintf("Write "); - wl_dump(offset, &value, sizeof(value)); - value = ~value; - return flashProgram(flash, offset, sizeof(value), (const uint8_t *)&value) == FLASH_NO_ERROR; -} - -bool backing_store_lock(void) { - bs_dprintf("Lock \n"); - eflStop(&EFLD1); - return true; -} - -bool backing_store_read(uint32_t address, backing_store_int_t *value) { - uint32_t offset = (base_offset + address); - backing_store_int_t *loc = (backing_store_int_t *)flashGetOffsetAddress(flash, offset); - *value = ~(*loc); - bs_dprintf("Read "); - wl_dump(offset, value, sizeof(backing_store_int_t)); - return true; -} diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h deleted file mode 100644 index e74cf85efd..0000000000 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_efl_config.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2022 Nick Brassel (@tzarc) -// SPDX-License-Identifier: GPL-2.0-or-later -#pragma once - -#ifndef __ASSEMBLER__ -# include <hal.h> -#endif - -// Work out how many bytes per write to internal flash -#ifndef BACKING_STORE_WRITE_SIZE -// These need to match EFL's XXXXXX_FLASH_LINE_SIZE, see associated code in `lib/chibios/os/hal/ports/**/hal_efl_lld.c`, -// or associated `stm32_registry.h` for the MCU in question (or equivalent for the family). -# if defined(QMK_MCU_SERIES_GD32VF103) -# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c -# elif defined(QMK_MCU_FAMILY_NUC123) -# define BACKING_STORE_WRITE_SIZE 4 // from hal_efl_lld.c -# elif defined(QMK_MCU_FAMILY_WB32) -# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c -# elif defined(QMK_MCU_FAMILY_STM32) -# if defined(STM32_FLASH_LINE_SIZE) // from some family's stm32_registry.h file -# define BACKING_STORE_WRITE_SIZE (STM32_FLASH_LINE_SIZE) -# else -# if defined(QMK_MCU_SERIES_STM32F0XX) -# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c -# elif defined(QMK_MCU_SERIES_STM32F1XX) -# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c -# elif defined(QMK_MCU_SERIES_STM32F3XX) -# define BACKING_STORE_WRITE_SIZE 2 // from hal_efl_lld.c -# elif defined(QMK_MCU_SERIES_STM32F4XX) -# define BACKING_STORE_WRITE_SIZE (1 << STM32_FLASH_PSIZE) // from hal_efl_lld.c -# elif defined(QMK_MCU_SERIES_STM32L4XX) -# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c -# elif defined(QMK_MCU_SERIES_STM32G0XX) -# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c -# elif defined(QMK_MCU_SERIES_STM32G4XX) -# define BACKING_STORE_WRITE_SIZE 8 // from hal_efl_lld.c -# else -# error "ChibiOS hasn't defined STM32_FLASH_LINE_SIZE, and could not automatically determine BACKING_STORE_WRITE_SIZE" // normally defined in stm32_registry.h, should be set by STM32_FLASH_LINE_SIZE -# endif -# endif -# else -# error "Could not automatically determine BACKING_STORE_WRITE_SIZE" -# endif -#endif - -// 2kB backing space allocated -#ifndef WEAR_LEVELING_BACKING_SIZE -# define WEAR_LEVELING_BACKING_SIZE 2048 -#endif // WEAR_LEVELING_BACKING_SIZE - -// 1kB logical EEPROM -#ifndef WEAR_LEVELING_LOGICAL_SIZE -# define WEAR_LEVELING_LOGICAL_SIZE 1024 -#endif // WEAR_LEVELING_LOGICAL_SIZE diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c deleted file mode 100644 index 7c6fd2d808..0000000000 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy.c +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2022 Nick Brassel (@tzarc) -// SPDX-License-Identifier: GPL-2.0-or-later -#include <stdbool.h> -#include <hal.h> -#include "timer.h" -#include "wear_leveling.h" -#include "wear_leveling_internal.h" -#include "legacy_flash_ops.h" - -bool backing_store_init(void) { - bs_dprintf("Init\n"); - return true; -} - -bool backing_store_unlock(void) { - bs_dprintf("Unlock\n"); - FLASH_Unlock(); - return true; -} - -bool backing_store_erase(void) { -#ifdef WEAR_LEVELING_DEBUG_OUTPUT - uint32_t start = timer_read32(); -#endif - - bool ret = true; - FLASH_Status status; - for (int i = 0; i < (WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT); ++i) { - status = FLASH_ErasePage(WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS + (i * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))); - if (status != FLASH_COMPLETE) { - ret = false; - } - } - - bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start))); - return ret; -} - -bool backing_store_write(uint32_t address, backing_store_int_t value) { - uint32_t offset = ((WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS) + address); - bs_dprintf("Write "); - wl_dump(offset, &value, sizeof(backing_store_int_t)); - return FLASH_ProgramHalfWord(offset, ~value) == FLASH_COMPLETE; -} - -bool backing_store_lock(void) { - bs_dprintf("Lock \n"); - FLASH_Lock(); - return true; -} - -bool backing_store_read(uint32_t address, backing_store_int_t* value) { - uint32_t offset = ((WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS) + address); - backing_store_int_t* loc = (backing_store_int_t*)offset; - *value = ~(*loc); - bs_dprintf("Read "); - wl_dump(offset, loc, sizeof(backing_store_int_t)); - return true; -} diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h deleted file mode 100644 index e64cab87d1..0000000000 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_legacy_config.h +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2022 Nick Brassel (@tzarc) -// SPDX-License-Identifier: GPL-2.0-or-later -#pragma once - -// Work out the page size to use -#ifndef WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE -# if defined(QMK_MCU_STM32F042) -# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 1024 -# elif defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072) -# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 2048 -# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411) -# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE 16384 -# endif -#endif - -// Work out how much flash space we have -#ifndef WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE -# define WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE ((*(uint32_t *)FLASHSIZE_BASE) & 0xFFFFU) // in kB -#endif - -// The base location of program memory -#ifndef WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE -# define WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE 0x08000000 -#endif - -// The number of pages to use -#ifndef WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT -# if defined(QMK_MCU_STM32F042) -# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 2 -# elif defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072) -# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 1 -# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411) -# define WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT 1 -# endif -#endif - -// The origin of the emulated eeprom -#ifndef WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS -# if defined(QMK_MCU_STM32F042) || defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072) -# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS ((uintptr_t)(WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE) + WEAR_LEVELING_LEGACY_EMULATION_FLASH_SIZE * 1024 - (WEAR_LEVELING_LEGACY_EMULATION_PAGE_COUNT * WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE)) -# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411) -# if defined(BOOTLOADER_STM32_DFU) -# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS (WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE + (1 * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))) // +16k -# elif defined(BOOTLOADER_TINYUF2) -# define WEAR_LEVELING_LEGACY_EMULATION_BASE_PAGE_ADDRESS (WEAR_LEVELING_LEGACY_EMULATION_FLASH_BASE + (3 * (WEAR_LEVELING_LEGACY_EMULATION_PAGE_SIZE))) // +48k -# endif -# endif -#endif - -// 2-byte writes -#ifndef BACKING_STORE_WRITE_SIZE -# define BACKING_STORE_WRITE_SIZE 2 -#endif - -// The amount of space to use for the entire set of emulation -#ifndef WEAR_LEVELING_BACKING_SIZE -# if defined(QMK_MCU_STM32F042) || defined(QMK_MCU_STM32F070) || defined(QMK_MCU_STM32F072) -# define WEAR_LEVELING_BACKING_SIZE 2048 -# elif defined(QMK_MCU_STM32F401) || defined(QMK_MCU_STM32F411) -# define WEAR_LEVELING_BACKING_SIZE 16384 -# endif -#endif - -// The logical amount of eeprom available -#ifndef WEAR_LEVELING_LOGICAL_SIZE -# define WEAR_LEVELING_LOGICAL_SIZE 1024 -#endif diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c deleted file mode 100644 index 640628e1e9..0000000000 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash.c +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. - * Copyright (c) 2022 Nick Brassel (@tzarc) - * Copyright (c) 2022 Stefan Kerkmann (@KarlK90) - * - * SPDX-License-Identifier: BSD-3-Clause - */ - -#include "pico/bootrom.h" -#include "hardware/flash.h" -#include "hardware/sync.h" -#include "hardware/structs/ssi.h" -#include "hardware/structs/ioqspi.h" - -#include <stdbool.h> -#include "timer.h" -#include "wear_leveling.h" -#include "wear_leveling_internal.h" - -#ifndef WEAR_LEVELING_RP2040_FLASH_BULK_COUNT -# define WEAR_LEVELING_RP2040_FLASH_BULK_COUNT 64 -#endif // WEAR_LEVELING_RP2040_FLASH_BULK_COUNT - -#define FLASHCMD_PAGE_PROGRAM 0x02 -#define FLASHCMD_READ_STATUS 0x05 -#define FLASHCMD_WRITE_ENABLE 0x06 - -extern uint8_t BOOT2_ROM[256]; -static uint32_t BOOT2_ROM_RAM[64]; - -static ssi_hw_t *const ssi = (ssi_hw_t *)XIP_SSI_BASE; - -// Sanity check -check_hw_layout(ssi_hw_t, ssienr, SSI_SSIENR_OFFSET); -check_hw_layout(ssi_hw_t, spi_ctrlr0, SSI_SPI_CTRLR0_OFFSET); - -static void __no_inline_not_in_flash_func(flash_enable_xip_via_boot2)(void) { - ((void (*)(void))BOOT2_ROM_RAM + 1)(); -} - -// Bitbanging the chip select using IO overrides, in case RAM-resident IRQs -// are still running, and the FIFO bottoms out. (the bootrom does the same) -static void __no_inline_not_in_flash_func(flash_cs_force)(bool high) { - uint32_t field_val = high ? IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW; - hw_write_masked(&ioqspi_hw->io[1].ctrl, field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB, IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS); -} - -// Also allow any unbounded loops to check whether the above abort condition -// was asserted, and terminate early -static int __no_inline_not_in_flash_func(flash_was_aborted)(void) { - return *(io_rw_32 *)(IO_QSPI_BASE + IO_QSPI_GPIO_QSPI_SD1_CTRL_OFFSET) & IO_QSPI_GPIO_QSPI_SD1_CTRL_INOVER_BITS; -} - -// Put bytes from one buffer, and get bytes into another buffer. -// These can be the same buffer. -// If tx is NULL then send zeroes. -// If rx is NULL then all read data will be dropped. -// -// If rx_skip is nonzero, this many bytes will first be consumed from the FIFO, -// before reading a further count bytes into *rx. -// E.g. if you have written a command+address just before calling this function. -static void __no_inline_not_in_flash_func(flash_put_get)(const uint8_t *tx, uint8_t *rx, size_t count, size_t rx_skip) { - // Make sure there is never more data in flight than the depth of the RX - // FIFO. Otherwise, when we are interrupted for long periods, hardware - // will overflow the RX FIFO. - const uint max_in_flight = 16 - 2; // account for data internal to SSI - size_t tx_count = count; - size_t rx_count = count; - while (tx_count || rx_skip || rx_count) { - // NB order of reads, for pessimism rather than optimism - uint32_t tx_level = ssi_hw->txflr; - uint32_t rx_level = ssi_hw->rxflr; - bool did_something = false; // Expect this to be folded into control flow, not register - if (tx_count && tx_level + rx_level < max_in_flight) { - ssi->dr0 = (uint32_t)(tx ? *tx++ : 0); - --tx_count; - did_something = true; - } - if (rx_level) { - uint8_t rxbyte = ssi->dr0; - did_something = true; - if (rx_skip) { - --rx_skip; - } else { - if (rx) *rx++ = rxbyte; - --rx_count; - } - } - // APB load costs 4 cycles, so only do it on idle loops (our budget is - // 48 cyc/byte) - if (!did_something && __builtin_expect(flash_was_aborted(), 0)) break; - } - flash_cs_force(1); -} - -// Convenience wrapper for above -// (And it's hard for the debug host to get the tight timing between -// cmd DR0 write and the remaining data) -static void __no_inline_not_in_flash_func(_flash_do_cmd)(uint8_t cmd, const uint8_t *tx, uint8_t *rx, size_t count) { - flash_cs_force(0); - ssi->dr0 = cmd; - flash_put_get(tx, rx, count, 1); -} - -// Timing of this one is critical, so do not expose the symbol to debugger etc -static void __no_inline_not_in_flash_func(flash_put_cmd_addr)(uint8_t cmd, uint32_t addr) { - flash_cs_force(0); - addr |= cmd << 24; - for (int i = 0; i < 4; ++i) { - ssi->dr0 = addr >> 24; - addr <<= 8; - } -} - -// Poll the flash status register until the busy bit (LSB) clears -static void __no_inline_not_in_flash_func(flash_wait_ready)(void) { - uint8_t stat; - do { - _flash_do_cmd(FLASHCMD_READ_STATUS, NULL, &stat, 1); - } while (stat & 0x1 && !flash_was_aborted()); -} - -// Set the WEL bit (needed before any program/erase operation) -static void __no_inline_not_in_flash_func(flash_enable_write)(void) { - _flash_do_cmd(FLASHCMD_WRITE_ENABLE, NULL, NULL, 0); -} - -static void __no_inline_not_in_flash_func(pico_program_bulk)(uint32_t flash_address, backing_store_int_t *values, size_t item_count) { - rom_connect_internal_flash_fn connect_internal_flash = (rom_connect_internal_flash_fn)rom_func_lookup_inline(ROM_FUNC_CONNECT_INTERNAL_FLASH); - rom_flash_exit_xip_fn flash_exit_xip = (rom_flash_exit_xip_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_EXIT_XIP); - rom_flash_flush_cache_fn flash_flush_cache = (rom_flash_flush_cache_fn)rom_func_lookup_inline(ROM_FUNC_FLASH_FLUSH_CACHE); - assert(connect_internal_flash && flash_exit_xip && flash_flush_cache); - - static backing_store_int_t bulk_write_buffer[WEAR_LEVELING_RP2040_FLASH_BULK_COUNT]; - - while (item_count) { - size_t batch_size = MIN(item_count, WEAR_LEVELING_RP2040_FLASH_BULK_COUNT); - for (size_t i = 0; i < batch_size; i++, values++, item_count--) { - bulk_write_buffer[i] = ~(*values); - } - __compiler_memory_barrier(); - - connect_internal_flash(); - flash_exit_xip(); - flash_enable_write(); - - flash_put_cmd_addr(FLASHCMD_PAGE_PROGRAM, flash_address); - flash_put_get((uint8_t *)bulk_write_buffer, NULL, batch_size * sizeof(backing_store_int_t), 4); - flash_wait_ready(); - flash_address += batch_size * sizeof(backing_store_int_t); - - flash_flush_cache(); - flash_enable_xip_via_boot2(); - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -// QMK Wear-Leveling Backing Store implementation - -static int interrupts; - -bool backing_store_init(void) { - bs_dprintf("Init\n"); - memcpy(BOOT2_ROM_RAM, BOOT2_ROM, sizeof(BOOT2_ROM)); - __compiler_memory_barrier(); - return true; -} - -bool backing_store_unlock(void) { - bs_dprintf("Unlock\n"); - return true; -} - -bool backing_store_erase(void) { -#ifdef WEAR_LEVELING_DEBUG_OUTPUT - uint32_t start = timer_read32(); -#endif - - // Ensure the backing size can be cleanly subtracted from the flash size without alignment issues. - _Static_assert((WEAR_LEVELING_BACKING_SIZE) % (FLASH_SECTOR_SIZE) == 0, "Backing size must be a multiple of FLASH_SECTOR_SIZE"); - - interrupts = save_and_disable_interrupts(); - flash_range_erase((WEAR_LEVELING_RP2040_FLASH_BASE), (WEAR_LEVELING_BACKING_SIZE)); - restore_interrupts(interrupts); - - bs_dprintf("Backing store erase took %ldms to complete\n", ((long)(timer_read32() - start))); - return true; -} - -bool backing_store_write(uint32_t address, backing_store_int_t value) { - return backing_store_write_bulk(address, &value, 1); -} - -bool backing_store_write_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) { - uint32_t offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address; - bs_dprintf("Write "); - wl_dump(offset, values, sizeof(backing_store_int_t) * item_count); - interrupts = save_and_disable_interrupts(); - pico_program_bulk(offset, values, item_count); - restore_interrupts(interrupts); - return true; -} - -bool backing_store_lock(void) { - return true; -} - -bool backing_store_read(uint32_t address, backing_store_int_t *value) { - return backing_store_read_bulk(address, value, 1); -} - -bool backing_store_read_bulk(uint32_t address, backing_store_int_t *values, size_t item_count) { - uint32_t offset = (WEAR_LEVELING_RP2040_FLASH_BASE) + address; - backing_store_int_t *loc = (backing_store_int_t *)((XIP_BASE) + offset); - for (size_t i = 0; i < item_count; ++i) { - values[i] = ~loc[i]; - } - bs_dprintf("Read "); - wl_dump(offset, values, item_count * sizeof(backing_store_int_t)); - return true; -} diff --git a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h b/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h deleted file mode 100644 index 93a9aa0372..0000000000 --- a/platforms/chibios/drivers/wear_leveling/wear_leveling_rp2040_flash_config.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2022 Nick Brassel (@tzarc) -// SPDX-License-Identifier: GPL-2.0-or-later -#pragma once - -#ifndef __ASSEMBLER__ -# include "hardware/flash.h" -#endif - -// 2-byte writes -#ifndef BACKING_STORE_WRITE_SIZE -# define BACKING_STORE_WRITE_SIZE 2 -#endif - -// 64kB backing space allocated -#ifndef WEAR_LEVELING_BACKING_SIZE -# define WEAR_LEVELING_BACKING_SIZE 8192 -#endif // WEAR_LEVELING_BACKING_SIZE - -// 32kB logical EEPROM -#ifndef WEAR_LEVELING_LOGICAL_SIZE -# define WEAR_LEVELING_LOGICAL_SIZE 4096 -#endif // WEAR_LEVELING_LOGICAL_SIZE - -// Define how much flash space we have (defaults to lib/pico-sdk/src/boards/include/boards/***) -#ifndef WEAR_LEVELING_RP2040_FLASH_SIZE -# define WEAR_LEVELING_RP2040_FLASH_SIZE (PICO_FLASH_SIZE_BYTES) -#endif - -// Define the location of emulated EEPROM -#ifndef WEAR_LEVELING_RP2040_FLASH_BASE -# define WEAR_LEVELING_RP2040_FLASH_BASE ((WEAR_LEVELING_RP2040_FLASH_SIZE) - (WEAR_LEVELING_BACKING_SIZE)) -#endif diff --git a/platforms/chibios/drivers/ws2812_bitbang.c b/platforms/chibios/drivers/ws2812_bitbang.c deleted file mode 100644 index c55e0f654c..0000000000 --- a/platforms/chibios/drivers/ws2812_bitbang.c +++ /dev/null @@ -1,109 +0,0 @@ -#include "ws2812.h" - -#include "gpio.h" -#include "chibios_config.h" - -/* Adapted from https://github.com/bigjosh/SimpleNeoPixelDemo/ */ - -#ifndef NOP_FUDGE -# if defined(STM32F0XX) || defined(STM32F1XX) || defined(GD32VF103) || defined(STM32F3XX) || defined(STM32F4XX) || defined(STM32L0XX) || defined(WB32F3G71xx) || defined(WB32FQ95xx) -# 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 - -// Push Pull or Open Drain Configuration -// Default Push Pull -#ifndef WS2812_EXTERNAL_PULLUP -# define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_PUSHPULL -#else -# define WS2812_OUTPUT_MODE PAL_MODE_OUTPUT_OPENDRAIN -#endif - -// 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. -#ifndef WS2812_RES -# define WS2812_RES (1000 * WS2812_TRST_US) // Width of the low gap between bits to cause a frame to latch -#endif - -#define NUMBER_NOPS 6 -#define CYCLES_PER_SEC (CPU_CLOCK / 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) - -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(WS2812_DI_PIN); - wait_ns(WS2812_T1H); - writePinLow(WS2812_DI_PIN); - wait_ns(WS2812_T1L); - } else { - // 0 - writePinHigh(WS2812_DI_PIN); - wait_ns(WS2812_T0H); - writePinLow(WS2812_DI_PIN); - wait_ns(WS2812_T0L); - } - } -} - -void ws2812_init(void) { - palSetLineMode(WS2812_DI_PIN, WS2812_OUTPUT_MODE); -} - -// 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 -#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB) - sendByte(ledarray[i].g); - sendByte(ledarray[i].r); - sendByte(ledarray[i].b); -#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB) - sendByte(ledarray[i].r); - sendByte(ledarray[i].g); - sendByte(ledarray[i].b); -#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR) - sendByte(ledarray[i].b); - sendByte(ledarray[i].g); - sendByte(ledarray[i].r); -#endif - -#ifdef RGBW - sendByte(ledarray[i].w); -#endif - } - - wait_ns(WS2812_RES); - - chSysUnlock(); -} diff --git a/platforms/chibios/drivers/ws2812_pwm.c b/platforms/chibios/drivers/ws2812_pwm.c deleted file mode 100644 index cfee547a82..0000000000 --- a/platforms/chibios/drivers/ws2812_pwm.c +++ /dev/null @@ -1,396 +0,0 @@ -#include "ws2812.h" -#include "gpio.h" -#include "chibios_config.h" - -/* Adapted from https://github.com/joewa/WS2812-LED-Driver_ChibiOS/ */ - -#ifdef RGBW -# define WS2812_CHANNELS 4 -#else -# define WS2812_CHANNELS 3 -#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 -#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE) && !defined(WS2812_DMAMUX_ID) -# error "please consult your MCU's datasheet and specify in your config.h: #define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM?_UP" -#endif - -/* Summarize https://www.st.com/resource/en/application_note/an4013-stm32-crossseries-timer-overview-stmicroelectronics.pdf to - * figure out if we are using a 32bit timer. This is needed to setup the DMA controller correctly. - * Ignore STM32H7XX and STM32U5XX as they are not supported by ChibiOS. - */ -#if !defined(STM32F1XX) && !defined(STM32L0XX) && !defined(STM32L1XX) -# define WS2812_PWM_TIMER_32BIT_PWMD2 1 -#endif -#if !defined(STM32F1XX) -# define WS2812_PWM_TIMER_32BIT_PWMD5 1 -#endif -#define WS2812_CONCAT1(a, b) a##b -#define WS2812_CONCAT(a, b) WS2812_CONCAT1(a, b) -#if WS2812_CONCAT(WS2812_PWM_TIMER_32BIT_, WS2812_PWM_DRIVER) -# define WS2812_PWM_TIMER_32BIT -#endif - -#ifndef WS2812_PWM_COMPLEMENTARY_OUTPUT -# define WS2812_PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH -#else -# if !STM32_PWM_USE_ADVANCED -# error "WS2812_PWM_COMPLEMENTARY_OUTPUT requires STM32_PWM_USE_ADVANCED == TRUE" -# endif -# define WS2812_PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH -#endif - -// Push Pull or Open Drain Configuration -// Default Push Pull -#ifndef WS2812_EXTERNAL_PULLUP -# if defined(USE_GPIOV1) -# define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL -# else -# define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_PWM_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL | PAL_OUTPUT_SPEED_HIGHEST | PAL_PUPDR_FLOATING -# endif -#else -# if defined(USE_GPIOV1) -# define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE_OPENDRAIN -# else -# define WS2812_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_PWM_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN | PAL_OUTPUT_SPEED_HIGHEST | PAL_PUPDR_FLOATING -# endif -#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 (CPU_CLOCK / 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 is defined in WS2812_TRST_US. - * Calculate the number of zeroes to add at the end assuming 1.25 uS/bit: - */ -#define WS2812_COLOR_BITS (WS2812_CHANNELS * 8) -#define WS2812_RESET_BIT_N (1000 * WS2812_TRST_US / WS2812_TIMING) -#define WS2812_COLOR_BIT_N (WS2812_LED_COUNT * WS2812_COLOR_BITS) /**< 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)) -#if (WS2812_DUTYCYCLE_0 > 255) -# error WS2812 PWM driver: High period for a 0 is more than a byte -#endif - -/** - * @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)) -#if (WS2812_DUTYCYCLE_1 > 255) -# error WS2812 PWM driver: High period for a 1 is more than a byte -#endif - -/* --- 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 WS2812_LED_COUNT) - * @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) (WS2812_COLOR_BITS * (led) + 8 * (byte) + (7 - (bit))) - -#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB) -/** - * @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 WS2812_LED_COUNT) - * @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 WS2812_LED_COUNT) - * @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 WS2812_LED_COUNT) - * @param[in] bit: The bit index [0, 7] - * - * @return The bit index - */ -# define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 2, (bit)) - -#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB) -/** - * @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 WS2812_LED_COUNT) - * @param[in] bit: The bit number [0, 7] - * - * @return The bit index - */ -# define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 0, (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 WS2812_LED_COUNT) - * @param[in] bit: The bit number [0, 7] - * - * @return The bit index - */ -# define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 1, (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 WS2812_LED_COUNT) - * @param[in] bit: The bit index [0, 7] - * - * @return The bit index - */ -# define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 2, (bit)) - -#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR) -/** - * @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 WS2812_LED_COUNT) - * @param[in] bit: The bit number [0, 7] - * - * @return The bit index - */ -# define WS2812_RED_BIT(led, bit) WS2812_BIT((led), 2, (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 WS2812_LED_COUNT) - * @param[in] bit: The bit number [0, 7] - * - * @return The bit index - */ -# define WS2812_GREEN_BIT(led, bit) WS2812_BIT((led), 1, (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 WS2812_LED_COUNT) - * @param[in] bit: The bit index [0, 7] - * - * @return The bit index - */ -# define WS2812_BLUE_BIT(led, bit) WS2812_BIT((led), 0, (bit)) -#endif - -#ifdef RGBW -/** - * @brief Determine the index in @ref ws2812_frame_buffer "the frame buffer" of a given white bit - * - * @note The white byte is the last byte in the color packet - * - * @param[in] led: The led index [0, @ref WS2812_LED_N) - * @param[in] bit: The bit index [0, 7] - * - * @return The bit index - */ -# define WS2812_WHITE_BIT(led, bit) WS2812_BIT((led), 3, (bit)) -#endif - -/* --- PRIVATE VARIABLES ---------------------------------------------------- */ - -// STM32F2XX, STM32F4XX and STM32F7XX do NOT zero pad DMA transfers of unequal data width. Buffer width must match TIMx CCR. -// For all other STM32 DMA transfer will automatically zero pad. We only need to set the right peripheral width. -#if defined(STM32F2XX) || defined(STM32F4XX) || defined(STM32F7XX) -# if defined(WS2812_PWM_TIMER_32BIT) -# define WS2812_DMA_MEMORY_WIDTH STM32_DMA_CR_MSIZE_WORD -# define WS2812_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_WORD -typedef uint32_t ws2812_buffer_t; -# else -# define WS2812_DMA_MEMORY_WIDTH STM32_DMA_CR_MSIZE_HWORD -# define WS2812_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_HWORD -typedef uint16_t ws2812_buffer_t; -# endif -#else -# define WS2812_DMA_MEMORY_WIDTH STM32_DMA_CR_MSIZE_BYTE -# if defined(WS2812_PWM_TIMER_32BIT) -# define WS2812_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_WORD -# else -# define WS2812_DMA_PERIPHERAL_WIDTH STM32_DMA_CR_PSIZE_HWORD -# endif -typedef uint8_t ws2812_buffer_t; -#endif - -static ws2812_buffer_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 - - palSetLineMode(WS2812_DI_PIN, WS2812_OUTPUT_MODE); - - // 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 = WS2812_PWM_OUTPUT_MODE, .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 -#if defined(WB32F3G71xx) || defined(WB32FQ95xx) - dmaStreamAlloc(WS2812_DMA_STREAM - WB32_DMA_STREAM(0), 10, NULL, NULL); - dmaStreamSetSource(WS2812_DMA_STREAM, ws2812_frame_buffer); - dmaStreamSetDestination(WS2812_DMA_STREAM, &(WS2812_PWM_DRIVER.tim->CCR[WS2812_PWM_CHANNEL - 1])); // Ziel ist der An-Zeit im Cap-Comp-Register - dmaStreamSetMode(WS2812_DMA_STREAM, WB32_DMA_CHCFG_HWHIF(WS2812_DMA_CHANNEL) | WB32_DMA_CHCFG_DIR_M2P | WB32_DMA_CHCFG_PSIZE_WORD | WB32_DMA_CHCFG_MSIZE_WORD | WB32_DMA_CHCFG_MINC | WB32_DMA_CHCFG_CIRC | WB32_DMA_CHCFG_TCIE | WB32_DMA_CHCFG_PL(3)); -#else - dmaStreamAlloc(WS2812_DMA_STREAM - STM32_DMA_STREAM(0), 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); - dmaStreamSetMode(WS2812_DMA_STREAM, STM32_DMA_CR_CHSEL(WS2812_DMA_CHANNEL) | STM32_DMA_CR_DIR_M2P | WS2812_DMA_PERIPHERAL_WIDTH | WS2812_DMA_MEMORY_WIDTH | STM32_DMA_CR_MINC | STM32_DMA_CR_CIRC | STM32_DMA_CR_PL(3)); -#endif - dmaStreamSetTransactionSize(WS2812_DMA_STREAM, WS2812_BIT_N); - // M2P: Memory 2 Periph; PL: Priority Level - -#if (STM32_DMA_SUPPORTS_DMAMUX == TRUE) - // If the MCU has a DMAMUX we need to assign the correct resource - dmaSetRequestSource(WS2812_DMA_STREAM, WS2812_DMAMUX_ID); -#endif - - // 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; - } -} -void ws2812_write_led_rgbw(uint16_t led_number, uint8_t r, uint8_t g, uint8_t b, uint8_t w) { - // 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; -#ifdef RGBW - ws2812_frame_buffer[WS2812_WHITE_BIT(led_number, bit)] = ((w >> bit) & 0x01) ? WS2812_DUTYCYCLE_1 : WS2812_DUTYCYCLE_0; -#endif - } -} - -// 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++) { -#ifdef RGBW - ws2812_write_led_rgbw(i, ledarray[i].r, ledarray[i].g, ledarray[i].b, ledarray[i].w); -#else - ws2812_write_led(i, ledarray[i].r, ledarray[i].g, ledarray[i].b); -#endif - } -} diff --git a/platforms/chibios/drivers/ws2812_spi.c b/platforms/chibios/drivers/ws2812_spi.c deleted file mode 100644 index f188576e04..0000000000 --- a/platforms/chibios/drivers/ws2812_spi.c +++ /dev/null @@ -1,210 +0,0 @@ -#include "ws2812.h" -#include "gpio.h" -#include "util.h" -#include "chibios_config.h" - -/* Adapted from https://github.com/gamazeps/ws2812b-chibios-SPIDMA/ */ - -// 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 - -#ifndef WS2812_SPI_SCK_PAL_MODE -# define WS2812_SPI_SCK_PAL_MODE 5 -#endif - -#ifndef WS2812_SPI_DIVISOR -# define WS2812_SPI_DIVISOR 16 -#endif - -// Push Pull or Open Drain Configuration -// Default Push Pull -#ifndef WS2812_EXTERNAL_PULLUP -# if defined(USE_GPIOV1) -# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL -# else -# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL -# endif -#else -# if defined(USE_GPIOV1) -# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE_OPENDRAIN -# else -# define WS2812_MOSI_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_MOSI_PAL_MODE) | PAL_OUTPUT_TYPE_OPENDRAIN -# endif -#endif - -// Define SPI config speed -// baudrate should target 3.2MHz -// F072 fpclk = 48MHz -// 48/16 = 3Mhz -#if WS2812_SPI_DIVISOR == 2 -# define WS2812_SPI_DIVISOR_CR1_BR_X (0) -#elif WS2812_SPI_DIVISOR == 4 -# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_0) -#elif WS2812_SPI_DIVISOR == 8 -# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1) -#elif WS2812_SPI_DIVISOR == 16 // default -# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_1 | SPI_CR1_BR_0) -#elif WS2812_SPI_DIVISOR == 32 -# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2) -#elif WS2812_SPI_DIVISOR == 64 -# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_0) -#elif WS2812_SPI_DIVISOR == 128 -# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1) -#elif WS2812_SPI_DIVISOR == 256 -# define WS2812_SPI_DIVISOR_CR1_BR_X (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0) -#else -# error "Configured WS2812_SPI_DIVISOR value is not supported at this time." -#endif - -// Use SPI circular buffer -#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER -# define WS2812_SPI_BUFFER_MODE 1 // circular buffer -#else -# define WS2812_SPI_BUFFER_MODE 0 // normal buffer -#endif - -#if defined(USE_GPIOV1) -# define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE_PUSHPULL -#else -# define WS2812_SCK_OUTPUT_MODE PAL_MODE_ALTERNATE(WS2812_SPI_SCK_PAL_MODE) | PAL_OUTPUT_TYPE_PUSHPULL -#endif - -#define BYTES_FOR_LED_BYTE 4 -#ifdef RGBW -# define WS2812_CHANNELS 4 -#else -# define WS2812_CHANNELS 3 -#endif -#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * WS2812_CHANNELS) -#define DATA_SIZE (BYTES_FOR_LED * WS2812_LED_COUNT) -#define RESET_SIZE (1000 * WS2812_TRST_US / (2 * WS2812_TIMING)) -#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]; - -#if (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_GRB) - 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); -#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_RGB) - for (int j = 0; j < 4; j++) - tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.r, j); - for (int j = 0; j < 4; j++) - tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, 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); -#elif (WS2812_BYTE_ORDER == WS2812_BYTE_ORDER_BGR) - for (int j = 0; j < 4; j++) - tx_start[BYTES_FOR_LED * pos + j] = get_protocol_eq(color.b, j); - for (int j = 0; j < 4; j++) - tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE + j] = get_protocol_eq(color.g, j); - for (int j = 0; j < 4; j++) - tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 2 + j] = get_protocol_eq(color.r, j); -#endif -#ifdef RGBW - for (int j = 0; j < 4; j++) - tx_start[BYTES_FOR_LED * pos + BYTES_FOR_LED_BYTE * 4 + j] = get_protocol_eq(color.w, j); -#endif -} - -void ws2812_init(void) { - palSetLineMode(WS2812_DI_PIN, WS2812_MOSI_OUTPUT_MODE); - -#ifdef WS2812_SPI_SCK_PIN - palSetLineMode(WS2812_SPI_SCK_PIN, WS2812_SCK_OUTPUT_MODE); -#endif // WS2812_SPI_SCK_PIN - - // TODO: more dynamic baudrate - static const SPIConfig spicfg = { -#ifndef HAL_LLD_SELECT_SPI_V2 -// HAL_SPI_V1 -# if SPI_SUPPORTS_CIRCULAR == TRUE - WS2812_SPI_BUFFER_MODE, -# endif - NULL, // end_cb - PAL_PORT(WS2812_DI_PIN), - PAL_PAD(WS2812_DI_PIN), -# if defined(WB32F3G71xx) || defined(WB32FQ95xx) - 0, - 0, - WS2812_SPI_DIVISOR -# else - WS2812_SPI_DIVISOR_CR1_BR_X, - 0 -# endif -#else - // HAL_SPI_V2 -# if SPI_SUPPORTS_CIRCULAR == TRUE - WS2812_SPI_BUFFER_MODE, -# endif -# if SPI_SUPPORTS_SLAVE_MODE == TRUE - false, -# endif - NULL, // data_cb - NULL, // error_cb - PAL_PORT(WS2812_DI_PIN), - PAL_PAD(WS2812_DI_PIN), - WS2812_SPI_DIVISOR_CR1_BR_X, - 0 -#endif - }; - - spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */ - spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */ - spiSelect(&WS2812_SPI); /* Slave Select assertion. */ -#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER - spiStartSend(&WS2812_SPI, ARRAY_SIZE(txbuf), txbuf); -#endif -} - -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). -#ifndef WS2812_SPI_USE_CIRCULAR_BUFFER -# ifdef WS2812_SPI_SYNC - spiSend(&WS2812_SPI, ARRAY_SIZE(txbuf), txbuf); -# else - spiStartSend(&WS2812_SPI, ARRAY_SIZE(txbuf), txbuf); -# endif -#endif -} |