Browse Source

Initial checkin (cdcacm).

master
flabbergast 1 year ago
commit
f767e7d75b
12 changed files with 893 additions and 0 deletions
  1. 6
    0
      .gitignore
  2. 3
    0
      .gitmodules
  3. 57
    0
      README.md
  4. 43
    0
      common.f0.mk
  5. 44
    0
      common.l0.mk
  6. 32
    0
      l052-bldr.ld
  7. 1
    0
      libopencm3
  8. 294
    0
      libopencm3.rules.mk
  9. 26
    0
      usb_cdcacm/Makefile.f0
  10. 26
    0
      usb_cdcacm/Makefile.l0
  11. 27
    0
      usb_cdcacm/Makefile.l0btld
  12. 334
    0
      usb_cdcacm/cdcacm.c

+ 6
- 0
.gitignore View File

@@ -0,0 +1,6 @@
*.o
*.d
*.elf
*.hex
*.bin
*.map

+ 3
- 0
.gitmodules View File

@@ -0,0 +1,3 @@
[submodule "libopencm3"]
path = libopencm3
url = https://github.com/libopencm3/libopencm3

+ 57
- 0
README.md View File

@@ -0,0 +1,57 @@
A few examples of code for [the bat board], using [libopencm3].

Most of them are just modified examples from the official [libopencm3 examples].

They aren't too specific for the bat boards, except the usual pinout considerations. What's sometimes not so easy to change/adapt is the chip. These examples are for STM32 F072 and L052.

## Short summary for the examples

### usb_cdcacm

The board presents itself as a serial port (CDC). It echoes back what it receives; toggling the LED after each received packet. Pressing the `PA1` button will print `hello, world` over this port.


## General setup notes

