aboutsummaryrefslogtreecommitdiff
path: root/drivers/chibios
diff options
context:
space:
mode:
authorJames Young <18669334+noroadsleft@users.noreply.github.com>2021-05-29 14:38:50 -0700
committerGitHub <noreply@github.com>2021-05-29 14:38:50 -0700
commit1646c0f26cfa21a7023d404008e4d0aa4917193d (patch)
tree337ab0498a929285a234518fee34a4d9dcf51656 /drivers/chibios
parentf55e39e8a2246f6f96fd5d4a84a866e2615cde7b (diff)
downloadqmk_firmware-1646c0f26cfa21a7023d404008e4d0aa4917193d.tar.gz
qmk_firmware-1646c0f26cfa21a7023d404008e4d0aa4917193d.zip
2021 May 29 Breaking Changes Update (#13034)
* Add Per Key functionality for AutoShift (#11536) * LED Matrix: Reactive effect buffers & advanced indicators (#12588) * [Keyboard] kint36: switch to sym_eager_pk debouncing (#12626) * [Keyboard] kint2pp: reduce input latency by ≈10ms (#12625) * LED Matrix: Split (#12633) * [CI] Format code according to conventions (#12650) * feat: infinite timeout for leader key (#6580) * feat: implement leader_no_timeout logic * docs(leader_key): infinite leader timeout docs * Format code according to conventions (#12680) * Update ADC driver for STM32F1xx, STM32F3xx, STM32F4xx (#12403) * Fix default ADC_RESOLUTION for ADCv3 (and ADCv4) Recent ChibiOS update removed ADC_CFGR1_RES_10BIT from the ADCv3 headers (that macro should not have been there, because ADCv3 has CFGR instead of CFGR1). Fix the default value for ADC_RESOLUTION to use ADC_CFGR_RES_10BITS if it is defined (that name is used for ADCv3 and ADCv4). * Update ADC docs to match the actually used resolution ADC driver for ChibiOS actually uses the 10-bit resolution by default (probably to match AVR); fix the documentation accordingly. Also add both ADC_CFGR_RES_10BITS and ADC_CFGR1_RES_10BIT constants (these names differ according to the ADC implementation in the particular MCU). * Fix pinToMux() for B12 and B13 on STM32F3xx Testing on STM32F303CCT6 revealed that the ADC mux values for B12 and B13 pins were wrong. * Add support for all possible analog pins on STM32F1xx Added ADC mux values for pins A0...A7, B0, B1, C0...C5 on STM32F1xx (they are the same at least for STM32F103x8 and larger F103 devices, and also F102, F105, F107 families). Actually tested on STM32F103C8T6 (therefore pins C0...C5 were not tested). Pins F6...F10, which are present on STM32F103x[C-G] in 144-pin packages, cannot be supported at the moment, because those pins are connected only to ADC3, but the ChibiOS ADC driver for STM32F1xx supports only ADC1. * Add support for all possible analog pins on STM32F4xx Added ADC mux values for pins A0...A7, B0, B1, C0...C5 and optionally F3...F10 (if STM32_ADC_USE_ADC3 is enabled). These mux values are apparently the same for all F4xx devices, except some smaller devices may not have ADC3. Actually tested on STM32F401CCU6, STM32F401CEU6, STM32F411CEU6 (using various WeAct “Blackpill” boards); only pins A0...A7, B0, B1 were tested. Pins F3...F10 are inside `#if STM32_ADC_USE_ADC3` because some devices which don't have ADC3 also don't have the GPIOF port, therefore the code which refers to Fx pins does not compile. * Fix STM32F3xx ADC mux table in documentation The ADC driver documentation had some errors in the mux table for STM32F3xx. Fix this table to match the datasheet and the actual code (mux settings for B12 and B13 were also tested on a real STM32F303CCT6 chip). * Add STM32F1xx ADC pins to the documentation * Add STM32F4xx ADC pins to the documentation * Add initial support for tinyuf2 bootloader (when hosted on F411 blackpill) (#12600) * Add support for jumping to tinyuf2 bootloader. Adds blackpill UF2 example. * Update flashing.md * Update chconf.h * Update config.h * Update halconf.h * Update mcuconf.h * eeprom driver: Refactor where eeprom driver initialisation (and EEPROM emulation initialisation) occurs to make it non-target-specific. (#12671) * Add support for MCU = STM32F446 (#12619) * Add support for MCU = STM32F446 * Update platforms/chibios/GENERIC_STM32_F446XE/configs/config.h * Restore mcuconf.h to the one used by RT-STM32F446RE-NUCLEO64 * stm32f446: update mcuconf.h and board.h for 16MHz operation, with USB enabled, and other peripherals disabled. * Format code according to conventions (#12682) * Format code according to conventions (#12687) * Add STM32L433 and L443 support (#12063) * initial L433 commit * change to XC * fix L433 * disable all peripherals * update system and peripheral clocks * 433 change * use its own board files * revert its own board files * l433 specific change * fix stm32l432xx define * remove duplicate #define * fix bootloader jump * move to L443xx and add i2c2, spi2, usart3 to mcuconf.h * move to L443 * move to L443 * fix sdmmc in mcuconf.h * include STM32L443 * add L443 * Include L443 in compatible microcontrollers * Include L443 in compatible microcontrollers * Update config bootloader jump description * Update ChibiOS define reasoning * Update quantum/mcu_selection.mk * fix git conflict * Updated Function96 with V2 files and removed chconf.h and halconf.h (#12613) * Fix bad PR merge for #6580. (#12721) * Change RGB/LED Matrix to use a simple define for USB suspend (#12697) * [CI] Format code according to conventions (#12731) * Fixing transport's led/rgb matrix suspend state logic (#12770) * [CI] Format code according to conventions (#12772) * Fix comment parsing (#12750) * Added OLED fade out support (#12086) * fix some references to bin/qmk that slipped in (#12832) * Resolve a number of warnings in `qmk generate-api` (#12833) * New command: qmk console (#12828) * stash poc * stash * tidy up implementation * Tidy up slightly for review * Tidy up slightly for review * Bodge environment to make tests pass * Refactor away from asyncio due to windows issues * Filter devices * align vid/pid printing * Add hidapi to the installers * start preparing for multiple hid_listeners * udev rules for hid_listen * refactor to move closer to end state * very basic implementation of the threaded model * refactor how vid/pid/index are supplied and parsed * windows improvements * read the report directly when usage page isn't available * add per-device colors, the choice to show names or numbers, and refactor * add timestamps * Add support for showing bootloaders * tweak the color for bootloaders * Align bootloader disconnect with connect color * add support for showing all bootloaders * fix the pyusb check * tweaks * fix exception * hide a stack trace behind -v * add --no-bootloaders option * add documentation for qmk console * Apply suggestions from code review * pyformat * clean up and flesh out KNOWN_BOOTLOADERS * Remove pointless SERIAL_LINK_ENABLE rules (#12846) * Make Swap Hands use PROGMEM (#12284) This converts the array that the Swap Hands feature uses to use PROGMEM, and to read from that array, as such. Since this array never changes at runtime, there is no reason to keep it in memory. Especially for AVR boards, as memory is a precious resource. * Fix another bin/qmk reference (#12856) * [Keymap] Turn OLED off on suspend in soundmonster keymap (#10419) * Fixup build errors on `develop` branch. (#12723) * LED Matrix: Effects! (#12651) * Fix syntax error when compiling for ARM (#12866) * Remove KEYMAP and LAYOUT_kc (#12160) * alias KEYMAP to LAYOUT * remove KEYMAP and LAYOUT_kc * Add setup, clone, and env to the list of commands we allow even with broken modules (#12868) * Rename `point_t` -> `led_point_t` (#12864) * [Keyboard] updated a vendor name / fixed minor keymap issues (#12881) * Add missing LED Matrix suspend code to suspend.c (#12878) * LED Matrix: Documentation (#12685) * Deprecate `send_unicode_hex_string()` (#12602) * Fix spelling mistake regarding LED Matrix in split_common. (#12888) * [Keymap] Fix QWERTY/DVORAK status output for kzar keymap (#12895) * Use milc.subcommand.config instead of qmk.cli.config (#12915) * Use milc.subcommand.config instead * pyformat * remove the config test * Add function to allow repeated blinking of one layer (#12237) * Implement function rgblight_blink_layer_repeat to allow repeated blinking of one layer at a time * Update doc * Rework rgblight blinking according to requested change * optimize storage * Fixup housekeeping from being invoked twice per loop. (#12933) * matrix: wait for row signal to go HIGH for every row (#12945) I noticed this discrepancy (last row of the matrix treated differently than the others) when optimizing the input latency of my keyboard controller, see also https://michael.stapelberg.ch/posts/2021-05-08-keyboard-input-latency-qmk-kinesis/ Before this commit, when tuning the delays I noticed ghost key presses when pressing the F2 key, which is on the last row of the keyboard matrix: the dead_grave key, which is on the first row of the keyboard matrix, would be incorrectly detected as pressed. After this commit, all keyboard matrix rows are interpreted correctly. I suspect that my setup is more susceptible to this nuance than others because I use GPIO_INPUT_PIN_DELAY=0 and hence don’t have another delay that might mask the problem. * ensure we do not conflict with existing keymap aliases (#12976) * Add support for up to 4 IS31FL3733 drivers (#12342) * Convert Encoder callbacks to be boolean functions (#12805) * [Keyboard] Fix Terrazzo build failure (#12977) * Do not hard set config in CPTC files (#11864) * [Keyboard] Corne - Remove legacy revision support (#12226) * [Keymap] Update to Drashna keymap and user code (based on develop) (#12936) * Add Full-duplex serial driver for ARM boards (#9842) * Document LED_MATRIX_FRAMEBUFFER_EFFECTS (#12987) * Backlight: add defines for default level and breathing state (#12560) * Add dire message about LUFA mass storage bootloader (#13014) * [Keyboard] Remove redundant legacy and common headers for crkbd (#13023) Was causing compiler errors on some systems. * Fix keyboards/keymaps for boolean encoder callback changes (#12985) * `backlight.c`: include `eeprom.h` (#13024) * Add changelog for 2021-05-29 Breaking Changes merge (#12939) * Add ChangeLog for 2021-05-29 Breaking Changes Merge: initial version * Add recent develop changes * Sort recent develop changes * Remove sections for ChibiOS changes per tzarc No ChibiOS changes this round. * Add and sort recent develop changes * add notes about keyboard moves/deletions * import changelog for PR 12172 Documents the change to BOOTMAGIC_ENABLE. * update section headings * re-sort changelog * add additional note regarding Bootmagic changes * remove changelog timestamp * update dates in main Breaking Changes docs * fix broken section anchors in previous changelogs * add link to backlight/eeprom patch to changelog * highlight some more changes * link PRs from section headers * Restore standard readme * run: qmk cformat --core-only
Diffstat (limited to 'drivers/chibios')
-rw-r--r--drivers/chibios/analog.c59
-rw-r--r--drivers/chibios/serial_usart.c69
-rw-r--r--drivers/chibios/serial_usart.h90
-rw-r--r--drivers/chibios/serial_usart_duplex.c261
-rw-r--r--drivers/chibios/ws2812_pwm.c11
-rw-r--r--drivers/chibios/ws2812_spi.c45
6 files changed, 472 insertions, 63 deletions
diff --git a/drivers/chibios/analog.c b/drivers/chibios/analog.c
index 2b3872afb..8c476fcac 100644
--- a/drivers/chibios/analog.c
+++ b/drivers/chibios/analog.c
@@ -101,7 +101,11 @@
101 101
102// Options are 12, 10, 8, and 6 bit. 102// Options are 12, 10, 8, and 6 bit.
103#ifndef ADC_RESOLUTION 103#ifndef ADC_RESOLUTION
104# define ADC_RESOLUTION ADC_CFGR1_RES_10BIT 104# ifdef ADC_CFGR_RES_10BITS // ADCv3, ADCv4
105# define ADC_RESOLUTION ADC_CFGR_RES_10BITS
106# else // ADCv1, ADCv5, or the bodge for ADCv2 above
107# define ADC_RESOLUTION ADC_CFGR1_RES_10BIT
108# endif
105#endif 109#endif
106 110
107static ADCConfig adcCfg = {}; 111static ADCConfig adcCfg = {};
@@ -119,7 +123,7 @@ static ADCConversionGroup adcConversionGroup = {
119 .smpr = ADC_SAMPLING_RATE, 123 .smpr = ADC_SAMPLING_RATE,
120#elif defined(USE_ADCV2) 124#elif defined(USE_ADCV2)
121# if !defined(STM32F1XX) 125# if !defined(STM32F1XX)
122 .cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without... 126 .cr2 = ADC_CR2_SWSTART, // F103 seem very unhappy with, F401 seems very unhappy without...
123# endif 127# endif
124 .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), 128 .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),
125 .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), 129 .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),
@@ -161,8 +165,8 @@ __attribute__((weak)) adc_mux pinToMux(pin_t pin) {
161 case B0: return TO_MUX( ADC_CHANNEL_IN12, 2 ); 165 case B0: return TO_MUX( ADC_CHANNEL_IN12, 2 );
162 case B1: return TO_MUX( ADC_CHANNEL_IN1, 2 ); 166 case B1: return TO_MUX( ADC_CHANNEL_IN1, 2 );
163 case B2: return TO_MUX( ADC_CHANNEL_IN12, 1 ); 167 case B2: return TO_MUX( ADC_CHANNEL_IN12, 1 );
164 case B12: return TO_MUX( ADC_CHANNEL_IN2, 3 ); 168 case B12: return TO_MUX( ADC_CHANNEL_IN3, 3 );
165 case B13: return TO_MUX( ADC_CHANNEL_IN3, 3 ); 169 case B13: return TO_MUX( ADC_CHANNEL_IN5, 2 );
166 case B14: return TO_MUX( ADC_CHANNEL_IN4, 3 ); 170 case B14: return TO_MUX( ADC_CHANNEL_IN4, 3 );
167 case B15: return TO_MUX( ADC_CHANNEL_IN5, 3 ); 171 case B15: return TO_MUX( ADC_CHANNEL_IN5, 3 );
168 case C0: return TO_MUX( ADC_CHANNEL_IN6, 0 ); // Can also be ADC2 172 case C0: return TO_MUX( ADC_CHANNEL_IN6, 0 ); // Can also be ADC2
@@ -189,11 +193,52 @@ __attribute__((weak)) adc_mux pinToMux(pin_t pin) {
189 case E15: return TO_MUX( ADC_CHANNEL_IN2, 3 ); 193 case E15: return TO_MUX( ADC_CHANNEL_IN2, 3 );
190 case F2: return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2 194 case F2: return TO_MUX( ADC_CHANNEL_IN10, 0 ); // Can also be ADC2
191 case F4: return TO_MUX( ADC_CHANNEL_IN5, 0 ); 195 case F4: return TO_MUX( ADC_CHANNEL_IN5, 0 );
192#elif defined(STM32F4XX) // TODO: add all pins 196#elif defined(STM32F4XX)
193 case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); 197 case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 );
194 //case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 ); 198 case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 );
195#elif defined(STM32F1XX) // TODO: add all pins 199 case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 );
200 case A3: return TO_MUX( ADC_CHANNEL_IN3, 0 );
201 case A4: return TO_MUX( ADC_CHANNEL_IN4, 0 );
202 case A5: return TO_MUX( ADC_CHANNEL_IN5, 0 );
203 case A6: return TO_MUX( ADC_CHANNEL_IN6, 0 );
204 case A7: return TO_MUX( ADC_CHANNEL_IN7, 0 );
205 case B0: return TO_MUX( ADC_CHANNEL_IN8, 0 );
206 case B1: return TO_MUX( ADC_CHANNEL_IN9, 0 );
207 case C0: return TO_MUX( ADC_CHANNEL_IN10, 0 );
208 case C1: return TO_MUX( ADC_CHANNEL_IN11, 0 );
209 case C2: return TO_MUX( ADC_CHANNEL_IN12, 0 );
210 case C3: return TO_MUX( ADC_CHANNEL_IN13, 0 );
211 case C4: return TO_MUX( ADC_CHANNEL_IN14, 0 );
212 case C5: return TO_MUX( ADC_CHANNEL_IN15, 0 );
213# if STM32_ADC_USE_ADC3
214 case F3: return TO_MUX( ADC_CHANNEL_IN9, 2 );
215 case F4: return TO_MUX( ADC_CHANNEL_IN14, 2 );
216 case F5: return TO_MUX( ADC_CHANNEL_IN15, 2 );
217 case F6: return TO_MUX( ADC_CHANNEL_IN4, 2 );
218 case F7: return TO_MUX( ADC_CHANNEL_IN5, 2 );
219 case F8: return TO_MUX( ADC_CHANNEL_IN6, 2 );
220 case F9: return TO_MUX( ADC_CHANNEL_IN7, 2 );
221 case F10: return TO_MUX( ADC_CHANNEL_IN8, 2 );
222# endif
223#elif defined(STM32F1XX)
196 case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 ); 224 case A0: return TO_MUX( ADC_CHANNEL_IN0, 0 );
225 case A1: return TO_MUX( ADC_CHANNEL_IN1, 0 );
226 case A2: return TO_MUX( ADC_CHANNEL_IN2, 0 );
227 case A3: return TO_MUX( ADC_CHANNEL_IN3, 0 );
228 case A4: return TO_MUX( ADC_CHANNEL_IN4, 0 );
229 case A5: return TO_MUX( ADC_CHANNEL_IN5, 0 );
230 case A6: return TO_MUX( ADC_CHANNEL_IN6, 0 );
231 case A7: return TO_MUX( ADC_CHANNEL_IN7, 0 );
232 case B0: return TO_MUX( ADC_CHANNEL_IN8, 0 );
233 case B1: return TO_MUX( ADC_CHANNEL_IN9, 0 );
234 case C0: return TO_MUX( ADC_CHANNEL_IN10, 0 );
235 case C1: return TO_MUX( ADC_CHANNEL_IN11, 0 );
236 case C2: return TO_MUX( ADC_CHANNEL_IN12, 0 );
237 case C3: return TO_MUX( ADC_CHANNEL_IN13, 0 );
238 case C4: return TO_MUX( ADC_CHANNEL_IN14, 0 );
239 case C5: return TO_MUX( ADC_CHANNEL_IN15, 0 );
240 // STM32F103x[C-G] in 144-pin packages also have analog inputs on F6...F10, but they are on ADC3, and the
241 // ChibiOS ADC driver for STM32F1xx currently supports only ADC1, therefore these pins are not usable.
197#endif 242#endif
198 } 243 }
199 244
diff --git a/drivers/chibios/serial_usart.c b/drivers/chibios/serial_usart.c
index 7c81b1646..cae29388c 100644
--- a/drivers/chibios/serial_usart.c
+++ b/drivers/chibios/serial_usart.c
@@ -1,13 +1,20 @@
1#include "quantum.h" 1/* Copyright 2021 QMK
2#include "serial.h" 2 *
3#include "print.h" 3 * This program is free software: you can redistribute it and/or modify
4 4 * it under the terms of the GNU General Public License as published by
5#include <ch.h> 5 * the Free Software Foundation, either version 3 of the License, or
6#include <hal.h> 6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
7 16
8#ifndef USART_CR1_M0 17#include "serial_usart.h"
9# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
10#endif
11 18
12#ifndef USE_GPIOV1 19#ifndef USE_GPIOV1
13// The default PAL alternate modes are used to signal that the pins are used for USART 20// The default PAL alternate modes are used to signal that the pins are used for USART
@@ -20,50 +27,10 @@
20# define SERIAL_USART_DRIVER SD1 27# define SERIAL_USART_DRIVER SD1
21#endif 28#endif
22 29
23#ifndef SERIAL_USART_CR1
24# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
25#endif
26
27#ifndef SERIAL_USART_CR2
28# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
29#endif
30
31#ifndef SERIAL_USART_CR3
32# define SERIAL_USART_CR3 0
33#endif
34
35#ifdef SOFT_SERIAL_PIN 30#ifdef SOFT_SERIAL_PIN
36# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN 31# define SERIAL_USART_TX_PIN SOFT_SERIAL_PIN
37#endif 32#endif
38 33
39#ifndef SELECT_SOFT_SERIAL_SPEED
40# define SELECT_SOFT_SERIAL_SPEED 1
41#endif
42
43#ifdef SERIAL_USART_SPEED
44// Allow advanced users to directly set SERIAL_USART_SPEED
45#elif SELECT_SOFT_SERIAL_SPEED == 0
46# define SERIAL_USART_SPEED 460800
47#elif SELECT_SOFT_SERIAL_SPEED == 1
48# define SERIAL_USART_SPEED 230400
49#elif SELECT_SOFT_SERIAL_SPEED == 2
50# define SERIAL_USART_SPEED 115200
51#elif SELECT_SOFT_SERIAL_SPEED == 3
52# define SERIAL_USART_SPEED 57600
53#elif SELECT_SOFT_SERIAL_SPEED == 4
54# define SERIAL_USART_SPEED 38400
55#elif SELECT_SOFT_SERIAL_SPEED == 5
56# define SERIAL_USART_SPEED 19200
57#else
58# error invalid SELECT_SOFT_SERIAL_SPEED value
59#endif
60
61#ifndef SERIAL_USART_TIMEOUT
62# define SERIAL_USART_TIMEOUT 100
63#endif
64
65#define HANDSHAKE_MAGIC 7
66
67static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) { 34static inline msg_t sdWriteHalfDuplex(SerialDriver* driver, uint8_t* data, uint8_t size) {
68 msg_t ret = sdWrite(driver, data, size); 35 msg_t ret = sdWrite(driver, data, size);
69 36
@@ -123,6 +90,10 @@ __attribute__((weak)) void usart_init(void) {
123#else 90#else
124 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN); 91 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_OPENDRAIN);
125#endif 92#endif
93
94#if defined(USART_REMAP)
95 USART_REMAP;
96#endif
126} 97}
127 98
128void usart_master_init(void) { 99void usart_master_init(void) {
diff --git a/drivers/chibios/serial_usart.h b/drivers/chibios/serial_usart.h
new file mode 100644
index 000000000..fee7b4d15
--- /dev/null
+++ b/drivers/chibios/serial_usart.h
@@ -0,0 +1,90 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#pragma once
18
19#include "quantum.h"
20#include "serial.h"
21#include "printf.h"
22
23#include <ch.h>
24#include <hal.h>
25
26#ifndef USART_CR1_M0
27# define USART_CR1_M0 USART_CR1_M // some platforms (f1xx) dont have this so
28#endif
29
30#ifndef SERIAL_USART_CR1
31# define SERIAL_USART_CR1 (USART_CR1_PCE | USART_CR1_PS | USART_CR1_M0) // parity enable, odd parity, 9 bit length
32#endif
33
34#ifndef SERIAL_USART_CR2
35# define SERIAL_USART_CR2 (USART_CR2_STOP_1) // 2 stop bits
36#endif
37
38#ifndef SERIAL_USART_CR3
39# define SERIAL_USART_CR3 0
40#endif
41
42#if defined(USART1_REMAP)
43# define USART_REMAP \
44 do { \
45 (AFIO->MAPR |= AFIO_MAPR_USART1_REMAP); \
46 } while (0)
47#elif defined(USART2_REMAP)
48# define USART_REMAP \
49 do { \
50 (AFIO->MAPR |= AFIO_MAPR_USART2_REMAP); \
51 } while (0)
52#elif defined(USART3_PARTIALREMAP)
53# define USART_REMAP \
54 do { \
55 (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_PARTIALREMAP); \
56 } while (0)
57#elif defined(USART3_FULLREMAP)
58# define USART_REMAP \
59 do { \
60 (AFIO->MAPR |= AFIO_MAPR_USART3_REMAP_FULLREMAP); \
61 } while (0)
62#endif
63
64#ifndef SELECT_SOFT_SERIAL_SPEED
65# define SELECT_SOFT_SERIAL_SPEED 1
66#endif
67
68#ifdef SERIAL_USART_SPEED
69// Allow advanced users to directly set SERIAL_USART_SPEED
70#elif SELECT_SOFT_SERIAL_SPEED == 0
71# define SERIAL_USART_SPEED 460800
72#elif SELECT_SOFT_SERIAL_SPEED == 1
73# define SERIAL_USART_SPEED 230400
74#elif SELECT_SOFT_SERIAL_SPEED == 2
75# define SERIAL_USART_SPEED 115200
76#elif SELECT_SOFT_SERIAL_SPEED == 3
77# define SERIAL_USART_SPEED 57600
78#elif SELECT_SOFT_SERIAL_SPEED == 4
79# define SERIAL_USART_SPEED 38400
80#elif SELECT_SOFT_SERIAL_SPEED == 5
81# define SERIAL_USART_SPEED 19200
82#else
83# error invalid SELECT_SOFT_SERIAL_SPEED value
84#endif
85
86#ifndef SERIAL_USART_TIMEOUT
87# define SERIAL_USART_TIMEOUT 100
88#endif
89
90#define HANDSHAKE_MAGIC 7
diff --git a/drivers/chibios/serial_usart_duplex.c b/drivers/chibios/serial_usart_duplex.c
new file mode 100644
index 000000000..cc9b889ac
--- /dev/null
+++ b/drivers/chibios/serial_usart_duplex.c
@@ -0,0 +1,261 @@
1/* Copyright 2021 QMK
2 *
3 * This program is free software: you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation, either version 3 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "serial_usart.h"
18
19#include <stdatomic.h>
20
21#if !defined(USE_GPIOV1)
22// The default PAL alternate modes are used to signal that the pins are used for USART
23# if !defined(SERIAL_USART_TX_PAL_MODE)
24# define SERIAL_USART_TX_PAL_MODE 7
25# endif
26# if !defined(SERIAL_USART_RX_PAL_MODE)
27# define SERIAL_USART_RX_PAL_MODE 7
28# endif
29#endif
30
31#if !defined(SERIAL_USART_DRIVER)
32# define SERIAL_USART_DRIVER UARTD1
33#endif
34
35#if !defined(SERIAL_USART_TX_PIN)
36# define SERIAL_USART_TX_PIN A9
37#endif
38
39#if !defined(SERIAL_USART_RX_PIN)
40# define SERIAL_USART_RX_PIN A10
41#endif
42
43#define SIGNAL_HANDSHAKE_RECEIVED 0x1
44
45void handle_transactions_slave(uint8_t sstd_index);
46static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake);
47
48/*
49 * UART driver configuration structure. We use the blocking DMA enabled API and
50 * the rxchar callback to receive handshake tokens but only on the slave halve.
51 */
52// clang-format off
53static UARTConfig uart_config = {
54 .txend1_cb = NULL,
55 .txend2_cb = NULL,
56 .rxend_cb = NULL,
57 .rxchar_cb = NULL,
58 .rxerr_cb = NULL,
59 .timeout_cb = NULL,
60 .speed = (SERIAL_USART_SPEED),
61 .cr1 = (SERIAL_USART_CR1),
62 .cr2 = (SERIAL_USART_CR2),
63 .cr3 = (SERIAL_USART_CR3)
64};
65// clang-format on
66
67static SSTD_t* Transaction_table = NULL;
68static uint8_t Transaction_table_size = 0;
69static atomic_uint_least8_t handshake = 0xFF;
70static thread_reference_t tp_target = NULL;
71
72/*
73 * This callback is invoked when a character is received but the application
74 * was not ready to receive it, the character is passed as parameter.
75 * Receive transaction table index from initiator, which doubles as basic handshake token. */
76static void receive_transaction_handshake(UARTDriver* uartp, uint16_t received_handshake) {
77 /* Check if received handshake is not a valid transaction id.
78 * Please note that we can still catch a seemingly valid handshake
79 * i.e. a byte from a ongoing transfer which is in the allowed range.
80 * So this check mainly prevents any obviously wrong handshakes and
81 * subsequent wakeups of the receiving thread, which is a costly operation. */
82 if (received_handshake > Transaction_table_size) {
83 return;
84 }
85
86 handshake = (uint8_t)received_handshake;
87 chSysLockFromISR();
88 /* Wakeup receiving thread to start a transaction. */
89 chEvtSignalI(tp_target, (eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
90 chSysUnlockFromISR();
91}
92
93__attribute__((weak)) void usart_init(void) {
94#if defined(USE_GPIOV1)
95 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_STM32_ALTERNATE_PUSHPULL);
96 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_INPUT);
97#else
98 palSetLineMode(SERIAL_USART_TX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_TX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
99 palSetLineMode(SERIAL_USART_RX_PIN, PAL_MODE_ALTERNATE(SERIAL_USART_RX_PAL_MODE) | PAL_STM32_OTYPE_PUSHPULL | PAL_STM32_OSPEED_HIGHEST);
100#endif
101}
102
103/*
104 * This thread runs on the slave half and reacts to transactions initiated from the master.
105 */
106static THD_WORKING_AREA(waSlaveThread, 1024);
107static THD_FUNCTION(SlaveThread, arg) {
108 (void)arg;
109 chRegSetThreadName("slave_usart_tx_rx");
110
111 while (true) {
112 /* We sleep as long as there is no handshake waiting for us. */
113 chEvtWaitAny((eventmask_t)SIGNAL_HANDSHAKE_RECEIVED);
114 handle_transactions_slave(handshake);
115 }
116}
117
118void soft_serial_target_init(SSTD_t* const sstd_table, int sstd_table_size) {
119 Transaction_table = sstd_table;
120 Transaction_table_size = (uint8_t)sstd_table_size;
121 usart_init();
122
123#if defined(USART_REMAP)
124 USART_REMAP;
125#endif
126
127 tp_target = chThdCreateStatic(waSlaveThread, sizeof(waSlaveThread), HIGHPRIO, SlaveThread, NULL);
128
129 // Start receiving handshake tokens on slave halve
130 uart_config.rxchar_cb = receive_transaction_handshake;
131 uartStart(&SERIAL_USART_DRIVER, &uart_config);
132}
133
134/**
135 * @brief React to transactions started by the master.
136 * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
137 */
138void inline handle_transactions_slave(uint8_t sstd_index) {
139 size_t buffer_size = 0;
140 msg_t msg = 0;
141 SSTD_t* trans = &Transaction_table[sstd_index];
142
143 /* Send back the handshake which is XORed as a simple checksum,
144 to signal that the slave is ready to receive possible transaction buffers */
145 sstd_index ^= HANDSHAKE_MAGIC;
146 buffer_size = (size_t)sizeof(sstd_index);
147 msg = uartSendTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
148
149 if (msg != MSG_OK) {
150 if (trans->status) {
151 *trans->status = TRANSACTION_NO_RESPONSE;
152 }
153 return;
154 }
155
156 /* Receive transaction buffer from the master. If this transaction requires it.*/
157 buffer_size = (size_t)trans->initiator2target_buffer_size;
158 if (buffer_size) {
159 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
160 if (msg != MSG_OK) {
161 if (trans->status) {
162 *trans->status = TRANSACTION_NO_RESPONSE;
163 }
164 return;
165 }
166 }
167
168 /* Send transaction buffer to the master. If this transaction requires it. */
169 buffer_size = (size_t)trans->target2initiator_buffer_size;
170 if (buffer_size) {
171 msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
172 if (msg != MSG_OK) {
173 if (trans->status) {
174 *trans->status = TRANSACTION_NO_RESPONSE;
175 }
176 return;
177 }
178 }
179
180 if (trans->status) {
181 *trans->status = TRANSACTION_ACCEPTED;
182 }
183}
184
185void soft_serial_initiator_init(SSTD_t* const sstd_table, int sstd_table_size) {
186 Transaction_table = sstd_table;
187 Transaction_table_size = (uint8_t)sstd_table_size;
188 usart_init();
189
190#if defined(SERIAL_USART_PIN_SWAP)
191 uart_config.cr2 |= USART_CR2_SWAP; // master has swapped TX/RX pins
192#endif
193
194#if defined(USART_REMAP)
195 USART_REMAP;
196#endif
197
198 uartStart(&SERIAL_USART_DRIVER, &uart_config);
199}
200
201/**
202 * @brief Start transaction from the master to the slave.
203 * This version uses duplex send and receive usart pheriphals and DMA backed transfers.
204 *
205 * @param index Transaction Table index of the transaction to start.
206 * @return int TRANSACTION_NO_RESPONSE in case of Timeout.
207 * TRANSACTION_TYPE_ERROR in case of invalid transaction index.
208 * TRANSACTION_END in case of success.
209 */
210#if !defined(SERIAL_USE_MULTI_TRANSACTION)
211int soft_serial_transaction(void) {
212 uint8_t sstd_index = 0;
213#else
214int soft_serial_transaction(int index) {
215 uint8_t sstd_index = index;
216#endif
217
218 if (sstd_index > Transaction_table_size) {
219 return TRANSACTION_TYPE_ERROR;
220 }
221
222 SSTD_t* const trans = &Transaction_table[sstd_index];
223 msg_t msg = 0;
224 size_t buffer_size = (size_t)sizeof(sstd_index);
225
226 /* Send transaction table index to the slave, which doubles as basic handshake token. */
227 uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index, TIME_MS2I(SERIAL_USART_TIMEOUT));
228
229 uint8_t sstd_index_shake = 0xFF;
230 buffer_size = (size_t)sizeof(sstd_index_shake);
231
232 /* Receive the handshake token from the slave. The token was XORed by the slave as a simple checksum.
233 If the tokens match, the master will start to send and receive possible transaction buffers. */
234 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, &sstd_index_shake, TIME_MS2I(SERIAL_USART_TIMEOUT));
235 if (msg != MSG_OK || (sstd_index_shake != (sstd_index ^ HANDSHAKE_MAGIC))) {
236 dprintln("USART: Handshake Failed");
237 return TRANSACTION_NO_RESPONSE;
238 }
239
240 /* Send transaction buffer to the slave. If this transaction requires it. */
241 buffer_size = (size_t)trans->initiator2target_buffer_size;
242 if (buffer_size) {
243 msg = uartSendFullTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->initiator2target_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
244 if (msg != MSG_OK) {
245 dprintln("USART: Send Failed");
246 return TRANSACTION_NO_RESPONSE;
247 }
248 }
249
250 /* Receive transaction buffer from the slave. If this transaction requires it. */
251 buffer_size = (size_t)trans->target2initiator_buffer_size;
252 if (buffer_size) {
253 msg = uartReceiveTimeout(&SERIAL_USART_DRIVER, &buffer_size, trans->target2initiator_buffer, TIME_MS2I(SERIAL_USART_TIMEOUT));
254 if (msg != MSG_OK) {
255 dprintln("USART: Receive Failed");
256 return TRANSACTION_NO_RESPONSE;
257 }
258 }
259
260 return TRANSACTION_END;
261}
diff --git a/drivers/chibios/ws2812_pwm.c b/drivers/chibios/ws2812_pwm.c
index 140120d48..e6af55b6b 100644
--- a/drivers/chibios/ws2812_pwm.c
+++ b/drivers/chibios/ws2812_pwm.c
@@ -27,6 +27,15 @@
27# error "please consult your MCU's datasheet and specify in your config.h: #define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM?_UP" 27# error "please consult your MCU's datasheet and specify in your config.h: #define WS2812_DMAMUX_ID STM32_DMAMUX1_TIM?_UP"
28#endif 28#endif
29 29
30#ifndef WS2812_PWM_COMPLEMENTARY_OUTPUT
31# define WS2812_PWM_OUTPUT_MODE PWM_OUTPUT_ACTIVE_HIGH
32#else
33# if !STM32_PWM_USE_ADVANCED
34# error "WS2812_PWM_COMPLEMENTARY_OUTPUT requires STM32_PWM_USE_ADVANCED == TRUE"
35# endif
36# define WS2812_PWM_OUTPUT_MODE PWM_COMPLEMENTARY_OUTPUT_ACTIVE_HIGH
37#endif
38
30// Push Pull or Open Drain Configuration 39// Push Pull or Open Drain Configuration
31// Default Push Pull 40// Default Push Pull
32#ifndef WS2812_EXTERNAL_PULLUP 41#ifndef WS2812_EXTERNAL_PULLUP
@@ -247,7 +256,7 @@ void ws2812_init(void) {
247 .channels = 256 .channels =
248 { 257 {
249 [0 ... 3] = {.mode = PWM_OUTPUT_DISABLED, .callback = NULL}, // Channels default to disabled 258 [0 ... 3] = {.mode = PWM_OUTPUT_DISABLED, .callback = NULL}, // Channels default to disabled
250 [WS2812_PWM_CHANNEL - 1] = {.mode = PWM_OUTPUT_ACTIVE_HIGH, .callback = NULL}, // Turn on the channel we care about 259 [WS2812_PWM_CHANNEL - 1] = {.mode = WS2812_PWM_OUTPUT_MODE, .callback = NULL}, // Turn on the channel we care about
251 }, 260 },
252 .cr2 = 0, 261 .cr2 = 0,
253 .dier = TIM_DIER_UDE, // DMA on update event for next period 262 .dier = TIM_DIER_UDE, // DMA on update event for next period
diff --git a/drivers/chibios/ws2812_spi.c b/drivers/chibios/ws2812_spi.c
index 89df2987b..e02cbabc0 100644
--- a/drivers/chibios/ws2812_spi.c
+++ b/drivers/chibios/ws2812_spi.c
@@ -32,6 +32,37 @@
32# endif 32# endif
33#endif 33#endif
34 34
35// Define SPI config speed
36// baudrate should target 3.2MHz
37// F072 fpclk = 48MHz
38// 48/16 = 3Mhz
39#if WS2812_SPI_DIVISOR == 2
40# define WS2812_SPI_DIVISOR (0)
41#elif WS2812_SPI_DIVISOR == 4
42# define WS2812_SPI_DIVISOR (SPI_CR1_BR_0)
43#elif WS2812_SPI_DIVISOR == 8
44# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1)
45#elif WS2812_SPI_DIVISOR == 16 // same as default
46# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0)
47#elif WS2812_SPI_DIVISOR == 32
48# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2)
49#elif WS2812_SPI_DIVISOR == 64
50# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_0)
51#elif WS2812_SPI_DIVISOR == 128
52# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1)
53#elif WS2812_SPI_DIVISOR == 256
54# define WS2812_SPI_DIVISOR (SPI_CR1_BR_2 | SPI_CR1_BR_1 | SPI_CR1_BR_0)
55#else
56# define WS2812_SPI_DIVISOR (SPI_CR1_BR_1 | SPI_CR1_BR_0) // default
57#endif
58
59// Use SPI circular buffer
60#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER
61# define WS2812_SPI_BUFFER_MODE 1 // circular buffer
62#else
63# define WS2812_SPI_BUFFER_MODE 0 // normal buffer
64#endif
65
35#define BYTES_FOR_LED_BYTE 4 66#define BYTES_FOR_LED_BYTE 4
36#define NB_COLORS 3 67#define NB_COLORS 3
37#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS) 68#define BYTES_FOR_LED (BYTES_FOR_LED_BYTE * NB_COLORS)
@@ -81,14 +112,14 @@ void ws2812_init(void) {
81 palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE); 112 palSetLineMode(RGB_DI_PIN, WS2812_OUTPUT_MODE);
82 113
83 // TODO: more dynamic baudrate 114 // TODO: more dynamic baudrate
84 static const SPIConfig spicfg = { 115 static const SPIConfig spicfg = {WS2812_SPI_BUFFER_MODE, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN), WS2812_SPI_DIVISOR};
85 0, NULL, PAL_PORT(RGB_DI_PIN), PAL_PAD(RGB_DI_PIN),
86 SPI_CR1_BR_1 | SPI_CR1_BR_0 // baudrate : fpclk / 8 => 1tick is 0.32us (2.25 MHz)
87 };
88 116
89 spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */ 117 spiAcquireBus(&WS2812_SPI); /* Acquire ownership of the bus. */
90 spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */ 118 spiStart(&WS2812_SPI, &spicfg); /* Setup transfer parameters. */
91 spiSelect(&WS2812_SPI); /* Slave Select assertion. */ 119 spiSelect(&WS2812_SPI); /* Slave Select assertion. */
120#ifdef WS2812_SPI_USE_CIRCULAR_BUFFER
121 spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf);
122#endif
92} 123}
93 124
94void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) { 125void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) {
@@ -104,9 +135,11 @@ void ws2812_setleds(LED_TYPE* ledarray, uint16_t leds) {
104 135
105 // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues. 136 // Send async - each led takes ~0.03ms, 50 leds ~1.5ms, animations flushing faster than send will cause issues.
106 // Instead spiSend can be used to send synchronously (or the thread logic can be added back). 137 // Instead spiSend can be used to send synchronously (or the thread logic can be added back).
107#ifdef WS2812_SPI_SYNC 138#ifndef WS2812_SPI_USE_CIRCULAR_BUFFER
139# ifdef WS2812_SPI_SYNC
108 spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); 140 spiSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf);
109#else 141# else
110 spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf); 142 spiStartSend(&WS2812_SPI, sizeof(txbuf) / sizeof(txbuf[0]), txbuf);
143# endif
111#endif 144#endif
112} 145}