From 003c9d202aeeb428640cecfece1c20f9bcd7ddbd Mon Sep 17 00:00:00 2001 From: Karl Palsson Date: Tue, 27 Sep 2016 23:05:26 +0000 Subject: usb-serial: functional on f1. --- tests/usb-serial-rs485/Makefile.stm32f103-generic | 38 ++++ tests/usb-serial-rs485/main-stm32f103-generic.c | 208 ++++++++++++++++++++++ tests/usb-serial-rs485/main-stm32f4-disco.c | 55 +++--- tests/usb-serial-rs485/syscfg.h | 19 +- tests/usb-serial-rs485/usb_cdcacm-arch.c | 42 ----- tests/usb-serial-rs485/usb_cdcacm.c | 94 +++++++--- tests/usb-serial-rs485/usb_cdcacm.h | 34 +++- 7 files changed, 379 insertions(+), 111 deletions(-) create mode 100644 tests/usb-serial-rs485/Makefile.stm32f103-generic create mode 100644 tests/usb-serial-rs485/main-stm32f103-generic.c (limited to 'tests') diff --git a/tests/usb-serial-rs485/Makefile.stm32f103-generic b/tests/usb-serial-rs485/Makefile.stm32f103-generic new file mode 100644 index 0000000..747238d --- /dev/null +++ b/tests/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 . +## + +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/tests/usb-serial-rs485/main-stm32f103-generic.c b/tests/usb-serial-rs485/main-stm32f103-generic.c new file mode 100644 index 0000000..0665c0b --- /dev/null +++ b/tests/usb-serial-rs485/main-stm32f103-generic.c @@ -0,0 +1,208 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2014 Karl Palsson + * + * 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 . + */ + +#include +#include +#include +#include + +#include +#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); + } + 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 + 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_interrupt_source(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) +{ + 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"); + } + + rcc_periph_clock_enable(RCC_OTGFS); +} + +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_mode_setup(RS485DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, +// 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/tests/usb-serial-rs485/main-stm32f4-disco.c b/tests/usb-serial-rs485/main-stm32f4-disco.c index f024e46..f5c21bf 100644 --- a/tests/usb-serial-rs485/main-stm32f4-disco.c +++ b/tests/usb-serial-rs485/main-stm32f4-disco.c @@ -38,13 +38,7 @@ #endif -static usbd_device *usbd_dev; -static 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; - +extern struct ringb rx_ring, tx_ring; static void usart_setup(void) { /* Enable the USART2 interrupt. */ @@ -115,22 +109,22 @@ void usart2_isr(void) // } } -/* 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) { - zero_copy_is_for_losers[zci++] = c; - c = ringb_get(r); - } - if (zci) { - trace_send16(STIMULUS_RING_DRAIN, zci); - glue_data_received_cb(zero_copy_is_for_losers, zci); - } +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) +{ +} + + int main(void) { rcc_clock_setup_hse_3v3(&rcc_hse_8mhz_3v3[RCC_CLOCK_3V3_168MHZ]); @@ -142,27 +136,18 @@ int main(void) gpio_mode_setup(RS485DE_PORT, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, RS485DE_PIN); - usart_setup(); - ringb_init(&rx_ring, rx_ring_data, sizeof(rx_ring_data)); - ringb_init(&tx_ring, tx_ring_data, sizeof(tx_ring_data)); + - usb_cdcacm_init(&usbd_dev); + 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); - 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; - } - + usb_cdcacm_poll(usbd_dev); } } diff --git a/tests/usb-serial-rs485/syscfg.h b/tests/usb-serial-rs485/syscfg.h index 7e2a6d8..50d36e4 100644 --- a/tests/usb-serial-rs485/syscfg.h +++ b/tests/usb-serial-rs485/syscfg.h @@ -24,7 +24,20 @@ 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 GPIOB +#define RS485DE_PIN GPIO9 // FIXME? (disconnected, just a pin)) /* 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 */ @@ -35,11 +48,7 @@ extern "C" { #define RS485DE_PIN GPIO14 /* red */ #define STREAM_USART2_TX 6 - -#define STIMULUS_RING_DRAIN 2 -#define STIMULUS_RING_PUSH 3 -#define STIMULUS_TXC 4 -#define STIMULUS_TX 5 +#endif #ifdef __cplusplus diff --git a/tests/usb-serial-rs485/usb_cdcacm-arch.c b/tests/usb-serial-rs485/usb_cdcacm-arch.c index 8680906..e868861 100644 --- a/tests/usb-serial-rs485/usb_cdcacm-arch.c +++ b/tests/usb-serial-rs485/usb_cdcacm-arch.c @@ -27,49 +27,7 @@ #include "syscfg.h" #include "ringb.h" -extern bool out_in_progress; -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(void) -{ -} - -// hacktastic -extern struct ringb tx_ring; -void glue_send_data_cb(uint8_t *buf, uint16_t len) -{ - if (len == 0) { - return; - } - gpio_set(LED_TX_PORT, LED_TX_PIN); - gpio_set(RS485DE_PORT, RS485DE_PIN); - 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); - } - usart_enable_tx_interrupt(USART2); - } -} - -void glue_set_line_state_cb(uint8_t dtr, uint8_t rts) -{ - (void) dtr; - (void) rts; - // LM4f has an implementation of this if you're keen -} int glue_set_line_coding_cb(uint32_t baud, uint8_t databits, enum usb_cdc_line_coding_bParityType cdc_parity, diff --git a/tests/usb-serial-rs485/usb_cdcacm.c b/tests/usb-serial-rs485/usb_cdcacm.c index 8617d2b..2e7f654 100644 --- a/tests/usb-serial-rs485/usb_cdcacm.c +++ b/tests/usb-serial-rs485/usb_cdcacm.c @@ -29,7 +29,9 @@ #include #include #include +// NOTHING ELSE! this file _does_ not know about part specifics! +#include "syscfg.h" #include "ringb.h" #define ER_DEBUG @@ -42,10 +44,8 @@ #endif -uint8_t usbd_control_buffer[128]; -usbd_device *acm_dev; - -bool out_in_progress; +static uint8_t usbd_control_buffer[128]; +static usbd_device *acm_dev; static const struct usb_device_descriptor dev = { .bLength = USB_DT_DEVICE_SIZE, @@ -181,13 +181,21 @@ static const struct usb_config_descriptor config = { .interface = ifaces, }; +static char serial[] = "none"; static const char *usb_strings[] = { "libopencm3", "usb_to_serial_cdcacm", - "none", + 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, @@ -212,9 +220,10 @@ static int cdcacm_control_request(usbd_device *usbd_dev, dtr = (req->wValue & (1 << 0)) ? 1 : 0; rts = (req->wValue & (1 << 1)) ? 1 : 0; - ER_DPRINTF("CTRLRQ: Set Line state: dtr:%d rts: %d\n", dtr, rts); + ER_DPRINTF("CTRLRQ:%d Set Line state: dtr:%d rts: %d\n", req->wIndex, dtr, rts); - glue_set_line_state_cb(dtr, rts); + // FIXME - need to get port based on wIndex I believe? + cdcacm_arch_set_line_state(0, dtr, rts); return 1; } @@ -237,8 +246,6 @@ static int cdcacm_control_request(usbd_device *usbd_dev, return 0; } -extern bool nakked; -extern struct ringb tx_ring; static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) { uint8_t buf[64]; @@ -246,8 +253,18 @@ static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) 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); - // push all of what we got into our outbound fifo - glue_send_data_cb(buf, 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); @@ -291,20 +308,55 @@ void cdcacm_line_state_changed_cb(uint8_t linemask) while (usbd_ep_write_packet(acm_dev, 0x83, buf, size) == size); } -void glue_data_received_cb(uint8_t *buf, uint16_t len) -{ - ER_DPRINTF("Drx %db\n", len); - usbd_ep_write_packet(acm_dev, 0x82, buf, len); + +/* 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) { + 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); + } } -void usb_cdcacm_init(usbd_device **usbd_dev) + +usbd_device * usb_cdcacm_init(const usbd_driver *driver, const char *userserial) { - usb_cdcacm_setup_pre_arch(); + 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; + } - *usbd_dev = usbd_init(&otgfs_usb_driver, &dev, &config, usb_strings, 4, + acm_dev = usbd_init(driver, &dev, &config, usb_strings, 4, usbd_control_buffer, sizeof (usbd_control_buffer)); - acm_dev = *usbd_dev; usbd_register_set_config_callback(acm_dev, cdcacm_set_config); - - usb_cdcacm_setup_post_arch(); + 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/tests/usb-serial-rs485/usb_cdcacm.h b/tests/usb-serial-rs485/usb_cdcacm.h index 3228d0a..d4e47e0 100644 --- a/tests/usb-serial-rs485/usb_cdcacm.h +++ b/tests/usb-serial-rs485/usb_cdcacm.h @@ -32,18 +32,36 @@ extern "C" { #include #include - void usb_cdcacm_init(usbd_device **usb_dev); + 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(void); - void cdcacm_send_data(uint8_t *buf, uint16_t len); + 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); - /* Call this if you have data to send to the usb host */ - void glue_data_received_cb(uint8_t *buf, uint16_t len); - /* These will be called by usb_cdcacm code */ - void glue_send_data_cb(uint8_t *buf, uint16_t len); + /** + * 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); - void glue_set_line_state_cb(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); -- cgit