You are expected to a C compiler (for ARM) and some development tools (e.g. make and git) installed and ready to use. You can get a [toolchain from arm.com]; or use one provided by your system package manager (if you're on a normal system like some linux, bsd or mac). For the development tools, use your package manager.

[libopencm3] is supposed to go into the `libopencm3` directory; it's git submodule if you're going the git way; otherwise just unpack it there. You'll need to run `make` in the `libopencm3` directory once, to compile the library. Any subsequent compilation of the examples here will just use these precompiled objects. This means that if you update, or make any changes to, libopencm3 sources, you'll need to recompile it explicitly like this. In other words, running `make` in any of the examples it not going to recompile the library.

From time to time there are breaking changes in the library (e.g. L0 support is relatively new), so no guarantees that things compile (although they should with the commit referenced for the submodule). From time to time I pull in the new updates, but again, no guarantees. (Timestamp 2017-12-02T18:41:26).

To compile an example, `cd` into its directory and run `make -f Makefile.l0btld` to compile. The three versions of the `Makefile`s are for STM32F072CBT6 (f0), STM32L052C8T6 (l0), STM32L052C8T6 with DFU bootloader occupying the first 8k of the flash (more about this setup on [the bat board] page). Cleaning up (important if you're switching to a different chip!) is done with the usual `make clean`.

I didn't set up flashing to the board into the `Makefiles`, so flashing should be done manually. Here are the most common options:

### DFU bootloader

This requires [dfu-util] software. Put the board into DFU mode (on F072 by holding the `PA1` button during reset; on L052 by shorting `GND` and `PB3` during reset). The command is then
```sh
dfu-util -d 0483:df11 -a 0 -s 0x08002000 -D cdcacm.bin
```
where the adjustable parts are of course the name of the firmware (`cdcacm.bin` above) and importantly the address: it should be `0x08002000` for L052 with the 8k DFU bootloader, otherwise `0x08000000`).

### built-in USART bootloader

This requires a means of communicating over serial port (ideally 3.3V levels; although the required pins are 5V tolerant on L052 and F072). This usually means an external hardware, an USB-to-serial converter (there are loads on e.g. ebay; the most commonly used chips are FT231X, FT232RL, CP2102, CH340G). Although on boards like the Pi you can use the built-in UART.

The built-in USART bootloader talks on PA9(TX) and PA10(RX) (and some others; but these are conveniently placed on the bat board). It is entered by holding the `PA1` button during a reset.

On the software side, I prefer [stm32flash]. The command is then
```sh
stm32flash -e 255 -v -w cdcacm.bin /dev/ttyUSB0
```
where the adjustable parts are of course firmware filename and the serial port (`/dev/ttyUSB0` above).




[the bat board]: https://flabbergast.github.io/bat-board
[libopencm3]: https://github.com/libopencm3/libopencm3
[libopencm3 examples]: https://github.com/libopencm3/libopencm3-examples
[toolchain from arm.com]: https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads
[dfu-util]: http://dfu-util.sourceforge.net/


License (for the original bits): CC-by-SA 2.0


+ 43
- 0
common.f0.mk View File

@@ -0,0 +1,43 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
##
## 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/>.
##

LIBNAME = opencm3_stm32f0
DEFS += -DSTM32F0

FP_FLAGS ?= -msoft-float
ARCH_FLAGS = -mthumb -mcpu=cortex-m0 $(FP_FLAGS)

################################################################################
# OpenOCD specific variables

OOCD ?= openocd
OOCD_INTERFACE ?= stlink-v2-1
OOCD_BOARD ?= st_nucleo_f0

################################################################################
# Black Magic Probe specific variables
# Set the BMP_PORT to a serial port and then BMP is used for flashing
BMP_PORT ?=

################################################################################
# texane/stlink specific variables
#STLINK_PORT ?= :4242

include ../libopencm3.rules.mk

+ 44
- 0
common.l0.mk View File

@@ -0,0 +1,44 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
##
## 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/>.
##

LIBNAME = opencm3_stm32l0
DEFS += -DSTM32L0

FP_FLAGS ?= -msoft-float
ARCH_FLAGS = -mthumb -mcpu=cortex-m0plus $(FP_FLAGS)

################################################################################
# OpenOCD specific variables

OOCD ?= openocd
OOCD_INTERFACE ?= stlink-v2-1
OOCD_BOARD ?= stm32l0discovery

################################################################################
# Black Magic Probe specific variables
# Set the BMP_PORT to a serial port and then BMP is used for flashing
BMP_PORT ?=

################################################################################
# texane/stlink specific variables
#STLINK_PORT ?= :4242


include ../libopencm3.rules.mk

+ 32
- 0
l052-bldr.ld View File

@@ -0,0 +1,32 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
*
* 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/>.
*/

/* Linker script for stm32duino (STM32F103RBT6, 128K flash, 20K RAM,
* minus 8k flash and 3k RAM for the DFU bootloader). */

/* Define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08002000, LENGTH = 64K-0x2000
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 8K
}

/* Include the common ld script. */
INCLUDE libopencm3_stm32l0.ld


+ 1
- 0
libopencm3

@@ -0,0 +1 @@
Subproject commit 953bf531ea1c0b36a5b32b05ede8c6c77e480009

+ 294
- 0
libopencm3.rules.mk View File

@@ -0,0 +1,294 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
## Copyright (C) 2013 Frantisek Burian <BuFran@seznam.cz>
##
## 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/>.
##

# Be silent per default, but 'make V=1' will show all compiler calls.
ifneq ($(V),1)
Q := @
NULL := 2>/dev/null
endif

###############################################################################
# Colour stuff
NO_COLOR=\033[0m
OK_COLOR=\033[32;01m
ERROR_COLOR=\033[31;01m
WARN_COLOR=\033[33;01m

OK_STRING=$(OK_COLOR)[OK]$(NO_COLOR)
ERROR_STRING=$(ERROR_COLOR)[ERRORS]$(NO_COLOR)
WARN_STRING=$(WARN_COLOR)[WARNINGS]$(NO_COLOR)

AWK_CMD = awk '{ printf "%-30s %-10s\n",$$1, $$2; }'
PRINT_ERROR = printf "$@ $(ERROR_STRING)\n" | $(AWK_CMD) && printf "$(CMD)\n$$LOG\n" && false
PRINT_WARNING = printf "$@ $(WARN_STRING)\n" | $(AWK_CMD) && printf "$(CMD)\n$$LOG\n"
PRINT_OK = printf "$@ $(OK_STRING)\n" | $(AWK_CMD)
BUILD_CMD = LOG=$$($(CMD) 2>&1) ; if [ $$? -eq 1 ]; then $(PRINT_ERROR); elif [ "$$LOG" != "" ] ; then $(PRINT_WARNING); else $(PRINT_OK); fi;
## can use separately with '@$(PRINT_OK)'

## can use with '$(call colorecho,<string>)'
define colorecho
@tput setaf 6
@echo $1
@tput sgr0
endef

## can use as '$(call buildcmd,<command_to_run_as_string>)'
define buildcmd
LOG=$$($1 2>&1) ; if [ $$? -eq 1 ]; then $(PRINT_ERROR); elif [ "$$LOG" != "" ] ; then $(PRINT_WARNING); else $(PRINT_OK); fi;
endef

###############################################################################
# Executables

PREFIX ?= arm-none-eabi

CC := $(PREFIX)-gcc
CXX := $(PREFIX)-g++
LD := $(PREFIX)-gcc
AR := $(PREFIX)-ar
AS := $(PREFIX)-as
OBJCOPY := $(PREFIX)-objcopy
OBJDUMP := $(PREFIX)-objdump
GDB := $(PREFIX)-gdb
STFLASH = $(shell which st-flash)
STYLECHECK := /checkpatch.pl
STYLECHECKFLAGS := --no-tree -f --terse --mailback
STYLECHECKFILES := $(shell find . -name '*.[ch]')
OPT := -Os
CSTD ?= -std=c99


###############################################################################
# Source files

LDSCRIPT ?= $(BINARY).ld

OBJS += $(BINARY).o


ifeq ($(strip $(OPENCM3_DIR)),)
# user has not specified the library path, so we try to detect it

# where we search for the library
LIBPATHS := ./libopencm3 ../../../../libopencm3 ../../../../../libopencm3

OPENCM3_DIR := $(wildcard $(LIBPATHS:=/locm3.sublime-project))
OPENCM3_DIR := $(firstword $(dir $(OPENCM3_DIR)))

ifeq ($(strip $(OPENCM3_DIR)),)
$(warning Cannot find libopencm3 library in the standard search paths.)
$(error Please specify it through OPENCM3_DIR variable!)
endif
endif

ifeq ($(V),1)
$(info Using $(OPENCM3_DIR) path to library)
endif

define ERR_DEVICE_LDSCRIPT_CONFLICT
You can either specify DEVICE=blah, and have the LDSCRIPT generated,
or you can provide LDSCRIPT, and ensure CPPFLAGS, LDFLAGS and LDLIBS
all contain the correct values for the target you wish to use.
You cannot provide both!
endef

ifeq ($(strip $(DEVICE)),)
# Old style, assume LDSCRIPT exists
DEFS += -I$(OPENCM3_DIR)/include
LDFLAGS += -L$(OPENCM3_DIR)/lib
LDLIBS += -l$(LIBNAME)
LDSCRIPT ?= $(BINARY).ld
else
# New style, assume device is provided, and we're generating the rest.
ifneq ($(strip $(LDSCRIPT)),)
$(error $(ERR_DEVICE_LDSCRIPT_CONFLICT))
endif
include $(OPENCM3_DIR)/mk/genlink-config.mk
endif

OPENCM3_SCRIPT_DIR = $(OPENCM3_DIR)/scripts
LOCAL_SCRIPT_DIR ?= ./scripts

###############################################################################
# C flags

TGT_CFLAGS += $(OPT) $(CSTD) -g
TGT_CFLAGS += $(ARCH_FLAGS)
TGT_CFLAGS += -Wextra -Wshadow -Wimplicit-function-declaration
TGT_CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes
TGT_CFLAGS += -fno-common -ffunction-sections -fdata-sections

###############################################################################
# C++ flags

TGT_CXXFLAGS += $(OPT) $(CXXSTD) -g
TGT_CXXFLAGS += $(ARCH_FLAGS)
TGT_CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++
TGT_CXXFLAGS += -fno-common -ffunction-sections -fdata-sections

###############################################################################
# C & C++ preprocessor common flags

TGT_CPPFLAGS += -MD
TGT_CPPFLAGS += -Wall -Wundef
TGT_CPPFLAGS += $(DEFS)

###############################################################################
# Linker flags

TGT_LDFLAGS += --static -nostartfiles
TGT_LDFLAGS += -T$(LDSCRIPT)
TGT_LDFLAGS += $(ARCH_FLAGS)
TGT_LDFLAGS += -Wl,-Map=$(*).map
TGT_LDFLAGS += -Wl,--gc-sections
ifeq ($(V),99)
TGT_LDFLAGS += -Wl,--print-gc-sections
endif

###############################################################################
# Used libraries

LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group

###############################################################################
###############################################################################
###############################################################################

.SUFFIXES: .elf .bin .hex .srec .list .map .images
.SECONDEXPANSION:
.SECONDARY:

all: elf hex bin

elf: $(BINARY).elf
bin: $(BINARY).bin
hex: $(BINARY).hex
srec: $(BINARY).srec
list: $(BINARY).list

images: $(BINARY).images
flash: $(BINARY).flash

# Either verify the user provided LDSCRIPT exists, or generate it.
ifeq ($(strip $(DEVICE)),)
$(LDSCRIPT):
ifeq (,$(wildcard $(LDSCRIPT)))
$(error Unable to find specified linker script: $(LDSCRIPT))
endif
else
include $(OPENCM3_DIR)/mk/genlink-rules.mk
endif

# Define a helper macro for debugging make errors online
# you can type "make print-OPENCM3_DIR" and it will show you
# how that ended up being resolved by all of the included
# makefiles.
print-%:
@echo $*=$($*)

%.images: %.bin %.hex %.srec %.list %.map
@#printf "*** $* images generated ***\n"

%.bin: %.elf
@#printf " OBJCOPY $(*).bin\n"
$(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin

%.hex: %.elf
@#printf " OBJCOPY $(*).hex\n"
$(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex

%.srec: %.elf
@#printf " OBJCOPY $(*).srec\n"
$(Q)$(OBJCOPY) -Osrec $(*).elf $(*).srec

%.list: %.elf
@#printf " OBJDUMP $(*).list\n"
$(Q)$(OBJDUMP) -S $(*).elf > $(*).list

%.elf %.map: $(OBJS) $(LDSCRIPT)
@#printf " LD $(*).elf\n"
$(Q)$(LD) $(TGT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $(*).elf

%.o: %.c
@#printf " CC $(*).c\n"
$(Q)$(CC) $(TGT_CFLAGS) $(CFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).c

%.o: %.cxx
@#printf " CXX $(*).cxx\n"
$(Q)$(CXX) $(TGT_CXXFLAGS) $(CXXFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).cxx

%.o: %.cpp
@#printf " CXX $(*).cpp\n"
$(Q)$(CXX) $(TGT_CXXFLAGS) $(CXXFLAGS) $(TGT_CPPFLAGS) $(CPPFLAGS) -o $(*).o -c $(*).cpp

clean:
@#printf " CLEAN\n"
$(Q)$(RM) *.o *.d *.elf *.bin *.hex *.srec *.list *.map generated.* ${OBJS} ${OBJS:%.o:%.d}

stylecheck: $(STYLECHECKFILES:=.stylecheck)
styleclean: $(STYLECHECKFILES:=.styleclean)

# the cat is due to multithreaded nature - we like to have consistent chunks of text on the output
%.stylecheck: %
$(Q)$(OPENCM3_SCRIPT_DIR)$(STYLECHECK) $(STYLECHECKFLAGS) $* > $*.stylecheck; \
if [ -s $*.stylecheck ]; then \
cat $*.stylecheck; \
else \
rm -f $*.stylecheck; \
fi;

%.styleclean:
$(Q)rm -f $*.stylecheck;


%.stlink-flash: %.bin
@printf " FLASH $<\n"
$(Q)$(STFLASH) write $(*).bin 0x8000000

ifeq ($(BMP_PORT),)
ifeq ($(OOCD_FILE),)
%.flash: %.elf
@printf " FLASH $<\n"
$(Q)(echo "halt; program $(realpath $(*).elf) verify reset" | nc -4 localhost 4444 2>/dev/null) || \
$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \
-f target/$(OOCD_TARGET).cfg \
-c "program $(*).elf verify reset exit" \
$(NULL)
else
%.flash: %.elf
@printf " FLASH $<\n"
$(Q)(echo "halt; program $(realpath $(*).elf) verify reset" | nc -4 localhost 4444 2>/dev/null) || \
$(OOCD) -f $(OOCD_FILE) \
-c "program $(*).elf verify reset exit" \
$(NULL)
endif
else
%.flash: %.elf
@printf " GDB $(*).elf (flash)\n"
$(GDB) --batch \
-ex 'target extended-remote $(BMP_PORT)' \
-x $(LOCAL_SCRIPT_DIR)/black_magic_probe_flash.scr \
$(*).elf
endif

.PHONY: images clean stylecheck styleclean elf bin hex srec list

-include $(OBJS:.o=.d)

+ 26
- 0
usb_cdcacm/Makefile.f0 View File

@@ -0,0 +1,26 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
##
## 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/>.
##

BINARY = cdcacm

OPENCM3_DIR=../libopencm3
LDSCRIPT = $(OPENCM3_DIR)/lib/stm32/f0/stm32f07xzb.ld

include ../common.f0.mk


+ 26
- 0
usb_cdcacm/Makefile.l0 View File

@@ -0,0 +1,26 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
##
## 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/>.
##

BINARY = cdcacm

OPENCM3_DIR=../libopencm3
LDSCRIPT = $(OPENCM3_DIR)/lib/stm32/l0/stm32l0xx8.ld

include ../common.l0.mk


+ 27
- 0
usb_cdcacm/Makefile.l0btld View File

@@ -0,0 +1,27 @@
##
## This file is part of the libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
##
## 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/>.
##

BINARY = cdcacm

OPENCM3_DIR=../libopencm3
LDSCRIPT = ../l052-bldr.ld
DEFS += -DBOOTLOADER8K

include ../common.l0.mk


+ 334
- 0
usb_cdcacm/cdcacm.c View File

@@ -0,0 +1,334 @@
/*
* This file is part of the libopencm3 project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
*
* 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 <stdlib.h>
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/flash.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/syscfg.h>
#include <libopencm3/stm32/crs.h>
#include <libopencm3/usb/usbd.h>
#include <libopencm3/usb/cdc.h>

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 = 64,
.idVendor = 0x0483,
.idProduct = 0x5740,
.bcdDevice = 0x0200,
.iManufacturer = 1,
.iProduct = 2,
.iSerialNumber = 3,
.bNumConfigurations = 1,
};

/*
* This notification endpoint isn't implemented. According to CDC spec its
* optional, but its absence causes a NULL pointer dereference in 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 = 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 = 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 const char *usb_strings[] = {
"flabbergast",
"CDC-ACM Test",
"DeMo",
};

/* Buffer to be used for control requests. */
uint8_t usbd_control_buffer[128];

/* Aux functions */
static void led_flip(void) {
if(GPIOA_IDR & (1<<15)) {
GPIOA_BRR = GPIO15;
} else {
GPIOA_BSRR = GPIO15;
}
}

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(0x83, buf, 10);
return 1;
}
case USB_CDC_REQ_SET_LINE_CODING:
if(*len < sizeof(struct usb_cdc_line_coding))
return 0;

return 1;
}
return 0;
}

static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep)
{
(void)ep;

char buf[64];
int len = usbd_ep_read_packet(usbd_dev, 0x01, buf, 64);

if (len) {
usbd_ep_write_packet(usbd_dev, 0x82, buf, len);
buf[len] = 0;
led_flip();
}
}

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);
}

int main(void)
{
usbd_device *usbd_dev;

#if defined(BOOTLOADER8K)
# define SCB_VTOR MMIO32(0xE000ED08)
SCB_VTOR = (uint32_t) 0x08002000;
#endif

#if defined(STM32F0)
rcc_clock_setup_in_hsi_out_48mhz();
crs_autotrim_usb_enable();
rcc_set_usbclk_source(RCC_HSI48);

// this is needed for F042 in TSSOP-20 package; USB is on shared pins
//rcc_periph_clock_enable(RCC_SYSCFG_COMP);
//SYSCFG_CFGR1 |= SYSCFG_CFGR1_PA11_PA12_RMP;
#elif defined(STM32L0)

// libopencm3 clock setup can be a bit ad-hoc, so let's configure manually
// 32MHz, PLL supplied by the internal HSI16 oscillator
/* Set the flash latency */
FLASH_ACR |= FLASH_ACR_LATENCY_1WS;
/* Turn on HSI16 */
RCC_CR |= RCC_CR_HSI16ON;
while ((RCC_CR & RCC_CR_HSI16RDY) == 0);
/* Make sure PLL is off (it should be after reset, but ...) */
RCC_CR &= ~RCC_CR_PLLON;
while (RCC_CR & RCC_CR_PLLRDY);
/* Set the PLL source to HSI16 */
RCC_CFGR &= ~(1<<16); // RCC_CFGR_PLLSRC
/* Set up the PLL */
uint32_t reg = RCC_CFGR
& ~( (RCC_CFGR_PLLMUL_MASK << RCC_CFGR_PLLMUL_SHIFT)
| (RCC_CFGR_PLLDIV_MASK << RCC_CFGR_PLLDIV_SHIFT));
RCC_CFGR = reg | (RCC_CFGR_PLLMUL_MUL4 << RCC_CFGR_PLLMUL_SHIFT)
| (RCC_CFGR_PLLDIV_DIV2 << RCC_CFGR_PLLDIV_SHIFT);
/* Turn on PLL and switch to it */
RCC_CR |= RCC_CR_PLLON;
while ((RCC_CR & RCC_CR_PLLRDY) == 0);
RCC_CFGR |= RCC_CFGR_SW_PLL;
/* Set the peripheral clock frequencies used. */
rcc_ahb_frequency = 32000000;
rcc_apb1_frequency = 32000000;
rcc_apb2_frequency = 32000000;
// end of manual clock configuration

// Enable VREFINT reference for HSI48
rcc_periph_clock_enable(RCC_SYSCFG);
SYSCFG_CFGR3 |= SYSCFG_CFGR3_ENREF_HSI48;

rcc_set_hsi48_source_rc48();
crs_autotrim_usb_enable();

rcc_osc_on(RCC_HSI48);
rcc_wait_for_osc_ready(RCC_HSI48);

/* MCO on PA9, to verify MCU speed with a scope */
gpio_mode_setup(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9);
gpio_set_af(GPIOA, GPIO_AF0, GPIO9);
RCC_CFGR |= (RCC_CFGR_MCO_SYSCLK << RCC_CFGR_MCO_SHIFT) | (RCC_CFGR_MCOPRE_DIV16 << 28);

#else
#error "Edit the sources and do the clock setup!"
#endif

rcc_periph_clock_enable(RCC_GPIOA);
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO15); // LED
gpio_mode_setup(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO1); // button, has external pull-down

usbd_dev = usbd_init(&st_usbfs_v2_usb_driver, &dev, &config, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer));

usbd_register_set_config_callback(usbd_dev, cdcacm_set_config);

char greeting[16] = "hello, world\r\n";

while (1) {
usbd_poll(usbd_dev);
if (gpio_get(GPIOA, GPIO1)) {
usbd_ep_write_packet(usbd_dev, 0x82, greeting, 15);
for (uint32_t i = 0; i < 2000000; i++) { /* Wait a bit, cheap "debounce" */
__asm__("nop");
}
}
}
}

Loading…
Cancel
Save