From a2707754a28862d91ba00981753b40bfd832fa78 Mon Sep 17 00:00:00 2001 From: Brett Parker Date: Wed, 30 Sep 2020 10:18:05 +0100 Subject: [PATCH] Initial commit of code --- .gitmodules | 3 + Makefile | 18 ++ libopencm3 | 1 + src/Makefile | 63 +++++++ src/bluepill.ld | 22 +++ src/global.c | 114 +++++++++++++ src/global.h | 310 +++++++++++++++++++++++++++++++++++ src/timer.c | 111 +++++++++++++ src/usb_cdcacm.c | 415 +++++++++++++++++++++++++++++++++++++++++++++++ src/usb_cdcacm.h | 38 +++++ 10 files changed, 1095 insertions(+) create mode 100644 .gitmodules create mode 100644 Makefile create mode 160000 libopencm3 create mode 100644 src/Makefile create mode 100644 src/bluepill.ld create mode 100644 src/global.c create mode 100644 src/global.h create mode 100644 src/timer.c create mode 100644 src/usb_cdcacm.c create mode 100644 src/usb_cdcacm.h diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..29e93ae --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "libopencm3"] + path = libopencm3 + url = git://github.com/libopencm3/libopencm3.git diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..358c687 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +ifneq ($(V), 1) +MFLAGS += --no-print-dir +Q := @ +endif + +all: + $(Q)if [ ! -f libopencm3/Makefile ]; then \ + echo "Initialising git submodules..." ;\ + git submodule init ;\ + git submodule update ;\ + fi + $(Q)$(MAKE) $(MFLAGS) -C libopencm3 lib + $(Q)$(MAKE) $(MFLAGS) -C src + +clean: + $(Q)$(MAKE) $(MFLAGS) -C libopencm3 $@ + $(Q)$(MAKE) $(MFLAGS) -C src $@ + diff --git a/libopencm3 b/libopencm3 new file mode 160000 index 0000000..5617ed4 --- /dev/null +++ b/libopencm3 @@ -0,0 +1 @@ +Subproject commit 5617ed466444790b787b6df8d7f21d1611905fd1 diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..8cc6471 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,63 @@ +ifneq ($(V), 1) +MAKEFLAGS += --no-print-dir +Q := @ +endif + +OPT_FLAGS ?= -O2 + +CFLAGS += -Wall -Wextra -Werror -Wno-char-subscripts\ + $(OPT_FLAGS) -std=gnu99 -g3 -MD \ + -I. +LDFLAGS += $(OPT_FLAGS) + +SRC = \ + timer.c \ + usb_cdcacm.c \ + +CROSS_COMPILE ?= arm-none-eabi- +CC = $(CROSS_COMPILE)gcc +OBJCOPY = $(CROSS_COMPILE)objcopy + +OPT_FLAGS = -Os +CFLAGS += -mcpu=cortex-m3 -mthumb \ + -DSTM32F1 -DDISCOVERY_STLINK -I../libopencm3/include \ + -I . +LDFLAGS_BOOT := $(LDFLAGS) --specs=nano.specs \ + -lopencm3_stm32f1 -Wl,--defsym,_stack=0x20005000 \ + -Wl,-T,bluepill.ld -nostartfiles -lc \ + -Wl,-Map=mapfile -mthumb -mcpu=cortex-m3 -Wl,-gc-sections \ + -L../libopencm3/lib +LDFLAGS = $(LDFLAGS_BOOT) + +LDFLAGS += --specs=nosys.specs + +all: timer.bin + +host_clean: + -$(Q)$(RM) timer.bin + +OBJ = $(SRC:.c=.o) + +timer.elf: $(OBJ) + @echo " LD $@" + $(Q)$(CC) -o $@ $(OBJ) $(LDFLAGS) + +%.o: %.c + @echo " CC $<" + $(Q)$(CC) $(CFLAGS) -c $< -o $@ + +%.bin: %.elf + @echo " OBJCOPY $@" + $(Q)$(OBJCOPY) -O binary $^ $@ + +%.hex: % + @echo " OBJCOPY $@" + $(Q)$(OBJCOPY) -O ihex $^ $@ + +.PHONY: clean host_clean FORCE + +clean: host_clean + $(Q)echo " CLEAN" + -$(Q)$(RM) -f *.o *.d *~ *.elf *.bin $(HOSTFILES) + -$(Q)$(RM) -f mapfile + diff --git a/src/bluepill.ld b/src/bluepill.ld new file mode 100644 index 0000000..ffa24a8 --- /dev/null +++ b/src/bluepill.ld @@ -0,0 +1,22 @@ +/* + * This file is part of the libopenstm32 project. + * + * Copyright (C) 2010 Thomas Otto + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + */ + +/* Include the common ld script from libopenstm32. */ +INCLUDE stm32/f1/stm32f103x8.ld +INCLUDE cortex-m-generic.ld diff --git a/src/global.c b/src/global.c new file mode 100644 index 0000000..6e03c56 --- /dev/null +++ b/src/global.c @@ -0,0 +1,114 @@ +/* This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + * + */ +/** global definitions and methods (code) + * @file global.c + * @author King Kévin + * @date 2016 + */ +/* standard libraries */ +#include // standard integer types +#include // general utilities + +/* STM32 (including CM3) libraries */ +#include // real-time control clock library +#include // general purpose input output library +#include // timer library +#include // interrupt handler +#include // external interrupt defines + +#include "global.h" // common methods +#include "string.h" // memory utilities + +volatile bool button_flag = false; + +char* b2s(uint64_t binary, uint8_t rjust) +{ + static char string[64+1] = {0}; // the string representation to return + uint8_t bit = LENGTH(string)-1; // the index of the bit to print + string[bit--] = '\0'; // terminate string + + while (binary) { + if (binary & 1) { + string[bit--] = '1'; + } else { + string[bit--] = '0'; + } + binary >>= 1; + } + + while (64-bit-10) { + string[bit--] = '0'; + } + + return string; +} + +/** switch on board LED */ +void led_on(void) +{ +#if defined(SYSTEM_BOARD) || defined(BLUE_PILL) || defined(CORE_BOARD) + gpio_clear(GPIO(LED_PORT), GPIO(LED_PIN)); +#elif defined(MAPLE_MINI) + gpio_set(GPIO(LED_PORT), GPIO(LED_PIN)); +#endif +} +/** switch off board LED */ +void led_off(void) +{ +#if defined(SYSTEM_BOARD) || defined(BLUE_PILL) || defined(CORE_BOARD) + gpio_set(GPIO(LED_PORT), GPIO(LED_PIN)); +#elif defined(MAPLE_MINI) + gpio_clear(GPIO(LED_PORT), GPIO(LED_PIN)); +#endif +} +/** toggle board LED */ +void led_toggle(void) +{ + gpio_toggle(GPIO(LED_PORT), GPIO(LED_PIN)); +} + +void board_setup(void) +{ + // setup LED + rcc_periph_clock_enable(RCC_GPIO(LED_PORT)); // enable clock for LED + gpio_set_mode(GPIO(LED_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_PIN)); // set LED pin to 'output push-pull' + led_off(); // switch off LED per default + + // setup button +#if defined(BUTTON_PORT) && defined(BUTTON_PIN) + rcc_periph_clock_enable(RCC_GPIO(BUTTON_PORT)); // enable clock for button + gpio_set_mode(GPIO(BUTTON_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO(BUTTON_PIN)); // set button pin to input + rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt + exti_select_source(EXTI(BUTTON_PIN), GPIO(BUTTON_PORT)); // mask external interrupt of this pin only for this port +#if defined(MAPLE_MINI) + gpio_clear(GPIO(BUTTON_PORT), GPIO(BUTTON_PIN)); // pull down to be able to detect button push (go high) + exti_set_trigger(EXTI(BUTTON_PIN), EXTI_TRIGGER_RISING); // trigger when button is pressed +#elif defined(CORE_BOARD) + gpio_set(GPIO(BUTTON_PORT), GPIO(BUTTON_PIN)); // pull up to be able to detect button push (go low) + exti_set_trigger(EXTI(BUTTON_PIN), EXTI_TRIGGER_FALLING); // trigger when button is pressed +#endif + exti_enable_request(EXTI(BUTTON_PIN)); // enable external interrupt + nvic_enable_irq(NVIC_EXTI_IRQ(BUTTON_PIN)); // enable interrupt +#endif +} + +#if defined(BUTTON_PIN) +/** interrupt service routine called when button is pressed */ +void EXTI_ISR(BUTTON_PIN)(void) +{ + exti_reset_request(EXTI(BUTTON_PIN)); // reset interrupt + button_flag = true; // perform button action +} +#endif diff --git a/src/global.h b/src/global.h new file mode 100644 index 0000000..49df00c --- /dev/null +++ b/src/global.h @@ -0,0 +1,310 @@ +/* This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + * + */ +/** global definitions and methods (API) + * @file global.h + * @author King Kévin + * @date 2016 + */ +#pragma once + +/** enable debugging functionalities */ +#define DEBUG false + +/** get the length of an array */ +#define LENGTH(x) (sizeof(x) / sizeof((x)[0])) +/** concatenate 2 arguments */ +#define CAT2(x,y) x##y +/** concatenate 3 arguments */ +#define CAT3(x,y,z) x##y##z +/** concatenate 4 arguments */ +#define CAT4(w,x,y,z) w##x##y##z + +/** @defgroup reg_macro macros to define values based on other defines values + * @note used when the value is calculated or isn't a value + * @{ + */ +/** get GPIO based on GPIO identifier */ +#define GPIO(x) CAT2(GPIO,x) +/** get RCC for GPIO based on GPIO identifier */ +#define RCC_GPIO(x) CAT2(RCC_GPIO,x) +/** get TIM based on TIM identifier */ +#define TIM(x) CAT2(TIM,x) +/** get RCC for timer based on TIM identifier */ +#define RCC_TIM(x) CAT2(RCC_TIM,x) +/** get NVIC IRQ for timer base on TIM identifier */ +#define NVIC_TIM_IRQ(x) CAT3(NVIC_TIM,x,_IRQ) +/** get interrupt service routine for timer base on TIM identifier */ +#define TIM_ISR(x) CAT3(tim,x,_isr) +/** get port based on TIMx_CHy identifier */ +#define TIM_CH_PORT(x,y) CAT4(GPIO_BANK_TIM,x,_CH,y) +/** get pin based on TIMx_CHy identifier */ +#define TIM_CH_PIN(x,y) CAT4(GPIO_TIM,x,_CH,y) +/** get RCC for port based on TIMx_CHy identifier */ +#define RCC_TIM_CH(x,y) CAT4(RCC_TIM,x,_CH,y) +#define RCC_TIM1_CH1 RCC_GPIOA /**< RCC for port for on TIM1_CH1 */ +#define RCC_TIM1_CH2 RCC_GPIOA /**< RCC for port for on TIM1_CH2 */ +#define RCC_TIM1_CH3 RCC_GPIOA /**< RCC for port for on TIM1_CH3 */ +#define RCC_TIM1_CH4 RCC_GPIOA /**< RCC for port for on TIM1_CH4 */ +#define RCC_TIM1_CH1N RCC_GPIOB /**< RCC for port for on TIM1_CH1N */ +#define RCC_TIM1_CH2N RCC_GPIOB /**< RCC for port for on TIM1_CH2N */ +#define RCC_TIM1_CH3N RCC_GPIOB /**< RCC for port for on TIM1_CH3N */ +#define RCC_TIM2_CH1_ETR RCC_GPIOA /**< RCC for port for on TIM2_CH1_ETR */ +#define RCC_TIM2_CH2 RCC_GPIOA /**< RCC for port for on TIM2_CH2 */ +#define RCC_TIM2_CH3 RCC_GPIOA /**< RCC for port for on TIM2_CH3 */ +#define RCC_TIM2_CH4 RCC_GPIOA /**< RCC for port for on TIM2_CH4 */ +#define RCC_TIM3_CH1 RCC_GPIOA /**< RCC for port for on TIM3_CH1 */ +#define RCC_TIM3_CH2 RCC_GPIOA /**< RCC for port for on TIM3_CH2 */ +#define RCC_TIM3_CH3 RCC_GPIOB /**< RCC for port for on TIM3_CH3 */ +#define RCC_TIM3_CH4 RCC_GPIOB /**< RCC for port for on TIM3_CH4 */ +#define RCC_TIM4_CH1 RCC_GPIOB /**< RCC for port for on TIM4_CH1 */ +#define RCC_TIM4_CH2 RCC_GPIOB /**< RCC for port for on TIM4_CH2 */ +#define RCC_TIM4_CH3 RCC_GPIOB /**< RCC for port for on TIM4_CH3 */ +#define RCC_TIM4_CH4 RCC_GPIOB /**< RCC for port for on TIM4_CH4 */ +#define RCC_TIM5_CH1 RCC_GPIOA /**< RCC for port for on TIM5_CH1 */ +#define RCC_TIM5_CH2 RCC_GPIOA /**< RCC for port for on TIM5_CH2 */ +#define RCC_TIM5_CH3 RCC_GPIOA /**< RCC for port for on TIM5_CH3 */ +#define RCC_TIM5_CH4 RCC_GPIOA /**< RCC for port for on TIM5_CH4 */ +/** get TIM_IC based on CHx identifier */ +#define TIM_IC(x) CAT2(TIM_IC,x) +/** get TIM_IC_IN_TI based on CHx identifier */ +#define TIM_IC_IN_TI(x) CAT2(TIM_IC_IN_TI,x) +/** get TIM_SR_CCxIF based on CHx identifier */ +#define TIM_SR_CCIF(x) CAT3(TIM_SR_CC,x,IF) +/** get TIM_DIER_CCxIE based on CHx identifier */ +#define TIM_DIER_CCIE(x) CAT3(TIM_DIER_CC,x,IE) +/** get TIM_CCRy register based on TIMx_CHy identifier */ +#define TIM_CCR(x,y) CAT2(TIM_CCR,y)(TIM(x)) +/** get external interrupt based on pin identifier */ +#define EXTI(x) CAT2(EXTI,x) +/** get NVIC IRQ for external interrupt base on external interrupt/pin */ +#define NVIC_EXTI_IRQ(x) CAT3(NVIC_EXTI,x,_IRQ) +#define NVIC_EXTI5_IRQ NVIC_EXTI9_5_IRQ /**< IRQ for line 9 to 5 for pin 5 */ +#define NVIC_EXTI6_IRQ NVIC_EXTI9_5_IRQ /**< IRQ for line 9 to 5 for pin 6 */ +#define NVIC_EXTI7_IRQ NVIC_EXTI9_5_IRQ /**< IRQ for line 9 to 5 for pin 7 */ +#define NVIC_EXTI8_IRQ NVIC_EXTI9_5_IRQ /**< IRQ for line 9 to 5 for pin 8 */ +#define NVIC_EXTI9_IRQ NVIC_EXTI9_5_IRQ /**< IRQ for line 9 to 5 for pin 9 */ +#define NVIC_EXTI10_IRQ NVIC_EXTI15_10_IRQ /**< IRQ for line 15 to 10 for pin 10 */ +#define NVIC_EXTI11_IRQ NVIC_EXTI15_10_IRQ /**< IRQ for line 15 to 10 for pin 11 */ +#define NVIC_EXTI12_IRQ NVIC_EXTI15_10_IRQ /**< IRQ for line 15 to 10 for pin 12 */ +#define NVIC_EXTI13_IRQ NVIC_EXTI15_10_IRQ /**< IRQ for line 15 to 10 for pin 13 */ +#define NVIC_EXTI14_IRQ NVIC_EXTI15_10_IRQ /**< IRQ for line 15 to 10 for pin 14 */ +#define NVIC_EXTI15_IRQ NVIC_EXTI15_10_IRQ /**< IRQ for line 15 to 10 for pin 15 */ +/** get interrupt service routine for timer base on external interrupt/pin */ +#define EXTI_ISR(x) CAT3(exti,x,_isr) +#define exti5_isr exti9_5_isr /**< isr for line 9 to 5 for pin 5 */ +#define exti6_isr exti9_5_isr /**< isr for line 9 to 5 for pin 6 */ +#define exti7_isr exti9_5_isr /**< isr for line 9 to 5 for pin 7 */ +#define exti8_isr exti9_5_isr /**< isr for line 9 to 5 for pin 8 */ +#define exti9_isr exti9_5_isr /**< isr for line 9 to 5 for pin 9 */ +#define exti10_isr exti15_10_isr /**< isr for line 15 to 10 for pin 10 */ +#define exti11_isr exti15_10_isr /**< isr for line 15 to 10 for pin 11 */ +#define exti12_isr exti15_10_isr /**< isr for line 15 to 10 for pin 12 */ +#define exti13_isr exti15_10_isr /**< isr for line 15 to 10 for pin 13 */ +#define exti14_isr exti15_10_isr /**< isr for line 15 to 10 for pin 14 */ +#define exti15_isr exti15_10_isr /**< isr for line 15 to 10 for pin 15 */ +/** get USART based on USART identifier */ +#define USART(x) CAT2(USART,x) +/** get RCC for USART based on USART identifier */ +#define USART_RCC(x) CAT2(RCC_USART,x) +/** get NVIC IRQ for USART based on USART identifier */ +#define USART_IRQ(x) CAT3(NVIC_USART,x,_IRQ) +/** get interrupt service routine for USART based on USART identifier */ +#define USART_ISR(x) CAT3(usart,x,_isr) +/** get port for USART based on USART identifier */ +#define USART_PORT(x) CAT2(USART_PORT,x) +#define USART_PORT1 GPIOA /**< USART 1 is on port A */ +#define USART_PORT2 GPIOA /**< USART 2 is on port A */ +#define USART_PORT3 GPIOB /**< USART 3 is on port B */ +/** get RCC for USART port based on USART identifier */ +#define USART_PORT_RCC(x) CAT2(RCC_USART_PORT,x) +#define RCC_USART_PORT1 RCC_GPIOA /**< USART 1 is on port A */ +#define RCC_USART_PORT2 RCC_GPIOA /**< USART 2 is on port A */ +#define RCC_USART_PORT3 RCC_GPIOB /**< USART 3 is on port B */ +/** get transmit pin for USART based on USART identifier */ +#define USART_PIN_TX(x) CAT3(GPIO_USART,x,_TX) +/** get receive pin for USART based on USART identifier */ +#define USART_PIN_RX(x) CAT3(GPIO_USART,x,_RX) +/** get port based on ADC12_IN identifier */ +#define ADC12_IN_PORT(x) CAT3(ADC12_IN,x,_PORT) +#define ADC12_IN0_PORT GPIOA /**< ADC12_IN0 is on PA0 */ +#define ADC12_IN1_PORT GPIOA /**< ADC12_IN1 is on PA1 */ +#define ADC12_IN2_PORT GPIOA /**< ADC12_IN2 is on PA2 */ +#define ADC12_IN3_PORT GPIOA /**< ADC12_IN3 is on PA3 */ +#define ADC12_IN4_PORT GPIOA /**< ADC12_IN4 is on PA4 */ +#define ADC12_IN5_PORT GPIOA /**< ADC12_IN5 is on PA5 */ +#define ADC12_IN6_PORT GPIOA /**< ADC12_IN6 is on PA6 */ +#define ADC12_IN7_PORT GPIOA /**< ADC12_IN7 is on PA7 */ +#define ADC12_IN8_PORT GPIOB /**< ADC12_IN8 is on PB0 */ +#define ADC12_IN9_PORT GPIOB /**< ADC12_IN9 is on PB1 */ +#define ADC12_IN10_PORT GPIOC /**< ADC12_IN10 is on PC0 */ +#define ADC12_IN11_PORT GPIOC /**< ADC12_IN11 is on PC1 */ +#define ADC12_IN12_PORT GPIOC /**< ADC12_IN12 is on PC2 */ +#define ADC12_IN13_PORT GPIOC /**< ADC12_IN13 is on PC3 */ +#define ADC12_IN14_PORT GPIOC /**< ADC12_IN14 is on PC4 */ +#define ADC12_IN15_PORT GPIOC /**< ADC12_IN15 is on PC5 */ +/** get pin based on ADC12_IN identifier */ +#define ADC12_IN_PIN(x) CAT3(ADC12_IN,x,_PIN) +#define ADC12_IN0_PIN GPIO0 /**< ADC12_IN0 is on PA0 */ +#define ADC12_IN1_PIN GPIO1 /**< ADC12_IN1 is on PA1 */ +#define ADC12_IN2_PIN GPIO2 /**< ADC12_IN2 is on PA2 */ +#define ADC12_IN3_PIN GPIO3 /**< ADC12_IN3 is on PA3 */ +#define ADC12_IN4_PIN GPIO4 /**< ADC12_IN4 is on PA4 */ +#define ADC12_IN5_PIN GPIO5 /**< ADC12_IN5 is on PA5 */ +#define ADC12_IN6_PIN GPIO6 /**< ADC12_IN6 is on PA6 */ +#define ADC12_IN7_PIN GPIO7 /**< ADC12_IN7 is on PA7 */ +#define ADC12_IN8_PIN GPIO0 /**< ADC12_IN8 is on PB0 */ +#define ADC12_IN9_PIN GPIO1 /**< ADC12_IN9 is on PB1 */ +#define ADC12_IN10_PIN GPIO0 /**< ADC12_IN10 is on PC0 */ +#define ADC12_IN11_PIN GPIO1 /**< ADC12_IN11 is on PC1 */ +#define ADC12_IN12_PIN GPIO2 /**< ADC12_IN12 is on PC2 */ +#define ADC12_IN13_PIN GPIO3 /**< ADC12_IN13 is on PC3 */ +#define ADC12_IN14_PIN GPIO4 /**< ADC12_IN14 is on PC4 */ +#define ADC12_IN15_PIN GPIO5 /**< ADC12_IN15 is on PC5 */ +/** get RCC based on ADC12_IN identifier */ +#define RCC_ADC12_IN(x) CAT2(RCC_ADC12_IN,x) +#define RCC_ADC12_IN0 RCC_GPIOA /**< ADC12_IN0 is on PA0 */ +#define RCC_ADC12_IN1 RCC_GPIOA /**< ADC12_IN1 is on PA1 */ +#define RCC_ADC12_IN2 RCC_GPIOA /**< ADC12_IN2 is on PA2 */ +#define RCC_ADC12_IN3 RCC_GPIOA /**< ADC12_IN3 is on PA3 */ +#define RCC_ADC12_IN4 RCC_GPIOA /**< ADC12_IN4 is on PA4 */ +#define RCC_ADC12_IN5 RCC_GPIOA /**< ADC12_IN5 is on PA5 */ +#define RCC_ADC12_IN6 RCC_GPIOA /**< ADC12_IN6 is on PA6 */ +#define RCC_ADC12_IN7 RCC_GPIOA /**< ADC12_IN7 is on PA7 */ +#define RCC_ADC12_IN8 RCC_GPIOB /**< ADC12_IN8 is on PB0 */ +#define RCC_ADC12_IN9 RCC_GPIOB /**< ADC12_IN9 is on PB1 */ +#define RCC_ADC12_IN10 RCC_GPIOC /**< ADC12_IN10 is on PC0 */ +#define RCC_ADC12_IN11 RCC_GPIOC /**< ADC12_IN11 is on PC1 */ +#define RCC_ADC12_IN12 RCC_GPIOC /**< ADC12_IN12 is on PC2 */ +#define RCC_ADC12_IN13 RCC_GPIOC /**< ADC12_IN13 is on PC3 */ +#define RCC_ADC12_IN14 RCC_GPIOC /**< ADC12_IN14 is on PC4 */ +#define RCC_ADC12_IN15 RCC_GPIOC /**< ADC12_IN15 is on PC5 */ +/** get channel based on ADC12_IN identifier */ +#define ADC_CHANNEL(x) CAT2(ADC_CHANNEL,x) +/** get I2C based on I2C identifier */ +#define I2C(x) CAT2(I2C,x) +/** get RCC for I2C based on I2C identifier */ +#define RCC_I2C(x) CAT2(RCC_I2C,x) +/** get RCC for GPIO port for SCL based on I2C identifier */ +#define RCC_I2C_SCL_PORT(x) CAT3(RCC_I2C,x,_PORT) +#define RCC_I2C1_PORT RCC_GPIOB /**< RCC for GPIO port for SCL for I2C1 */ +#define RCC_I2C2_PORT RCC_GPIOB /**< RCC for GPIO port for SCL for I2C2 */ +/** get RCC for GPIO port for SCL based on I2C identifier */ +#define RCC_I2C_SDA_PORT(x) CAT3(RCC_I2C,x,_PORT) +#define RCC_I2C1_SDA_PORT RCC_GPIOB /**< RCC for GPIO port for SDA for I2C1 */ +#define RCC_I2C2_SDA_PORT RCC_GPIOB /**< RCC for GPIO port for SDA for I2C2 */ +/** get I2C port for SCL pin based on I2C identifier */ +#define I2C_SCL_PORT(x) CAT3(GPIO_BANK_I2C,x,_SCL) +/** get I2C port for SDA pin based on I2C identifier */ +#define I2C_SDA_PORT(x) CAT3(GPIO_BANK_I2C,x,_SDA) +/** get I2C pin for SCL pin based on I2C identifier */ +#define I2C_SCL_PIN(x) CAT3(GPIO_I2C,x,_SCL) +/** get I2C port for SDA pin based on I2C identifier */ +#define I2C_SDA_PIN(x) CAT3(GPIO_I2C,x,_SDA) +/** get SPI based on SPI identifier */ +#define SPI(x) CAT2(SPI,x) +/** get RCC for SPI based on SPI identifier */ +#define RCC_SPI(x) CAT2(RCC_SPI,x) +/** get RCC for GPIO port for SPI NSS signals */ +#define RCC_SPI_NSS_PORT(x) CAT3(RCC_SPI,x,_NSS_PORT) +#define RCC_SPI1_NSS_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1 */ +#define RCC_SPI1_RE_NSS_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1_RE */ +#define RCC_SPI2_NSS_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI2 */ +/** get RCC for GPIO port for SPI SCK signals */ +#define RCC_SPI_SCK_PORT(x) CAT3(RCC_SPI,x,_SCK_PORT) +#define RCC_SPI1_SCK_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1 */ +#define RCC_SPI1_RE_SCK_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI1_RE */ +#define RCC_SPI2_SCK_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI2 */ +/** get RCC for GPIO port for SPI MISO signals */ +#define RCC_SPI_MISO_PORT(x) CAT3(RCC_SPI,x,_MISO_PORT) +#define RCC_SPI1_MISO_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1 */ +#define RCC_SPI1_RE_MISO_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI1_RE */ +#define RCC_SPI2_MISO_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI2 */ +/** get RCC for GPIO port for SPI MOSI signals */ +#define RCC_SPI_MOSI_PORT(x) CAT3(RCC_SPI,x,_MOSI_PORT) +#define RCC_SPI1_MOSI_PORT RCC_GPIOA /**< RCC for GPIO port for NSS for SPI1 */ +#define RCC_SPI1_RE_MOSI_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI1_RE */ +#define RCC_SPI2_MOSI_PORT RCC_GPIOB /**< RCC for GPIO port for NSS for SPI2 */ +/** get SPI port for NSS signal based on SPI identifier */ +#define SPI_NSS_PORT(x) CAT3(GPIO_BANK_SPI,x,_NSS) +/** get SPI port for SCK signal based on SPI identifier */ +#define SPI_SCK_PORT(x) CAT3(GPIO_BANK_SPI,x,_SCK) +/** get SPI port for MISO signal based on SPI identifier */ +#define SPI_MISO_PORT(x) CAT3(GPIO_BANK_SPI,x,_MISO) +/** get SPI port for MOSI signal based on SPI identifier */ +#define SPI_MOSI_PORT(x) CAT3(GPIO_BANK_SPI,x,_MOSI) +/** get SPI pin for NSS signal based on SPI identifier */ +#define SPI_NSS_PIN(x) CAT3(GPIO_SPI,x,_NSS) +/** get SPI pin for SCK signal based on SPI identifier */ +#define SPI_SCK_PIN(x) CAT3(GPIO_SPI,x,_SCK) +/** get SPI pin for MISO signal based on SPI identifier */ +#define SPI_MISO_PIN(x) CAT3(GPIO_SPI,x,_MISO) +/** get SPI pin for MOSI signal based on SPI identifier */ +#define SPI_MOSI_PIN(x) CAT3(GPIO_SPI,x,_MOSI) + +/** @} */ + +/** @defgroup board_led board LED GPIO + * @{ + */ +#if defined(SYSTEM_BOARD) || defined(CORE_BOARD) +/* on system and core board LED is on pin 11/PA1 */ +#define LED_PORT A /**< GPIO port (port A) */ +#define LED_PIN 1 /**< GPIO pin (pin PA1) */ +#elif defined(BLUE_PILL) +/* on minimum system LED is on pin 2/PC13 */ +#define LED_PORT C /**< GPIO port (port C on blue pill) */ +#define LED_PIN 13 /**< GPIO pin (pin PC13 on system board) */ +#elif defined (MAPLE_MINI) +/* on maple mini LED is on pin 19/PB1 */ +#define LED_PORT B /**< GPIO port (port B on maple mini) */ +#define LED_PIN 1 /**< GPIO pin (pin PB1 on maple mini) */ +#endif +/** @} */ + +/** @defgroup board_button board user button GPIO + * @{ + */ +#if defined(MAPLE_MINI) +/* on maple mini user button is on 32/PB8 */ +#define BUTTON_PORT B /**< GPIO port (port B on maple mini) */ +#define BUTTON_PIN 8 /**< GPIO pin (pin PB8 on maple mini) */ +#elif defined(CORE_BOARD) +/* on core board user button is on PA8 */ +#define BUTTON_PORT A /**< GPIO port (port A) */ +#define BUTTON_PIN 8 /**< GPIO pin (pin PA8) */ +#endif +/** @} */ + +extern volatile bool button_flag; /**< flag set when board user button has been pressed/released */ + +/** get binary representation of a number + * @param[in] binary number to represent in binary + * @param[in] rjust justify representation with leading zeros + * @return string with binary representation of the number + */ +char* b2s(uint64_t binary, uint8_t rjust); + +/** switch on board LED */ +void led_on(void); + +/** switch off board LED */ +void led_off(void); + +/** toggle board LED */ +void led_toggle(void); + +/** setup board peripherals */ +void board_setup(void); + diff --git a/src/timer.c b/src/timer.c new file mode 100644 index 0000000..db8b8bb --- /dev/null +++ b/src/timer.c @@ -0,0 +1,111 @@ +// vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2010 Gareth McMullin + * + * 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 +#include +#include + +#include "usb_cdcacm.h" + +uint32_t n_millis_total = 0; +uint32_t n_millis_cur = 0; +uint8_t n_millis_loops = 0; +usbd_device *usbd_dev; +static uint32_t game_time = 14 * 60 * 1000; // 15 minutes of 1ms ticks + +void sys_tick_handler(void) { + ++n_millis_cur; + ++n_millis_total; + + if (n_millis_total >= game_time) { + gpio_clear(GPIOC, GPIO13); // turn on LED + return; + } + + /* calculate remaining seconds to print out on the serial port, only do this every + 1000 millis or so */ + if (n_millis_loops >= 4) { + char seconds_remaining[5]; + sprintf(seconds_remaining, "%d", (int)((game_time - n_millis_total) / 1000)); + for (int i = 0; i < 4; i++) { + cdcacm_putchar(seconds_remaining[i]); + } + cdcacm_putchar('\r'); + cdcacm_putchar('\n'); + } + + if (n_millis_cur >= 250) { + gpio_toggle(GPIOC, GPIO13); + ++n_millis_loops; + n_millis_cur = 0; + return; + } + + return; +} + +static void clock_setup(void) { + /* Run CPU at 72MHz */ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + rcc_periph_clock_enable(RCC_GPIOA); + rcc_periph_clock_enable(RCC_GPIOC); + + /* 72MHz / 8 => 9000000 counts per second */ + systick_set_clocksource(STK_CSR_CLKSOURCE_AHB_DIV8); + + /* 9000000 / 9000 = 1000 overflows per second - every 1ms one interrupt */ + /* SysTick interrupt every N clock pulses: set reload to N-1 */ + systick_set_reload(8999); // 1 ms + systick_interrupt_enable(); + systick_counter_enable(); +} + +static void gpio_setup(void) { + // Built-in LED on blue pill board, PC13 + gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_PUSHPULL, GPIO13); + gpio_set(GPIOC, GPIO13); + + // Pull up the USB D+ line for reset + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, + GPIO_CNF_OUTPUT_OPENDRAIN, GPIO12); + gpio_clear(GPIOA, GPIO12); +} + +int main(void) +{ + clock_setup(); + gpio_setup(); + cdcacm_setup(); + + gpio_set(GPIOC, GPIO13); + + while (1) { + __asm__("nop"); + } + + return 0; +} + diff --git a/src/usb_cdcacm.c b/src/usb_cdcacm.c new file mode 100644 index 0000000..3117273 --- /dev/null +++ b/src/usb_cdcacm.c @@ -0,0 +1,415 @@ +/* This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program. If not, see . + * + */ +/** library for USB CDC ACM communication (code) + * @file usb_cdcacm.c + * @author King Kévin + * @date 2016 + */ + +/* standard libraries */ +#include // standard integer types +#include // standard I/O facilities +#include // general utilities + +/* STM32 (including CM3) libraries */ +#include // real-time control clock library +#include // general purpose input output library +#include // interrupt handler +#include // reset utilities +#include // Cortex M3 utilities +#include // USB library +#include // USB CDC library +#include // synchronisation utilities + +#include "global.h" +#include "usb_cdcacm.h" // USB CDC ACM header and definitions + +/** USB CDC ACM device descriptor + * @note as defined in USB CDC specification section 5 + */ +static const struct usb_device_descriptor device_descriptor = { + .bLength = USB_DT_DEVICE_SIZE, // the size of this header in bytes, 18 + .bDescriptorType = USB_DT_DEVICE, // a value of 1 indicates that this is a device descriptor + .bcdUSB = 0x0200, // this device supports USB 2.0 + .bDeviceClass = USB_CLASS_CDC, // use the CDC device class + .bDeviceSubClass = 0, // unused + .bDeviceProtocol = 0, // unused + .bMaxPacketSize0 = 64, // packet size for endpoint zero in bytes + .idVendor = 0xc440, // Vendor ID (CuVo...) + .idProduct = 0x0d00, // product ID within the Vendor ID space (...odoo) + .bcdDevice = 0x0100, // version number for the device + .iManufacturer = 1, // the index of the string in the string table that represents the name of the manufacturer of this device. + .iProduct = 2, // the index of the string in the string table that represents the name of the product + .iSerialNumber = 3, // the index of the string in the string table that represents the serial number of this item in string form. + .bNumConfigurations = 1, // the number of possible configurations this device has +}; + +/** USB CDC ACM data endpoints + * @note as defined in USB CDC specification section 5 + */ +static const struct usb_endpoint_descriptor data_endpoints[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, // the size of the endpoint descriptor in bytes + .bDescriptorType = USB_DT_ENDPOINT, // a value of 5 indicates that this describes an endpoint + .bEndpointAddress = 0x01, // OUT (from host) direction (0<<7), endpoint 1 + .bmAttributes = USB_ENDPOINT_ATTR_BULK, // bulk mode + .wMaxPacketSize = 64, // maximum packet size + .bInterval = 1, // the frequency, in number of frames, that we're going to be sending data +},{ + .bLength = USB_DT_ENDPOINT_SIZE, // the size of the endpoint descriptor in bytes + .bDescriptorType = USB_DT_ENDPOINT, // a value of 5 indicates that this describes an endpoint + .bEndpointAddress = 0x82, // IN (to host) direction (1<<7), endpoint 2 + .bmAttributes = USB_ENDPOINT_ATTR_BULK, // bulk mode + .wMaxPacketSize = 64, // maximum packet size + .bInterval = 1, // the frequency, in number of frames, that we're going to be sending data +}}; + +/** USB CDC ACM communication endpoints + * @note 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 communication_endpoints[] = {{ + .bLength = USB_DT_ENDPOINT_SIZE, // the size of the endpoint descriptor in bytes + .bDescriptorType = USB_DT_ENDPOINT, // a value of 5 indicates that this describes an endpoint + .bEndpointAddress = 0x83, // IN (to host) direction (1<<7), endpoint 3 + .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, // interrupt mode + .wMaxPacketSize = 16, // maximum packet size + .bInterval = 255, // the frequency, in number of frames, that we're going to be sending data +}}; + +/** USB CDC ACM functional descriptor + * @return + * @note as defined in USB CDC specification section 5.2.3 + */ +static const struct { + struct usb_cdc_header_descriptor header; /**< header */ + struct usb_cdc_call_management_descriptor call_mgmt; /**< call management descriptor */ + struct usb_cdc_acm_descriptor acm; /**< descriptor */ + struct usb_cdc_union_descriptor cdc_union; /**< descriptor */ +} __attribute__((packed)) cdcacm_functional_descriptors = { + .header = { + .bFunctionLength = sizeof(struct usb_cdc_header_descriptor), /**< descriptor length */ + .bDescriptorType = CS_INTERFACE, /**< descriptor type */ + .bDescriptorSubtype = USB_CDC_TYPE_HEADER, /**< descriptor subtype */ + .bcdCDC = 0x0110, /**< CDC value */ + }, + .call_mgmt = { + .bFunctionLength = sizeof(struct usb_cdc_call_management_descriptor), /**< descriptor length */ + .bDescriptorType = CS_INTERFACE, /**< descriptor type */ + .bDescriptorSubtype = USB_CDC_TYPE_CALL_MANAGEMENT, /**< descriptor subtype */ + .bmCapabilities = 0, /**< capabilities */ + .bDataInterface = 1, /**< data interface */ + }, + .acm = { + .bFunctionLength = sizeof(struct usb_cdc_acm_descriptor), /**< descriptor length */ + .bDescriptorType = CS_INTERFACE, /**< descriptor type */ + .bDescriptorSubtype = USB_CDC_TYPE_ACM, /**< descriptor subtype */ + .bmCapabilities = 0, /**< capabilities */ + }, + .cdc_union = { + .bFunctionLength = sizeof(struct usb_cdc_union_descriptor), /**< descriptor length */ + .bDescriptorType = CS_INTERFACE, /**< descriptor type */ + .bDescriptorSubtype = USB_CDC_TYPE_UNION, /**< descriptor subtype */ + .bControlInterface = 0, /**< control interface */ + .bSubordinateInterface0 = 1, /**< subordinate interface */ + }, +}; + +/** USB CDC interface descriptor + * @note as defined in USB CDC specification section 5.1.3 + */ +static const struct usb_interface_descriptor communication_interface[] = {{ + .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_NONE, + .iInterface = 0, + + .endpoint = communication_endpoints, + + .extra = &cdcacm_functional_descriptors, + .extralen = sizeof(cdcacm_functional_descriptors), +}}; + +/** USB CDC ACM data class interface descriptor + * @note as defined in USB CDC specification section 5.1.3 + */ +static const struct usb_interface_descriptor data_interface[] = {{ + .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_endpoints, +}}; + +/** USB CDC ACM interface descriptor */ +static const struct usb_interface interfaces[] = {{ + .num_altsetting = 1, + .altsetting = communication_interface, +}, { + .num_altsetting = 1, + .altsetting = data_interface, +}}; + +/** USB CDC ACM configuration descriptor */ +static const struct usb_config_descriptor config = { + .bLength = USB_DT_CONFIGURATION_SIZE, // the length of this header in bytes + .bDescriptorType = USB_DT_CONFIGURATION, // a value of 2 indicates that this is a configuration descriptor + .wTotalLength = 0, // this should hold the total size of the configuration descriptor including all sub interfaces. it is automatically filled in by the USB stack in libopencm3 + .bNumInterfaces = 2, // the number of interfaces in this configuration + .bConfigurationValue = 1, // the index of this configuration + .iConfiguration = 0, // a string index describing this configuration (zero means not provided) + .bmAttributes = 0x80, // self powered (0<<6), supports remote wakeup (0<<5) + .bMaxPower = 0x32, // the maximum amount of current that this device will draw in 2mA units + // end of header + .interface = interfaces, // pointer to an array of interfaces +}; + +/** USB string table + * @note starting with index 1 + */ +static const char *usb_strings[] = { + "BarBillards v1", + "CDC-ACM", + "STM32F1", +}; + +static uint8_t usbd_control_buffer[128] = {0}; /**< buffer to be used for control requests */ +static usbd_device *usb_device = NULL; /**< structure holding all the info related to the USB device */ +static bool connected = false; /**< is the USB device is connected to a host */ + +/* input and output ring buffer, indexes, and available memory */ +static uint8_t rx_buffer[CDCACM_BUFFER] = {0}; /**< ring buffer for received data */ +static volatile uint8_t rx_i = 0; /**< current position of read received data */ +static volatile uint8_t rx_used = 0; /**< how much data has been received and not red */ +mutex_t rx_lock = MUTEX_UNLOCKED; /**< lock to update rx_i or rx_used */ +static uint8_t tx_buffer[CDCACM_BUFFER] = {0}; /**< ring buffer for data to transmit */ +static volatile uint8_t tx_i = 0; /**< current position if transmitted data */ +static volatile uint8_t tx_used = 0; /**< how much data needs to be transmitted */ +mutex_t tx_lock = MUTEX_UNLOCKED; /**< lock to update tx_i or tx_used */ +volatile uint8_t cdcacm_received = 0; // same as rx_used, but since the user can write this variable we don't rely on it + +/** disconnect USB by pulling down D+ to for re-enumerate */ +static void usb_disconnect(void) +{ + /* short USB disconnect to force re-enumerate */ +#if defined(SYSTEM_BOARD) || defined(BLUE_PILL) + // pull USB D+ low for a short while + rcc_periph_clock_enable(RCC_GPIOA); + gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); + gpio_clear(GPIOA, GPIO12); + for (uint32_t i = 0; i < 0x2000; i++) { + __asm__("nop"); + } +#elif defined(MAPLE_MINI) + // disconnect USB D+ using dedicated DISC line/circuit on PB9 + rcc_periph_clock_enable(RCC_GPIOB); + gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO9); + gpio_set(GPIOB, GPIO9); + for (uint32_t i = 0; i < 0x2000; i++) { + __asm__("nop"); + } + gpio_clear(GPIOB, GPIO9); +#endif +} + +/** incoming USB CDC ACM control request + * @param[in] usbd_dev USB device descriptor + * @param[in] req control request information + * @param[in] buf control request data + * @param[in] len control request data length + * @param[in] complete not used + * @return 0 if succeeded, error else + * @note resets device when configured with 5 bits + */ +static enum usbd_request_return_codes 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: + connected = req->wValue ? true : false; // check if terminal is open + //bool dtr = (req->wValue & (1 << 0)) ? true : false; + //bool rts = (req->wValue & (1 << 1)) ? true : false; + /* 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. + */ + uint8_t reply[10] = {0}; + struct usb_cdc_notification *notif = (void *)reply; + /* 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; + reply[8] = req->wValue & 3; + reply[9] = 0; + usbd_ep_write_packet(usbd_dev, 0x83, reply, LENGTH(reply)); + return USBD_REQ_HANDLED; + break; + case USB_CDC_REQ_SET_LINE_CODING: + // ignore if length is wrong + if (*len < sizeof(struct usb_cdc_line_coding)) { + return USBD_REQ_NOTSUPP; + } + // get the line coding + struct usb_cdc_line_coding *coding = (struct usb_cdc_line_coding *)*buf; + /* reset device is the data bits is set to 5 + * this is used to allowing rebooting the device in DFU mode for reflashing + * to reset the device from the host you can use stty --file /dev/ttyACM0 115200 raw cs5 + */ + if (coding->bDataBits==5) { + usb_disconnect(); // force re-enumerate after reset + scb_reset_system(); // reset device + while (true); // wait for the reset to happen + } + return USBD_REQ_HANDLED; + break; + default: + return USBD_REQ_HANDLED; + } + return USBD_REQ_NOTSUPP; +} + +/** USB CDC ACM data received callback + * @param[in] usbd_dev USB device descriptor + * @param[in] ep endpoint where data came in + */ +static void cdcacm_data_rx_cb(usbd_device *usbd_dev, uint8_t ep) +{ + (void)ep; + + char usb_data[64] = {0}; // buffer to read data + uint16_t usb_length = 0; // length of incoming data + + /* receive data */ + usb_length = usbd_ep_read_packet(usbd_dev, 0x01, usb_data, sizeof(usb_data)); + if (usb_length) { // copy received data + for (uint16_t i=0; i 64 ? 64 : tx_used); // length of data to be transmitted (respect max packet size) + usb_length = (usb_length > (LENGTH(tx_buffer)-tx_i) ? LENGTH(tx_buffer)-tx_i : usb_length); // since here we use the source array not as ring buffer, only go up to the end + while (usb_length != usbd_ep_write_packet(usb_device, 0x82, (void*)(&tx_buffer[tx_i]), usb_length)); // ensure data is written into transmit buffer + tx_i = (tx_i+usb_length)%LENGTH(tx_buffer); // update location on buffer + tx_used -= usb_length; // update used size + mutex_unlock(&tx_lock); // release lock + } else { + usbd_ep_write_packet(usb_device, 0x82, NULL, 0); // trigger empty tx for a later callback + } + usbd_poll(usb_device); // ensure the data gets sent +} + +/** set USB CDC ACM configuration + * @param[in] usbd_dev USB device descriptor + * @param[in] wValue not used + */ +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, cdcacm_data_tx_cb); + 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); +} + +void cdcacm_setup(void) +{ + connected = false; // start with USB not connected + usb_disconnect(); // force re-enumerate (useful after a restart or if there is a bootloader using another USB profile) + + /* initialize USB */ + usb_device = usbd_init(&st_usbfs_v1_usb_driver, &device_descriptor, &config, usb_strings, 3, usbd_control_buffer, sizeof(usbd_control_buffer)); + usbd_register_set_config_callback(usb_device, cdcacm_set_config); + + /* enable interrupts (to not have to poll all the time) */ + nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ); // without this USB isn't detected by the host + + /* reset buffer states */ + rx_i = 0; + rx_used = 0; + mutex_unlock(&rx_lock); + cdcacm_received = 0; + tx_i = 0; + tx_used = 0; + mutex_unlock(&tx_lock); +} + +char cdcacm_getchar(void) +{ + while (!rx_used) { // idle until data is available + __WFI(); // sleep until interrupt (not sure if it's a good idea here) + } + char to_return = rx_buffer[rx_i]; // get the next available character + rx_i = (rx_i+1)%LENGTH(rx_buffer); // update used buffer + rx_used--; // update used buffer + cdcacm_received = rx_used; // update available data + return to_return; +} + +void cdcacm_putchar(char c) +{ + if (!usb_device || !connected) { + return; + } + mutex_lock(&tx_lock); // get lock to prevent race condition + if (tx_used. + * + */ +/** library for USB CDC ACM communication (API) + * @file usb_cdcacm.h + * @author King Kévin + * @date 2016 + */ +#pragma once + +/** transmit and receive buffer sizes */ +#define CDCACM_BUFFER 64 +/** how many bytes available in the received buffer since last read */ +extern volatile uint8_t cdcacm_received; + +/** setup USB CDC ACM peripheral */ +void cdcacm_setup(void); +/** get character received over USB (blocking) + * @return character received over USB + * @note blocks until character is received over USB when received buffer is empty + */ +char cdcacm_getchar(void); +/** send character over USB (non-blocking) + * @param[in] c character to send + * @note blocks if transmit buffer is full, else puts in buffer and returns + */ +void cdcacm_putchar(char c); -- 2.30.2