From a008da49b42547aa960cc0e2918b27d5adb5a2a2 Mon Sep 17 00:00:00 2001 From: podhrmic Date: Fri, 28 Nov 2014 10:50:13 -0800 Subject: [PATCH] [stm32] usb_serial: CDC USB Device driver for STM32F1/4 add usb_serial implementation and using that for telemetry_usb closes #998 --- .../telemetry_transparent_usb.makefile | 11 +- .../telemetry_transparent_usb.makefile | 11 +- conf/modules/usb_serial_stm32_example1.xml | 27 + conf/modules/usb_serial_stm32_example2.xml | 30 + sw/airborne/arch/lpc21/usb_ser_hw.c | 6 + sw/airborne/arch/stm32/usb_ser_hw.c | 552 ++++++++++++++++++ sw/airborne/firmwares/fixedwing/main_ap.c | 4 + sw/airborne/firmwares/rotorcraft/main.c | 8 + sw/airborne/mcu_periph/usb_serial.h | 10 +- sw/airborne/modules/com/usb_serial_stm32.h | 39 ++ .../modules/com/usb_serial_stm32_example1.c | 136 +++++ .../modules/com/usb_serial_stm32_example2.c | 96 +++ 12 files changed, 923 insertions(+), 7 deletions(-) create mode 100644 conf/modules/usb_serial_stm32_example1.xml create mode 100644 conf/modules/usb_serial_stm32_example2.xml create mode 100644 sw/airborne/arch/stm32/usb_ser_hw.c create mode 100644 sw/airborne/modules/com/usb_serial_stm32.h create mode 100644 sw/airborne/modules/com/usb_serial_stm32_example1.c create mode 100644 sw/airborne/modules/com/usb_serial_stm32_example2.c diff --git a/conf/firmwares/subsystems/fixedwing/telemetry_transparent_usb.makefile b/conf/firmwares/subsystems/fixedwing/telemetry_transparent_usb.makefile index 6f4a886bb3..b792904363 100644 --- a/conf/firmwares/subsystems/fixedwing/telemetry_transparent_usb.makefile +++ b/conf/firmwares/subsystems/fixedwing/telemetry_transparent_usb.makefile @@ -11,8 +11,15 @@ ap.srcs += $(SRC_FIRMWARE)/datalink.c ap.srcs += $(SRC_ARCH)/usb_ser_hw.c $(SRC_ARCH)/lpcusb/usbhw_lpc.c $(SRC_ARCH)/lpcusb/usbcontrol.c ap.srcs += $(SRC_ARCH)/lpcusb/usbstdreq.c $(SRC_ARCH)/lpcusb/usbinit.c else +ifeq ($(ARCH), stm32) +ap.CFLAGS += -DDOWNLINK -DPERIODIC_TELEMETRY -DDOWNLINK_DEVICE=usb_serial -DPPRZ_UART=UsbS +ap.CFLAGS += -DDOWNLINK_TRANSPORT=pprz_tp -DDATALINK=PPRZ -DUSE_USB_SERIAL +ap.srcs += subsystems/datalink/downlink.c subsystems/datalink/pprz_transport.c +ap.srcs += $(SRC_FIRMWARE)/datalink.c +ap.srcs += $(SRC_ARCH)/usb_ser_hw.c +else ifneq ($(ARCH), sim) -$(error telemetry_transparent_usb currently only implemented for the lpc21) +$(error telemetry_transparent_usb currently only implemented for the lpc21 and stm32) +endif endif endif - diff --git a/conf/firmwares/subsystems/rotorcraft/telemetry_transparent_usb.makefile b/conf/firmwares/subsystems/rotorcraft/telemetry_transparent_usb.makefile index 065d599df1..cf241fd59a 100644 --- a/conf/firmwares/subsystems/rotorcraft/telemetry_transparent_usb.makefile +++ b/conf/firmwares/subsystems/rotorcraft/telemetry_transparent_usb.makefile @@ -9,8 +9,15 @@ ap.srcs += $(SRC_FIRMWARE)/datalink.c $(SRC_FIRMWARE)/rotorcraft_telemetry.c ap.srcs += $(SRC_ARCH)/usb_ser_hw.c $(SRC_ARCH)/lpcusb/usbhw_lpc.c $(SRC_ARCH)/lpcusb/usbcontrol.c ap.srcs += $(SRC_ARCH)/lpcusb/usbstdreq.c $(SRC_ARCH)/lpcusb/usbinit.c else +ifeq ($(ARCH), stm32) +ap.CFLAGS += -DDOWNLINK -DPERIODIC_TELEMETRY -DDOWNLINK_DEVICE=usb_serial -DPPRZ_UART=UsbS +ap.CFLAGS += -DDOWNLINK_TRANSPORT=pprz_tp -DDATALINK=PPRZ -DUSE_USB_SERIAL -DDefaultPeriodic='&telemetry_Main' +ap.srcs += subsystems/datalink/downlink.c subsystems/datalink/pprz_transport.c subsystems/datalink/telemetry.c +ap.srcs += $(SRC_FIRMWARE)/datalink.c $(SRC_FIRMWARE)/rotorcraft_telemetry.c +ap.srcs += $(SRC_ARCH)/usb_ser_hw.c +else ifneq ($(ARCH), sim) -$(error telemetry_transparent_usb currently only implemented for the lpc21) +$(error telemetry_transparent_usb currently only implemented for the lpc21 and stm32) +endif endif endif - diff --git a/conf/modules/usb_serial_stm32_example1.xml b/conf/modules/usb_serial_stm32_example1.xml new file mode 100644 index 0000000000..463e29677b --- /dev/null +++ b/conf/modules/usb_serial_stm32_example1.xml @@ -0,0 +1,27 @@ + + + + + + Serial-over-USB console on STM32. + Example of USB-serial module on STM32 architecture, using libopencm3 library. + This example emulates a console - i.e. user can send commands to Paparazzi and get response + To be used with Lisa M/MX 2.1 + + + +
+ +
+ + + + + + + ap.srcs += $(SRC_ARCH)/usb_ser_hw.c + + + + +
diff --git a/conf/modules/usb_serial_stm32_example2.xml b/conf/modules/usb_serial_stm32_example2.xml new file mode 100644 index 0000000000..c92642f4e9 --- /dev/null +++ b/conf/modules/usb_serial_stm32_example2.xml @@ -0,0 +1,30 @@ + + + + + + STM32 USB-serial example. + Example of USB-serial module on STM32 architecture, using libopencm3 library. + This example tests the capability of USB-serial port by sending a lots of data. + To be used with Lisa M/MX 2.1 + + + +
+ +
+ + + + + + + + + + ap.srcs += $(SRC_ARCH)/usb_ser_hw.c + + + + +
diff --git a/sw/airborne/arch/lpc21/usb_ser_hw.c b/sw/airborne/arch/lpc21/usb_ser_hw.c index 7a880dd408..d4f81b2c00 100644 --- a/sw/airborne/arch/lpc21/usb_ser_hw.c +++ b/sw/airborne/arch/lpc21/usb_ser_hw.c @@ -565,6 +565,12 @@ static void usb_serial_transmit(struct usb_serial_periph* p __attribute__((unuse static void usb_serial_send(struct usb_serial_periph* p __attribute__((unused))) { } +// Empty for lpc21 +void VCOM_event(void) {} + +// Empty for lpc21 +void VCOM_transmit_message(void) {} + void VCOM_init(void) { // initialise stack USBInit(); diff --git a/sw/airborne/arch/stm32/usb_ser_hw.c b/sw/airborne/arch/stm32/usb_ser_hw.c new file mode 100644 index 0000000000..d420feae8f --- /dev/null +++ b/sw/airborne/arch/stm32/usb_ser_hw.c @@ -0,0 +1,552 @@ +/* + * Copyright (C) 2014 Michal Podhradsky, + * based on example from libopencm3 + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/** + * @file arch/stm32/usb_ser_hw.c + * CDC USB device driver for STM32 architecture (STM32F1, STM32F4) + */ + +/* This version derived from libopencm3 example */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mcu_periph/usb_serial.h" + + +/* Max packet size for USB transfer */ +#define MAX_PACKET_SIZE 64 +/* Max fifo size for storing data */ +#define VCOM_FIFO_SIZE 128 + +typedef struct { + int head; + int tail; + uint8_t *buf; +} fifo_t; + +static uint8_t txdata[VCOM_FIFO_SIZE]; +static uint8_t rxdata[VCOM_FIFO_SIZE]; + +static fifo_t txfifo; +static fifo_t rxfifo; + +void fifo_init(fifo_t *fifo, uint8_t *buf); +bool_t fifo_put(fifo_t *fifo, uint8_t c); +bool_t fifo_get(fifo_t *fifo, uint8_t *pc); +int fifo_avail(fifo_t *fifo); +int fifo_free(fifo_t *fifo); +inline char *get_dev_unique_id(char *serial_no); + + + +usbd_device *my_usbd_dev; + +static const struct usb_device_descriptor dev = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = USB_CLASS_CDC, + .bDeviceSubClass = 0, + .bDeviceProtocol = 0, + .bMaxPacketSize0 = MAX_PACKET_SIZE, + .idVendor = 0x1d50, // OpenMoko, Inc. + .idProduct = 0x603d, // Paparazzi LPC(STM32)USB Serial + .bcdDevice = 0x0200, + .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 = 255, + } +}; + +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 = MAX_PACKET_SIZE, + .bInterval = 1, + }, { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x82, + .bmAttributes = USB_ENDPOINT_ATTR_BULK, + .wMaxPacketSize = MAX_PACKET_SIZE, + .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 = 0, + }, + .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_no[24]; + +/* Description of the device as it appears after enumeration */ +static const char *usb_strings[] = { + "Paparazzi UAV", + "CDC Serial STM32", + serial_no, +}; + +/** + * Serial is 96bit so 12bytes so 12 hexa numbers, or 24 decimal + */ +inline char *get_dev_unique_id(char *s) +{ +#if defined STM32F4 + volatile uint8_t *unique_id = (volatile uint8_t *)0x1FFF7A10; +#else + volatile uint8_t *unique_id = (volatile uint8_t *)0x1FFFF7E8; +#endif + int i; + + // Fetch serial number from chip's unique ID + for (i = 0; i < 24; i += 2) { + s[i] = ((*unique_id >> 4) & 0xF) + '0'; + s[i + 1] = (*unique_id++ & 0xF) + '0'; + } + for (i = 0; i < 24; i++) + if (s[i] > '9') { + s[i] += 'A' - '9' - 1; + } + + return s; +} + +/* + * Buffer to be used for control requests. + * (from libopencm3 examples) + */ +uint8_t usbd_control_buffer[128]; + +/** + * CDC device control request + * (from libopencm3 examples) + */ +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)) +{ + (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. + */ + char local_buf[10]; + struct usb_cdc_notification *notif = (void *)local_buf; + + /* We echo signals back to host as notification. */ + notif->bmRequestType = 0xA1; + notif->bNotification = USB_CDC_NOTIFY_SERIAL_STATE; + notif->wValue = 0; + notif->wIndex = 0; + notif->wLength = 2; + local_buf[8] = req->wValue & 3; + local_buf[9] = 0; + usbd_ep_write_packet(usbd_dev, 0x83, local_buf, 10); + return 1; + } + case USB_CDC_REQ_SET_LINE_CODING: + if (*len < sizeof(struct usb_cdc_line_coding)) { + return 0; + } + + return 1; + default: + return 0; + } +} + +/** + * RX callback for CDC device + * (from libopencm3 examples) + */ +static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) +{ + (void)ep; + uint8_t buf[MAX_PACKET_SIZE]; + static int len = 0; + // read packet + len = usbd_ep_read_packet(usbd_dev, 0x01, buf, MAX_PACKET_SIZE); + + // write to fifo + for (int i = 0; i < len; i++) { + fifo_put(&rxfifo, buf[i]); + } +} + +// store USB connection status +static bool_t usb_connected; + +// use suspend callback to detect disconnect +static void suspend_cb(void) +{ + usb_connected = FALSE; +} + +/** + * Set configuration and control callbacks for CDC device + * (from libopencm3 examples) + */ +static void cdcacm_set_config(usbd_device *usbd_dev, uint16_t wValue) +{ + (void)wValue; + + usbd_ep_setup(usbd_dev, 0x01, USB_ENDPOINT_ATTR_BULK, MAX_PACKET_SIZE, cdcacm_data_rx_cb); + usbd_ep_setup(usbd_dev, 0x82, USB_ENDPOINT_ATTR_BULK, MAX_PACKET_SIZE, 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); + + // use config and suspend callback to detect connect + usb_connected = TRUE; + usbd_register_suspend_callback(usbd_dev, suspend_cb); +} + + +void fifo_init(fifo_t *fifo, uint8_t *buf) +{ + fifo->head = 0; + fifo->tail = 0; + fifo->buf = buf; +} + + + +bool_t fifo_put(fifo_t *fifo, uint8_t c) +{ + int next; + + // check if FIFO has room + next = (fifo->head + 1) % VCOM_FIFO_SIZE; + if (next == fifo->tail) { + // full + return FALSE; + } + + fifo->buf[fifo->head] = c; + fifo->head = next; + + return TRUE; +} + + +bool_t fifo_get(fifo_t *fifo, uint8_t *pc) +{ + int next; + + // check if FIFO has data + if (fifo->head == fifo->tail) { + return FALSE; + } + + next = (fifo->tail + 1) % VCOM_FIFO_SIZE; + + *pc = fifo->buf[fifo->tail]; + fifo->tail = next; + + return TRUE; +} + + +int fifo_avail(fifo_t *fifo) +{ + return (VCOM_FIFO_SIZE + fifo->head - fifo->tail) % VCOM_FIFO_SIZE; +} + + +int fifo_free(fifo_t *fifo) +{ + return (VCOM_FIFO_SIZE - 1 - fifo_avail(fifo)); +} + + +/** + * Writes one character to VCOM port + * @param [in] c character to write + * @returns character to be written + * Note we don't really have an instant feeedback from USB driver if + * the character was written, so we always return c + */ +int VCOM_putchar(int c) +{ + // check if there are at least two more bytes left in queue + if (VCOM_check_free_space(2)) { + // if yes, add char + fifo_put(&txfifo, c); + } else { + // less than 2 bytes available, add byte and send data now + fifo_put(&txfifo, c); + VCOM_transmit_message(); + } + return c; +} + +/** + * Reads one character from VCOM port + * @returns character read, or -1 if character could not be read + */ +int VCOM_getchar(void) +{ + static int result = 0; + uint8_t c; + result = fifo_get(&rxfifo, &c) ? c : -1; + return result; +} + +/** + * Checks if buffer free in VCOM buffer + * @returns TRUE if len bytes are free + */ +bool_t VCOM_check_free_space(uint8_t len) +{ + return (fifo_free(&txfifo) >= len ? TRUE : FALSE); +} + +/** + * Checks if data available in VCOM buffer + * @returns nonzero if char is available in the queue, zero otherwise + */ +int VCOM_check_available(void) +{ + return (fifo_avail(&rxfifo)); +} + +/** + * Poll usb (required by libopencm3) + * VCOM_poll() should be called from module event function + */ +void VCOM_event(void) +{ + usbd_poll(my_usbd_dev); +} + +/** + * Send data from fifo right now (up to MAX_PACKET_SIZE) + */ +void VCOM_transmit_message() +{ + /* + if (len > MAX_PACKET_SIZE) { + len = MAX_PACKET_SIZE; + } + usbd_ep_write_packet(my_usbd_dev, 0x82, buf, len); + */ + uint8_t buf[MAX_PACKET_SIZE]; + uint8_t data; + uint16_t idx = 0; + while (fifo_get(&txfifo, &data) && (idx < MAX_PACKET_SIZE)) { + buf[idx] = data; + idx++; + } + usbd_ep_write_packet(my_usbd_dev, 0x82, buf, idx); +} + + +/* + * USE_USB_LINE_CODING is not used in case of example1, example2 and telemetry + */ +#ifdef USE_USB_LINE_CODING +void VCOM_allow_linecoding(uint8_t mode) {} +void VCOM_set_linecoding(uint8_t mode) {} +#endif + +/* + * For USB telemetry & generic device API + */ +// Periph with generic device API +struct usb_serial_periph usb_serial; + +// Functions for the generic device API +static int usb_serial_check_free_space(struct usb_serial_periph *p __attribute__((unused)), + uint8_t len) +{ + return (int)VCOM_check_free_space(len); +} + +// Only transmit when USB is connected +static void usb_serial_transmit(struct usb_serial_periph *p __attribute__((unused)), uint8_t byte) +{ + if (usb_connected) { + VCOM_putchar(byte); + } +} + +// Only send message when USB is connected +static void usb_serial_send(struct usb_serial_periph *p __attribute__((unused))) +{ + if (usb_connected) { + VCOM_transmit_message(); + } +} + +void VCOM_init(void) +{ + // initialise fifos + fifo_init(&txfifo, txdata); + fifo_init(&rxfifo, rxdata); + + /* set up GPIO pins */ +#if defined STM32F4 + gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, + GPIO11 | GPIO12); + gpio_set_af(GPIOA, GPIO_AF10, GPIO11 | GPIO12); +#endif + + /* USB clock */ + rcc_periph_clock_enable(RCC_OTGFS); + + /* Get serial number */ + get_dev_unique_id(serial_no); + + /* usb driver init*/ + my_usbd_dev = usbd_init(&otgfs_usb_driver, &dev, &config, + usb_strings, 3, + usbd_control_buffer, sizeof(usbd_control_buffer)); + + usbd_register_set_config_callback(my_usbd_dev, cdcacm_set_config); + + // disconnected by default + usb_connected = FALSE; + + // Configure generic device + usb_serial.device.periph = (void *)(&usb_serial); + usb_serial.device.check_free_space = (check_free_space_t) usb_serial_check_free_space; + usb_serial.device.transmit = (transmit_t) usb_serial_transmit; + usb_serial.device.send_message = (send_message_t) usb_serial_send; +} diff --git a/sw/airborne/firmwares/fixedwing/main_ap.c b/sw/airborne/firmwares/fixedwing/main_ap.c index 586f38b883..de81a12cfb 100644 --- a/sw/airborne/firmwares/fixedwing/main_ap.c +++ b/sw/airborne/firmwares/fixedwing/main_ap.c @@ -656,6 +656,10 @@ void event_task_ap( void ) { uart_event(); #endif +#if USE_USB_SERIAL + VCOM_event(); +#endif + #if USE_AHRS && USE_IMU ImuEvent(on_gyro_event, on_accel_event, on_mag_event); #endif diff --git a/sw/airborne/firmwares/rotorcraft/main.c b/sw/airborne/firmwares/rotorcraft/main.c index d1cd09b215..4cc1880415 100644 --- a/sw/airborne/firmwares/rotorcraft/main.c +++ b/sw/airborne/firmwares/rotorcraft/main.c @@ -83,6 +83,10 @@ PRINT_CONFIG_MSG_VALUE("USE_BARO_BOARD is TRUE, reading onboard baro: ", BARO_BO #include "generated/modules.h" #include "subsystems/abi.h" +#ifdef USE_USB_SERIAL +#include "mcu_periph/usb_serial.h" +#endif + /* if PRINT_CONFIG is defined, print some config options */ PRINT_CONFIG_VAR(PERIODIC_FREQUENCY) @@ -283,6 +287,10 @@ STATIC_INLINE void main_event( void ) { udp_event(); #endif +#ifdef USE_USB_SERIAL + VCOM_event(); +#endif + DatalinkEvent(); if (autopilot_rc) { diff --git a/sw/airborne/mcu_periph/usb_serial.h b/sw/airborne/mcu_periph/usb_serial.h index cc37f3df2d..ec2ed7acdc 100644 --- a/sw/airborne/mcu_periph/usb_serial.h +++ b/sw/airborne/mcu_periph/usb_serial.h @@ -31,7 +31,6 @@ #include #include "std.h" #include "mcu_periph/link_device.h" -//#include "usb_serial_hw.h" struct usb_serial_periph { /** Generic device interface */ @@ -47,13 +46,18 @@ bool_t VCOM_check_free_space(uint8_t len); int VCOM_check_available(void); void VCOM_set_linecoding(uint8_t mode); void VCOM_allow_linecoding(uint8_t mode); +void VCOM_transmit_message(void); +void VCOM_event(void); +/* + * Macros can be used in subsystems that normally work with serial ports + * e.g. use UsbS instead of UART1 + */ #define UsbSInit() VCOM_init() #define UsbSCheckFreeSpace(_x) VCOM_check_free_space(_x) #define UsbSTransmit(_x) VCOM_putchar(_x) -#define UsbSSendMessage() {} +#define UsbSSendMessage() VCOM_transmit_message() #define UsbSGetch() VCOM_getchar() #define UsbSChAvailable() VCOM_check_available() - #endif /* USB_S_H */ diff --git a/sw/airborne/modules/com/usb_serial_stm32.h b/sw/airborne/modules/com/usb_serial_stm32.h new file mode 100644 index 0000000000..affd513743 --- /dev/null +++ b/sw/airborne/modules/com/usb_serial_stm32.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 Michal Podhradsky + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/** + * @file modules/com/usb_serial_stm32.h + * header for serial over USB modules + */ + +#ifndef USB_SERIAL_STM32_H +#define USB_SERIAL_STM32_H + +#include "mcu_periph/usb_serial.h" + +void init_usb_serial(void); +void periodic_usb_serial(void); +void event_usb_serial(void); + +void usb_serial_parse_packet(int c); + +#endif // USB_SERIAL_STM32_H diff --git a/sw/airborne/modules/com/usb_serial_stm32_example1.c b/sw/airborne/modules/com/usb_serial_stm32_example1.c new file mode 100644 index 0000000000..35a55dce59 --- /dev/null +++ b/sw/airborne/modules/com/usb_serial_stm32_example1.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2014 Michal Podhradsky + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/** + * @file modules/com/usb_serial_stm32_example1.c + * + * USB_SERIAL_STM32 example 1 - a template for a console to autopilot + * + */ + +#include "modules/com/usb_serial_stm32.h" +#include + +void send_command(void); +void cmd_execute(void); + +char cmd_buf[64]; +uint8_t cmd_idx; +bool_t cmd_avail; +uint8_t prompt = '$'; + +/** + * Init module, call VCOM_init() from here + */ +void init_usb_serial(void) +{ + VCOM_init(); + cmd_idx = 0; + cmd_avail = FALSE; +} + + +/** + * Parse data from buffer + * Note that the function receives int, not char + * Because we want to be able to catch -1 in case no + * more data were available + */ +void usb_serial_parse_packet(int data) +{ + if (data == -1) { return; } + char c = (char)data; + + if (c == '\r' || c == '\n') { + // command complete + cmd_avail = TRUE; + // add termination characters and the prompt into buffer + VCOM_putchar('\r'); + VCOM_putchar('\n'); + VCOM_putchar(prompt); + } else { + // buffer command + cmd_buf[cmd_idx++] = c; + // echo char back and transmit immediately + VCOM_putchar((uint8_t)c); + VCOM_transmit_message(); + } +} + +/** + * Helper function + */ +static inline void ReadUsbBuffer(void) +{ + while (UsbSChAvailable() && !cmd_avail) { + usb_serial_parse_packet(UsbSGetch()); + } +} + +/** + * Execute command from user + * use strncmp + */ +void cmd_execute(void) +{ + // copy command into tx buffer for user feedback + for (int i = 0; i < cmd_idx; i++) { + VCOM_putchar(cmd_buf[i]); + } + + if (strncmp("help", cmd_buf, cmd_idx) == 0) { + uint8_t response[] = " - user asked for help"; + for (uint16_t i = 0; i < sizeof(response); i++) { + VCOM_putchar(response[i]); + } + } else { + uint8_t response[] = " Command not recognized"; + for (uint16_t i = 0; i < sizeof(response); i++) { + VCOM_putchar(response[i]); + } + } + + // add termination characters and the prompt into buffer + VCOM_putchar('\r'); + VCOM_putchar('\n'); + VCOM_putchar(prompt); + + // reset counter + cmd_idx = 0; + // transmit message + VCOM_transmit_message(); +} + +/** + * Call VCOM_poll() from module event function + */ +void event_usb_serial(void) +{ + VCOM_event(); + if (UsbSChAvailable()) { + ReadUsbBuffer(); + } + if (cmd_avail) { + cmd_execute(); + cmd_avail = FALSE; + } +} diff --git a/sw/airborne/modules/com/usb_serial_stm32_example2.c b/sw/airborne/modules/com/usb_serial_stm32_example2.c new file mode 100644 index 0000000000..af125312f9 --- /dev/null +++ b/sw/airborne/modules/com/usb_serial_stm32_example2.c @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 Michal Podhradsky + * + * This file is part of paparazzi. + * + * paparazzi is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * paparazzi is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with paparazzi; see the file COPYING. If not, write to + * the Free Software Foundation, 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +/** + * @file modules/com/usb_serial_stm32_example2.c + * + * USB_SERIAL_STM32 example 2 - sends lot of data through serial port. User can control + * the flow by pressing "S" for stop and "R" for run. + */ + +#include "modules/com/usb_serial_stm32.h" + +void send_command(void); + +uint8_t run; +uint8_t prompt = '$'; + +/* A lot of text */ +uint8_t big_buffer[] = " ASCII stands for American Standard Code for Information Interchange. Computers can only understand numbers, so an ASCII code is the numerical representation of a character such as 'a' or '@' or an action of some sort. ASCII was developed a long time ago and now the non-printing characters are rarely used for their original purpose. Below is the ASCII character table and this includes descriptions of the first 32 non-printing characters. ASCII was actually designed for use with teletypes and so the descriptions are somewhat obscure. If someone says they want your CV however in ASCII format, all this means is they want 'plain' text with no formatting such as tabs, bold or underscoring - the raw format that any computer can understand. This is usually so they can easily import the file into their own applications without issues. Notepad.exe creates ASCII text, or in MS Word you can save a file as 'text only' "; + +/** + * Init module, call VCOM_init() from here + */ +void init_usb_serial(void) +{ + VCOM_init(); + run = FALSE; +} + +/** + * Periodic function in case you needed to send data periodically + * like telemetry + * Note that the data are sent once the buffer is full, not immediately + */ +void periodic_usb_serial(void) +{ + if (run) { + for (uint16_t i = 0; i < sizeof(big_buffer); i++) { + VCOM_putchar(big_buffer[i]); + } + } +} + +/** + * Parse data from buffer + * Note that the function receives int, not char + * Because we want to be able to catch -1 in case no + * more data were available + */ +void usb_serial_parse_packet(int data) +{ + if (data == -1) { return; } + uint8_t c = (uint8_t)data; + VCOM_putchar(prompt); + VCOM_putchar(data); + VCOM_putchar('\r'); + VCOM_putchar('\n'); + + if (c == 'S') { + run = FALSE; + } + if (c == 'R') { + run = TRUE; + } + VCOM_transmit_message(); +} + +/** + * Call VCOM_poll() from module event function + */ +void event_usb_serial(void) +{ + VCOM_event(); + if (UsbSChAvailable()) { + usb_serial_parse_packet(UsbSGetch()); + } +}