diff options
author | jaseg <git@jaseg.net> | 2018-07-17 15:45:41 +0200 |
---|---|---|
committer | jaseg <git@jaseg.net> | 2018-07-17 15:45:41 +0200 |
commit | fe0edb53e01b8ccc77624768a56e405014a353bc (patch) | |
tree | b4a40c02c74368458ee50466415452f87d4a840e | |
parent | fe9853e865eeb6d6e49f89349a1cad05388abd6b (diff) | |
parent | 6df66b77ba5b27bce5630694742f2dac57b8d3eb (diff) | |
download | olsndot-fe0edb53e01b8ccc77624768a56e405014a353bc.tar.gz olsndot-fe0edb53e01b8ccc77624768a56e405014a353bc.tar.bz2 olsndot-fe0edb53e01b8ccc77624768a56e405014a353bc.zip |
Merge usb-serial-rs485 tree from libopencm3-tests repository
-rw-r--r-- | usb-serial-rs485/LICENSE | 165 | ||||
-rw-r--r-- | usb-serial-rs485/Makefile.stm32f103-generic | 38 | ||||
-rw-r--r-- | usb-serial-rs485/Makefile.stm32f4-disco | 38 | ||||
-rw-r--r-- | usb-serial-rs485/README | 33 | ||||
-rw-r--r-- | usb-serial-rs485/main-stm32f103-generic.c | 204 | ||||
-rw-r--r-- | usb-serial-rs485/main-stm32f4-disco.c | 199 | ||||
-rw-r--r-- | usb-serial-rs485/ringb.c | 59 | ||||
-rw-r--r-- | usb-serial-rs485/ringb.h | 60 | ||||
-rw-r--r-- | usb-serial-rs485/syscfg.h | 59 | ||||
-rw-r--r-- | usb-serial-rs485/usb_cdcacm-arch.c | 82 | ||||
-rw-r--r-- | usb-serial-rs485/usb_cdcacm.c | 363 | ||||
-rw-r--r-- | usb-serial-rs485/usb_cdcacm.h | 74 |
12 files changed, 1374 insertions, 0 deletions
diff --git a/usb-serial-rs485/LICENSE b/usb-serial-rs485/LICENSE new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/usb-serial-rs485/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/usb-serial-rs485/Makefile.stm32f103-generic b/usb-serial-rs485/Makefile.stm32f103-generic new file mode 100644 index 0000000..747238d --- /dev/null +++ b/usb-serial-rs485/Makefile.stm32f103-generic @@ -0,0 +1,38 @@ +## +## This file is part of the libopencm3 project. +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library 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 Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see <http://www.gnu.org/licenses/>. +## + +BOARD = stm32f103-generic +PROJECT = usb-serial-rs485-$(BOARD) +BUILD_DIR = bin-$(BOARD) + +SHARED_DIR = ../../shared + +CFILES = main-$(BOARD).c +CFILES += usb_cdcacm.c usb_cdcacm-arch.c +CFILES += ringb.c +CFILES += trace.c trace_stdio.c + +VPATH += $(SHARED_DIR) +INCLUDES += $(patsubst %,-I%, . $(SHARED_DIR)) +OPENCM3_DIR=../../libopencm3 + +# Copy this to some arch shared? +DEVICE=stm32f103c8 +OOCD_FILE= ../../openocd/openocd.stm32f4-disco.cfg + +include ../../rules.mk + diff --git a/usb-serial-rs485/Makefile.stm32f4-disco b/usb-serial-rs485/Makefile.stm32f4-disco new file mode 100644 index 0000000..1c60091 --- /dev/null +++ b/usb-serial-rs485/Makefile.stm32f4-disco @@ -0,0 +1,38 @@ +## +## This file is part of the libopencm3 project. +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library 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 Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see <http://www.gnu.org/licenses/>. +## + +BOARD = stm32f4-disco +PROJECT = usb-serial-rs485-$(BOARD) +BUILD_DIR = bin-$(BOARD) + +SHARED_DIR = ../../shared + +CFILES = main-$(BOARD).c +CFILES += usb_cdcacm.c usb_cdcacm-arch.c +CFILES += ringb.c +CFILES += trace.c trace_stdio.c + +VPATH += $(SHARED_DIR) +INCLUDES += $(patsubst %,-I%, . $(SHARED_DIR)) +OPENCM3_DIR=../../libopencm3 + +# Copy this to some arch shared? +DEVICE=stm32f405xg +OOCD_FILE= ../../openocd/openocd.stm32f4-disco.cfg + +include ../../rules.mk + diff --git a/usb-serial-rs485/README b/usb-serial-rs485/README new file mode 100644 index 0000000..0e5b2cd --- /dev/null +++ b/usb-serial-rs485/README @@ -0,0 +1,33 @@ +------------------------------------------------------------------------------ +README +------------------------------------------------------------------------------ + +current status: +f1: frootloop3 gets corrupt data in rx path, _after_ serial rx, +but the usb in packets (wireshark) show the corruption. (LA on serial lines is clean) +f4: frootloop3 tests clean +=> suspect libopecm3 usb bug? + +frootloop3: github.com/karlp/frootloop3 + + + +This implements a USB CDC-ACM device, connected to USART2 (PA2-tx/PA3-rx) +An interrupt driven tx ring buffer is used for usb->serial, and a similar +interrupt driven rx ring buffer is used for serial->usb. +Baud rates from 2400-460800 are tested with zmodem transfers in both directions. + +Parity not yet finished, and baudrates below 1200 need fiddling with clock prescalers. +When it's finished, rs485 support should be available via the TC interrupt. + +A GPIO is toggled around USART transmissions to control an RS485 line +transceiver, following ST's app note. The RS485 driver enable line is, +by default, just the onboard red LEDs. + +The orange/blue LEDs indicate tx/rx activity, but especially for the rx line, +you won't really see it without heavy data rates. + +This example is heavily based on the existing usb_cdcacm and usart demos. + + +KARL - check https://github.com/dhylands/libopencm3-usb-serial/blob/master/usb.c to see if it passes the same tests or not diff --git a/usb-serial-rs485/main-stm32f103-generic.c b/usb-serial-rs485/main-stm32f103-generic.c new file mode 100644 index 0000000..415a18b --- /dev/null +++ b/usb-serial-rs485/main-stm32f103-generic.c @@ -0,0 +1,204 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Karl Palsson <karlp@tweak.net.au> + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <libopencm3/cm3/nvic.h> +#include <libopencm3/stm32/gpio.h> +#include <libopencm3/stm32/rcc.h> +#include <libopencm3/stm32/usart.h> + +#include <stdio.h> +#include "syscfg.h" +#include "usb_cdcacm.h" +#include "ringb.h" +#include "trace.h" + +#define ER_DEBUG +#ifdef ER_DEBUG +#define ER_DPRINTF(fmt, ...) \ + do { printf(fmt, ## __VA_ARGS__); } while (0) +#else +#define ER_DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +static inline void gpio_really(uint32_t port, uint16_t pin, const bool set) +{ + int shift = set ? 0 : 16; + GPIO_BSRR(port) = pin << shift; +} + + +extern struct ringb rx_ring, tx_ring; +static void usart_setup(void) +{ + /* Enable the USART2 interrupt. */ + nvic_enable_irq(NVIC_USART2_IRQ); + + /* USART2 pins are on port A */ + rcc_periph_clock_enable(RCC_GPIOA); + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART2_TX); + gpio_set_mode(GPIOA, GPIO_MODE_INPUT, + GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_USART2_RX); + + /* Enable clocks for USART2. */ + rcc_periph_clock_enable(RCC_USART2); + + /* Setup USART2 parameters. */ + usart_set_baudrate(USART2, 115200); + usart_set_databits(USART2, 8); + usart_set_stopbits(USART2, USART_STOPBITS_1); + usart_set_mode(USART2, USART_MODE_TX_RX); + usart_set_parity(USART2, USART_PARITY_NONE); + usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE); + + /* Enable USART2 Receive interrupt. */ + usart_enable_rx_interrupt(USART2); + + /* Finally enable the USART. */ + usart_enable(USART2); +} + +void usart2_isr(void) +{ + gpio_really(GPIOA, GPIO5, 1); + // usbser-rxne() + /* Check if we were called because of RXNE. */ + if (usart_get_interrupt_source(USART2, USART_SR_RXNE)) { + gpio_set(LED_RX_PORT, LED_RX_PIN); + uint8_t c = usart_recv(USART2); + if (ringb_put(&rx_ring, c)) { + // good, + } else { + // fatal, you should always have drained by now. + // (when you've got it all ironed out, _actually_ + // just drop and count drops), but not yet... + ER_DPRINTF("rx buffer full\n"); + while(1); + } + trace_send8(STIMULUS_RING_PUSH, c); + gpio_clear(LED_RX_PORT, LED_RX_PIN); + } + // usbser-irq-txe() + if (usart_get_interrupt_source(USART2, USART_SR_TXE)) { + if (ringb_depth(&tx_ring) == 0) { + // turn off tx empty interrupts, nothing left to send + cdcacm_arch_txirq(0, 0); + ER_DPRINTF("OFF\n"); + // Turn on tx complete interrupts, for rs485 de + USART_CR1(USART2) |= USART_CR1_TCIE; + } else { + int c = ringb_get(&tx_ring); + usart_send(USART2, c); + } + } + // usbser-irq-txc? rs485 is auto on some devices, but can be emulated anyway + if (usart_get_interrupt_source(USART2, USART_SR_TC)) { + ER_DPRINTF("TC"); + // turn off the complete irqs, we're done now. + USART_CR1(USART2) &= ~USART_CR1_TCIE; + USART_SR(USART2) &= ~USART_SR_TC; + cdcacm_arch_pin(0, CDCACM_PIN_RS485DE, 0); + } + gpio_really(GPIOA, GPIO5, 0); +} + +void usb_cdcacm_setup_pre_arch(void) +{ + // Hack to reenumerate + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); + gpio_clear(GPIOA, GPIO12); + for (unsigned int i = 0; i < 800000; i++) { + __asm__("nop"); + } +} + +void usb_cdcacm_setup_post_arch(usbd_device *dev) +{ + (void)dev; +} + +void cdcacm_arch_pin(int port, enum cdcacm_pin pin, bool set) +{ + (void)port; // TODO if you want to handle multiple ports + switch (pin) { + case CDCACM_PIN_LED_TX: + gpio_really(LED_TX_PORT, LED_TX_PIN, set); + break; + case CDCACM_PIN_LED_RX: + gpio_really(LED_RX_PORT, LED_RX_PIN, set); + break; + case CDCACM_PIN_RS485DE: + gpio_really(RS485DE_PORT, RS485DE_PIN, set); + break; + default: + break; + } +} + +void cdcacm_arch_txirq(int port, bool set) { + (void)port; //FIXME if you make this multi port + if (set) { + usart_enable_tx_interrupt(USART2); + } else { + usart_disable_tx_interrupt(USART2); + } +} + +void cdcacm_arch_set_line_state(int port, uint8_t dtr, uint8_t rts) +{ + (void)port; // FIXME if you want multiple ports + (void) dtr; + (void) rts; + // LM4f has an implementation of this if you're keen +} + + + + +int main(void) +{ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + ER_DPRINTF("And we're alive!\n"); + /* Led */ + rcc_periph_clock_enable(RCC_GPIOC); + gpio_set_mode(LED_RX_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, LED_RX_PIN); + // IRQ timing + rcc_periph_clock_enable(RCC_GPIOA); + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO5); + gpio_set_mode(RS485DE_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, RS485DE_PIN); + + usart_setup(); + + usb_cdcacm_setup_pre_arch(); + usbd_device *usbd_dev = usb_cdcacm_init(&st_usbfs_v1_usb_driver, + "stm32f103-generic"); + usb_cdcacm_setup_post_arch(usbd_dev); + + + ER_DPRINTF("Looping...\n"); + volatile int i = 0; + while (1) { + usbd_poll(usbd_dev); + usb_cdcacm_poll(usbd_dev); + } + +} + diff --git a/usb-serial-rs485/main-stm32f4-disco.c b/usb-serial-rs485/main-stm32f4-disco.c new file mode 100644 index 0000000..ac3cdff --- /dev/null +++ b/usb-serial-rs485/main-stm32f4-disco.c @@ -0,0 +1,199 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Karl Palsson <karlp@tweak.net.au> + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <libopencm3/cm3/nvic.h> +#include <libopencm3/stm32/gpio.h> +#include <libopencm3/stm32/rcc.h> +#include <libopencm3/stm32/usart.h> + +#include <stdio.h> +#include "syscfg.h" +#include "usb_cdcacm.h" +#include "ringb.h" +#include "trace.h" + +#define ER_DEBUG +#ifdef ER_DEBUG +#define ER_DPRINTF(fmt, ...) \ + do { printf(fmt, ## __VA_ARGS__); } while (0) +#else +#define ER_DPRINTF(fmt, ...) \ + do { } while (0) +#endif + +static inline void gpio_really(uint32_t port, uint16_t pin, const bool set) +{ + int shift = set ? 0 : 16; + GPIO_BSRR(port) = pin << shift; +} + +extern struct ringb rx_ring, tx_ring; +static void usart_setup(void) +{ + /* Enable the USART2 interrupt. */ + nvic_enable_irq(NVIC_USART2_IRQ); + + /* USART2 pins are on port A */ + rcc_periph_clock_enable(RCC_GPIOA); + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO3); + gpio_set_af(GPIOA, GPIO_AF7, GPIO2 | GPIO3); + + /* Enable clocks for USART2. */ + rcc_periph_clock_enable(RCC_USART2); + + /* Setup USART2 parameters. */ + usart_set_baudrate(USART2, 115200); + usart_set_databits(USART2, 8); + usart_set_stopbits(USART2, USART_STOPBITS_1); + usart_set_mode(USART2, USART_MODE_TX_RX); + usart_set_parity(USART2, USART_PARITY_NONE); + usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE); + + /* Enable USART2 Receive interrupt. */ + usart_enable_rx_interrupt(USART2); + + /* Finally enable the USART. */ + usart_enable(USART2); +} + +void usart2_isr(void) +{ + gpio_really(GPIOA, GPIO5, 1); + // usbser-rxne() + /* Check if we were called because of RXNE. */ + if (usart_get_flag(USART2, USART_SR_RXNE)) { + gpio_set(LED_RX_PORT, LED_RX_PIN); + uint8_t c = usart_recv(USART2); + if (ringb_put(&rx_ring, c)) { + // good, + } else { + // fatal, you should always have drained by now. + // (when you've got it all ironed out, _actually_ + // just drop and count drops), but not yet... + ER_DPRINTF("rx buffer full\n"); + while(1); + } + gpio_clear(LED_RX_PORT, LED_RX_PIN); + } + // usbser-irq-txe() + if (usart_get_flag(USART2, USART_SR_TXE)) { + if (ringb_depth(&tx_ring) == 0) { + // turn off tx empty interrupts, nothing left to send + usart_disable_tx_interrupt(USART2); + ER_DPRINTF("OFF\n"); + // Turn on tx complete interrupts, for rs485 de +// USART_CR1(USART2) |= ~USART_CR1_TCIE; + } else { + int c = ringb_get(&tx_ring); + usart_send(USART2, c); + } + } + // usbser-irq-txc? rs485 is auto on some devices, but can be emulated anyway +// if (usart_get_flag(USART2, USART_SR_TC)) { +// ER_DPRINTF("TC"); +// // turn off the complete irqs, we're done now. +// USART_SR(USART2) &= ~USART_SR_TC; +// USART_CR1(USART2) &= ~USART_CR1_TCIE; +// gpio_clear(LED_TX_PORT, LED_TX_PIN); +// gpio_clear(RS485DE_PORT, RS485DE_PIN); +// } + gpio_really(GPIOA, GPIO5, 0); +} + +void usb_cdcacm_setup_pre_arch(void) +{ + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_OTGFS); + + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO9 | GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO9 | GPIO11 | GPIO12); + +} + +void usb_cdcacm_setup_post_arch(usbd_device *dev) +{ + (void)dev; +} + + +void cdcacm_arch_pin(int port, enum cdcacm_pin pin, bool set) +{ + (void)port; // TODO if you want to handle multiple ports + switch (pin) { + case CDCACM_PIN_LED_TX: + gpio_really(LED_TX_PORT, LED_TX_PIN, set); + break; + case CDCACM_PIN_LED_RX: + gpio_really(LED_RX_PORT, LED_RX_PIN, set); + break; + case CDCACM_PIN_RS485DE: + gpio_really(RS485DE_PORT, RS485DE_PIN, set); + break; + default: + break; + } +} + +void cdcacm_arch_txirq(int port, bool set) { + (void)port; //FIXME if you make this multi port + if (set) { + usart_enable_tx_interrupt(USART2); + } else { + usart_disable_tx_interrupt(USART2); + } +} + +void cdcacm_arch_set_line_state(int port, uint8_t dtr, uint8_t rts) +{ + (void)port; // FIXME if you want multiple ports + (void) dtr; + (void) rts; + // LM4f has an implementation of this if you're keen +} + + +int main(void) +{ + rcc_clock_setup_hse_3v3(&rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_168MHZ]); + ER_DPRINTF("And we're alive!\n"); + /* Leds and rs485 are on port D */ + rcc_periph_clock_enable(RCC_GPIOD); + gpio_mode_setup(LED_RX_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + LED_RX_PIN | LED_TX_PIN); + gpio_mode_setup(RS485DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, + RS485DE_PIN); + + rcc_periph_clock_enable(RCC_GPIOA); + gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO5); + + usart_setup(); + usb_cdcacm_setup_pre_arch(); + usbd_device *usbd_dev = usb_cdcacm_init(&otgfs_usb_driver, "stm32f4-disco"); + usb_cdcacm_setup_post_arch(usbd_dev); + + ER_DPRINTF("Looping...\n"); + volatile int i = 0; + while (1) { + usbd_poll(usbd_dev); + usb_cdcacm_poll(usbd_dev); + } + +} + diff --git a/usb-serial-rs485/ringb.c b/usb-serial-rs485/ringb.c new file mode 100644 index 0000000..245325b --- /dev/null +++ b/usb-serial-rs485/ringb.c @@ -0,0 +1,59 @@ + +#include "ringb.h" +void ringb_init(struct ringb *ring, uint8_t *buf, int len) +{ + ring->buf_len = len; + ring->buf = buf; + ring->idx_r = 0; + ring->idx_w = 0; +} + +int ringb_get(struct ringb *ring) { +#if 1 + int rval; + if (ring->idx_r != ring->idx_w) { + rval = ring->buf[ring->idx_r]; + ring->idx_r = (ring->idx_r + 1) % ring->buf_len; + return rval; + } + return -1; +#else + if (((ring->idx_w - ring->idx_r) % ring->buf_len) >= 0) { + int rval = ring->buf[ring->idx_r]; + ring->idx_r = (ring->idx_r + 1) % ring->buf_len; + return rval; + } else { + return -1; + } +#endif +} + +bool ringb_put(struct ringb *ring, uint8_t c) { +#if 1 + unsigned int next = (ring->idx_w + 1) % ring->buf_len; + if (next != ring->idx_r) { + ring->buf[ring->idx_w] = c; + ring->idx_w = next; + return true; + } + return false; +#else + if (((ring->idx_w - ring->idx_r) % ring->buf_len) == 0) { + return false; + } + ring->buf[ring->idx_w] = c; + ring->idx_w = (ring->idx_w + 1) % ring->buf_len; + return true; +#endif + +} + + +void ringb_flush(struct ringb *ring) { + ring->idx_r = 0; + ring->idx_w = 0; +} + +int ringb_depth(struct ringb *ring) { + return ((unsigned int)(ring->idx_w - ring->idx_r) % ring->buf_len); +}
\ No newline at end of file diff --git a/usb-serial-rs485/ringb.h b/usb-serial-rs485/ringb.h new file mode 100644 index 0000000..c3d5356 --- /dev/null +++ b/usb-serial-rs485/ringb.h @@ -0,0 +1,60 @@ +/* + * Karl Palsson <karlp@tweak.net.au> + * Considered to be released under your choice of: + * MIT/ISC/Apache2/BSD2Clause/GPLv2 + * If you're looking for elegant compact performance you've come to the wrong + * place hombre. + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stdint.h> + +struct ringb { + volatile unsigned int idx_r; + volatile unsigned int idx_w; + uint8_t *buf; + int buf_len; +}; + +/** + * Load up a ring buffer. Always suceeds + * @param ring struct saving state, provided by the user + * @param buf where the data will be kept + * @param len size of buf in in elements. + */ +void ringb_init(struct ringb *ring, uint8_t *buf, int len); + +/** + * push data in + * @param ring + * @param c + * @return true if space was available + */ +bool ringb_put(struct ringb *ring, uint8_t c); + +/** + * pull data out + * @param ring + * @return -1 for no data, uint8_t range for valid. + */ +int ringb_get(struct ringb *ring); + +/** + * Toss data and reset to empty + * @param ring + */ +void ringb_flush(struct ringb *ring); + +int ringb_depth(struct ringb *ring); + + + +#ifdef __cplusplus +} +#endif diff --git a/usb-serial-rs485/syscfg.h b/usb-serial-rs485/syscfg.h new file mode 100644 index 0000000..d643af1 --- /dev/null +++ b/usb-serial-rs485/syscfg.h @@ -0,0 +1,59 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Karl Palsson <karlp@tweak.net.au> + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef SYSCFG_H +#define SYSCFG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define STIMULUS_RING_DRAIN 2 +#define STIMULUS_RING_PUSH 3 +#define STIMULUS_TXC 4 +#define STIMULUS_TX 5 + +#if defined STM32F1 +#define LED_RX_PORT GPIOC +#define LED_RX_PIN GPIO13 +#define LED_TX_PORT GPIOC +#define LED_TX_PIN GPIO13 +#define RS485DE_PORT GPIOA +#define RS485DE_PIN GPIO8 +/* TODO: should really make a stm32f4discovery.h file... */ +#elif defined STM32F4 + +#define LED_RX_PORT GPIOD +#define LED_RX_PIN GPIO15 /* Blue, but you won't see this one much */ +#define LED_TX_PORT GPIOD +#define LED_TX_PIN GPIO13 /* orange */ +/* On stm32f4 discovery, this is actually another led... */ +#define RS485DE_PORT GPIOD +#define RS485DE_PIN GPIO14 /* red */ + +#define STREAM_USART2_TX 6 +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* SYSCFG_H */ + diff --git a/usb-serial-rs485/usb_cdcacm-arch.c b/usb-serial-rs485/usb_cdcacm-arch.c new file mode 100644 index 0000000..e868861 --- /dev/null +++ b/usb-serial-rs485/usb_cdcacm-arch.c @@ -0,0 +1,82 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Karl Palsson <karlp@tweak.net.au> + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <libopencm3/cm3/nvic.h> +#include <libopencm3/stm32/dma.h> +#include <libopencm3/stm32/gpio.h> +#include <libopencm3/stm32/rcc.h> +#include <libopencm3/stm32/usart.h> + +#include "usb_cdcacm.h" +#include "syscfg.h" +#include "ringb.h" + + + +int glue_set_line_coding_cb(uint32_t baud, uint8_t databits, + enum usb_cdc_line_coding_bParityType cdc_parity, + enum usb_cdc_line_coding_bCharFormat cdc_stopbits) +{ + int uart_parity; + int uart_stopbits; + + if (databits < 8 || databits > 9) { + return 0; + } + + /* Be careful here, ST counts parity as a data bit */ + switch (cdc_parity) { + case USB_CDC_NO_PARITY: + uart_parity = USART_PARITY_NONE; + break; + case USB_CDC_ODD_PARITY: + uart_parity = USART_PARITY_ODD; + databits++; + break; + case USB_CDC_EVEN_PARITY: + uart_parity = USART_PARITY_EVEN; + databits++; + break; + default: + return 0; + } + + switch (cdc_stopbits) { + case USB_CDC_1_STOP_BITS: + uart_stopbits = USART_STOPBITS_1; + break; + case USB_CDC_2_STOP_BITS: + uart_stopbits = USART_STOPBITS_2; + break; + default: + return 0; + } + + /* Disable the UART while we mess with its settings */ + usart_disable(USART2); + /* Set communication parameters */ + usart_set_baudrate(USART2, baud); + usart_set_databits(USART2, databits); + usart_set_parity(USART2, uart_parity); + usart_set_stopbits(USART2, uart_stopbits); + /* Back to work. */ + usart_enable(USART2); + + return 1; +} diff --git a/usb-serial-rs485/usb_cdcacm.c b/usb-serial-rs485/usb_cdcacm.c new file mode 100644 index 0000000..099c4ad --- /dev/null +++ b/usb-serial-rs485/usb_cdcacm.c @@ -0,0 +1,363 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz> + * Copyright (C) 2013 Alexandru Gagniuc <mr.nuke.me@gmail.com> + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + */ +/* + * This package is _meant_ to be platform independent, just a full + * cdc-acm impl, with callbacks + */ + +#include "usb_cdcacm.h" + +#include <stdio.h> +#include <stdlib.h> +#include <libopencm3/usb/usbd.h> +#include <libopencm3/usb/cdc.h> +#include <libopencm3/cm3/scb.h> +// NOTHING ELSE! this file _does_ not know about part specifics! + +#include "syscfg.h" +#include "ringb.h" + +#define ER_DEBUG +#ifdef ER_DEBUG +#define ER_DPRINTF(fmt, ...) \ + do { printf(fmt, ## __VA_ARGS__); } while (0) +#else +#define ER_DPRINTF(fmt, ...) \ + do { } while (0) +#endif + + +static uint8_t usbd_control_buffer[128]; +static usbd_device *acm_dev; + +static const struct usb_device_descriptor dev = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x2000, + .bDeviceClass = USB_CLASS_CDC, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = 64, + .idVendor = 0xc03e, + .idProduct = 0xb007, + .bcdDevice = 0x2000, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 3, + .bNumConfigurations = 1, +}; + +/* + * This notification endpoint isn't implemented. According to CDC spec it's + * optional, but its absence causes a NULL pointer dereference in the + * Linux cdc_acm driver. + */ +static const struct usb_endpoint_descriptor comm_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x83, + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, + .wMaxPacketSize = 16, + .bInterval = 1, +}}; + +static const struct usb_endpoint_descriptor data_endp[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x01, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 1, +}, { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x82, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = 64, + .bInterval = 1, +}}; + +static const struct { + struct usb_cdc_header_descriptor header; + struct usb_cdc_call_management_descriptor call_mgmt; + struct usb_cdc_acm_descriptor acm; + struct usb_cdc_union_descriptor cdc_union; +} __attribute__ ((packed)) cdcacm_functional_descriptors = { + .header = { + .bFunctionLength = sizeof(struct usb_cdc_header_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_HEADER, + .bcdCDC = 0x0110, + }, + .call_mgmt = { + .bFunctionLength = + sizeof(struct usb_cdc_call_management_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, + .bmCapabilities = 0, + .bDataInterface = 1, + }, + .acm = { + .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_ACM, + .bmCapabilities = (1 << 1), + }, + .cdc_union = { + .bFunctionLength = sizeof(struct usb_cdc_union_descriptor), + .bDescriptorType = CS_INTERFACE, + .bDescriptorSubtype = USB_CDC_TYPE_UNION, + .bControlInterface = 0, + .bSubordinateInterface0 = 1, + } +}; + +static const struct usb_interface_descriptor comm_iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_CDC, + .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, + .bInterfaceProtocol = USB_CDC_PROTOCOL_AT, + .iInterface = 0, + + .endpoint = comm_endp, + + .extra = &cdcacm_functional_descriptors, + .extralen = sizeof(cdcacm_functional_descriptors) +}}; + +static const struct usb_interface_descriptor data_iface[] = {{ + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 1, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_DATA, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0, + + .endpoint = data_endp, +}}; + +static const struct usb_interface ifaces[] = {{ + .num_altsetting = 1, + .altsetting = comm_iface, +}, { + .num_altsetting = 1, + .altsetting = data_iface, +}}; + +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, + .bDescriptorType = USB_DT_CONFIGURATION, + .wTotalLength = 0, + .bNumInterfaces = 2, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = 0x80, + .bMaxPower = 0x32, + + .interface = ifaces, +}; + +static char serial[] = "none"; +static const char *usb_strings[] = { + "libopencm3", + "usb_to_serial_cdcacm", + serial, + "DEMO", +}; + +struct ringb rx_ring; +static uint8_t rx_ring_data[64]; +struct ringb tx_ring; +static uint8_t tx_ring_data[128]; +bool nakked = false; + + +static int cdcacm_control_request(usbd_device *usbd_dev, + struct usb_setup_data *req, uint8_t **buf, + uint16_t *len, + void (**complete) (usbd_device *usbd_dev, + struct usb_setup_data * + req)) +{ + uint8_t dtr, rts; + + (void) complete; + (void) buf; + (void) usbd_dev; + + switch (req->bRequest) { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + { + /* + * This Linux cdc_acm driver requires this to be implemented + * even though it's optional in the CDC spec, and we don't + * advertise it in the ACM functional descriptor. + */ + + dtr = (req->wValue & (1 << 0)) ? 1 : 0; + rts = (req->wValue & (1 << 1)) ? 1 : 0; + ER_DPRINTF("CTRLRQ:%d Set Line state: dtr:%d rts: %d\n", req->wIndex, dtr, rts); + + // FIXME - need to get port based on wIndex I believe? + cdcacm_arch_set_line_state(0, dtr, rts); + + return 1; + } + case USB_CDC_REQ_SET_LINE_CODING: + { + struct usb_cdc_line_coding *coding; + + if (*len < sizeof (struct usb_cdc_line_coding)) + return 0; + + coding = (struct usb_cdc_line_coding *) *buf; + ER_DPRINTF("CTRLRQ: line coding: %lu(%u:%u:%u)\n", coding->dwDTERate, + coding->bDataBits, coding->bParityType, coding->bCharFormat); + return glue_set_line_coding_cb(coding->dwDTERate, + coding->bDataBits, + coding->bParityType, + coding->bCharFormat); + } + } + return 0; +} + +static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) +{ + uint8_t buf[64]; + /* nak right now, we're not sure whether we'll be able to even process this!*/ + usbd_ep_nak_set(usbd_dev, ep, 1); + int len = usbd_ep_read_packet(usbd_dev, ep, buf, 64); + ER_DPRINTF("Hrx%db\n", len); + cdcacm_arch_pin(0, CDCACM_PIN_LED_TX, 1); + cdcacm_arch_pin(0, CDCACM_PIN_RS485DE, 1); + for (int x = 0; x < len; x++) { + if (!ringb_put(&tx_ring, buf[x])) { + // failed to process usb traffic properly. + // should _never_ happen, means we failed to nak in time. + // this is _never_recoverable beyond watchdog reset. + while(1); + } + // look up port to suart mapping which side? + cdcacm_arch_txirq(0, 1); + } + if (ringb_depth(&tx_ring) < 64) { + ER_DPRINTF("ACK\n"); + usbd_ep_nak_set(usbd_dev, ep, 0); + } else { + nakked = true; + } +} + +static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue) +{ + (void) wValue; + + usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, 64, + cdcacm_data_rx_cb); + usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, 64, NULL); + usbd_ep_setup(usbd_dev, 0x83, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); + + usbd_register_control_callback(usbd_dev, + USB_REQ_TYPE_CLASS | + USB_REQ_TYPE_INTERFACE, + USB_REQ_TYPE_TYPE | + USB_REQ_TYPE_RECIPIENT, + cdcacm_control_request); +} + +/* FIXME - need to report this! */ +void cdcacm_line_state_changed_cb(uint8_t linemask) +{ + const int size = sizeof (struct usb_cdc_notification) + 2; + uint8_t buf[size]; + + struct usb_cdc_notification *notify = (void *) buf; + notify->bmRequestType = 0xa1; + notify->bNotification = USB_CDC_NOTIFY_SERIAL_STATE; + notify->wValue = 0; + notify->wIndex = 1; + notify->wLength = 2; + uint16_t *data = (void *) &buf[sizeof (struct usb_cdc_notification)]; + *data = linemask; + + while (usbd_ep_write_packet(acm_dev, 0x83, buf, size) == size); +} + + +/* Y0, moron, nothing's stopping rx irqs from happening, have fun when you overflow temp buffer! */ +static void task_drain_rx(struct ringb *r) { + uint8_t zero_copy_is_for_losers[sizeof(rx_ring_data)]; + int zci = 0; + int c = ringb_get(r); + while (c >= 0) { + //trace_send8(STIMULUS_RING_DRAIN, c); + zero_copy_is_for_losers[zci++] = c; + c = ringb_get(r); + } + if (zci) { + //trace_send16(STIMULUS_RING_DRAIN, zci); + ER_DPRINTF("Drx %db\n", zci); + usbd_ep_write_packet(acm_dev, 0x82, zero_copy_is_for_losers, zci); + } +} + + +usbd_device * usb_cdcacm_init(const usbd_driver *driver, const char *userserial) +{ + ringb_init(&rx_ring, rx_ring_data, sizeof(rx_ring_data)); + ringb_init(&tx_ring, tx_ring_data, sizeof(tx_ring_data)); + if (userserial) { + usb_strings[2] = userserial; + } + + acm_dev = usbd_init(driver, &dev, &config, usb_strings, 4, + usbd_control_buffer, sizeof (usbd_control_buffer)); + usbd_register_set_config_callback(acm_dev, cdcacm_set_config); + return acm_dev; +} + + +void usb_cdcacm_poll(usbd_device *usbd_dev) // FIXME -drop to acm_dev internal +{ + // Originally, calling this every 50 times caused some rx character droppage, + // and every 500 times caused _none_. _probably_ needs to be tied to + // a timer and something like the current baud rate and the inter character time + static int i = 0; + if (i++ > 500) { + // hacktastic + if (ringb_depth(&tx_ring) < 64 && nakked) { + usbd_ep_nak_set(usbd_dev, 1, 0); + nakked = false; + } + + task_drain_rx(&rx_ring); + i = 0; + } + + +}
\ No newline at end of file diff --git a/usb-serial-rs485/usb_cdcacm.h b/usb-serial-rs485/usb_cdcacm.h new file mode 100644 index 0000000..d4e47e0 --- /dev/null +++ b/usb-serial-rs485/usb_cdcacm.h @@ -0,0 +1,74 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Karl Palsson <karlp@tweak.net.au> + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see <http://www.gnu.org/licenses/>. + */ +/* + * This is the header file for a usb_cdcacm implmentation, usb_cdcacm.c is the + * platform independent portion, and usb_cdcacm-arch.c should be re-implemented + * for other platforms. + */ + +#ifndef USB_CDCACM_H +#define USB_CDCACM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <libopencm3/usb/usbd.h> +#include <libopencm3/usb/cdc.h> + + enum cdcacm_pin { + CDCACM_PIN_NONE, + CDCACM_PIN_LED_TX, + CDCACM_PIN_LED_RX, + CDCACM_PIN_RS485DE, + }; + + usbd_device * usb_cdcacm_init(const usbd_driver *driver, const char *userserial); + void usb_cdcacm_setup_pre_arch(void); + void usb_cdcacm_setup_post_arch(usbd_device *dev); + void usb_cdcacm_poll(usbd_device *usbd_dev); + void cdcacm_line_state_changed_cb(uint8_t linemask); + + /** + * Called by the cdcacm core to toggle pins as need be + * @param port which serial port, 0 normally + * @param pin logical pin + * @param set set or clear + */ + void cdcacm_arch_pin(int port, enum cdcacm_pin pin, bool set); + + /** + * enable the tx emmpty irq for the logical port + * @param port + * @param set + */ + void cdcacm_arch_txirq(int port, bool set); + + void cdcacm_arch_set_line_state(int port, uint8_t dtr, uint8_t rts); + + int glue_set_line_coding_cb(uint32_t baud, uint8_t databits, + enum usb_cdc_line_coding_bParityType cdc_parity, + enum usb_cdc_line_coding_bCharFormat cdc_stopbits); + +#ifdef __cplusplus +} +#endif + +#endif /* USB_CDCACM_H */ + |