From 4695b47da68a5b2f75270bea21e15b8f1b9fd6ff Mon Sep 17 00:00:00 2001 From: Nicolas Schodet Date: Mon, 25 Jul 2016 15:18:40 +0200 Subject: Switch to CMSIS --- .gitmodules | 3 - build/stm32.mk | 30 +- build/stm32f1.mk | 2 +- build/stm32f4.mk | 2 +- lib/libopencm3 | 1 - ucoo/arch/Module | 2 + ucoo/arch/arch.arm.hh | 37 ++ ucoo/arch/arch.hh | 16 +- ucoo/arch/arch.host.hh | 35 ++ ucoo/arch/arch.stm32.cc | 4 +- ucoo/arch/arch.stm32f1.cc | 13 +- ucoo/arch/arch.stm32f4.cc | 16 +- ucoo/arch/arch_common.arm.hh | 4 +- ucoo/arch/arch_common.hh | 8 - ucoo/arch/arch_common.host.hh | 4 +- ucoo/arch/interrupt.arm.hh | 107 ++++++ ucoo/arch/interrupt.stm32f1.hh | 104 +++++ ucoo/arch/interrupt.stm32f4.hh | 127 ++++++ ucoo/arch/ld/common.ld | 67 ++++ ucoo/arch/ld/stm32f1.ld | 6 + ucoo/arch/ld/stm32f4.ld | 6 + ucoo/arch/rcc.stm32.hh | 36 ++ ucoo/arch/rcc.stm32f1.cc | 134 +++++++ ucoo/arch/rcc.stm32f1.hh | 143 +++++++ ucoo/arch/rcc.stm32f4.cc | 125 ++++++ ucoo/arch/rcc.stm32f4.hh | 193 ++++++++++ ucoo/arch/reg.hh | 35 ++ ucoo/arch/reg.stm32.hh | 80 ++++ ucoo/arch/reg.stm32f1.hh | 180 +++++++++ ucoo/arch/reg.stm32f4.hh | 347 +++++++++++++++++ ucoo/arch/stm32f1/stm32f1.ld | 6 - ucoo/arch/stm32f4/stm32f4.ld | 6 - ucoo/arch/vector.arm.cc | 136 +++++++ ucoo/arch/vector.stm32f1.hh | 220 +++++++++++ ucoo/arch/vector.stm32f4.hh | 307 +++++++++++++++ ucoo/base/fs/romfs/test/Makefile | 2 +- ucoo/base/proto/test/Makefile | 2 +- ucoo/base/test/Config | 10 +- ucoo/base/test/test.stm32.cc | 31 +- ucoo/base/test/test/Makefile | 2 +- ucoo/common.hh | 4 + ucoo/dev/avrisp/test/test_avrisp.cc | 10 +- ucoo/dev/lcd/test/test_lcd.cc | 16 +- ucoo/hal/adc/adc_hard.stm32f4.cc | 36 +- ucoo/hal/adc/adc_hard.stm32f4.hh | 67 +++- ucoo/hal/adc/test/Makefile | 2 +- ucoo/hal/adc/test/test_adc.cc | 9 +- ucoo/hal/exti/exti.stm32.cc | 209 ++++------ ucoo/hal/exti/exti.stm32.hh | 17 +- ucoo/hal/exti/test/test_exti.stm32.cc | 16 +- ucoo/hal/gpio/gpio.stm32f1.cc | 60 ++- ucoo/hal/gpio/gpio.stm32f1.hh | 67 +++- ucoo/hal/gpio/gpio.stm32f4.cc | 104 ++++- ucoo/hal/gpio/gpio.stm32f4.hh | 74 +++- ucoo/hal/gpio/test/test_gpio.stm32f1.cc | 14 +- ucoo/hal/gpio/test/test_gpio.stm32f4.cc | 20 +- ucoo/hal/i2c/i2c_hard.stm32.cc | 187 ++++----- ucoo/hal/i2c/i2c_hard.stm32.hh | 26 +- ucoo/hal/i2c/test/Makefile | 2 +- ucoo/hal/i2c/test/test_i2c.cc | 28 +- ucoo/hal/sdram/sdram.stm32f4.cc | 94 ++--- ucoo/hal/sdram/sdram.stm32f4.hh | 3 +- ucoo/hal/sdram/test/test_sdram.stm32f4.cc | 39 +- .../self_programming/self_programming.stm32f1.cc | 42 +- .../self_programming/self_programming.stm32f4.cc | 38 +- ucoo/hal/spi/spi_hard.stm32.cc | 86 ++--- ucoo/hal/spi/spi_hard.stm32.hh | 21 +- ucoo/hal/spi/test/test_spi.cc | 19 +- ucoo/hal/timer/test/Makefile | 2 +- ucoo/hal/timer/test/test_timer.cc | 22 +- ucoo/hal/timer/timer.stm32.hh | 29 +- ucoo/hal/timer/timer.stm32.tcc | 428 +++++++++++---------- ucoo/hal/uart/test/Makefile | 2 +- ucoo/hal/uart/test/test_uart.cc | 17 +- ucoo/hal/uart/test/test_uart_disc.cc | 23 +- ucoo/hal/uart/uart.stm32.cc | 121 +++--- ucoo/hal/uart/uart.stm32.hh | 18 +- ucoo/hal/usb/Config | 13 +- ucoo/hal/usb/Module | 6 +- ucoo/hal/usb/test/Config | 2 +- ucoo/hal/usb/test/Makefile | 2 +- ucoo/hal/usb/test/test_usb.cc | 59 +-- ucoo/hal/usb/usb.hh | 4 +- ucoo/hal/usb/usb.stm32.cc | 311 --------------- ucoo/hal/usb/usb.stm32.hh | 106 ----- ucoo/hal/usb/usb_application.cc | 222 +++++++++++ ucoo/hal/usb/usb_application.hh | 158 ++++++++ ucoo/hal/usb/usb_cdc.cc | 235 +++++++++++ ucoo/hal/usb/usb_cdc.hh | 81 ++++ ucoo/hal/usb/usb_cdc_def.hh | 75 ++++ ucoo/hal/usb/usb_cdc_desc.hh | 174 +++++++++ ucoo/hal/usb/usb_def.hh | 71 ++++ ucoo/hal/usb/usb_desc.hh | 406 +++++++++++++++++++ ucoo/hal/usb/usb_desc.stm32.c | 303 --------------- ucoo/hal/usb/usb_desc.stm32.h | 33 -- ucoo/hal/usb/usb_driver.cc | 130 +++++++ ucoo/hal/usb/usb_driver.hh | 120 ++++++ ucoo/hal/usb/usb_dwc_otg.stm32.cc | 412 ++++++++++++++++++++ ucoo/hal/usb/usb_dwc_otg.stm32.hh | 94 +++++ ucoo/math/test/Makefile | 2 +- ucoo/utils/bits.hh | 45 +++ ucoo/utils/buffer.hh | 74 ++++ ucoo/utils/buffer.tcc | 125 ++++++ ucoo/utils/delay.arm.cc | 33 +- ucoo/utils/irq_locked.hh | 45 +++ ucoo/utils/rate_limit.tcc | 2 +- ucoo/utils/table_lookup.hh | 51 +++ ucoo/utils/test/Makefile | 2 +- ucoo/utils/test/test_delay.cc | 30 +- ucoo/utils/trace.hh | 2 +- ucoo/utils/trace.tcc | 4 +- 111 files changed, 6199 insertions(+), 1794 deletions(-) delete mode 160000 lib/libopencm3 create mode 100644 ucoo/arch/arch.arm.hh create mode 100644 ucoo/arch/arch.host.hh create mode 100644 ucoo/arch/interrupt.arm.hh create mode 100644 ucoo/arch/interrupt.stm32f1.hh create mode 100644 ucoo/arch/interrupt.stm32f4.hh create mode 100644 ucoo/arch/ld/common.ld create mode 100644 ucoo/arch/ld/stm32f1.ld create mode 100644 ucoo/arch/ld/stm32f4.ld create mode 100644 ucoo/arch/rcc.stm32.hh create mode 100644 ucoo/arch/rcc.stm32f1.cc create mode 100644 ucoo/arch/rcc.stm32f1.hh create mode 100644 ucoo/arch/rcc.stm32f4.cc create mode 100644 ucoo/arch/rcc.stm32f4.hh create mode 100644 ucoo/arch/reg.hh create mode 100644 ucoo/arch/reg.stm32.hh create mode 100644 ucoo/arch/reg.stm32f1.hh create mode 100644 ucoo/arch/reg.stm32f4.hh delete mode 100644 ucoo/arch/stm32f1/stm32f1.ld delete mode 100644 ucoo/arch/stm32f4/stm32f4.ld create mode 100644 ucoo/arch/vector.arm.cc create mode 100644 ucoo/arch/vector.stm32f1.hh create mode 100644 ucoo/arch/vector.stm32f4.hh delete mode 100644 ucoo/hal/usb/usb.stm32.cc delete mode 100644 ucoo/hal/usb/usb.stm32.hh create mode 100644 ucoo/hal/usb/usb_application.cc create mode 100644 ucoo/hal/usb/usb_application.hh create mode 100644 ucoo/hal/usb/usb_cdc.cc create mode 100644 ucoo/hal/usb/usb_cdc.hh create mode 100644 ucoo/hal/usb/usb_cdc_def.hh create mode 100644 ucoo/hal/usb/usb_cdc_desc.hh create mode 100644 ucoo/hal/usb/usb_def.hh create mode 100644 ucoo/hal/usb/usb_desc.hh delete mode 100644 ucoo/hal/usb/usb_desc.stm32.c delete mode 100644 ucoo/hal/usb/usb_desc.stm32.h create mode 100644 ucoo/hal/usb/usb_driver.cc create mode 100644 ucoo/hal/usb/usb_driver.hh create mode 100644 ucoo/hal/usb/usb_dwc_otg.stm32.cc create mode 100644 ucoo/hal/usb/usb_dwc_otg.stm32.hh create mode 100644 ucoo/utils/bits.hh create mode 100644 ucoo/utils/buffer.hh create mode 100644 ucoo/utils/buffer.tcc create mode 100644 ucoo/utils/irq_locked.hh create mode 100644 ucoo/utils/table_lookup.hh diff --git a/.gitmodules b/.gitmodules index f5fdeb9..24de4ec 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,3 @@ -[submodule "lib/libopencm3"] - path = lib/libopencm3 - url = https://github.com/libopencm3/libopencm3.git [submodule "lib/cmsis"] path = lib/cmsis url = http://git.ni.fr.eu.org/nicolas/cmsis.git diff --git a/build/stm32.mk b/build/stm32.mk index 3a3311b..46c7b8c 100644 --- a/build/stm32.mk +++ b/build/stm32.mk @@ -6,19 +6,15 @@ ifndef stm32_once stm32_once := 1 -# Check for libopencm3. -LIBOPENCM3_PATH ?= $(UCOO_BASE)/lib/libopencm3 -define stm32_libopencm3 -ifneq ($$(LIBOPENCM3_PATH),) - $1_LIBOPENCM3_LIB := $$(LIBOPENCM3_PATH)/lib/libopencm3_$(if $2,$2,$1).a - ifeq ($$(wildcard $$($1_LIBOPENCM3_LIB)),) - $$(error Can not find libopencm3 library, please run "make lib" in \ - $$(LIBOPENCM3_PATH) or change LIBOPENCM3_PATH (you can set it to \ - empty string to use system library)) - endif - $1_LIBOPENCM3_CPPFLAGS = -I$$(LIBOPENCM3_PATH)/include - $1_LIBOPENCM3_LDFLAGS = -L$$(LIBOPENCM3_PATH)/lib +# Check for cmsis. +CMSIS_PATH ?= $(UCOO_BASE)/lib/cmsis +define stm32_cmsis +$1_CMSIS_LIB := $$(CMSIS_PATH)/Include/cmsis_gcc.h +ifeq ($$(wildcard $$($1_CMSIS_LIB)),) + $$(error Can not find CMSIS library, please put it in $$(CMSIS_PATH) or \ + change CMSIS_PATH) endif +$1_CMSIS_CPPFLAGS = -I$$(CMSIS_PATH)/Include -I$$(CMSIS_PATH)/Device endef @@ -29,21 +25,19 @@ stm32_cortex-m4_CPU_CFLAGS = \ -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16 define stm32_flags $1_CPPFLAGS = $$(CPPFLAGS) $$($1_DEFS) \ - $$($1_STM32_DEFS) $$($1_LIBOPENCM3_CPPFLAGS) + $$($1_STM32_DEFS) $$($1_CMSIS_CPPFLAGS) $1_CFLAGS = $$(CFLAGS) \ $$(stm32_$$($1_CPU)_CPU_CFLAGS) \ -Wl,--gc-sections -ffunction-sections $1_CXXFLAGS = $$(sort $$($1_CFLAGS) $$(CXXFLAGS)) $1_ASFLAGS = $$(ASFLAGS) $1_LDSCRIPT ?= $(if $2,$2,$1).ld -$1_LDSCRIPT_PATH ?= $$(UCOO_BASE)/ucoo/arch/$(if $2,$2,$1) +$1_LDSCRIPT_PATH ?= $$(UCOO_BASE)/ucoo/arch/ld $1_START_ADDRESS ?= $$(if $$(filter y,$$($1_BOOTLOADED)),0x8010000) $1_SECTION_START = -Wl,--section-start=.text=$$($1_START_ADDRESS) $1_LDFLAGS = $$(LDFLAGS) -T$$($1_LDSCRIPT) -L$$($1_LDSCRIPT_PATH) \ - $$(if $$($1_START_ADDRESS),$$($1_SECTION_START)) \ - $$($1_LIBOPENCM3_LDFLAGS) -$1_LDLIBS = -nostartfiles $$(LDLIBS) $$($1_LIBS) \ - -lopencm3_$(if $2,$2,$1) + $$(if $$($1_START_ADDRESS),$$($1_SECTION_START)) +$1_LDLIBS = -nostartfiles $$(LDLIBS) $$($1_LIBS) endef diff --git a/build/stm32f1.mk b/build/stm32f1.mk index cb9410f..32209de 100644 --- a/build/stm32f1.mk +++ b/build/stm32f1.mk @@ -4,7 +4,7 @@ include $(UCOO_BASE)/build/stm32.mk -$(eval $(call stm32_libopencm3,stm32f1)) +$(eval $(call stm32_cmsis,stm32f1)) stm32f1_SUBTARGETS = stm32 arm newlib diff --git a/build/stm32f4.mk b/build/stm32f4.mk index 8d0aa67..4a73247 100644 --- a/build/stm32f4.mk +++ b/build/stm32f4.mk @@ -4,7 +4,7 @@ include $(UCOO_BASE)/build/stm32.mk -$(eval $(call stm32_libopencm3,stm32f4)) +$(eval $(call stm32_cmsis,stm32f4)) stm32f4_SUBTARGETS = stm32 arm newlib diff --git a/lib/libopencm3 b/lib/libopencm3 deleted file mode 160000 index 9b8d44e..0000000 --- a/lib/libopencm3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9b8d44e8a3cc52d83aba3b5547c2bb56cdbd8b63 diff --git a/ucoo/arch/Module b/ucoo/arch/Module index 9a4989e..625cfd3 100644 --- a/ucoo/arch/Module +++ b/ucoo/arch/Module @@ -1,3 +1,5 @@ ucoo_arch_SOURCES := arch.host.cc \ arch.stm32.cc arch.stm32f1.cc arch.stm32f4.cc \ + rcc.stm32f1.cc rcc.stm32f4.cc \ + vector.arm.cc \ syscalls.newlib.cc syscalls.cc diff --git a/ucoo/arch/arch.arm.hh b/ucoo/arch/arch.arm.hh new file mode 100644 index 0000000..65c835d --- /dev/null +++ b/ucoo/arch/arch.arm.hh @@ -0,0 +1,37 @@ +#ifndef ucoo_arch_arch_arm_hh +#define ucoo_arch_arch_arm_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +namespace ucoo { + +static inline void +arch_dsb () +{ + __asm__ __volatile__ ("dsb" : : : "memory"); +} + +} // namespace ucoo + +#endif // ucoo_arch_arch_arm_hh diff --git a/ucoo/arch/arch.hh b/ucoo/arch/arch.hh index e0889bd..2401868 100644 --- a/ucoo/arch/arch.hh +++ b/ucoo/arch/arch.hh @@ -24,6 +24,14 @@ // // }}} +#if defined (TARGET_arm) +# include "arch.arm.hh" +#elif defined (TARGET_host) +# include "arch.host.hh" +#else +# error "not implemented for this target" +#endif + namespace ucoo { /// Initialise arch, take program arguments. @@ -34,14 +42,6 @@ arch_init (int argc, const char **argv); void arch_reset (); -#ifdef TARGET_host - -/// Retrieve program arguments. -void -arch_get_args (int &argc, const char **&argv); - -#endif - } // namespace ucoo #endif // ucoo_arch_arch_hh diff --git a/ucoo/arch/arch.host.hh b/ucoo/arch/arch.host.hh new file mode 100644 index 0000000..b08864e --- /dev/null +++ b/ucoo/arch/arch.host.hh @@ -0,0 +1,35 @@ +#ifndef ucoo_arch_arch_host_hh +#define ucoo_arch_arch_host_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +namespace ucoo { + +/// Retrieve program arguments. +void +arch_get_args (int &argc, const char **&argv); + +} // namespace ucoo + +#endif // ucoo_arch_arch_host_hh diff --git a/ucoo/arch/arch.stm32.cc b/ucoo/arch/arch.stm32.cc index aa07575..f48f9fc 100644 --- a/ucoo/arch/arch.stm32.cc +++ b/ucoo/arch/arch.stm32.cc @@ -24,14 +24,14 @@ #include "ucoo/arch/arch.hh" #include "ucoo/common.hh" -#include +#include "ucoo/arch/reg.hh" namespace ucoo { void arch_reset () { - SCB_AIRCR = SCB_AIRCR_VECTKEY | SCB_AIRCR_SYSRESETREQ; + NVIC_SystemReset (); } void diff --git a/ucoo/arch/arch.stm32f1.cc b/ucoo/arch/arch.stm32f1.cc index a2d659c..ca6664b 100644 --- a/ucoo/arch/arch.stm32f1.cc +++ b/ucoo/arch/arch.stm32f1.cc @@ -22,17 +22,20 @@ // // }}} #include "ucoo/arch/arch.hh" -#include "ucoo/common.hh" - -#include +#include "ucoo/arch/rcc.stm32.hh" namespace ucoo { void arch_init (int argc, const char **argv) { - rcc_clock_setup_in_hse_12mhz_out_72mhz (); - rcc_periph_clock_enable (RCC_AFIO); + rcc_sys_clock_setup_pll (72000000, 12000000, + 1, // prediv1 => 12 MHz / 1 = 12 MHz + 6, // pllmul => 12 MHz * 6 = 72 MHz + 2, // apb1_pre => 36 MHz + 1, // apb2_pre => 72 MHz + 6); // adc_pre => 12 MHz + rcc_peripheral_clock_enable (Rcc::AFIO); } } // namespace ucoo diff --git a/ucoo/arch/arch.stm32f4.cc b/ucoo/arch/arch.stm32f4.cc index 74c7306..c569c65 100644 --- a/ucoo/arch/arch.stm32f4.cc +++ b/ucoo/arch/arch.stm32f4.cc @@ -22,18 +22,22 @@ // // }}} #include "ucoo/arch/arch.hh" -#include "ucoo/common.hh" - -#include +#include "ucoo/arch/rcc.stm32.hh" namespace ucoo { void arch_init (int argc, const char **argv) { - rcc_clock_setup_hse_3v3 (&hse_8mhz_3v3[CLOCK_3V3_120MHZ]); - rcc_ahb_frequency = 120000000; - rcc_periph_clock_enable (RCC_SYSCFG); + rcc_sys_clock_setup_pll (120000000, 8000000, + 4, // pllm => 8 MHz / 4 = 2 MHz + 120, // plln => 2 MHz * 120 = 240 MHz + 2, // pllp => 240 MHz / 2 = 120 MHz + 5, // pllq => 240 MHz / 5 = 48 MHz + 4, // apb1_pre => 30 MHz + 2, // apb2_pre => 60 MHz + SupplyRange::V2_7); + rcc_peripheral_clock_enable (Rcc::SYSCFG); } } // namespace ucoo diff --git a/ucoo/arch/arch_common.arm.hh b/ucoo/arch/arch_common.arm.hh index 7b4abfd..b1c7138 100644 --- a/ucoo/arch/arch_common.arm.hh +++ b/ucoo/arch/arch_common.arm.hh @@ -29,6 +29,7 @@ namespace ucoo { /// Type used to save irq state. typedef unsigned int irq_flags_t; +/// Lock interrupts and return previous state. static inline irq_flags_t irq_lock (void) { @@ -40,7 +41,8 @@ irq_lock (void) return flags; } -inline void +/// Restore interrupts lock state after an irq_lock. +static inline void irq_restore (irq_flags_t flags) { __asm__ __volatile__ ("msr PRIMASK, %0" diff --git a/ucoo/arch/arch_common.hh b/ucoo/arch/arch_common.hh index ac70e62..dd8458c 100644 --- a/ucoo/arch/arch_common.hh +++ b/ucoo/arch/arch_common.hh @@ -32,12 +32,4 @@ # error "not implemented for this target" #endif -namespace ucoo { - -/// Give some time to other tasks when running in a tight loop. -void -yield (); - -} // namespace ucoo - #endif // ucoo_arch_arch_common_hh diff --git a/ucoo/arch/arch_common.host.hh b/ucoo/arch/arch_common.host.hh index 0ee2553..7130a0d 100644 --- a/ucoo/arch/arch_common.host.hh +++ b/ucoo/arch/arch_common.host.hh @@ -29,6 +29,7 @@ namespace ucoo { /// Type used to save irq state. typedef unsigned int irq_flags_t; +/// Lock interrupts and return previous state. static inline irq_flags_t irq_lock (void) { @@ -36,7 +37,8 @@ irq_lock (void) return 0; } -inline void +/// Restore interrupts lock state after an irq_lock. +static inline void irq_restore (irq_flags_t) { // Nothing on host, there is no interrupts. diff --git a/ucoo/arch/interrupt.arm.hh b/ucoo/arch/interrupt.arm.hh new file mode 100644 index 0000000..10ef261 --- /dev/null +++ b/ucoo/arch/interrupt.arm.hh @@ -0,0 +1,107 @@ +#ifndef ucoo_arch_interrupt_arm_hh +#define ucoo_arch_interrupt_arm_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/arch/reg.hh" + +#if defined (TARGET_stm32f4) +# include "ucoo/arch/interrupt.stm32f4.hh" +#elif defined (TARGET_stm32f1) +# include "ucoo/arch/interrupt.stm32f1.hh" +#else +# error "not implemented for this target" +#endif + +namespace ucoo { + +/// Standard ARM Cortex exceptions. +enum class Exception +{ + RESET = 1, + NMI = 2, + HARD_FAULT = 3, + MEMORY_MANAGEMENT_FAULT = 4, + BUS_FAULT = 5, + USAGE_FAULT = 6, + SV_CALL = 11, + PEND_SV = 14, + SYSTICK = 15, + COUNT +}; + +/// Exception vectors are a specialisation of this template. +template +void +interrupt (); + +/// IRQ vectors are a specialisation of this template. +template +void +interrupt (); + +/// Enable interrupt source. +static inline void +interrupt_enable (Irq irq) +{ + const unsigned int n = static_cast (irq); + NVIC->ISER[n / 32] = 1 << (n % 32); +} + +/// Disable interrupt source. +static inline void +interrupt_disable (Irq irq) +{ + const unsigned int n = static_cast (irq); + NVIC->ICER[n / 32] = 1 << (n % 32); +} + +/// Test if a interrupt source is enabled. +static inline bool +interrupt_is_enabled (Irq irq) +{ + const unsigned int n = static_cast (irq); + return NVIC->ISER[n / 32] & (1 << (n % 32)); +} + +/// Set interrupt priority. Warning: a limited number of MSB bits are used +/// depending on the hardware implementation. +static inline void +interrupt_set_priority (Irq irq, uint8_t priority) +{ + const unsigned int n = static_cast (irq); + NVIC->IP[n] = priority; +} + +/// Set exception priority. Warning: a limited number of MSB bits are used +/// depending on the hardware implementation. +static inline void +interrupt_set_priority (Exception ex, uint8_t priority) +{ + const unsigned int n = static_cast (ex); + SCB->SHP[n - 4] = priority; +} + +} // namespace ucoo + +#endif // ucoo_arch_interrupt_arm_hh diff --git a/ucoo/arch/interrupt.stm32f1.hh b/ucoo/arch/interrupt.stm32f1.hh new file mode 100644 index 0000000..74316ce --- /dev/null +++ b/ucoo/arch/interrupt.stm32f1.hh @@ -0,0 +1,104 @@ +#ifndef ucoo_arch_interrupt_stm32f1_hh +#define ucoo_arch_interrupt_stm32f1_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +namespace ucoo { + +enum class Irq +{ + WWDG, + PVD, + TAMPER, + RTC, + FLASH, + RCC, + EXTI0, + EXTI1, + EXTI2, + EXTI3, + EXTI4, + DMA1_Channel1, + DMA1_Channel2, + DMA1_Channel3, + DMA1_Channel4, + DMA1_Channel5, + DMA1_Channel6, + DMA1_Channel7, + ADC1_2, + CAN1_TX, + CAN1_RX0, + CAN1_RX1, + CAN1_SCE, + EXTI9_5, + TIM1_BRK, + TIM1_UP, + TIM1_TRG_COM, + TIM1_CC, + TIM2, + TIM3, + TIM4, + I2C1_EV, + I2C1_ER, + I2C2_EV, + I2C2_ER, + SPI1, + SPI2, + USART1, + USART2, + USART3, + EXTI15_10, + RTC_Alarm, + OTG_FS_WKUP, + RESERVED_43, + RESERVED_44, + RESERVED_45, + RESERVED_46, + RESERVED_47, + RESERVED_48, + RESERVED_49, + TIM5, + SPI3, + UART4, + UART5, + TIM6, + TIM7, + DMA2_Channel1, + DMA2_Channel2, + DMA2_Channel3, + DMA2_Channel4, + DMA2_Channel5, + RESERVED_61, + RESERVED_62, + CAN2_TX, + CAN2_RX0, + CAN2_RX1, + CAN2_SCE, + OTG_FS, + COUNT +}; + +} // namespace ucoo + +#endif // ucoo_arch_interrupt_stm32f1_hh diff --git a/ucoo/arch/interrupt.stm32f4.hh b/ucoo/arch/interrupt.stm32f4.hh new file mode 100644 index 0000000..ffd3912 --- /dev/null +++ b/ucoo/arch/interrupt.stm32f4.hh @@ -0,0 +1,127 @@ +#ifndef ucoo_arch_interrupt_stm32f4_hh +#define ucoo_arch_interrupt_stm32f4_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +namespace ucoo { + +enum class Irq +{ + WWDG, + PVD, + TAMP_STAMP, + RTC_WKUP, + FLASH, + RCC, + EXTI0, + EXTI1, + EXTI2, + EXTI3, + EXTI4, + DMA1_Stream0, + DMA1_Stream1, + DMA1_Stream2, + DMA1_Stream3, + DMA1_Stream4, + DMA1_Stream5, + DMA1_Stream6, + ADC, + CAN1_TX, + CAN1_RX0, + CAN1_RX1, + CAN1_SCE, + EXTI9_5, + TIM1_BRK_TIM9, + TIM1_UP_TIM10, + TIM1_TRG_COM_TIM11, + TIM1_CC, + TIM2, + TIM3, + TIM4, + I2C1_EV, + I2C1_ER, + I2C2_EV, + I2C2_ER, + SPI1, + SPI2, + USART1, + USART2, + USART3, + EXTI15_10, + RTC_Alarm, + OTG_FS_WKUP, + TIM8_BRK_TIM12, + TIM8_UP_TIM13, + TIM8_TRG_COM_TIM14, + TIM8_CC, + DMA1_Stream7, + FMC, + SDIO, + TIM5, + SPI3, + UART4, + UART5, + TIM6_DAC, + TIM7, + DMA2_Stream0, + DMA2_Stream1, + DMA2_Stream2, + DMA2_Stream3, + DMA2_Stream4, + ETH, + ETH_WKUP, + CAN2_TX, + CAN2_RX0, + CAN2_RX1, + CAN2_SCE, + OTG_FS, + DMA2_Stream5, + DMA2_Stream6, + DMA2_Stream7, + USART6, + I2C3_EV, + I2C3_ER, + OTG_HS_EP1_OUT, + OTG_HS_EP1_IN, + OTG_HS_WKUP, + OTG_HS, + DCMI, + CRYP, + HASH_RNG, + FPU, + UART7, + UART8, + SPI4, + SPI5, + SPI6, + SAI1, + LTDC, + LTDC_ER, + DMA2D, + COUNT +}; + +} // namespace ucoo + +#endif // ucoo_arch_interrupt_stm32f4_hh diff --git a/ucoo/arch/ld/common.ld b/ucoo/arch/ld/common.ld new file mode 100644 index 0000000..8b5efb2 --- /dev/null +++ b/ucoo/arch/ld/common.ld @@ -0,0 +1,67 @@ +ENTRY(entry) + +SECTIONS +{ + .text : { + KEEP(*(.vectors)) + *(.text*) + . = ALIGN(4); + *(.rodata*) + . = ALIGN(4); + } >rom + + .preinit_array : { + . = ALIGN(4); + __preinit_array_start = .; + KEEP (*(.preinit_array)) + __preinit_array_end = .; + } >rom + .init_array : { + . = ALIGN(4); + __init_array_start = .; + KEEP (*(SORT(.init_array.*))) + KEEP (*(.init_array)) + __init_array_end = .; + } >rom + .fini_array : { + . = ALIGN(4); + __fini_array_start = .; + KEEP (*(.fini_array)) + KEEP (*(SORT(.fini_array.*))) + __fini_array_end = .; + } >rom + + .ARM.extab : { + *(.ARM.extab*) + } >rom + .ARM.exidx : { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } >rom + + . = ALIGN(4); + _etext = .; + + .data : { + _data = .; + *(.data*) + . = ALIGN(4); + _edata = .; + } >ram AT >rom + _data_loadaddr = LOADADDR(.data); + + .bss : { + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } >ram + + /DISCARD/ : { *(.eh_frame) } + + . = ALIGN(4); + end = .; +} + +PROVIDE(_stack = ORIGIN(ram) + LENGTH(ram)); diff --git a/ucoo/arch/ld/stm32f1.ld b/ucoo/arch/ld/stm32f1.ld new file mode 100644 index 0000000..2231ccb --- /dev/null +++ b/ucoo/arch/ld/stm32f1.ld @@ -0,0 +1,6 @@ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 256K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K +} +INCLUDE common.ld diff --git a/ucoo/arch/ld/stm32f4.ld b/ucoo/arch/ld/stm32f4.ld new file mode 100644 index 0000000..f752426 --- /dev/null +++ b/ucoo/arch/ld/stm32f4.ld @@ -0,0 +1,6 @@ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K +} +INCLUDE common.ld diff --git a/ucoo/arch/rcc.stm32.hh b/ucoo/arch/rcc.stm32.hh new file mode 100644 index 0000000..ac8f76c --- /dev/null +++ b/ucoo/arch/rcc.stm32.hh @@ -0,0 +1,36 @@ +#ifndef ucoo_arch_rcc_stm32_hh +#define ucoo_arch_rcc_stm32_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +#if defined (TARGET_stm32f4) +# include "rcc.stm32f4.hh" +#elif defined (TARGET_stm32f1) +# include "rcc.stm32f1.hh" +#else +# error "not implemented for this target" +#endif + + +#endif // ucoo_arch_rcc_stm32_hh diff --git a/ucoo/arch/rcc.stm32f1.cc b/ucoo/arch/rcc.stm32f1.cc new file mode 100644 index 0000000..3b88e9b --- /dev/null +++ b/ucoo/arch/rcc.stm32f1.cc @@ -0,0 +1,134 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/arch/rcc.stm32f1.hh" +#include "ucoo/common.hh" +#include "ucoo/utils/table_lookup.hh" + +namespace ucoo { + +static const int hsi_freq_hz = 8000000; + +int rcc_sys_freq_hz = hsi_freq_hz; +int rcc_ahb_freq_hz = hsi_freq_hz; +int rcc_apb1_freq_hz = hsi_freq_hz; +int rcc_apb2_freq_hz = hsi_freq_hz; +int rcc_apb1_timer_freq_hz = hsi_freq_hz; +int rcc_apb2_timer_freq_hz = hsi_freq_hz; +int rcc_usb_freq_hz = 0; + +void +rcc_sys_clock_setup_pll (int sys_freq_hz, int hse_freq_hz, + int prediv1, int pllmul, + int apb1_pre, int apb2_pre, int adc_pre) +{ + // Switch to HSI as a safe fall back. + reg::RCC->CR |= RCC_CR_HSION; + while (!(reg::RCC->CR & RCC_CR_HSIRDY)) + ; + reg::RCC->CFGR = (reg::RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_HSI; + while ((reg::RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) + ; + // Start HSE if needed. + uint32_t pllsrc = 0; + int pll_in_freq_hz = hsi_freq_hz / 2; + if (hse_freq_hz) + { + pll_in_freq_hz = hse_freq_hz; + pllsrc = RCC_CFGR_PLLSRC; + reg::RCC->CR |= RCC_CR_HSEON; + while (!(reg::RCC->CR & RCC_CR_HSERDY)) + ; + // May divide by 2. + assert (prediv1 == 1 || prediv1 == 2); + if (prediv1 == 2) + { + pllsrc |= RCC_CFGR_PLLXTPRE; + pll_in_freq_hz /= 2; + } + } + // Change buses and ADC prescalers. + static const LookupTable adc_pre_table[] = + { + { 2, RCC_CFGR_ADCPRE_DIV2 }, + { 4, RCC_CFGR_ADCPRE_DIV4 }, + { 6, RCC_CFGR_ADCPRE_DIV6 }, + { 8, RCC_CFGR_ADCPRE_DIV8 }, + }; + static const LookupTable apb1_pre_table[] = + { + { 1, RCC_CFGR_PPRE1_DIV1 }, + { 2, RCC_CFGR_PPRE1_DIV2 }, + { 4, RCC_CFGR_PPRE1_DIV4 }, + { 8, RCC_CFGR_PPRE1_DIV8 }, + { 16, RCC_CFGR_PPRE1_DIV16 }, + }; + static const LookupTable apb2_pre_table[] = + { + { 1, RCC_CFGR_PPRE2_DIV1 }, + { 2, RCC_CFGR_PPRE2_DIV2 }, + { 4, RCC_CFGR_PPRE2_DIV4 }, + { 8, RCC_CFGR_PPRE2_DIV8 }, + { 16, RCC_CFGR_PPRE2_DIV16 }, + }; + uint32_t pre = simple_table_lookup (adc_pre_table, adc_pre) + | simple_table_lookup (apb1_pre_table, apb1_pre) + | simple_table_lookup (apb2_pre_table, apb2_pre); + // Start PLL. + static const LookupTable pllmul_table[] = + { + { 4, RCC_CFGR_PLLMULL4 }, + { 5, RCC_CFGR_PLLMULL5 }, + { 6, RCC_CFGR_PLLMULL6 }, + { 7, RCC_CFGR_PLLMULL7 }, + { 8, RCC_CFGR_PLLMULL8 }, + { 9, RCC_CFGR_PLLMULL9 }, + // Other values (2, 3, 10...16 or 6.5 on connectivity line) not + // handled. + }; + reg::RCC->CFGR = (reg::RCC->CFGR & ~( + RCC_CFGR_PLLMULL | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC + | RCC_CFGR_ADCPRE_Msk | RCC_CFGR_PPRE1_Msk | RCC_CFGR_PPRE2_Msk)) + | pllsrc | pre | simple_table_lookup (pllmul_table, pllmul); + reg::RCC->CR |= RCC_CR_PLLON; + while (!(reg::RCC->CR & RCC_CR_PLLRDY)) + ; + // Setup flash. + int mhz_per_ws = 24; + int ws = (sys_freq_hz - 1) / 1000000 / mhz_per_ws; + reg::FLASH->ACR = (reg::FLASH->ACR & ~FLASH_ACR_LATENCY) | ws; + // Switch to PLL. + reg::RCC->CFGR = (reg::RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL; + while ((reg::RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) + ; + // Update frequencies. + assert (pll_in_freq_hz * pllmul == sys_freq_hz); + rcc_ahb_freq_hz = rcc_sys_freq_hz = sys_freq_hz; + rcc_apb1_freq_hz = sys_freq_hz / apb1_pre; + rcc_apb2_freq_hz = sys_freq_hz / apb2_pre; + rcc_apb1_timer_freq_hz = rcc_apb1_freq_hz * (apb1_pre == 1 ? 1 : 2); + rcc_apb2_timer_freq_hz = rcc_apb2_freq_hz * (apb2_pre == 1 ? 1 : 2); + rcc_usb_freq_hz = sys_freq_hz * 2 / 3; +} + +} // namespace ucoo diff --git a/ucoo/arch/rcc.stm32f1.hh b/ucoo/arch/rcc.stm32f1.hh new file mode 100644 index 0000000..3896752 --- /dev/null +++ b/ucoo/arch/rcc.stm32f1.hh @@ -0,0 +1,143 @@ +#ifndef ucoo_arch_rcc_stm32f1_hh +#define ucoo_arch_rcc_stm32f1_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/arch/arch.hh" +#include "ucoo/arch/reg.hh" + +namespace ucoo { + +enum class Bus +{ + AHB = 0, + APB1 = 2, + APB2 = 1, +}; + +namespace details { + +constexpr uint32_t +rcc_enum (Bus bus, uint32_t bit, int pos = 0) +{ + return bit == 1 + ? (static_cast (bus) << 5) | pos + : rcc_enum (bus, (bit >> 1) | (bit << 31), pos + 1); +} + +} // namespace details + +/// Constants to handle reset and clock for each peripheral. +enum class Rcc +{ + DMA1 = details::rcc_enum (Bus::AHB, RCC_AHBENR_DMA1EN), + DMA2 = details::rcc_enum (Bus::AHB, RCC_AHBENR_DMA2EN), + SRAM = details::rcc_enum (Bus::AHB, RCC_AHBENR_SRAMEN), + FLITF = details::rcc_enum (Bus::AHB, RCC_AHBENR_FLITFEN), + CRC = details::rcc_enum (Bus::AHB, RCC_AHBENR_CRCEN), + OTGFS = details::rcc_enum (Bus::AHB, RCC_AHBENR_OTGFSEN), + AFIO = details::rcc_enum (Bus::APB2, RCC_APB2ENR_AFIOEN), + GPIOA = details::rcc_enum (Bus::APB2, RCC_APB2ENR_IOPAEN), + GPIOB = details::rcc_enum (Bus::APB2, RCC_APB2ENR_IOPBEN), + GPIOC = details::rcc_enum (Bus::APB2, RCC_APB2ENR_IOPCEN), + GPIOD = details::rcc_enum (Bus::APB2, RCC_APB2ENR_IOPDEN), + GPIOE = details::rcc_enum (Bus::APB2, RCC_APB2ENR_IOPEEN), + ADC1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_ADC1EN), + ADC2 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_ADC2EN), + TIM1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_TIM1EN), + SPI1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_SPI1EN), + USART1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_USART1EN), + TIM2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM2EN), + TIM3 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM3EN), + TIM4 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM4EN), + TIM5 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM5EN), + TIM6 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM6EN), + TIM7 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM7EN), + WWDG = details::rcc_enum (Bus::APB1, RCC_APB1ENR_WWDGEN), + SPI2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_SPI2EN), + SPI3 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_SPI3EN), + USART2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_USART2EN), + USART3 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_USART3EN), + UART4 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_UART4EN), + UART5 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_UART5EN), + I2C1 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_I2C1EN), + I2C2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_I2C2EN), + CAN1 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_CAN1EN), + CAN2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_CAN2EN), + BKP = details::rcc_enum (Bus::APB1, RCC_APB1ENR_BKPEN), + PWR = details::rcc_enum (Bus::APB1, RCC_APB1ENR_PWREN), + DAC = details::rcc_enum (Bus::APB1, RCC_APB1ENR_DACEN), +}; + +/// Enable clock for given peripheral. +static inline void +rcc_peripheral_clock_enable (Rcc rcc) +{ + int bus_index = static_cast (rcc) >> 5; + int bit_index = static_cast (rcc) & 0x1f; + (®::RCC->AHBENR)[bus_index] |= 1 << bit_index; + arch_dsb (); +} + +/// Disable clock for given peripheral. +static inline void +rcc_peripheral_clock_disable (Rcc rcc) +{ + int bus_index = static_cast (rcc) >> 5; + int bit_index = static_cast (rcc) & 0x1f; + (®::RCC->AHBENR)[bus_index] &= ~(1 << bit_index); + arch_dsb (); +} + +/// Frequency of the main system clock. +extern int rcc_sys_freq_hz; + +/// Frequency of AHB bus. +extern int rcc_ahb_freq_hz; + +/// Frequency of APB1 bus. +extern int rcc_apb1_freq_hz; + +/// Frequency of APB2 bus. +extern int rcc_apb2_freq_hz; + +/// Frequency of timers on APB1 bus. +extern int rcc_apb1_timer_freq_hz; + +/// Frequency of timers on APB2 bus. +extern int rcc_apb2_timer_freq_hz; + +/// Frequency of the USB clock. +extern int rcc_usb_freq_hz; + +/// Setup system clock using PLL, from HSE clock if not 0, else HSI clock. +/// Also setup flash access. Only support configuration common to all F1 +/// lines. +void +rcc_sys_clock_setup_pll (int sys_freq_hz, int hse_freq_hz, + int prediv1, int pllmul, + int apb1_pre, int apb2_pre, int adc_pre); + +} // namespace ucoo + +#endif // ucoo_arch_rcc_stm32f1_hh diff --git a/ucoo/arch/rcc.stm32f4.cc b/ucoo/arch/rcc.stm32f4.cc new file mode 100644 index 0000000..36e31b6 --- /dev/null +++ b/ucoo/arch/rcc.stm32f4.cc @@ -0,0 +1,125 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/arch/rcc.stm32f4.hh" +#include "ucoo/common.hh" +#include "ucoo/utils/table_lookup.hh" + +namespace ucoo { + +static const int hsi_freq_hz = 16000000; + +int rcc_sys_freq_hz = hsi_freq_hz; +int rcc_ahb_freq_hz = hsi_freq_hz; +int rcc_apb1_freq_hz = hsi_freq_hz; +int rcc_apb2_freq_hz = hsi_freq_hz; +int rcc_apb1_timer_freq_hz = hsi_freq_hz; +int rcc_apb2_timer_freq_hz = hsi_freq_hz; +int rcc_pll48_freq_hz = 0; + +void +rcc_sys_clock_setup_pll (int sys_freq_hz, int hse_freq_hz, + int pllm, int plln, int pllp, int pllq, + int apb1_pre, int apb2_pre, + SupplyRange vrange) +{ + // Switch to HSI as a safe fall back. + reg::RCC->CR |= RCC_CR_HSION; + while (!(reg::RCC->CR & RCC_CR_HSIRDY)) + ; + reg::RCC->CFGR = (reg::RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_HSI; + while ((reg::RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI) + ; + // Start HSE if needed. + uint32_t pllsrc = RCC_PLLCFGR_PLLSRC_HSI; + int pll_in_freq_hz = hsi_freq_hz; + if (hse_freq_hz) + { + pll_in_freq_hz = hse_freq_hz; + pllsrc = RCC_PLLCFGR_PLLSRC_HSE; + reg::RCC->CR |= RCC_CR_HSEON; + while (!(reg::RCC->CR & RCC_CR_HSERDY)) + ; + } + // Adapt voltage regulator scale. + if (sys_freq_hz <= 120000000) + reg::PWR->CR = (reg::PWR->CR & ~PWR_CR_VOS) | PWR_CR_VOS_Scale3; + else if (sys_freq_hz <= 144000000) + reg::PWR->CR = (reg::PWR->CR & ~PWR_CR_VOS) | PWR_CR_VOS_Scale2; + else + reg::PWR->CR = (reg::PWR->CR & ~PWR_CR_VOS) | PWR_CR_VOS_Scale1; + // Change buses prescalers. + uint32_t ppre = 0; + static const LookupTable apb1_pre_table[] = + { + { 1, RCC_CFGR_PPRE1_DIV1 }, + { 2, RCC_CFGR_PPRE1_DIV2 }, + { 4, RCC_CFGR_PPRE1_DIV4 }, + { 8, RCC_CFGR_PPRE1_DIV8 }, + { 16, RCC_CFGR_PPRE1_DIV16 }, + }; + ppre |= simple_table_lookup (apb1_pre_table, apb1_pre); + static const LookupTable apb2_pre_table[] = + { + { 1, RCC_CFGR_PPRE2_DIV1 }, + { 2, RCC_CFGR_PPRE2_DIV2 }, + { 4, RCC_CFGR_PPRE2_DIV4 }, + { 8, RCC_CFGR_PPRE2_DIV8 }, + { 16, RCC_CFGR_PPRE2_DIV16 }, + }; + ppre |= simple_table_lookup (apb2_pre_table, apb2_pre); + reg::RCC->CFGR = (reg::RCC->CFGR & ~(RCC_CFGR_PPRE1 | RCC_CFGR_PPRE2)) + | ppre; + // Start PLL. + reg::RCC->PLLCFGR = pllsrc + | pllm << RCC_PLLCFGR_PLLM_Pos + | plln << RCC_PLLCFGR_PLLN_Pos + | (pllp / 2 - 1) << RCC_PLLCFGR_PLLP_Pos + | pllq << RCC_PLLCFGR_PLLQ_Pos; + reg::RCC->CR |= RCC_CR_PLLON; + while (!(reg::RCC->CR & RCC_CR_PLLRDY)) + ; + // Setup flash. + int mhz_per_ws_per_vrange[] = { 20, 22, 24, 30 }; + int mhz_per_ws = mhz_per_ws_per_vrange[static_cast (vrange)]; + int ws = (sys_freq_hz - 1) / 1000000 / mhz_per_ws; + reg::FLASH->ACR = ws + | FLASH_ACR_ICEN | FLASH_ACR_DCEN + | (ws ? FLASH_ACR_PRFTEN : 0); // XXX not for buggy F40x! + // Switch to PLL. + reg::RCC->CFGR = (reg::RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_PLL; + while ((reg::RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) + ; + // Disable unused HSI. + reg::RCC->CR &= ~RCC_CR_HSION; + // Update frequencies. + assert (pll_in_freq_hz / pllm * plln / pllp == sys_freq_hz); + rcc_ahb_freq_hz = rcc_sys_freq_hz = sys_freq_hz; + rcc_apb1_freq_hz = sys_freq_hz / apb1_pre; + rcc_apb2_freq_hz = sys_freq_hz / apb2_pre; + rcc_apb1_timer_freq_hz = rcc_apb1_freq_hz * (apb1_pre == 1 ? 1 : 2); + rcc_apb2_timer_freq_hz = rcc_apb2_freq_hz * (apb2_pre == 1 ? 1 : 2); + rcc_pll48_freq_hz = pll_in_freq_hz / pllm * plln / pllq; +} + +} // namespace ucoo diff --git a/ucoo/arch/rcc.stm32f4.hh b/ucoo/arch/rcc.stm32f4.hh new file mode 100644 index 0000000..64c46b5 --- /dev/null +++ b/ucoo/arch/rcc.stm32f4.hh @@ -0,0 +1,193 @@ +#ifndef ucoo_arch_rcc_stm32f4_hh +#define ucoo_arch_rcc_stm32f4_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/arch/arch.hh" +#include "ucoo/arch/reg.hh" + +namespace ucoo { + +enum class Bus +{ + AHB1 = 0, + AHB2 = 1, + AHB3 = 2, + APB1 = 4, + APB2 = 5, +}; + +/// Supply range. +enum class SupplyRange +{ + /// 1.8 V to 2.1 V. + V1_8, + /// 2.1 V to 2.4 V. + V2_1, + /// 2.4 V to 2.7 V. + V2_4, + /// 2.7 V to 3.6 V. + V2_7, +}; + +namespace details { + +constexpr uint32_t +rcc_enum (Bus bus, uint32_t bit, int pos = 0) +{ + return bit == 1 + ? (static_cast (bus) << 5) | pos + : rcc_enum (bus, (bit >> 1) | (bit << 31), pos + 1); +} + +} // namespace details + +/// Constants to handle reset and clock for each peripheral. +enum class Rcc +{ + GPIOA = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOAEN), + GPIOB = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOBEN), + GPIOC = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOCEN), + GPIOD = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIODEN), + GPIOE = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOEEN), + GPIOF = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOFEN), + GPIOG = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOGEN), + GPIOH = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOHEN), + GPIOI = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOIEN), + GPIOJ = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOJEN), + GPIOK = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_GPIOKEN), + CRC = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_CRCEN), + BKPSRAM = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_BKPSRAMEN), + CCMDATARAM = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_CCMDATARAMEN), + DMA1 = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_DMA1EN), + DMA2 = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_DMA2EN), + DMA2D = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_DMA2DEN), + ETHMAC = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_ETHMACEN), + ETHMACTX = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_ETHMACTXEN), + ETHMACRX = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_ETHMACRXEN), + ETHMACPTP = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_ETHMACPTPEN), + OTGHS = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_OTGHSEN), + OTGHSULPI = details::rcc_enum (Bus::AHB1, RCC_AHB1ENR_OTGHSULPIEN), + DCMI = details::rcc_enum (Bus::AHB2, RCC_AHB2ENR_DCMIEN), + CRYP = details::rcc_enum (Bus::AHB2, RCC_AHB2ENR_CRYPEN), + HASH = details::rcc_enum (Bus::AHB2, RCC_AHB2ENR_HASHEN), + RNG = details::rcc_enum (Bus::AHB2, RCC_AHB2ENR_RNGEN), + OTGFS = details::rcc_enum (Bus::AHB2, RCC_AHB2ENR_OTGFSEN), + FMC = details::rcc_enum (Bus::AHB3, RCC_AHB3ENR_FMCEN), + TIM2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM2EN), + TIM3 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM3EN), + TIM4 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM4EN), + TIM5 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM5EN), + TIM6 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM6EN), + TIM7 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM7EN), + TIM12 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM12EN), + TIM13 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM13EN), + TIM14 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_TIM14EN), + WWDG = details::rcc_enum (Bus::APB1, RCC_APB1ENR_WWDGEN), + SPI2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_SPI2EN), + SPI3 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_SPI3EN), + USART2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_USART2EN), + USART3 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_USART3EN), + UART4 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_UART4EN), + UART5 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_UART5EN), + I2C1 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_I2C1EN), + I2C2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_I2C2EN), + I2C3 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_I2C3EN), + CAN1 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_CAN1EN), + CAN2 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_CAN2EN), + PWR = details::rcc_enum (Bus::APB1, RCC_APB1ENR_PWREN), + DAC = details::rcc_enum (Bus::APB1, RCC_APB1ENR_DACEN), + UART7 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_UART7EN), + UART8 = details::rcc_enum (Bus::APB1, RCC_APB1ENR_UART8EN), + TIM1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_TIM1EN), + TIM8 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_TIM8EN), + USART1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_USART1EN), + USART6 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_USART6EN), + ADC1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_ADC1EN), + ADC2 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_ADC2EN), + ADC3 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_ADC3EN), + SDIO = details::rcc_enum (Bus::APB2, RCC_APB2ENR_SDIOEN), + SPI1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_SPI1EN), + SPI4 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_SPI4EN), + SYSCFG = details::rcc_enum (Bus::APB2, RCC_APB2ENR_SYSCFGEN), + TIM9 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_TIM9EN), + TIM10 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_TIM10EN), + TIM11 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_TIM11EN), + SPI5 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_SPI5EN), + SPI6 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_SPI6EN), + SAI1 = details::rcc_enum (Bus::APB2, RCC_APB2ENR_SAI1EN), + LTDC = details::rcc_enum (Bus::APB2, RCC_APB2ENR_LTDCEN), +}; + +/// Enable clock for given peripheral. +static inline void +rcc_peripheral_clock_enable (Rcc rcc) +{ + int bus_index = static_cast (rcc) >> 5; + int bit_index = static_cast (rcc) & 0x1f; + (®::RCC->AHB1ENR)[bus_index] |= 1 << bit_index; + arch_dsb (); +} + +/// Disable clock for given peripheral. +static inline void +rcc_peripheral_clock_disable (Rcc rcc) +{ + int bus_index = static_cast (rcc) >> 5; + int bit_index = static_cast (rcc) & 0x1f; + (®::RCC->AHB1ENR)[bus_index] &= ~(1 << bit_index); + arch_dsb (); +} + +/// Frequency of the main system clock. +extern int rcc_sys_freq_hz; + +/// Frequency of AHB bus. +extern int rcc_ahb_freq_hz; + +/// Frequency of APB1 bus. +extern int rcc_apb1_freq_hz; + +/// Frequency of APB2 bus. +extern int rcc_apb2_freq_hz; + +/// Frequency of timers on APB1 bus. +extern int rcc_apb1_timer_freq_hz; + +/// Frequency of timers on APB2 bus. +extern int rcc_apb2_timer_freq_hz; + +/// Frequency of the should-be-48-MHz clock (used for USB, RNG & SDIO). +extern int rcc_pll48_freq_hz; + +/// Setup system clock using PLL, from HSE clock if not 0, else HSI clock. +/// Also setup flash access and voltage regulator scale. +void +rcc_sys_clock_setup_pll (int sys_freq_hz, int hse_freq_hz, + int pllm, int plln, int pllp, int pllq, + int apb1_pre, int apb2_pre, + SupplyRange vrange); + +} // namespace ucoo + +#endif // ucoo_arch_rcc_stm32f4_hh diff --git a/ucoo/arch/reg.hh b/ucoo/arch/reg.hh new file mode 100644 index 0000000..d913511 --- /dev/null +++ b/ucoo/arch/reg.hh @@ -0,0 +1,35 @@ +#ifndef ucoo_arch_reg_hh +#define ucoo_arch_reg_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +#if defined (TARGET_stm32f4) +# include "reg.stm32f4.hh" +#elif defined (TARGET_stm32f1) +# include "reg.stm32f1.hh" +#else +# error "not implemented for this target" +#endif + +#endif // ucoo_arch_reg_hh diff --git a/ucoo/arch/reg.stm32.hh b/ucoo/arch/reg.stm32.hh new file mode 100644 index 0000000..4d46354 --- /dev/null +++ b/ucoo/arch/reg.stm32.hh @@ -0,0 +1,80 @@ +#ifndef ucoo_arch_reg_stm32_hh +#define ucoo_arch_reg_stm32_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +#define TIM_SMCR_SMS_TM (6 << 0) + +#define TIM_SMCR_TS_TI1FP1 (5 << 4) +#define TIM_SMCR_TS_TI1FP2 (6 << 4) + +#define TIM_CCMR1_OC1M_PWM1 (6 << 4) +#define TIM_CCMR1_OC2M_PWM1 (6 << 12) + +#define TIM_CCMR2_OC3M_PWM1 (6 << 4) +#define TIM_CCMR2_OC4M_PWM1 (6 << 12) + +#define USART_CR2_STOP_Bits_1 0 +#define USART_CR2_STOP_Bits_0_5 USART_CR2_STOP_0 +#define USART_CR2_STOP_Bits_2 USART_CR2_STOP_1 +#define USART_CR2_STOP_Bits_1_5 (USART_CR2_STOP_1 | USART_CR2_STOP_0) + +#define USB_OTG_GRXSTSP_PKTSTS_GlobalOutNak (1 << 17) +#define USB_OTG_GRXSTSP_PKTSTS_Out (2 << 17) +#define USB_OTG_GRXSTSP_PKTSTS_OutCompleted (3 << 17) +#define USB_OTG_GRXSTSP_PKTSTS_SetupCompleted (4 << 17) +#define USB_OTG_GRXSTSP_PKTSTS_Setup (6 << 17) + +#define FLASH_KEYR_KEY1 0x45670123 +#define FLASH_KEYR_KEY2 0xcdef89ab + +namespace ucoo { + +struct USB_OTG_FIFOTypeDef +{ + __IO uint32_t FIFO; + uint32_t reserved[(0x1000 - sizeof (uint32_t)) / 4]; +}; + +/// USB OTG registers, only define device registers. +struct USB_OTG_TypeDef +{ + USB_OTG_GlobalTypeDef global; + uint32_t reserved0[(USB_OTG_DEVICE_BASE - USB_OTG_GLOBAL_BASE + - sizeof (USB_OTG_GlobalTypeDef)) / 4]; + USB_OTG_DeviceTypeDef device; + uint32_t reserved1[(USB_OTG_IN_ENDPOINT_BASE - USB_OTG_DEVICE_BASE + - sizeof (USB_OTG_DeviceTypeDef)) / 4]; + USB_OTG_INEndpointTypeDef ep_in[4]; + uint32_t reserved2[(USB_OTG_OUT_ENDPOINT_BASE - USB_OTG_IN_ENDPOINT_BASE + - sizeof (USB_OTG_INEndpointTypeDef[4])) / 4]; + USB_OTG_OUTEndpointTypeDef ep_out[4]; + uint32_t reserved3[(USB_OTG_FIFO_BASE - USB_OTG_OUT_ENDPOINT_BASE + - sizeof (USB_OTG_OUTEndpointTypeDef[4])) / 4]; + USB_OTG_FIFOTypeDef fifo[4]; +}; + +} // namespace ucoo + +#endif // ucoo_arch_reg_stm32_hh diff --git a/ucoo/arch/reg.stm32f1.hh b/ucoo/arch/reg.stm32f1.hh new file mode 100644 index 0000000..47855cf --- /dev/null +++ b/ucoo/arch/reg.stm32f1.hh @@ -0,0 +1,180 @@ +#ifndef ucoo_arch_reg_stm32f1_hh +#define ucoo_arch_reg_stm32f1_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ST/STM32F1xx/Include/stm32f105xc.h" +#include "ucoo/arch/reg.stm32.hh" + +#define GPIO_CNF_Input_Analog 0 +#define GPIO_CNF_Input_Float 1 +#define GPIO_CNF_Input_PullUpDown 2 + +#define GPIO_CNF_Output_PushPull 0 +#define GPIO_CNF_Output_OpenDrain 1 +#define GPIO_CNF_Output_AFPushPull 2 +#define GPIO_CNF_Output_AFOpenDrain 3 + +#define GPIO_MODE_Input 0 +#define GPIO_MODE_Output_2MHz 1 +#define GPIO_MODE_Output_10MHz 2 +#define GPIO_MODE_Output_50MHz 3 + +#define DESIG_BASE 0x1ffff7e0 + +struct DESIG_TypeDef +{ + __IO uint16_t FLASH_SIZE; + uint16_t reserved1; + uint32_t reserved2; + __IO uint32_t U_ID[3]; +}; + +#undef SysTick + +#undef TIM2 +#undef TIM3 +#undef TIM4 +#undef TIM5 +#undef TIM6 +#undef TIM7 +#undef RTC +#undef WWDG +#undef IWDG +#undef SPI2 +#undef SPI3 +#undef USART2 +#undef USART3 +#undef UART4 +#undef UART5 +#undef I2C1 +#undef I2C2 +#undef CAN1 +#undef CAN2 +#undef BKP +#undef PWR +#undef DAC +#undef AFIO +#undef EXTI +#undef GPIOA +#undef GPIOB +#undef GPIOC +#undef GPIOD +#undef GPIOE +#undef ADC1 +#undef ADC2 +#undef ADC12_COMMON +#undef TIM1 +#undef SPI1 +#undef USART1 +#undef SDIO +#undef DMA1 +#undef DMA2 +#undef DMA1_Channel1 +#undef DMA1_Channel2 +#undef DMA1_Channel3 +#undef DMA1_Channel4 +#undef DMA1_Channel5 +#undef DMA1_Channel6 +#undef DMA1_Channel7 +#undef DMA2_Channel1 +#undef DMA2_Channel2 +#undef DMA2_Channel3 +#undef DMA2_Channel4 +#undef DMA2_Channel5 +#undef RCC +#undef CRC +#undef FLASH +#undef OB +#undef DBGMCU +#undef USB_OTG_FS + +namespace ucoo { + +namespace reg { + +constexpr auto SysTick = reinterpret_cast (SysTick_BASE); + +constexpr auto TIM2 = reinterpret_cast (TIM2_BASE); +constexpr auto TIM3 = reinterpret_cast (TIM3_BASE); +constexpr auto TIM4 = reinterpret_cast (TIM4_BASE); +constexpr auto TIM5 = reinterpret_cast (TIM5_BASE); +constexpr auto TIM6 = reinterpret_cast (TIM6_BASE); +constexpr auto TIM7 = reinterpret_cast (TIM7_BASE); +constexpr auto RTC = reinterpret_cast (RTC_BASE); +constexpr auto WWDG = reinterpret_cast (WWDG_BASE); +constexpr auto IWDG = reinterpret_cast (IWDG_BASE); +constexpr auto SPI2 = reinterpret_cast (SPI2_BASE); +constexpr auto SPI3 = reinterpret_cast (SPI3_BASE); +constexpr auto USART2 = reinterpret_cast (USART2_BASE); +constexpr auto USART3 = reinterpret_cast (USART3_BASE); +constexpr auto UART4 = reinterpret_cast (UART4_BASE); +constexpr auto UART5 = reinterpret_cast (UART5_BASE); +constexpr auto I2C1 = reinterpret_cast (I2C1_BASE); +constexpr auto I2C2 = reinterpret_cast (I2C2_BASE); +constexpr auto CAN1 = reinterpret_cast (CAN1_BASE); +constexpr auto CAN2 = reinterpret_cast (CAN2_BASE); +constexpr auto BKP = reinterpret_cast (BKP_BASE); +constexpr auto PWR = reinterpret_cast (PWR_BASE); +constexpr auto DAC = reinterpret_cast (DAC_BASE); +constexpr auto AFIO = reinterpret_cast (AFIO_BASE); +constexpr auto EXTI = reinterpret_cast (EXTI_BASE); +constexpr auto GPIOA = reinterpret_cast (GPIOA_BASE); +constexpr auto GPIOB = reinterpret_cast (GPIOB_BASE); +constexpr auto GPIOC = reinterpret_cast (GPIOC_BASE); +constexpr auto GPIOD = reinterpret_cast (GPIOD_BASE); +constexpr auto GPIOE = reinterpret_cast (GPIOE_BASE); +constexpr auto ADC1 = reinterpret_cast (ADC1_BASE); +constexpr auto ADC2 = reinterpret_cast (ADC2_BASE); +constexpr auto ADC12_COMMON = reinterpret_cast (ADC1_BASE); +constexpr auto TIM1 = reinterpret_cast (TIM1_BASE); +constexpr auto SPI1 = reinterpret_cast (SPI1_BASE); +constexpr auto USART1 = reinterpret_cast (USART1_BASE); +constexpr auto SDIO = reinterpret_cast (SDIO_BASE); +constexpr auto DMA1 = reinterpret_cast (DMA1_BASE); +constexpr auto DMA2 = reinterpret_cast (DMA2_BASE); +constexpr auto DMA1_Channel1 = reinterpret_cast (DMA1_Channel1_BASE); +constexpr auto DMA1_Channel2 = reinterpret_cast (DMA1_Channel2_BASE); +constexpr auto DMA1_Channel3 = reinterpret_cast (DMA1_Channel3_BASE); +constexpr auto DMA1_Channel4 = reinterpret_cast (DMA1_Channel4_BASE); +constexpr auto DMA1_Channel5 = reinterpret_cast (DMA1_Channel5_BASE); +constexpr auto DMA1_Channel6 = reinterpret_cast (DMA1_Channel6_BASE); +constexpr auto DMA1_Channel7 = reinterpret_cast (DMA1_Channel7_BASE); +constexpr auto DMA2_Channel1 = reinterpret_cast (DMA2_Channel1_BASE); +constexpr auto DMA2_Channel2 = reinterpret_cast (DMA2_Channel2_BASE); +constexpr auto DMA2_Channel3 = reinterpret_cast (DMA2_Channel3_BASE); +constexpr auto DMA2_Channel4 = reinterpret_cast (DMA2_Channel4_BASE); +constexpr auto DMA2_Channel5 = reinterpret_cast (DMA2_Channel5_BASE); +constexpr auto RCC = reinterpret_cast (RCC_BASE); +constexpr auto CRC = reinterpret_cast (CRC_BASE); +constexpr auto FLASH = reinterpret_cast (FLASH_R_BASE); +constexpr auto OB = reinterpret_cast (OB_BASE); +constexpr auto DBGMCU = reinterpret_cast (DBGMCU_BASE); +constexpr auto USB_OTG_FS = reinterpret_cast (USB_OTG_FS_PERIPH_BASE); +constexpr auto DESIG = reinterpret_cast (DESIG_BASE); + +} // namespace reg + +} // namespace ucoo + +#endif // ucoo_arch_reg_stm32f1_hh diff --git a/ucoo/arch/reg.stm32f4.hh b/ucoo/arch/reg.stm32f4.hh new file mode 100644 index 0000000..31ad39d --- /dev/null +++ b/ucoo/arch/reg.stm32f4.hh @@ -0,0 +1,347 @@ +#ifndef ucoo_arch_reg_stm32f4_hh +#define ucoo_arch_reg_stm32f4_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +// Use a superset of all supported chips. +#include "ST/STM32F4xx/Include/stm32f439xx.h" +#include "ucoo/arch/reg.stm32.hh" + +#define SCB_CPACR_CP10_Pos 20 +#define SCB_CPACR_CP10_Denied (0 << SCB_CPACR_CP10_Pos) +#define SCB_CPACR_CP10_Privileged (1 << SCB_CPACR_CP10_Pos) +#define SCB_CPACR_CP10_Full (3 << SCB_CPACR_CP10_Pos) +#define SCB_CPACR_CP10_Msk (3 << SCB_CPACR_CP10_Pos) + +#define SCB_CPACR_CP11_Pos 22 +#define SCB_CPACR_CP11_Denied (0 << SCB_CPACR_CP11_Pos) +#define SCB_CPACR_CP11_Privileged (1 << SCB_CPACR_CP11_Pos) +#define SCB_CPACR_CP11_Full (3 << SCB_CPACR_CP11_Pos) +#define SCB_CPACR_CP11_Msk (3 << SCB_CPACR_CP11_Pos) + +#define PWR_CR_VOS_Scale3 (PWR_CR_VOS_0) +#define PWR_CR_VOS_Scale2 (PWR_CR_VOS_1) +#define PWR_CR_VOS_Scale1 (PWR_CR_VOS_1 | PWR_CR_VOS_0) + +#define RCC_PLLCFGR_PLLM_Pos 0 +#define RCC_PLLCFGR_PLLN_Pos 6 +#define RCC_PLLCFGR_PLLP_Pos 16 +#define RCC_PLLCFGR_PLLQ_Pos 24 + +#define GPIO_MODER_Input 0 +#define GPIO_MODER_Output 1 +#define GPIO_MODER_AF 2 +#define GPIO_MODER_Analog 3 + +#define SPI_CR1_BR_Pos 3 + +#define USB_OTG_GRXSTSP_BCNT_Pos 4 +#define USB_OTG_GRXSTSP_PKTSTS_Pos 17 +#define USB_OTG_DOEPTSIZ_PKTCNT_Pos 19 +#define USB_OTG_DIEPTSIZ_PKTCNT_Pos 19 + +#define FLASH_CR_PSIZE_x32 FLASH_CR_PSIZE_1 + +#define FMC_SDCR_RPIPE_None 0 +#define FMC_SDCR_RPIPE_1Clk FMC_SDCR1_RPIPE_0 +#define FMC_SDCR_RPIPE_2Clk FMC_SDCR1_RPIPE_1 +#define FMC_SDCR_RBURST FMC_SDCR1_RBURST +#define FMC_SDCR_SDCLK_Disabled 0 +#define FMC_SDCR_SDCLK_2Hclk FMC_SDCR1_SDCLK_1 +#define FMC_SDCR_SDCLK_3Hclk (FMC_SDCR1_SDCLK_1 | FMC_SDCR1_SDCLK_0) +#define FMC_SDCR_CAS_Pos 7 +#define FMC_SDCR_NB FMC_SDCR1_NB +#define FMC_SDCR_MWID_8B 0 +#define FMC_SDCR_MWID_16B FMC_SDCR1_MWID_0 +#define FMC_SDCR_MWID_32B FMC_SDCR1_MWID_1 +#define FMC_SDCR_NR_Pos 2 +#define FMC_SDCR_NC_Pos 0 + +#define FMC_SDTR_TRCD_Pos 24 +#define FMC_SDTR_TRP_Pos 20 +#define FMC_SDTR_TWR_Pos 16 +#define FMC_SDTR_TRC_Pos 12 +#define FMC_SDTR_TRAS_Pos 8 +#define FMC_SDTR_TXSR_Pos 4 +#define FMC_SDTR_TMRD_Pos 0 + +#define FMC_SDCR_DNC_Mask (FMC_SDCR1_SDCLK | FMC_SDCR1_RPIPE \ + | FMC_SDCR1_RBURST) +#define FMC_SDTR_DNC_Mask (FMC_SDTR1_TRP | FMC_SDTR1_TRC) + +#define FMC_SDCMR_MODE_Normal 0 +#define FMC_SDCMR_MODE_ClockConfigEna 1 +#define FMC_SDCMR_MODE_Pall 2 +#define FMC_SDCMR_MODE_AutoRefresh 3 +#define FMC_SDCMR_MODE_LoadModeRegister 4 +#define FMC_SDCMR_MODE_SelfRefresh 5 +#define FMC_SDCMR_MODE_PowerDown 6 + +#define FMC_SDCMR_NRFS_Pos 5 +#define FMC_SDCMR_MRD_Pos 9 + +#define SDRAM_MODE_BURST_LENGTH_1 0x0000 +#define SDRAM_MODE_BURST_LENGTH_2 0x0001 +#define SDRAM_MODE_BURST_LENGTH_4 0x0002 +#define SDRAM_MODE_BURST_LENGTH_8 0x0004 +#define SDRAM_MODE_BURST_TYPE_SEQUENTIAL 0x0000 +#define SDRAM_MODE_BURST_TYPE_INTERLEAVED 0x0008 +#define SDRAM_MODE_CAS_LATENCY_2 0x0020 +#define SDRAM_MODE_CAS_LATENCY_3 0x0030 +#define SDRAM_MODE_OPERATING_MODE_STANDARD 0x0000 +#define SDRAM_MODE_WRITEBURST_MODE_PROGRAMMED 0x0000 +#define SDRAM_MODE_WRITEBURST_MODE_SINGLE 0x0200 + +#define FMC_BANK5_BASE 0xc0000000 +#define FMC_BANK6_BASE 0xd0000000 + +#define DESIG_BASE 0x1fff7a10 +#define DESIG_FLASH_SIZE_BASE 0x1fff7a22 +#define DESIG_UNIQUE_ID_BASE 0x1fff7a10 + +struct DESIG_TypeDef +{ + __IO uint32_t U_ID[3]; + uint32_t reserved0x1c; + uint16_t reserved0x20; + __IO uint16_t FLASH_SIZE; +}; + +#undef FPU +#undef SysTick + +#undef TIM2 +#undef TIM3 +#undef TIM4 +#undef TIM5 +#undef TIM6 +#undef TIM7 +#undef TIM12 +#undef TIM13 +#undef TIM14 +#undef RTC +#undef WWDG +#undef IWDG +#undef I2S2ext +#undef SPI2 +#undef SPI3 +#undef I2S3ext +#undef USART2 +#undef USART3 +#undef UART4 +#undef UART5 +#undef I2C1 +#undef I2C2 +#undef I2C3 +#undef CAN1 +#undef CAN2 +#undef PWR +#undef DAC +#undef UART7 +#undef UART8 +#undef TIM1 +#undef TIM8 +#undef USART1 +#undef USART6 +#undef ADC +#undef ADC1 +#undef ADC2 +#undef ADC3 +#undef SDIO +#undef SPI1 +#undef SPI4 +#undef SYSCFG +#undef EXTI +#undef TIM9 +#undef TIM10 +#undef TIM11 +#undef SPI5 +#undef SPI6 +#undef SAI1 +#undef SAI1_Block_A +#undef SAI1_Block_B +#undef LTDC +#undef LTDC_Layer1 +#undef LTDC_Layer2 +#undef GPIOA +#undef GPIOB +#undef GPIOC +#undef GPIOD +#undef GPIOE +#undef GPIOF +#undef GPIOG +#undef GPIOH +#undef GPIOI +#undef GPIOJ +#undef GPIOK +#undef CRC +#undef RCC +#undef FLASH +#undef DMA1 +#undef DMA1_Stream0 +#undef DMA1_Stream1 +#undef DMA1_Stream2 +#undef DMA1_Stream3 +#undef DMA1_Stream4 +#undef DMA1_Stream5 +#undef DMA1_Stream6 +#undef DMA1_Stream7 +#undef DMA2 +#undef DMA2_Stream0 +#undef DMA2_Stream1 +#undef DMA2_Stream2 +#undef DMA2_Stream3 +#undef DMA2_Stream4 +#undef DMA2_Stream5 +#undef DMA2_Stream6 +#undef DMA2_Stream7 +#undef ETH +#undef DMA2D +#undef DCMI +#undef CRYP +#undef HASH +#undef HASH_DIGEST +#undef RNG +#undef FMC_Bank1 +#undef FMC_Bank1E +#undef FMC_Bank2_3 +#undef FMC_Bank4 +#undef FMC_Bank5_6 +#undef DBGMCU +#undef USB_OTG_FS +#undef USB_OTG_HS + +namespace ucoo { + +namespace reg { + +constexpr auto FPU = reinterpret_cast (FPU_BASE); +constexpr auto SysTick = reinterpret_cast (SysTick_BASE); + +constexpr auto TIM2 = reinterpret_cast (TIM2_BASE); +constexpr auto TIM3 = reinterpret_cast (TIM3_BASE); +constexpr auto TIM4 = reinterpret_cast (TIM4_BASE); +constexpr auto TIM5 = reinterpret_cast (TIM5_BASE); +constexpr auto TIM6 = reinterpret_cast (TIM6_BASE); +constexpr auto TIM7 = reinterpret_cast (TIM7_BASE); +constexpr auto TIM12 = reinterpret_cast (TIM12_BASE); +constexpr auto TIM13 = reinterpret_cast (TIM13_BASE); +constexpr auto TIM14 = reinterpret_cast (TIM14_BASE); +constexpr auto RTC = reinterpret_cast (RTC_BASE); +constexpr auto WWDG = reinterpret_cast (WWDG_BASE); +constexpr auto IWDG = reinterpret_cast (IWDG_BASE); +constexpr auto I2S2ext = reinterpret_cast (I2S2ext_BASE); +constexpr auto SPI2 = reinterpret_cast (SPI2_BASE); +constexpr auto SPI3 = reinterpret_cast (SPI3_BASE); +constexpr auto I2S3ext = reinterpret_cast (I2S3ext_BASE); +constexpr auto USART2 = reinterpret_cast (USART2_BASE); +constexpr auto USART3 = reinterpret_cast (USART3_BASE); +constexpr auto UART4 = reinterpret_cast (UART4_BASE); +constexpr auto UART5 = reinterpret_cast (UART5_BASE); +constexpr auto I2C1 = reinterpret_cast (I2C1_BASE); +constexpr auto I2C2 = reinterpret_cast (I2C2_BASE); +constexpr auto I2C3 = reinterpret_cast (I2C3_BASE); +constexpr auto CAN1 = reinterpret_cast (CAN1_BASE); +constexpr auto CAN2 = reinterpret_cast (CAN2_BASE); +constexpr auto PWR = reinterpret_cast (PWR_BASE); +constexpr auto DAC = reinterpret_cast (DAC_BASE); +constexpr auto UART7 = reinterpret_cast (UART7_BASE); +constexpr auto UART8 = reinterpret_cast (UART8_BASE); +constexpr auto TIM1 = reinterpret_cast (TIM1_BASE); +constexpr auto TIM8 = reinterpret_cast (TIM8_BASE); +constexpr auto USART1 = reinterpret_cast (USART1_BASE); +constexpr auto USART6 = reinterpret_cast (USART6_BASE); +constexpr auto ADC = reinterpret_cast (ADC_BASE); +constexpr auto ADC1 = reinterpret_cast (ADC1_BASE); +constexpr auto ADC2 = reinterpret_cast (ADC2_BASE); +constexpr auto ADC3 = reinterpret_cast (ADC3_BASE); +constexpr auto SDIO = reinterpret_cast (SDIO_BASE); +constexpr auto SPI1 = reinterpret_cast (SPI1_BASE) ; +constexpr auto SPI4 = reinterpret_cast (SPI4_BASE); +constexpr auto SYSCFG = reinterpret_cast (SYSCFG_BASE); +constexpr auto EXTI = reinterpret_cast (EXTI_BASE); +constexpr auto TIM9 = reinterpret_cast (TIM9_BASE); +constexpr auto TIM10 = reinterpret_cast (TIM10_BASE); +constexpr auto TIM11 = reinterpret_cast (TIM11_BASE); +constexpr auto SPI5 = reinterpret_cast (SPI5_BASE); +constexpr auto SPI6 = reinterpret_cast (SPI6_BASE); +constexpr auto SAI1 = reinterpret_cast (SAI1_BASE); +constexpr auto SAI1_Block_A = reinterpret_cast (SAI1_Block_A_BASE); +constexpr auto SAI1_Block_B = reinterpret_cast (SAI1_Block_B_BASE); +constexpr auto LTDC = reinterpret_cast (LTDC_BASE); +constexpr auto LTDC_Layer1 = reinterpret_cast (LTDC_Layer1_BASE); +constexpr auto LTDC_Layer2 = reinterpret_cast (LTDC_Layer2_BASE); +constexpr auto GPIOA = reinterpret_cast (GPIOA_BASE); +constexpr auto GPIOB = reinterpret_cast (GPIOB_BASE); +constexpr auto GPIOC = reinterpret_cast (GPIOC_BASE); +constexpr auto GPIOD = reinterpret_cast (GPIOD_BASE); +constexpr auto GPIOE = reinterpret_cast (GPIOE_BASE); +constexpr auto GPIOF = reinterpret_cast (GPIOF_BASE); +constexpr auto GPIOG = reinterpret_cast (GPIOG_BASE); +constexpr auto GPIOH = reinterpret_cast (GPIOH_BASE); +constexpr auto GPIOI = reinterpret_cast (GPIOI_BASE); +constexpr auto GPIOJ = reinterpret_cast (GPIOJ_BASE); +constexpr auto GPIOK = reinterpret_cast (GPIOK_BASE); +constexpr auto CRC = reinterpret_cast (CRC_BASE); +constexpr auto RCC = reinterpret_cast (RCC_BASE); +constexpr auto FLASH = reinterpret_cast (FLASH_R_BASE); +constexpr auto DMA1 = reinterpret_cast (DMA1_BASE); +constexpr auto DMA1_Stream0 = reinterpret_cast (DMA1_Stream0_BASE); +constexpr auto DMA1_Stream1 = reinterpret_cast (DMA1_Stream1_BASE); +constexpr auto DMA1_Stream2 = reinterpret_cast (DMA1_Stream2_BASE); +constexpr auto DMA1_Stream3 = reinterpret_cast (DMA1_Stream3_BASE); +constexpr auto DMA1_Stream4 = reinterpret_cast (DMA1_Stream4_BASE); +constexpr auto DMA1_Stream5 = reinterpret_cast (DMA1_Stream5_BASE); +constexpr auto DMA1_Stream6 = reinterpret_cast (DMA1_Stream6_BASE); +constexpr auto DMA1_Stream7 = reinterpret_cast (DMA1_Stream7_BASE); +constexpr auto DMA2 = reinterpret_cast (DMA2_BASE); +constexpr auto DMA2_Stream0 = reinterpret_cast (DMA2_Stream0_BASE); +constexpr auto DMA2_Stream1 = reinterpret_cast (DMA2_Stream1_BASE); +constexpr auto DMA2_Stream2 = reinterpret_cast (DMA2_Stream2_BASE); +constexpr auto DMA2_Stream3 = reinterpret_cast (DMA2_Stream3_BASE); +constexpr auto DMA2_Stream4 = reinterpret_cast (DMA2_Stream4_BASE); +constexpr auto DMA2_Stream5 = reinterpret_cast (DMA2_Stream5_BASE); +constexpr auto DMA2_Stream6 = reinterpret_cast (DMA2_Stream6_BASE); +constexpr auto DMA2_Stream7 = reinterpret_cast (DMA2_Stream7_BASE); +constexpr auto ETH = reinterpret_cast (ETH_BASE) ; +constexpr auto DMA2D = reinterpret_cast (DMA2D_BASE); +constexpr auto DCMI = reinterpret_cast (DCMI_BASE); +constexpr auto CRYP = reinterpret_cast (CRYP_BASE); +constexpr auto HASH = reinterpret_cast (HASH_BASE); +constexpr auto HASH_DIGEST = reinterpret_cast (HASH_DIGEST_BASE); +constexpr auto RNG = reinterpret_cast (RNG_BASE); +constexpr auto FMC_Bank1 = reinterpret_cast (FMC_Bank1_R_BASE); +constexpr auto FMC_Bank1E = reinterpret_cast (FMC_Bank1E_R_BASE); +constexpr auto FMC_Bank2_3 = reinterpret_cast (FMC_Bank2_3_R_BASE); +constexpr auto FMC_Bank4 = reinterpret_cast (FMC_Bank4_R_BASE); +constexpr auto FMC_Bank5_6 = reinterpret_cast (FMC_Bank5_6_R_BASE); +constexpr auto DBGMCU = reinterpret_cast (DBGMCU_BASE); +constexpr auto USB_OTG_FS = reinterpret_cast (USB_OTG_FS_PERIPH_BASE); +constexpr auto USB_OTG_HS = reinterpret_cast (USB_OTG_HS_PERIPH_BASE); +constexpr auto DESIG = reinterpret_cast (DESIG_BASE); + +} // namespace reg + +} // namespace ucoo + +#endif // ucoo_arch_reg_stm32f4_hh diff --git a/ucoo/arch/stm32f1/stm32f1.ld b/ucoo/arch/stm32f1/stm32f1.ld deleted file mode 100644 index de413c4..0000000 --- a/ucoo/arch/stm32f1/stm32f1.ld +++ /dev/null @@ -1,6 +0,0 @@ -MEMORY -{ - rom (rx) : ORIGIN = 0x08000000, LENGTH = 256K - ram (rwx) : ORIGIN = 0x20000000, LENGTH = 64K -} -INCLUDE libopencm3_stm32f1.ld diff --git a/ucoo/arch/stm32f4/stm32f4.ld b/ucoo/arch/stm32f4/stm32f4.ld deleted file mode 100644 index ceef7af..0000000 --- a/ucoo/arch/stm32f4/stm32f4.ld +++ /dev/null @@ -1,6 +0,0 @@ -MEMORY -{ - rom (rx) : ORIGIN = 0x08000000, LENGTH = 1024K - ram (rwx) : ORIGIN = 0x20000000, LENGTH = 128K -} -INCLUDE libopencm3_stm32f4.ld diff --git a/ucoo/arch/vector.arm.cc b/ucoo/arch/vector.arm.cc new file mode 100644 index 0000000..3e0922a --- /dev/null +++ b/ucoo/arch/vector.arm.cc @@ -0,0 +1,136 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/common.hh" +#include "ucoo/arch/interrupt.arm.hh" + +#if defined (TARGET_stm32f4) +# include "ucoo/arch/vector.stm32f4.hh" +#elif defined (TARGET_stm32f1) +# include "ucoo/arch/vector.stm32f1.hh" +#else +# error "not implemented for this target" +#endif + +extern uint32_t _stack; + +extern "C" void entry (); + +namespace ucoo { + +typedef void (*vector_t) (void); + +struct vector_table_t +{ + uint32_t *initial_stack; + vector_t exception[static_cast (Exception::COUNT) - 1]; + vector_t irq[static_cast (Irq::COUNT)]; +}; + +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); + +vector_table_t vector_table __attribute__ ((section (".vectors"))) = +{ + .initial_stack = &_stack, + { + entry, + interrupt, + interrupt, + interrupt, + interrupt, + interrupt, + nullptr, + nullptr, + nullptr, + nullptr, + interrupt, + nullptr, + nullptr, + interrupt, + interrupt + }, + { + UCOO_VECTOR_IRQ + } +}; + +extern "C" void +undefined_handler () +{ + halt (); +} + +} // namespace ucoo + +int main (); + +extern uint32_t _data_loadaddr; +extern uint32_t _data; +extern uint32_t _edata; +extern uint32_t _ebss; +extern uint32_t __preinit_array_start; +extern uint32_t __preinit_array_end; +extern uint32_t __init_array_start; +extern uint32_t __init_array_end; +extern uint32_t __fini_array_start; +extern uint32_t __fini_array_end; + +extern "C" void __attribute__ ((naked)) +entry () +{ + uint32_t *src = &_data_loadaddr, *dst = &_data; + while (dst < &_edata) + *dst++ = *src++; + while (dst < &_ebss) + *dst++ = 0; + SCB->CCR |= SCB_CCR_STKALIGN_Msk; +#if __FPU_USED + SCB->CPACR |= SCB_CPACR_CP10_Full | SCB_CPACR_CP11_Full; +#endif + uint32_t *fp; + fp = &__preinit_array_start; + while (fp < &__preinit_array_end) + reinterpret_cast (*fp++) (); + fp = &__init_array_start; + while (fp < &__init_array_end) + reinterpret_cast (*fp++) (); + main (); + fp = &__fini_array_start; + while (fp < &__fini_array_end) + reinterpret_cast (*fp++) (); +} diff --git a/ucoo/arch/vector.stm32f1.hh b/ucoo/arch/vector.stm32f1.hh new file mode 100644 index 0000000..39d3fd4 --- /dev/null +++ b/ucoo/arch/vector.stm32f1.hh @@ -0,0 +1,220 @@ +#ifndef ucoo_arch_vector_stm32f1_hh +#define ucoo_arch_vector_stm32f1_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +#define UCOO_VECTOR_IRQ \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + nullptr, \ + nullptr, \ + nullptr, \ + nullptr, \ + nullptr, \ + nullptr, \ + nullptr, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + nullptr, \ + nullptr, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt + +namespace ucoo { + +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); + +} // namespace ucoo + +#endif // ucoo_arch_vector_stm32f1_hh diff --git a/ucoo/arch/vector.stm32f4.hh b/ucoo/arch/vector.stm32f4.hh new file mode 100644 index 0000000..5fe1bae --- /dev/null +++ b/ucoo/arch/vector.stm32f4.hh @@ -0,0 +1,307 @@ +#ifndef ucoo_arch_vector_stm32f4_hh +#define ucoo_arch_vector_stm32f4_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +#define UCOO_VECTOR_IRQ \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt, \ + interrupt + +namespace ucoo { + +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); +template<> void interrupt () + __attribute__ ((weak, alias ("undefined_handler"))); + +} // namespace ucoo + +#endif // ucoo_arch_vector_stm32f4_hh diff --git a/ucoo/base/fs/romfs/test/Makefile b/ucoo/base/fs/romfs/test/Makefile index c4ec6bc..3a99d3d 100644 --- a/ucoo/base/fs/romfs/test/Makefile +++ b/ucoo/base/fs/romfs/test/Makefile @@ -4,7 +4,7 @@ TARGETS = host stm32f4 PROGS = test_romfs test_romfs_SOURCES = test_romfs.cc -MODULES = ucoo/base/test ucoo/base/fs/romfs ucoo/hal/usb +MODULES = ucoo/base/test ucoo/base/fs/romfs ucoo/hal/usb ucoo/hal/gpio COMPILE_DEPS = $(OBJDIR)/test_fs.h EXTRA_CLEAN = $(OBJDIR)/test_fs.h diff --git a/ucoo/base/proto/test/Makefile b/ucoo/base/proto/test/Makefile index 8c093f0..40469ac 100644 --- a/ucoo/base/proto/test/Makefile +++ b/ucoo/base/proto/test/Makefile @@ -4,6 +4,6 @@ TARGETS = host stm32f4 PROGS = test_proto test_proto_SOURCES = test_proto.cc -MODULES = ucoo/base/proto ucoo/base/test ucoo/hal/usb +MODULES = ucoo/base/proto ucoo/base/test ucoo/hal/usb ucoo/hal/gpio include $(BASE)/build/top.mk diff --git a/ucoo/base/test/Config b/ucoo/base/test/Config index 703bbee..e7ec1b9 100644 --- a/ucoo/base/test/Config +++ b/ucoo/base/test/Config @@ -1,9 +1,13 @@ [ucoo/base/test] +# Before test startup, wait for a key to be pressed. wait = false -test_stream_uart = -1 +# Use USB or UART as test stream? +test_stream_use_usb = true +test_stream_uart_instance = Uart::Instance::USART1 test_stream_uart_speed = 115200 -test_stream_vendor = "APBTeam" -test_stream_product = "test" +test_stream_usb_instance = UsbDriverDwcOtg::Instance::OTG_FS +test_stream_usb_vendor = u"APBTeam" +test_stream_usb_product = u"test" [ucoo/base/test:stm32] wait = true diff --git a/ucoo/base/test/test.stm32.cc b/ucoo/base/test/test.stm32.cc index cb47db3..3d7a9fa 100644 --- a/ucoo/base/test/test.stm32.cc +++ b/ucoo/base/test/test.stm32.cc @@ -24,8 +24,9 @@ #include "test.hh" #include "config/ucoo/base/test.hh" -#if CONFIG_UCOO_BASE_TEST_TEST_STREAM_UART == -1 +#if CONFIG_UCOO_BASE_TEST_TEST_STREAM_USE_USB # include "ucoo/hal/usb/usb.hh" +# include "ucoo/hal/usb/usb_cdc.hh" #else # include "ucoo/hal/uart/uart.hh" #endif @@ -37,15 +38,27 @@ namespace ucoo { Stream & test_stream (void) { -#if CONFIG_UCOO_BASE_TEST_TEST_STREAM_UART == -1 - static UsbStreamControl usc (CONFIG_UCOO_BASE_TEST_TEST_STREAM_VENDOR, - CONFIG_UCOO_BASE_TEST_TEST_STREAM_PRODUCT); - static UsbStream us (usc, 0); - usc.enable (); - return us; -#else - static Uart u (CONFIG_UCOO_BASE_TEST_TEST_STREAM_UART); static bool enabled = false; +#if CONFIG_UCOO_BASE_TEST_TEST_STREAM_USE_USB + static const auto string_descs_pack = usb_descs_pack ( + usb_string_desc (USB_LANGUAGE_EN_US), + usb_string_desc (CONFIG_UCOO_BASE_TEST_TEST_STREAM_USB_VENDOR), + usb_string_desc (CONFIG_UCOO_BASE_TEST_TEST_STREAM_USB_PRODUCT)); + static const auto string_descs = ucoo::usb_descs (string_descs_pack); + static UsbDriverDwcOtg driver ( + CONFIG_UCOO_BASE_TEST_TEST_STREAM_USB_INSTANCE, + usb_cdc_default_device_desc (), + usb_cdc_default_configuration_desc (), + string_descs); + static UsbApplicationCdcAcm cdc (driver); + if (!enabled) + { + enabled = true; + driver.enable (); + } + return cdc; +#else + static Uart u (CONFIG_UCOO_BASE_TEST_TEST_STREAM_UART_INSTANCE); if (!enabled) { enabled = true; diff --git a/ucoo/base/test/test/Makefile b/ucoo/base/test/test/Makefile index 64e5021..b4293ec 100644 --- a/ucoo/base/test/test/Makefile +++ b/ucoo/base/test/test/Makefile @@ -4,6 +4,6 @@ TARGETS = host stm32f4 PROGS = test_test test_test_SOURCES = test_test.cc -MODULES = ucoo/utils ucoo/base/test ucoo/hal/usb +MODULES = ucoo/utils ucoo/base/test ucoo/hal/usb ucoo/hal/gpio include $(BASE)/build/top.mk diff --git a/ucoo/common.hh b/ucoo/common.hh index 708b9a1..5e982d8 100644 --- a/ucoo/common.hh +++ b/ucoo/common.hh @@ -100,6 +100,10 @@ assert_perror (bool condition) halt_perror (); } +/// Give some time to other tasks when running in a tight loop. +void +yield (); + /// Get array length at compile time. template constexpr int diff --git a/ucoo/dev/avrisp/test/test_avrisp.cc b/ucoo/dev/avrisp/test/test_avrisp.cc index f38cf52..319a6b8 100644 --- a/ucoo/dev/avrisp/test/test_avrisp.cc +++ b/ucoo/dev/avrisp/test/test_avrisp.cc @@ -29,8 +29,6 @@ #include "ucoo/hal/spi/spi_soft.hh" #include "ucoo/utils/delay.hh" -#include - class TestAvrIspIntf : public ucoo::AvrIspIntf { public: @@ -76,10 +74,10 @@ main (int argc, const char **argv) { ucoo::arch_init (argc, argv); ucoo::Stream &ts = ucoo::test_stream (); - rcc_peripheral_enable_clock (&RCC_AHB1ENR, RCC_AHB1ENR_IOPDEN - | RCC_AHB1ENR_IOPEEN); - ucoo::Gpio reset (GPIOD, 10), sck (GPIOE, 7), mosi (GPIOD, 8), - miso (GPIOD, 9); + ucoo::GPIOD.enable (); + ucoo::GPIOE.enable (); + ucoo::Gpio reset (ucoo::GPIOD[10]), sck (ucoo::GPIOE[7]), + mosi (ucoo::GPIOD[8]), miso (ucoo::GPIOD[9]); TestAvrIspIntf intf (reset, sck, mosi, miso); ucoo::AvrIsp isp (intf); ucoo::AvrIspProto proto (isp); diff --git a/ucoo/dev/lcd/test/test_lcd.cc b/ucoo/dev/lcd/test/test_lcd.cc index c654287..d4c0f79 100644 --- a/ucoo/dev/lcd/test/test_lcd.cc +++ b/ucoo/dev/lcd/test/test_lcd.cc @@ -29,19 +29,17 @@ #include "ucoo/arch/arch.hh" -#include - int main (int argc, const char **argv) { ucoo::arch_init (argc, argv); - rcc_periph_clock_enable (RCC_GPIOB); - ucoo::Gpio rw (GPIOB, 10); - ucoo::Gpio rs (GPIOB, 11); - ucoo::Gpio cs (GPIOB, 12); - ucoo::Gpio sck (GPIOB, 13); - ucoo::Gpio miso (GPIOB, 14); - ucoo::Gpio mosi (GPIOB, 15); + ucoo::GPIOB.enable (); + ucoo::Gpio rw (ucoo::GPIOB[10]); + ucoo::Gpio rs (ucoo::GPIOB[11]); + ucoo::Gpio cs (ucoo::GPIOB[12]); + ucoo::Gpio sck (ucoo::GPIOB[13]); + ucoo::Gpio miso (ucoo::GPIOB[14]); + ucoo::Gpio mosi (ucoo::GPIOB[15]); ucoo::SpiSoftMaster spi (sck, mosi, miso); rw.reset (); rw.output (); diff --git a/ucoo/hal/adc/adc_hard.stm32f4.cc b/ucoo/hal/adc/adc_hard.stm32f4.cc index c89d610..ef56fd3 100644 --- a/ucoo/hal/adc/adc_hard.stm32f4.cc +++ b/ucoo/hal/adc/adc_hard.stm32f4.cc @@ -21,23 +21,17 @@ // DEALINGS IN THE SOFTWARE. // // }}} -#include "adc_hard.stm32f4.hh" - -#include -#include +#include "ucoo/hal/adc/adc_hard.stm32f4.hh" namespace ucoo { -static const enum rcc_periph_clken adc_clken[] = { - RCC_ADC1, RCC_ADC2, RCC_ADC3 -}; +static ADC_TypeDef * const bases[] = { reg::ADC1, reg::ADC2, reg::ADC3 }; +static const Rcc rccs[] = { Rcc::ADC1, Rcc::ADC2, Rcc::ADC3 }; -AdcHard::AdcHard (int n) - : n_ (n) +AdcHard::AdcHard (AdcHard::Instance inst) + : base_ (bases[static_cast (inst)]), + rcc_ (rccs[static_cast (inst)]) { - static const uint32_t bases[] = { ADC1, ADC2, ADC3 }; - assert (n < (int) lengthof (bases)); - base_ = bases[n]; } AdcHard::~AdcHard () @@ -48,26 +42,26 @@ AdcHard::~AdcHard () void AdcHard::enable () { - rcc_periph_clock_enable (adc_clken[n_]); - ADC_CR2 (base_) = ADC_CR2_ADON; + rcc_peripheral_clock_enable (rcc_); + base_->CR2 = ADC_CR2_ADON; } void AdcHard::disable () { - ADC_CR2 (base_) = 0; - rcc_periph_clock_disable (adc_clken[n_]); + base_->CR2 = 0; + rcc_peripheral_clock_disable (rcc_); } int AdcHard::read (int channel) { - ADC_SQR3 (base_) = channel; - ADC_CR2 (base_) |= ADC_CR2_SWSTART; - while (!(ADC_SR (base_) & ADC_SR_EOC)) + base_->SQR3 = channel; + base_->CR2 |= ADC_CR2_SWSTART; + while (!(base_->SR & ADC_SR_EOC)) yield (); - ADC_SR (base_) = ~ADC_SR_EOC; - return ADC_DR (base_); + base_->SR = ~ADC_SR_EOC; + return base_->DR; } } // namespace ucoo diff --git a/ucoo/hal/adc/adc_hard.stm32f4.hh b/ucoo/hal/adc/adc_hard.stm32f4.hh index 7ab0d7f..3947b7c 100644 --- a/ucoo/hal/adc/adc_hard.stm32f4.hh +++ b/ucoo/hal/adc/adc_hard.stm32f4.hh @@ -26,17 +26,47 @@ #include "ucoo/intf/adc.hh" #include "ucoo/common.hh" +#include "ucoo/arch/reg.hh" +#include "ucoo/arch/rcc.stm32.hh" + namespace ucoo { +class AdcHard; + +/// Single ADC channel. +class AdcHardChannel : public Adc +{ + public: + /// See Adc::read. + inline int read (); + /// See Adc::get_resolution. + inline int get_resolution () const; + private: + /// Constructor. + AdcHardChannel (AdcHard &adc, int channel) + : adc_ (adc), channel_ (channel) { } + friend AdcHard; + private: + AdcHard &adc_; + int channel_; +}; + /// ADC interface. This control a full ADC, use AdcHardChannel for a single /// channel. class AdcHard { public: static const int resolution = 1 << 12; + /// Available ADC. + enum class Instance + { + ADC1, + ADC2, + ADC3, + }; public: - /// Constructor for the Nth ADC. - AdcHard (int n); + /// Constructor for an ADC instance. + AdcHard (Instance inst); /// Shutdown. ~AdcHard (); /// Enable, power on. @@ -45,28 +75,27 @@ class AdcHard void disable (); /// Make a single measure. int read (int channel); + /// Return an ADC channel. + AdcHardChannel operator[] (int channel) + { return AdcHardChannel (*this, channel); } private: - /// ADC index. - int n_; /// ADC base address. - uint32_t base_; + ADC_TypeDef * const base_; + /// ADC RCC identifier. + const Rcc rcc_; }; -/// Single ADC channel. -class AdcHardChannel : public Adc +inline int +AdcHardChannel::read () { - public: - /// Constructor. - AdcHardChannel (AdcHard &adc, int channel) - : adc_ (adc), channel_ (channel) { } - /// See Adc::read. - int read () { return adc_.read (channel_); } - /// See Adc::get_resolution. - int get_resolution () const { return AdcHard::resolution; } - private: - AdcHard &adc_; - int channel_; -}; + return adc_.read (channel_); +} + +inline int +AdcHardChannel::get_resolution () const +{ + return AdcHard::resolution; +} } // namespace ucoo diff --git a/ucoo/hal/adc/test/Makefile b/ucoo/hal/adc/test/Makefile index c6c8a3c..603093d 100644 --- a/ucoo/hal/adc/test/Makefile +++ b/ucoo/hal/adc/test/Makefile @@ -4,6 +4,6 @@ TARGETS = stm32f4 stm32f4_PROGS = test_adc test_adc_SOURCES = test_adc.cc -MODULES = ucoo/hal/adc ucoo/base/test ucoo/hal/usb ucoo/utils +MODULES = ucoo/hal/adc ucoo/base/test ucoo/hal/usb ucoo/hal/gpio ucoo/utils include $(BASE)/build/top.mk diff --git a/ucoo/hal/adc/test/test_adc.cc b/ucoo/hal/adc/test/test_adc.cc index 58cca96..03825da 100644 --- a/ucoo/hal/adc/test/test_adc.cc +++ b/ucoo/hal/adc/test/test_adc.cc @@ -26,9 +26,8 @@ #include "ucoo/arch/arch.hh" #include "ucoo/base/test/test.hh" #include "ucoo/utils/delay.hh" - -#include #include "ucoo/hal/gpio/gpio.hh" +#include "ucoo/arch/reg.hh" #include @@ -38,10 +37,10 @@ main (int argc, const char **argv) ucoo::arch_init (argc, argv); ucoo::test_stream_setup (); // Have fun with temperature sensor. - ucoo::AdcHard adc (0); + ucoo::AdcHard adc (ucoo::AdcHard::Instance::ADC1); adc.enable (); - ADC_CCR = ADC_CCR_TSVREFE; - ucoo::AdcHardChannel c (adc, 16); + ucoo::reg::ADC->CCR = ADC_CCR_TSVREFE; + ucoo::AdcHardChannel c = adc[16]; while (1) { int r = c.read (); diff --git a/ucoo/hal/exti/exti.stm32.cc b/ucoo/hal/exti/exti.stm32.cc index 120b25e..c3e6064 100644 --- a/ucoo/hal/exti/exti.stm32.cc +++ b/ucoo/hal/exti/exti.stm32.cc @@ -22,13 +22,8 @@ // // }}} #include "ucoo/hal/exti/exti.stm32.hh" -#include "ucoo/common.hh" -#include -#include -#if !defined AFIO_BASE -# include -#endif +#include "ucoo/arch/interrupt.arm.hh" namespace ucoo { @@ -36,7 +31,7 @@ namespace ucoo { struct exti_hardware_t { /// Corresponding IRQ. - int irq; + Irq irq; /// Pointer to number of enabled EXTI for shared vectors, or NULL. int *enabled; }; @@ -48,109 +43,101 @@ static int exti_vector_9_5_enabled; static int exti_vector_15_10_enabled; /// Information on EXTI hardware array. -struct exti_hardware_t exti_hardware[] = -{ - { NVIC_EXTI0_IRQ, nullptr }, - { NVIC_EXTI1_IRQ, nullptr }, - { NVIC_EXTI2_IRQ, nullptr }, - { NVIC_EXTI3_IRQ, nullptr }, - { NVIC_EXTI4_IRQ, nullptr }, - { NVIC_EXTI9_5_IRQ, &exti_vector_9_5_enabled }, - { NVIC_EXTI9_5_IRQ, &exti_vector_9_5_enabled }, - { NVIC_EXTI9_5_IRQ, &exti_vector_9_5_enabled }, - { NVIC_EXTI9_5_IRQ, &exti_vector_9_5_enabled }, - { NVIC_EXTI9_5_IRQ, &exti_vector_9_5_enabled }, - { NVIC_EXTI15_10_IRQ, &exti_vector_15_10_enabled }, - { NVIC_EXTI15_10_IRQ, &exti_vector_15_10_enabled }, - { NVIC_EXTI15_10_IRQ, &exti_vector_15_10_enabled }, - { NVIC_EXTI15_10_IRQ, &exti_vector_15_10_enabled }, - { NVIC_EXTI15_10_IRQ, &exti_vector_15_10_enabled }, - { NVIC_EXTI15_10_IRQ, &exti_vector_15_10_enabled }, +static const exti_hardware_t exti_hardware[] = +{ + { Irq::EXTI0, nullptr }, + { Irq::EXTI1, nullptr }, + { Irq::EXTI2, nullptr }, + { Irq::EXTI3, nullptr }, + { Irq::EXTI4, nullptr }, + { Irq::EXTI9_5, &exti_vector_9_5_enabled }, + { Irq::EXTI9_5, &exti_vector_9_5_enabled }, + { Irq::EXTI9_5, &exti_vector_9_5_enabled }, + { Irq::EXTI9_5, &exti_vector_9_5_enabled }, + { Irq::EXTI9_5, &exti_vector_9_5_enabled }, + { Irq::EXTI15_10, &exti_vector_15_10_enabled }, + { Irq::EXTI15_10, &exti_vector_15_10_enabled }, + { Irq::EXTI15_10, &exti_vector_15_10_enabled }, + { Irq::EXTI15_10, &exti_vector_15_10_enabled }, + { Irq::EXTI15_10, &exti_vector_15_10_enabled }, + { Irq::EXTI15_10, &exti_vector_15_10_enabled }, }; static ExtiHard *exti_instances[lengthof (exti_hardware)]; -} // namespace ucoo - -extern "C" { - // If the interrupt is triggered again after being cleared, the interrupt // handler will be reentered. There is no need to loop. -void -exti0_isr () +template<> +void interrupt () { - EXTI_PR = 1u << 0; - assert (ucoo::exti_instances[0]); - ucoo::exti_instances[0]->isr (); + reg::EXTI->PR = 1u << 0; + assert (exti_instances[0]); + exti_instances[0]->isr (); } -void -exti1_isr () +template<> +void interrupt () { - EXTI_PR = 1u << 1; - assert (ucoo::exti_instances[1]); - ucoo::exti_instances[1]->isr (); + reg::EXTI->PR = 1u << 1; + assert (exti_instances[1]); + exti_instances[1]->isr (); } -void -exti2_isr () +template<> +void interrupt () { - EXTI_PR = 1u << 2; - assert (ucoo::exti_instances[2]); - ucoo::exti_instances[2]->isr (); + reg::EXTI->PR = 1u << 2; + assert (exti_instances[2]); + exti_instances[2]->isr (); } -void -exti3_isr () +template<> +void interrupt () { - EXTI_PR = 1u << 3; - assert (ucoo::exti_instances[3]); - ucoo::exti_instances[3]->isr (); + reg::EXTI->PR = 1u << 3; + assert (exti_instances[3]); + exti_instances[3]->isr (); } -void -exti4_isr () +template<> +void interrupt () { - EXTI_PR = 1u << 4; - assert (ucoo::exti_instances[4]); - ucoo::exti_instances[4]->isr (); + reg::EXTI->PR = 1u << 4; + assert (exti_instances[4]); + exti_instances[4]->isr (); } -void -exti9_5_isr () +template<> +void interrupt () { - uint32_t pending = EXTI_PR & 0x3e0; - EXTI_PR = pending; + uint32_t pending = reg::EXTI->PR & 0x3e0; + reg::EXTI->PR = pending; for (int i = 5; i <= 9; i++) { if (pending & (1u << i)) { - assert (ucoo::exti_instances[i]); - ucoo::exti_instances[i]->isr (); + assert (exti_instances[i]); + exti_instances[i]->isr (); } } } -void -exti15_10_isr () +template<> +void interrupt () { - uint32_t pending = EXTI_PR & 0xfc00; - EXTI_PR = pending; + uint32_t pending = reg::EXTI->PR & 0xfc00; + reg::EXTI->PR = pending; for (int i = 10; i <= 15; i++) { if (pending & (1u << i)) { - assert (ucoo::exti_instances[i]); - ucoo::exti_instances[i]->isr (); + assert (exti_instances[i]); + exti_instances[i]->isr (); } } } -} - -namespace ucoo { - ExtiHard::ExtiHard (int n) : n_ (n) { @@ -169,14 +156,14 @@ void ExtiHard::enable () { assert (handler_); - EXTI_IMR |= 1u << n_; + reg::EXTI->IMR |= 1u << n_; if (exti_hardware[n_].enabled) { if (!(*exti_hardware[n_].enabled)++) - nvic_enable_irq (exti_hardware[n_].irq); + interrupt_enable (exti_hardware[n_].irq); } else - nvic_enable_irq (exti_hardware[n_].irq); + interrupt_enable (exti_hardware[n_].irq); } void @@ -185,20 +172,20 @@ ExtiHard::disable () if (exti_hardware[n_].enabled) { if (!--*exti_hardware[n_].enabled) - nvic_disable_irq (exti_hardware[n_].irq); + interrupt_disable (exti_hardware[n_].irq); } else - nvic_disable_irq (exti_hardware[n_].irq); - EXTI_IMR &= ~(1u << n_); - EXTI_PR = 1u << n_; + interrupt_disable (exti_hardware[n_].irq); + reg::EXTI->IMR &= ~(1u << n_); + reg::EXTI->PR = 1u << n_; } bool ExtiHard::mask () { - bool irq_enabled = nvic_get_irq_enabled (exti_hardware[n_].irq); + bool irq_enabled = interrupt_is_enabled (exti_hardware[n_].irq); if (irq_enabled) - nvic_disable_irq (exti_hardware[n_].irq); + interrupt_disable (exti_hardware[n_].irq); return irq_enabled; } @@ -206,7 +193,7 @@ void ExtiHard::unmask (bool saved_state) { if (saved_state) - nvic_enable_irq (exti_hardware[n_].irq); + interrupt_enable (exti_hardware[n_].irq); } void @@ -215,74 +202,32 @@ ExtiHard::set_trigger (Edge edge) switch (edge) { case Edge::RISING: - EXTI_RTSR |= 1u << n_; - EXTI_FTSR &= ~(1u << n_); + reg::EXTI->RTSR |= 1u << n_; + reg::EXTI->FTSR &= ~(1u << n_); break; case Edge::FALLING: - EXTI_RTSR &= ~(1u << n_); - EXTI_FTSR |= 1u << n_; + reg::EXTI->RTSR &= ~(1u << n_); + reg::EXTI->FTSR |= 1u << n_; break; case Edge::BOTH: - EXTI_RTSR |= (1u << n_); - EXTI_FTSR |= (1u << n_); + reg::EXTI->RTSR |= (1u << n_); + reg::EXTI->FTSR |= (1u << n_); break; } } void -ExtiHard::set_source (uint32_t gpio_port) +ExtiHard::set_source (const GpioPort &gpio_port) { - int port; - switch (gpio_port) - { - case GPIOA: - port = 0; - break; - case GPIOB: - port = 1; - break; - case GPIOC: - port = 2; - break; - case GPIOD: - port = 3; - break; -#if defined GPIOE - case GPIOE: - port = 4; - break; -#endif -#if defined GPIOF - case GPIOF: - port = 5; - break; -#endif -#if defined GPIOG - case GPIOG: - port = 6; - break; -#endif -#if defined GPIOH - case GPIOH: - port = 7; - break; -#endif -#if defined GPIOI - case GPIOI: - port = 8; - break; -#endif - default: - assert_unreachable (); - } + int port = gpio_port.get_port_index (); int shift = (n_ % 4) * 4; int reg = n_ / 4; uint32_t mask = 0xf << shift; uint32_t bits = port << shift; #if defined AFIO_BASE - AFIO_EXTICR (reg) = (AFIO_EXTICR (reg) & ~mask) | bits; + reg::AFIO->EXTICR[reg] = (reg::AFIO->EXTICR[reg] & ~mask) | bits; #else - SYSCFG_EXTICR (reg) = (SYSCFG_EXTICR (reg) & ~mask) | bits; + reg::SYSCFG->EXTICR[reg] = (reg::SYSCFG->EXTICR[reg] & ~mask) | bits; #endif } diff --git a/ucoo/hal/exti/exti.stm32.hh b/ucoo/hal/exti/exti.stm32.hh index 90a8098..3ac20d2 100644 --- a/ucoo/hal/exti/exti.stm32.hh +++ b/ucoo/hal/exti/exti.stm32.hh @@ -24,8 +24,8 @@ // // }}} #include "ucoo/intf/exti.hh" - -#include +#include "ucoo/hal/gpio/gpio.hh" +#include "ucoo/arch/interrupt.arm.hh" namespace ucoo { @@ -43,9 +43,9 @@ class ExtiHard : public Exti /// Constructor, take the EXTI number. ExtiHard (int n); /// Constructor, shortcut to set source. - ExtiHard (uint32_t gpio_port, int n); + ExtiHard (const GpioPort &gpio_port, int n); /// Constructor, shortcut to set source and trigger. - ExtiHard (uint32_t gpio_port, int n, Edge edge); + ExtiHard (const GpioPort &gpio_port, int n, Edge edge); /// Destructor. ~ExtiHard (); /// See Exti::enable. @@ -59,23 +59,26 @@ class ExtiHard : public Exti /// Set trigger edge. void set_trigger (Edge edge); /// Select source, take GPIO port address. - void set_source (uint32_t gpio_port); + void set_source (const GpioPort &gpio_port); + private: /// Handle interrupts. void isr (); + template + friend void interrupt (); private: /// EXTI line number. int n_; }; inline -ExtiHard::ExtiHard (uint32_t gpio_port, int n) +ExtiHard::ExtiHard (const GpioPort &gpio_port, int n) : ExtiHard (n) { set_source (gpio_port); } inline -ExtiHard::ExtiHard (uint32_t gpio_port, int n, Edge edge) +ExtiHard::ExtiHard (const GpioPort &gpio_port, int n, Edge edge) : ExtiHard (gpio_port, n) { set_trigger (edge); diff --git a/ucoo/hal/exti/test/test_exti.stm32.cc b/ucoo/hal/exti/test/test_exti.stm32.cc index 57b80a5..3aee1b0 100644 --- a/ucoo/hal/exti/test/test_exti.stm32.cc +++ b/ucoo/hal/exti/test/test_exti.stm32.cc @@ -26,8 +26,6 @@ #include "ucoo/hal/exti/exti.hh" #include "ucoo/utils/delay.hh" -#include - ucoo::Gpio *led4p; void @@ -40,16 +38,16 @@ int main (int argc, const char **argv) { ucoo::arch_init (argc, argv); - rcc_periph_clock_enable (RCC_GPIOB); - rcc_periph_clock_enable (RCC_GPIOD); + ucoo::GPIOB.enable (); + ucoo::GPIOD.enable (); // For this test, shorten B6 & B7 to have loopback. - ucoo::Gpio loop_out (GPIOB, 6); - ucoo::Gpio loop_in (GPIOB, 7); - ucoo::Gpio led3 (GPIOD, 13); - ucoo::Gpio led4 (GPIOD, 12); + ucoo::Gpio loop_out (ucoo::GPIOB[6]); + ucoo::Gpio loop_in (ucoo::GPIOB[7]); + ucoo::Gpio led3 (ucoo::GPIOD[13]); + ucoo::Gpio led4 (ucoo::GPIOD[12]); led4p = &led4; ucoo::ExtiHard exti (7); - exti.set_source (GPIOB); + exti.set_source (ucoo::GPIOB); exti.set_trigger (ucoo::ExtiHard::Edge::RISING); exti.register_event (led4_handler); exti.enable (); diff --git a/ucoo/hal/gpio/gpio.stm32f1.cc b/ucoo/hal/gpio/gpio.stm32f1.cc index 0e6c7ee..4927b4e 100644 --- a/ucoo/hal/gpio/gpio.stm32f1.cc +++ b/ucoo/hal/gpio/gpio.stm32f1.cc @@ -21,30 +21,37 @@ // DEALINGS IN THE SOFTWARE. // // }}} -#include "gpio.stm32f1.hh" +#include "ucoo/hal/gpio/gpio.stm32f1.hh" +#include "ucoo/common.hh" namespace ucoo { +GpioPort GPIOA (reg::GPIOA, Rcc::GPIOA); +GpioPort GPIOB (reg::GPIOB, Rcc::GPIOB); +GpioPort GPIOC (reg::GPIOC, Rcc::GPIOC); +GpioPort GPIOD (reg::GPIOD, Rcc::GPIOD); +GpioPort GPIOE (reg::GPIOE, Rcc::GPIOE); + void Gpio::set () { - GPIO_BSRR (port_) = mask_; + port_->BSRR = mask_; } void Gpio::reset () { - GPIO_BRR (port_) = mask_; + port_->BRR = mask_; } /// Helper to avoid virtual dance. static inline void -Gpio_set (uint32_t port, uint16_t mask, bool state) +Gpio_set (GPIO_TypeDef *port, uint16_t mask, bool state) { if (state) - GPIO_BSRR (port) = mask; + port->BSRR = mask; else - GPIO_BRR (port) = mask; + port->BRR = mask; } void @@ -57,13 +64,13 @@ void Gpio::toggle () { // Avoid read/modify/write ODR, to achieve atomic operation. - Gpio_set (port_, mask_, !(GPIO_ODR (port_) & mask_)); + Gpio_set (port_, mask_, !(port_->ODR & mask_)); } bool Gpio::get () const { - return GPIO_IDR (port_) & mask_; + return port_->IDR & mask_; } /// Set four bits in a register for the corresponding one-bit mask. @@ -81,11 +88,11 @@ qmask_set (uint32_t mask, uint32_t reg, uint32_t bits) void Gpio::input () { - uint32_t cnf_mode = static_cast (input_cnf_) | GPIO_MODE_INPUT; + uint32_t cnf_mode = static_cast (input_cnf_) | GPIO_MODE_Input; if (mask_ & 0xff) - GPIO_CRL (port_) = qmask_set (mask_, GPIO_CRL (port_), cnf_mode); + port_->CRL = qmask_set (mask_, port_->CRL, cnf_mode); else - GPIO_CRH (port_) = qmask_set (mask_ >> 8, GPIO_CRH (port_), cnf_mode); + port_->CRH = qmask_set (mask_ >> 8, port_->CRH, cnf_mode); output_ = false; } @@ -95,9 +102,9 @@ Gpio::output () uint32_t cnf_mode = static_cast (output_cnf_) | static_cast (speed_); if (mask_ & 0xff) - GPIO_CRL (port_) = qmask_set (mask_, GPIO_CRL (port_), cnf_mode); + port_->CRL = qmask_set (mask_, port_->CRL, cnf_mode); else - GPIO_CRH (port_) = qmask_set (mask_ >> 8, GPIO_CRH (port_), cnf_mode); + port_->CRH = qmask_set (mask_ >> 8, port_->CRH, cnf_mode); output_ = true; } @@ -125,4 +132,31 @@ Gpio::speed (Speed s) output (); } +int +GpioPort::get_port_index () const +{ + int port; + switch (rcc_) + { + case Rcc::GPIOA: + port = 0; + break; + case Rcc::GPIOB: + port = 1; + break; + case Rcc::GPIOC: + port = 2; + break; + case Rcc::GPIOD: + port = 3; + break; + case Rcc::GPIOE: + port = 4; + break; + default: + assert_unreachable (); + } + return port; +} + } // namespace ucoo diff --git a/ucoo/hal/gpio/gpio.stm32f1.hh b/ucoo/hal/gpio/gpio.stm32f1.hh index 7673998..0cc9bf1 100644 --- a/ucoo/hal/gpio/gpio.stm32f1.hh +++ b/ucoo/hal/gpio/gpio.stm32f1.hh @@ -24,38 +24,38 @@ // // }}} #include "ucoo/intf/io.hh" - -#include +#include "ucoo/arch/reg.hh" +#include "ucoo/arch/rcc.stm32.hh" namespace ucoo { +class GpioPort; + /// General purpose input/output on STM32F1. class Gpio : public Io { public: enum class InputCnf : uint8_t { - ANALOG = GPIO_CNF_INPUT_ANALOG << 2, - FLOAT = GPIO_CNF_INPUT_FLOAT << 2, + ANALOG = GPIO_CNF_Input_Analog << 2, + FLOAT = GPIO_CNF_Input_Float << 2, // Up/Down is selected using output register. - PULL_UPDOWN = GPIO_CNF_INPUT_PULL_UPDOWN << 2, + PULL_UPDOWN = GPIO_CNF_Input_PullUpDown << 2, }; enum class OutputCnf : uint8_t { - PUSHPULL = GPIO_CNF_OUTPUT_PUSHPULL << 2, - OPENDRAIN = GPIO_CNF_OUTPUT_OPENDRAIN << 2, - ALTFN_PUSH_PULL = GPIO_CNF_OUTPUT_ALTFN_PUSHPULL << 2, - ALTFN_OPENDRAIN = GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN << 2, + PUSHPULL = GPIO_CNF_Output_PushPull << 2, + OPENDRAIN = GPIO_CNF_Output_OpenDrain << 2, + AF_PUSH_PULL = GPIO_CNF_Output_AFPushPull << 2, + AF_OPENDRAIN = GPIO_CNF_Output_AFOpenDrain << 2, }; enum class Speed : uint8_t { - SPEED_2MHZ = GPIO_MODE_OUTPUT_2_MHZ, - SPEED_10MHZ = GPIO_MODE_OUTPUT_10_MHZ, - SPEED_50MHZ = GPIO_MODE_OUTPUT_50_MHZ, + SPEED_2MHZ = GPIO_MODE_Output_2MHz, + SPEED_10MHZ = GPIO_MODE_Output_10MHz, + SPEED_50MHZ = GPIO_MODE_Output_50MHz, }; public: - /// Constructor, take the PORT base address, and pin BIT number. - Gpio (uint32_t port, int bit); /// See Io::set. void set (); /// See Io::reset. @@ -76,9 +76,13 @@ class Gpio : public Io void output_cnf (OutputCnf cnf); /// Set output speed. void speed (Speed s); + private: + /// Constructor, take a port, and pin BIT number. + Gpio (GpioPort &port, int bit); + friend GpioPort; private: /// Port register base address. - const uint32_t port_; + GPIO_TypeDef * const port_; /// IO bitmask. const uint16_t mask_; /// Configuration for input. @@ -91,9 +95,32 @@ class Gpio : public Io bool output_; }; +/// General purpose input/output port on STM32F1. +class GpioPort +{ + GPIO_TypeDef * const port_; + const Rcc rcc_; + friend class Gpio; + public: + /// Constructor. + GpioPort (GPIO_TypeDef *port, Rcc rcc) + : port_ (port), rcc_ (rcc) { } + /// Enable port. + void enable () + { rcc_peripheral_clock_enable (rcc_); } + /// Disable port. + void disable () + { rcc_peripheral_clock_disable (rcc_); } + /// Return a port GPIO. + Gpio operator[] (int bit) + { return Gpio (*this, bit); } + /// Get the index of the port, used with Exti. + int get_port_index () const; +}; + inline -Gpio::Gpio (uint32_t port, int bit) - : port_ (port), mask_ (1u << bit), +Gpio::Gpio (GpioPort &port, int bit) + : port_ (port.port_), mask_ (1u << bit), input_cnf_ (InputCnf::FLOAT), output_cnf_ (OutputCnf::PUSHPULL), speed_ (Speed::SPEED_10MHZ), @@ -101,6 +128,12 @@ Gpio::Gpio (uint32_t port, int bit) { } +extern GpioPort GPIOA; +extern GpioPort GPIOB; +extern GpioPort GPIOC; +extern GpioPort GPIOD; +extern GpioPort GPIOE; + } // namespace ucoo #endif // ucoo_hal_gpio_gpio_stm32f1_hh diff --git a/ucoo/hal/gpio/gpio.stm32f4.cc b/ucoo/hal/gpio/gpio.stm32f4.cc index 60e3231..1640b03 100644 --- a/ucoo/hal/gpio/gpio.stm32f4.cc +++ b/ucoo/hal/gpio/gpio.stm32f4.cc @@ -21,30 +21,43 @@ // DEALINGS IN THE SOFTWARE. // // }}} -#include "gpio.stm32f4.hh" +#include "ucoo/hal/gpio/gpio.stm32f4.hh" +#include "ucoo/common.hh" namespace ucoo { +GpioPort GPIOA (reg::GPIOA, Rcc::GPIOA); +GpioPort GPIOB (reg::GPIOB, Rcc::GPIOB); +GpioPort GPIOC (reg::GPIOC, Rcc::GPIOC); +GpioPort GPIOD (reg::GPIOD, Rcc::GPIOD); +GpioPort GPIOE (reg::GPIOE, Rcc::GPIOE); +GpioPort GPIOF (reg::GPIOF, Rcc::GPIOF); +GpioPort GPIOG (reg::GPIOG, Rcc::GPIOG); +GpioPort GPIOH (reg::GPIOH, Rcc::GPIOH); +GpioPort GPIOI (reg::GPIOI, Rcc::GPIOI); +GpioPort GPIOJ (reg::GPIOJ, Rcc::GPIOJ); +GpioPort GPIOK (reg::GPIOK, Rcc::GPIOK); + void Gpio::set () { - GPIO_BSRR (port_) = mask_; + port_->BSRR = mask_; } void Gpio::reset () { - GPIO_BSRR (port_) = mask_ << 16; + port_->BSRR = mask_ << 16; } /// Helper to avoid virtual dance. static inline void -Gpio_set (uint32_t port, uint16_t mask, bool state) +Gpio_set (GPIO_TypeDef *port, uint16_t mask, bool state) { if (state) - GPIO_BSRR (port) = mask; + port->BSRR = mask; else - GPIO_BSRR (port) = mask << 16; + port->BSRR = mask << 16; } void @@ -57,13 +70,13 @@ void Gpio::toggle () { // Avoid read/modify/write ODR, to achieve atomic operation. - Gpio_set (port_, mask_, !(GPIO_ODR (port_) & mask_)); + Gpio_set (port_, mask_, !(port_->ODR & mask_)); } bool Gpio::get () const { - return GPIO_IDR (port_) & mask_; + return port_->IDR & mask_; } /// Set two bits in a register for the corresponding one-bit mask. @@ -92,46 +105,95 @@ qmask_set (uint32_t mask, uint32_t reg, uint32_t bits) void Gpio::input () { - GPIO_MODER (port_) = dmask_set (mask_, GPIO_MODER (port_), - GPIO_MODE_INPUT); + port_->MODER = dmask_set (mask_, port_->MODER, GPIO_MODER_Input); } void Gpio::output () { - GPIO_MODER (port_) = dmask_set (mask_, GPIO_MODER (port_), - GPIO_MODE_OUTPUT); + port_->MODER = dmask_set (mask_, port_->MODER, GPIO_MODER_Output); } void Gpio::pull (Pull dir) { - GPIO_PUPDR (port_) = dmask_set (mask_, GPIO_PUPDR (port_), - static_cast (dir)); + port_->PUPDR = dmask_set (mask_, port_->PUPDR, + static_cast (dir)); } void Gpio::speed (Speed s) { - GPIO_OSPEEDR (port_) = dmask_set (mask_, GPIO_OSPEEDR (port_), - static_cast (s)); + port_->OSPEEDR = dmask_set (mask_, port_->OSPEEDR, + static_cast (s)); +} + +void +Gpio::type (Type t) +{ + port_->OTYPER = (port_->OTYPER & ~mask_) + | (mask_ * static_cast (t)); } void Gpio::af (int num) { - GPIO_MODER (port_) = dmask_set (mask_, GPIO_MODER (port_), GPIO_MODE_AF); + port_->MODER = dmask_set (mask_, port_->MODER, GPIO_MODER_AF); if (mask_ & 0xff) - GPIO_AFRL (port_) = qmask_set (mask_, GPIO_AFRL (port_), num); + port_->AFR[0] = qmask_set (mask_, port_->AFR[0], num); else - GPIO_AFRH (port_) = qmask_set (mask_ >> 8, GPIO_AFRH (port_), num); + port_->AFR[1] = qmask_set (mask_ >> 8, port_->AFR[1], num); } void Gpio::analog () { - GPIO_MODER (port_) = dmask_set (mask_, GPIO_MODER (port_), - GPIO_MODE_ANALOG); + port_->MODER = dmask_set (mask_, port_->MODER, GPIO_MODER_Analog); +} + +int +GpioPort::get_port_index () const +{ + int port; + switch (rcc_) + { + case Rcc::GPIOA: + port = 0; + break; + case Rcc::GPIOB: + port = 1; + break; + case Rcc::GPIOC: + port = 2; + break; + case Rcc::GPIOD: + port = 3; + break; + case Rcc::GPIOE: + port = 4; + break; + case Rcc::GPIOF: + port = 5; + break; + case Rcc::GPIOG: + port = 6; + break; + case Rcc::GPIOH: + port = 7; + break; + case Rcc::GPIOI: + port = 8; + break; + case Rcc::GPIOJ: + port = 9; + break; + case Rcc::GPIOK: + port = 10; + break; + default: + assert_unreachable (); + } + return port; } } // namespace ucoo diff --git a/ucoo/hal/gpio/gpio.stm32f4.hh b/ucoo/hal/gpio/gpio.stm32f4.hh index fa73fa2..356a575 100644 --- a/ucoo/hal/gpio/gpio.stm32f4.hh +++ b/ucoo/hal/gpio/gpio.stm32f4.hh @@ -24,31 +24,36 @@ // // }}} #include "ucoo/intf/io.hh" - -#include +#include "ucoo/arch/reg.hh" +#include "ucoo/arch/rcc.stm32.hh" namespace ucoo { +class GpioPort; + /// General purpose input/output on STM32F4. class Gpio : public Io { public: enum class Pull : uint32_t { - NONE = GPIO_PUPD_NONE, - UP = GPIO_PUPD_PULLUP, - DOWN = GPIO_PUPD_PULLDOWN, + NONE, + UP, + DOWN, }; enum class Speed : uint32_t { - SPEED_2MHZ = GPIO_OSPEED_2MHZ, - SPEED_25MHZ = GPIO_OSPEED_25MHZ, - SPEED_50MHZ = GPIO_OSPEED_50MHZ, - SPEED_100MHZ = GPIO_OSPEED_100MHZ, + SPEED_2MHZ, + SPEED_25MHZ, + SPEED_50MHZ, + SPEED_100MHZ, + }; + enum class Type : uint32_t + { + PUSH_PULL, + OPEN_DRAIN, }; public: - /// Constructor, take the PORT base address, and pin BIT number. - Gpio (uint32_t port, int bit); /// See Io::set. void set (); /// See Io::reset. @@ -67,23 +72,64 @@ class Gpio : public Io void pull (Pull dir); /// Set output speed. void speed (Speed s); + /// Set output type. + void type (Type t); /// Set alternate function. void af (int num); /// Set as analog. void analog (); + private: + /// Constructor, take a port, and pin BIT number. + Gpio (GpioPort &port, int bit); + friend GpioPort; private: /// Port register base address. - const uint32_t port_; + GPIO_TypeDef * const port_; /// IO bitmask. const uint16_t mask_; }; +/// General purpose input/output port on STM32F4. +class GpioPort +{ + GPIO_TypeDef * const port_; + const Rcc rcc_; + friend class Gpio; + public: + /// Constructor. + GpioPort (GPIO_TypeDef *port, Rcc rcc) + : port_ (port), rcc_ (rcc) { } + /// Enable port. + void enable () + { rcc_peripheral_clock_enable (rcc_); } + /// Disable port. + void disable () + { rcc_peripheral_clock_disable (rcc_); } + /// Return a port GPIO. + Gpio operator[] (int bit) + { return Gpio (*this, bit); } + /// Get the index of the port, used with Exti. + int get_port_index () const; +}; + inline -Gpio::Gpio (uint32_t port, int bit) - : port_ (port), mask_ (1u << bit) +Gpio::Gpio (GpioPort &port, int bit) + : port_ (port.port_), mask_ (1u << bit) { } +extern GpioPort GPIOA; +extern GpioPort GPIOB; +extern GpioPort GPIOC; +extern GpioPort GPIOD; +extern GpioPort GPIOE; +extern GpioPort GPIOF; +extern GpioPort GPIOG; +extern GpioPort GPIOH; +extern GpioPort GPIOI; +extern GpioPort GPIOJ; +extern GpioPort GPIOK; + } // namespace ucoo #endif // ucoo_hal_gpio_gpio_stm32f4_hh diff --git a/ucoo/hal/gpio/test/test_gpio.stm32f1.cc b/ucoo/hal/gpio/test/test_gpio.stm32f1.cc index 0135a32..d3fe1c2 100644 --- a/ucoo/hal/gpio/test/test_gpio.stm32f1.cc +++ b/ucoo/hal/gpio/test/test_gpio.stm32f1.cc @@ -25,8 +25,6 @@ #include "ucoo/hal/gpio/gpio.hh" #include "ucoo/utils/delay.hh" -#include - void test (ucoo::Io &loop_in, ucoo::Io &led1, ucoo::Io &led2, ucoo::Io &led3, ucoo::Io &led4) @@ -54,12 +52,12 @@ int main (int argc, const char **argv) { ucoo::arch_init (argc, argv); - rcc_periph_clock_enable (RCC_GPIOC); - ucoo::Gpio loop_in (GPIOC, 5); - ucoo::Gpio led1 (GPIOC, 6); - ucoo::Gpio led2 (GPIOC, 7); - ucoo::Gpio led3 (GPIOC, 8); - ucoo::Gpio led4 (GPIOC, 9); + ucoo::GPIOC.enable (); + ucoo::Gpio loop_in (ucoo::GPIOC[5]); + ucoo::Gpio led1 (ucoo::GPIOC[6]); + ucoo::Gpio led2 (ucoo::GPIOC[7]); + ucoo::Gpio led3 (ucoo::GPIOC[8]); + ucoo::Gpio led4 (ucoo::GPIOC[9]); test (loop_in, led1, led2, led3, led4); return 0; } diff --git a/ucoo/hal/gpio/test/test_gpio.stm32f4.cc b/ucoo/hal/gpio/test/test_gpio.stm32f4.cc index a51017c..6932a05 100644 --- a/ucoo/hal/gpio/test/test_gpio.stm32f4.cc +++ b/ucoo/hal/gpio/test/test_gpio.stm32f4.cc @@ -25,8 +25,6 @@ #include "ucoo/hal/gpio/gpio.hh" #include "ucoo/utils/delay.hh" -#include - void test (ucoo::Io &loop_out, ucoo::Io &loop_in, ucoo::Io &led3, ucoo::Io &led4, ucoo::Io &led5, ucoo::Io &led6) @@ -56,16 +54,16 @@ int main (int argc, const char **argv) { ucoo::arch_init (argc, argv); - rcc_periph_clock_enable (RCC_GPIOB); - rcc_periph_clock_enable (RCC_GPIOD); + ucoo::GPIOB.enable (); + ucoo::GPIOD.enable (); // For this test, shorten B6 & B7 to have loopback. - gpio_mode_setup (GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO7); - ucoo::Gpio loop_out (GPIOB, 6); - ucoo::Gpio loop_in (GPIOB, 7); - ucoo::Gpio led3 (GPIOD, 13); - ucoo::Gpio led4 (GPIOD, 12); - ucoo::Gpio led5 (GPIOD, 14); - ucoo::Gpio led6 (GPIOD, 15); + ucoo::Gpio loop_out (ucoo::GPIOB[6]); + ucoo::Gpio loop_in (ucoo::GPIOB[7]); + loop_in.pull (ucoo::Gpio::Pull::UP); + ucoo::Gpio led3 (ucoo::GPIOD[13]); + ucoo::Gpio led4 (ucoo::GPIOD[12]); + ucoo::Gpio led5 (ucoo::GPIOD[14]); + ucoo::Gpio led6 (ucoo::GPIOD[15]); test (loop_out, loop_in, led3, led4, led5, led6); return 0; } diff --git a/ucoo/hal/i2c/i2c_hard.stm32.cc b/ucoo/hal/i2c/i2c_hard.stm32.cc index 14a4486..5e767d6 100644 --- a/ucoo/hal/i2c/i2c_hard.stm32.cc +++ b/ucoo/hal/i2c/i2c_hard.stm32.cc @@ -21,11 +21,9 @@ // DEALINGS IN THE SOFTWARE. // // }}} -#include "i2c_hard.stm32.hh" - -#include -#include -#include +#include "ucoo/hal/i2c/i2c_hard.stm32.hh" +#include "ucoo/arch/interrupt.arm.hh" +#include "ucoo/arch/rcc.stm32.hh" #include "ucoo/utils/trace.hh" @@ -38,53 +36,56 @@ static Trace i2c_trace; struct i2c_hardware_t { /// I2C base address. - uint32_t base; - /// Clock enable identifier. - enum rcc_periph_clken clken; - /// Corresponding event IRQ (error IRQ is next one). - int ev_irq; + I2C_TypeDef *base; + /// RCC identifier, to enable clock. + Rcc rcc; + /// Corresponding event IRQ. + Irq ev_irq; + /// Corresponding error IRQ. + Irq er_irq; }; /// Information on I2C hardware array, this is zero indexed, I2C1 is at index /// 0. static const i2c_hardware_t i2c_hardware[] = { - { I2C1_BASE, RCC_I2C1, NVIC_I2C1_EV_IRQ }, - { I2C2_BASE, RCC_I2C2, NVIC_I2C2_EV_IRQ }, + { reg::I2C1, Rcc::I2C1, Irq::I2C1_EV, Irq::I2C1_ER }, + { reg::I2C2, Rcc::I2C2, Irq::I2C2_EV, Irq::I2C2_ER }, #ifdef I2C3_BASE - { I2C3_BASE, RCC_I2C3, NVIC_I2C3_EV_IRQ }, + { reg::I2C3, Rcc::I2C3, Irq::I2C3_EV, Irq::I2C3_ER }, #endif }; static I2cHard *i2c_instances[lengthof (i2c_hardware)]; -} // namespace ucoo - -extern "C" { - -void i2c1_ev_isr () { ucoo::I2cHard::ev_isr (0); } +template<> +void interrupt () { I2cHard::ev_isr (0); } -void i2c1_er_isr () { ucoo::I2cHard::er_isr (0); } +template<> +void interrupt () { I2cHard::er_isr (0); } -void i2c2_ev_isr () { ucoo::I2cHard::ev_isr (1); } +template<> +void interrupt () { I2cHard::ev_isr (1); } -void i2c2_er_isr () { ucoo::I2cHard::er_isr (1); } +template<> +void interrupt () { I2cHard::er_isr (1); } -void i2c3_ev_isr () { ucoo::I2cHard::ev_isr (2); } +#ifdef I2C3_BASE -void i2c3_er_isr () { ucoo::I2cHard::er_isr (2); } +template<> +void interrupt () { I2cHard::ev_isr (2); } -} +template<> +void interrupt () { I2cHard::er_isr (2); } -namespace ucoo { +#endif -I2cHard::I2cHard (int n) - : n_ (n), enabled_ (false), slave_addr_ (0), slave_data_handler_ (0), - master_ (false), master_status_ (STATUS_ERROR), master_buf_ (0) +I2cHard::I2cHard (Instance inst) + : n_ (static_cast (inst)) { - assert (n < lengthof (i2c_instances)); - assert (!i2c_instances[n]); - i2c_instances[n] = this; + assert (n_ < lengthof (i2c_instances)); + assert (!i2c_instances[n_]); + i2c_instances[n_] = this; } I2cHard::~I2cHard () @@ -97,38 +98,38 @@ void I2cHard::enable (int speed) { enabled_ = true; - uint32_t base = i2c_hardware[n_].base; + auto base = i2c_hardware[n_].base; // Turn on. - rcc_periph_clock_enable (i2c_hardware[n_].clken); + rcc_peripheral_clock_enable (i2c_hardware[n_].rcc); // Reset. - I2C_CR1 (base) = I2C_CR1_SWRST; + base->CR1 = I2C_CR1_SWRST; // TODO: make sure the bus is free!!! How! - I2C_CR1 (base) = 0; + base->CR1 = 0; // Compute clock parameters. - int pclk = rcc_apb1_frequency; - int pclk_mhz = pclk / 1000000; + int pclk_hz = rcc_apb1_freq_hz; + int pclk_mhz = pclk_hz / 1000000; uint16_t ccr, tris; if (speed <= 100000) { - ccr = pclk / speed / 2; + ccr = pclk_hz / speed / 2; tris = pclk_mhz + 1; } else { assert (speed <= 400000); - ccr = I2C_CCR_FS | I2C_CCR_DUTY | (pclk / speed / 25); + ccr = I2C_CCR_FS | I2C_CCR_DUTY | (pclk_hz / speed / 25); tris = pclk_mhz * 3 / 10 + 1; } // Set all parameters. - I2C_CCR (base) = ccr; - I2C_TRISE (base) = tris; - I2C_OAR1 (base) = slave_addr_ | (1 << 14); - I2C_OAR2 (base) = 0; - I2C_CR2 (base) = I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | pclk_mhz; + base->CCR = ccr; + base->TRISE = tris; + base->OAR1 = slave_addr_ | (1 << 14); + base->OAR2 = 0; + base->CR2 = I2C_CR2_ITEVTEN | I2C_CR2_ITERREN | pclk_mhz; // Enable. - nvic_enable_irq (i2c_hardware[n_].ev_irq); - nvic_enable_irq (i2c_hardware[n_].ev_irq + 1); - I2C_CR1 (base) = I2C_CR1_ACK | I2C_CR1_PE; + interrupt_enable (i2c_hardware[n_].ev_irq); + interrupt_enable (i2c_hardware[n_].er_irq); + base->CR1 = I2C_CR1_ACK | I2C_CR1_PE; } @@ -138,14 +139,14 @@ I2cHard::disable () if (enabled_) { enabled_ = false; - uint32_t base = i2c_hardware[n_].base; + auto base = i2c_hardware[n_].base; // TODO: wait for end of transfer? // Disable. - nvic_disable_irq (i2c_hardware[n_].ev_irq); - nvic_disable_irq (i2c_hardware[n_].ev_irq + 1); - I2C_CR1 (base) = 0; + interrupt_disable (i2c_hardware[n_].ev_irq); + interrupt_disable (i2c_hardware[n_].er_irq); + base->CR1 = 0; // Turn off. - rcc_periph_clock_disable (i2c_hardware[n_].clken); + rcc_peripheral_clock_disable (i2c_hardware[n_].rcc); } } @@ -155,9 +156,9 @@ I2cHard::transfer (uint8_t addr, char *buf, int count) assert (enabled_ && count); // No need to lock, master is not busy. assert (master_status_ != STATUS_BUSY); - uint32_t base = i2c_hardware[n_].base; + auto base = i2c_hardware[n_].base; // Wait until STOP condition terminated, polling is the only way. - while (I2C_CR1 (base) & I2C_CR1_STOP) + while (base->CR1 & I2C_CR1_STOP) barrier (); // Now, program next transfer. master_status_ = STATUS_BUSY; @@ -165,7 +166,7 @@ I2cHard::transfer (uint8_t addr, char *buf, int count) master_buf_ = buf; master_count_ = count; // TODO: multimaster: about ACK, may have to lock IRQ for multimaster. - I2C_CR1 (base) |= I2C_CR1_START; + base->CR1 |= I2C_CR1_START; } void @@ -204,24 +205,24 @@ I2cHard::register_data (uint8_t addr, DataHandler &data_handler) slave_data_handler_ = &data_handler; if (enabled_) { - uint32_t base = i2c_hardware[n_].base; + auto base = i2c_hardware[n_].base; // Just in case a transfer is triggered right now. barrier (); // According to datasheet, bit 14 should be 1! - I2C_OAR1 (base) = addr | (1 << 14); + base->OAR1 = addr | (1 << 14); } } void I2cHard::ev_isr (int n) { - uint32_t base = i2c_hardware[n].base; + auto base = i2c_hardware[n].base; assert (i2c_instances[n]); I2cHard &i2c = *i2c_instances[n]; i2c_trace ("<%d> event isr", n); while (1) { - uint16_t sr1 = I2C_SR1 (base); + uint16_t sr1 = base->SR1; i2c_trace ("<%d> sr1=%04x", n, sr1); // Can not read SR2 because doing so would clear the ADDR bit. if (i2c.master_) @@ -233,42 +234,42 @@ I2cHard::ev_isr (int n) // before reading SR2, and STOP after... Crappy hardware! if ((i2c.master_slave_addr_ & 1) && i2c.buf_count_ == 1) { - I2C_CR1 (base) = I2C_CR1_PE; - sr2 = I2C_SR2 (base); - I2C_CR1 (base) = I2C_CR1_STOP | I2C_CR1_PE; + base->CR1 = I2C_CR1_PE; + sr2 = base->SR2; + base->CR1 = I2C_CR1_STOP | I2C_CR1_PE; // TODO: what to wait now? Unsupported for now. } else if ((i2c.master_slave_addr_ & 1) && i2c.buf_count_ == 2) { - I2C_CR1 (base) = I2C_CR1_POS | I2C_CR1_PE; - sr2 = I2C_SR2 (base); + base->CR1 = I2C_CR1_POS | I2C_CR1_PE; + sr2 = base->SR2; // Wait for BTF. } else { - sr2 = I2C_SR2 (base); - I2C_CR2 (base) |= I2C_CR2_ITBUFEN; + sr2 = base->SR2; + base->CR2 |= I2C_CR2_ITBUFEN; } i2c_trace ("<%d> master sr2=%04x", n, sr2); } - else if (sr1 & I2C_SR1_TxE + else if (sr1 & I2C_SR1_TXE && i2c.buf_index_ < i2c.buf_count_) { i2c_trace ("<%d> master tx index=%d", n, i2c.buf_index_); // Send next byte. - I2C_DR (base) = i2c.master_buf_[i2c.buf_index_++]; + base->DR = i2c.master_buf_[i2c.buf_index_++]; // Wait for BTF if last one. if (i2c.buf_index_ == i2c.buf_count_) - I2C_CR2 (base) &= ~I2C_CR2_ITBUFEN; + base->CR2 &= ~I2C_CR2_ITBUFEN; } - else if (sr1 & I2C_SR1_RxNE + else if (sr1 & I2C_SR1_RXNE && i2c.buf_count_ - i2c.buf_index_ > 3) { i2c_trace ("<%d> master rx index=%d", n, i2c.buf_index_); - i2c.master_buf_[i2c.buf_index_++] = I2C_DR (base); + i2c.master_buf_[i2c.buf_index_++] = base->DR; if (i2c.buf_count_ - i2c.buf_index_ == 3) // Wait for BTF. - I2C_CR2 (base) &= ~I2C_CR2_ITBUFEN; + base->CR2 &= ~I2C_CR2_ITBUFEN; } else if (sr1 & I2C_SR1_BTF) { @@ -276,7 +277,7 @@ I2cHard::ev_isr (int n) if (!(i2c.master_slave_addr_ & 1)) { // End of transmission. - I2C_CR1 (base) = I2C_CR1_ACK | I2C_CR1_STOP | I2C_CR1_PE; + base->CR1 = I2C_CR1_ACK | I2C_CR1_STOP | I2C_CR1_PE; i2c.master_ = false; i2c.master_status_ = i2c.buf_index_; if (i2c.finished_handler_) @@ -285,17 +286,17 @@ I2cHard::ev_isr (int n) else if (i2c.buf_count_ - i2c.buf_index_ == 3) { // Near end of reception. - I2C_CR1 (base) = I2C_CR1_PE; - i2c.master_buf_[i2c.buf_index_++] = I2C_DR (base); + base->CR1 = I2C_CR1_PE; + i2c.master_buf_[i2c.buf_index_++] = base->DR; // Wait for BTF. } else { // End of reception. - I2C_CR1 (base) = I2C_CR1_ACK | I2C_CR1_STOP | I2C_CR1_PE; + base->CR1 = I2C_CR1_ACK | I2C_CR1_STOP | I2C_CR1_PE; if (i2c.buf_count_ - i2c.buf_index_ == 2) - i2c.master_buf_[i2c.buf_index_++] = I2C_DR (base); - i2c.master_buf_[i2c.buf_index_++] = I2C_DR (base); + i2c.master_buf_[i2c.buf_index_++] = base->DR; + i2c.master_buf_[i2c.buf_index_++] = base->DR; i2c.master_ = false; i2c.master_status_ = i2c.buf_index_; if (i2c.finished_handler_) @@ -309,7 +310,7 @@ I2cHard::ev_isr (int n) { if (sr1 & I2C_SR1_ADDR) { - uint16_t sr2 = I2C_SR2 (base); + uint16_t sr2 = base->SR2; i2c_trace ("<%d> slave sr2=%04x", n, sr2); // Initiate new slave transfer. if (sr2 & I2C_SR2_TRA) @@ -320,20 +321,20 @@ I2cHard::ev_isr (int n) else i2c.buf_count_ = sizeof (i2c.slave_buf_); i2c.buf_index_ = 0; - I2C_CR2 (base) |= I2C_CR2_ITBUFEN; + base->CR2 |= I2C_CR2_ITBUFEN; } - else if (sr1 & I2C_SR1_TxE) + else if (sr1 & I2C_SR1_TXE) { i2c_trace ("<%d> slave tx index=%d", n, i2c.buf_index_); uint8_t b = 0xff; if (i2c.buf_index_ < i2c.buf_count_) b = i2c.slave_buf_[i2c.buf_index_++]; - I2C_DR (base) = b; + base->DR = b; } - else if (sr1 & I2C_SR1_RxNE) + else if (sr1 & I2C_SR1_RXNE) { i2c_trace ("<%d> slave rx index=%d", n, i2c.buf_index_); - uint8_t b = I2C_DR (base); + uint8_t b = base->DR; if (i2c.buf_index_ < i2c.buf_count_) i2c.slave_buf_[i2c.buf_index_++] = b; } @@ -343,14 +344,14 @@ I2cHard::ev_isr (int n) i2c.slave_data_handler_->to_recv (i2c.slave_buf_, i2c.buf_index_); // TODO: multimaster: there is no way to write in this // register if a START was requested to switch to master mode! - I2C_CR1 (base) = I2C_CR1_ACK | I2C_CR1_PE; - I2C_CR2 (base) &= ~I2C_CR2_ITBUFEN; + base->CR1 = I2C_CR1_ACK | I2C_CR1_PE; + base->CR2 &= ~I2C_CR2_ITBUFEN; } else if (sr1 & I2C_SR1_SB) { i2c_trace ("<%d> master start", n); // Starting master mode. - I2C_DR (base) = i2c.master_slave_addr_; + base->DR = i2c.master_slave_addr_; i2c.master_ = true; i2c.buf_count_ = i2c.master_count_; i2c.buf_index_ = 0; @@ -365,30 +366,30 @@ I2cHard::ev_isr (int n) void I2cHard::er_isr (int n) { - uint32_t base = i2c_hardware[n].base; + auto base = i2c_hardware[n].base; assert (i2c_instances[n]); I2cHard &i2c = *i2c_instances[n]; - uint16_t sr1 = I2C_SR1 (base); - I2C_SR1 (base) = 0; + uint16_t sr1 = base->SR1; + base->SR1 = 0; i2c_trace ("<%d> error isr sr1=%04x", n, sr1); if (i2c.master_) { if (sr1 & I2C_SR1_ARLO) { // Try again. - I2C_CR1 (base) = I2C_CR1_ACK | I2C_CR1_START | I2C_CR1_PE; + base->CR1 = I2C_CR1_ACK | I2C_CR1_START | I2C_CR1_PE; i2c.master_ = false; } else if (sr1 & I2C_SR1_AF) { - I2C_CR1 (base) = I2C_CR1_ACK | I2C_CR1_STOP | I2C_CR1_PE; + base->CR1 = I2C_CR1_ACK | I2C_CR1_STOP | I2C_CR1_PE; i2c.master_ = false; i2c.master_status_ = i2c.buf_index_; if (i2c.finished_handler_) i2c.finished_handler_->finished (i2c.master_status_); } } - I2C_CR2 (base) &= ~I2C_CR2_ITBUFEN; + base->CR2 &= ~I2C_CR2_ITBUFEN; // TODO: handle misplaced STOP errata. } diff --git a/ucoo/hal/i2c/i2c_hard.stm32.hh b/ucoo/hal/i2c/i2c_hard.stm32.hh index 98e4930..81c5deb 100644 --- a/ucoo/hal/i2c/i2c_hard.stm32.hh +++ b/ucoo/hal/i2c/i2c_hard.stm32.hh @@ -27,14 +27,26 @@ #include "config/ucoo/hal/i2c.hh" +#include "ucoo/arch/reg.hh" + namespace ucoo { /// I2C interface, using dedicated hardware. class I2cHard : public I2c { + public: + /// Available instances. + enum class Instance + { + I2C1, + I2C2, +#ifdef I2C3_BASE + I2C3, +#endif + }; public: /// Constructor for the Nth I2C. - I2cHard (int n); + I2cHard (Instance inst); /// Shutdown. ~I2cHard (); /// Enable and setup @@ -62,11 +74,11 @@ class I2cHard : public I2c /// I2C number. int n_; /// Is it enabled? - bool enabled_; + bool enabled_ = false; /// Slave address. - uint8_t slave_addr_; + uint8_t slave_addr_ = 0; /// Handler called to source or sink data for slave exchanges. - DataHandler *slave_data_handler_; + DataHandler *slave_data_handler_ = nullptr; /// Slave buffer. char slave_buf_[CONFIG_UCOO_HAL_I2C_SLAVE_BUFFER_SIZE]; /// Current buffer count (bytes to send), or buffer size (available room). @@ -75,11 +87,11 @@ class I2cHard : public I2c /// byte). int buf_index_; /// Master access granted. - bool master_; + bool master_ = false; /// Current master transfer status. - int master_status_; + int master_status_ = STATUS_ERROR; /// Current master transfer buffer. - char *master_buf_; + char *master_buf_ = nullptr; /// Current master transfer size, copied to buf_count_ when active. int master_count_; /// Current master transfer slave address, LSB is set for receiver mode. diff --git a/ucoo/hal/i2c/test/Makefile b/ucoo/hal/i2c/test/Makefile index ca6915e..2a3bb91 100644 --- a/ucoo/hal/i2c/test/Makefile +++ b/ucoo/hal/i2c/test/Makefile @@ -4,6 +4,6 @@ TARGETS = host stm32f4 PROGS = test_i2c test_i2c_SOURCES = test_i2c.cc -MODULES = ucoo/hal/i2c ucoo/utils ucoo/base/test ucoo/hal/usb +MODULES = ucoo/hal/i2c ucoo/utils ucoo/base/test ucoo/hal/usb ucoo/hal/gpio include $(BASE)/build/top.mk diff --git a/ucoo/hal/i2c/test/test_i2c.cc b/ucoo/hal/i2c/test/test_i2c.cc index 8b6ef9b..bef74b4 100644 --- a/ucoo/hal/i2c/test/test_i2c.cc +++ b/ucoo/hal/i2c/test/test_i2c.cc @@ -27,7 +27,6 @@ #include "ucoo/base/test/test.hh" #ifdef TARGET_stm32 -# include # include "ucoo/hal/gpio/gpio.hh" #endif @@ -198,20 +197,19 @@ main (int argc, const char **argv) // Connect I2C1 to I2C3 for the test. // I2C1: B6: SCL, B9: SDA // I2C3: A8: SCL, C9: SDA - rcc_periph_clock_enable (RCC_GPIOA); - rcc_periph_clock_enable (RCC_GPIOB); - rcc_periph_clock_enable (RCC_GPIOC); - gpio_mode_setup (GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO6 | GPIO9); - gpio_mode_setup (GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO8); - gpio_mode_setup (GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO9); - gpio_set_output_options (GPIOB, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO6 | GPIO9); - gpio_set_output_options (GPIOA, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO8); - gpio_set_output_options (GPIOC, GPIO_OTYPE_OD, GPIO_OSPEED_2MHZ, GPIO9); - gpio_set_af (GPIOB, GPIO_AF4, GPIO6 | GPIO9); - gpio_set_af (GPIOA, GPIO_AF4, GPIO8); - gpio_set_af (GPIOC, GPIO_AF4, GPIO9); - ucoo::I2cHard i2c1 (0); - ucoo::I2cHard i2c2 (2); + ucoo::GPIOA.enable (); + ucoo::GPIOB.enable (); + ucoo::GPIOC.enable (); + ucoo::GPIOB[6].af (4); + ucoo::GPIOB[6].type (ucoo::Gpio::Type::OPEN_DRAIN); + ucoo::GPIOB[9].af (4); + ucoo::GPIOB[9].type (ucoo::Gpio::Type::OPEN_DRAIN); + ucoo::GPIOA[8].af (4); + ucoo::GPIOA[8].type (ucoo::Gpio::Type::OPEN_DRAIN); + ucoo::GPIOC[9].af (4); + ucoo::GPIOC[9].type (ucoo::Gpio::Type::OPEN_DRAIN); + ucoo::I2cHard i2c1 (ucoo::I2cHard::Instance::I2C1); + ucoo::I2cHard i2c2 (ucoo::I2cHard::Instance::I2C2); i2c1.enable (); i2c2.enable (); #endif diff --git a/ucoo/hal/sdram/sdram.stm32f4.cc b/ucoo/hal/sdram/sdram.stm32f4.cc index 07baf54..4c224c0 100644 --- a/ucoo/hal/sdram/sdram.stm32f4.cc +++ b/ucoo/hal/sdram/sdram.stm32f4.cc @@ -22,12 +22,9 @@ // // }}} #include "ucoo/hal/sdram/sdram.hh" +#include "ucoo/arch/rcc.stm32.hh" #include "ucoo/utils/delay.hh" -#include -#include -#include - namespace ucoo { Sdram::Sdram (std::initializer_list ios, const Param ¶ms) @@ -41,20 +38,25 @@ Sdram::enable () // Setup GPIO, turn controller on. for (auto io : ios_) { - gpio_mode_setup (io.gpio, GPIO_MODE_AF, GPIO_PUPD_NONE, io.pins); - gpio_set_output_options (io.gpio, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, - io.pins); - gpio_set_af (io.gpio, GPIO_AF12, io.pins); + for (int i = 0; i < 16; i++) + { + if (io.pins & (1 << i)) + { + Gpio gpio (io.port[i]); + gpio.af (12); + gpio.speed (Gpio::Speed::SPEED_50MHZ); + } + } } - rcc_periph_clock_enable (RCC_FSMC); + rcc_peripheral_clock_enable (Rcc::FMC); // Prepare SDRAM clock, can only be HCLK/2 or HCLK/3. Can go slower than // requested, but not faster. - int clock_div = (rcc_ahb_frequency + params_.clock_hz - 1) + int clock_div = (rcc_ahb_freq_hz + params_.clock_hz - 1) / params_.clock_hz; if (clock_div < 2) clock_div = 2; assert (clock_div <= 3); - int clock_hz = rcc_ahb_frequency / clock_div; + int clock_hz = rcc_ahb_freq_hz / clock_div; int clock_ns = 1000000000 / clock_hz; // Prepare and set parameters. assert (params_.bank == 1 || params_.bank == 2); @@ -64,50 +66,50 @@ Sdram::enable () assert (params_.row_bits >= 11 && params_.row_bits <= 13); assert (params_.col_bits >= 8 && params_.col_bits <= 11); uint32_t sdcr = - FMC_SDCR_RPIPE_NONE + FMC_SDCR_RPIPE_None | FMC_SDCR_RBURST - | (clock_div == 2 ? FMC_SDCR_SDCLK_2HCLK : FMC_SDCR_SDCLK_3HCLK) - | (params_.cas << FMC_SDCR_CAS_SHIFT) - | (params_.banks == 2 ? FMC_SDCR_NB2 : FMC_SDCR_NB4) - | (params_.bits == 8 ? FMC_SDCR_MWID_8b - : (params_.bits == 16 ? FMC_SDCR_MWID_16b : FMC_SDCR_MWID_32b)) - | ((params_.row_bits - 11) << FMC_SDCR_NR_SHIFT) - | ((params_.col_bits - 8) << FMC_SDCR_NC_SHIFT); + | (clock_div == 2 ? FMC_SDCR_SDCLK_2Hclk : FMC_SDCR_SDCLK_3Hclk) + | (params_.cas << FMC_SDCR_CAS_Pos) + | (params_.banks == 2 ? 0 : FMC_SDCR_NB) + | (params_.bits == 8 ? FMC_SDCR_MWID_8B + : (params_.bits == 16 ? FMC_SDCR_MWID_16B : FMC_SDCR_MWID_32B)) + | ((params_.row_bits - 11) << FMC_SDCR_NR_Pos) + | ((params_.col_bits - 8) << FMC_SDCR_NC_Pos); uint32_t sdtr = - ((params_.trcd - 1) << FMC_SDTR_TRCD_SHIFT) - | ((params_.trp - 1) << FMC_SDTR_TRP_SHIFT) - | ((params_.twr - 1) << FMC_SDTR_TWR_SHIFT) - | ((params_.trc - 1) << FMC_SDTR_TRC_SHIFT) - | ((params_.tras - 1) << FMC_SDTR_TRAS_SHIFT) - | ((params_.txsr - 1) << FMC_SDTR_TXSR_SHIFT) - | ((params_.tmrd - 1) << FMC_SDTR_TMRD_SHIFT); + ((params_.trcd - 1) << FMC_SDTR_TRCD_Pos) + | ((params_.trp - 1) << FMC_SDTR_TRP_Pos) + | ((params_.twr - 1) << FMC_SDTR_TWR_Pos) + | ((params_.trc - 1) << FMC_SDTR_TRC_Pos) + | ((params_.tras - 1) << FMC_SDTR_TRAS_Pos) + | ((params_.txsr - 1) << FMC_SDTR_TXSR_Pos) + | ((params_.tmrd - 1) << FMC_SDTR_TMRD_Pos); if (params_.bank == 1) { - FMC_SDCR1 = sdcr; - FMC_SDTR1 = sdtr; + reg::FMC_Bank5_6->SDCR[0] = sdcr; + reg::FMC_Bank5_6->SDTR[0] = sdtr; } else { - FMC_SDCR1 = sdcr & FMC_SDCR_DNC_MASK; - FMC_SDCR2 = sdcr; - FMC_SDTR1 = sdtr & FMC_SDTR_DNC_MASK; - FMC_SDTR2 = sdtr; + reg::FMC_Bank5_6->SDCR[0] = sdcr & FMC_SDCR_DNC_Mask; + reg::FMC_Bank5_6->SDCR[1] = sdcr; + reg::FMC_Bank5_6->SDTR[0] = sdtr & FMC_SDTR_DNC_Mask; + reg::FMC_Bank5_6->SDTR[1] = sdtr; } // Initialise SDRAM. uint32_t bank = params_.bank == 1 ? FMC_SDCMR_CTB1 : FMC_SDCMR_CTB2; - while (FMC_SDSR & FMC_SDSR_BUSY) + while (reg::FMC_Bank5_6->SDSR & FMC_SDSR_BUSY) ; - FMC_SDCMR = bank | FMC_SDCMR_MODE_CLOCK_CONFIG_ENA; + reg::FMC_Bank5_6->SDCMR = bank | FMC_SDCMR_MODE_ClockConfigEna; delay_us (params_.init_clock_delay_us); - while (FMC_SDSR & FMC_SDSR_BUSY) + while (reg::FMC_Bank5_6->SDSR & FMC_SDSR_BUSY) ; - FMC_SDCMR = bank | FMC_SDCMR_MODE_PALL; - while (FMC_SDSR & FMC_SDSR_BUSY) + reg::FMC_Bank5_6->SDCMR = bank | FMC_SDCMR_MODE_Pall; + while (reg::FMC_Bank5_6->SDSR & FMC_SDSR_BUSY) ; - FMC_SDCMR = bank | ((params_.init_auto_refresh - 1) - << FMC_SDCMR_NRFS_SHIFT) - | FMC_SDCMR_MODE_AUTO_REFRESH; - while (FMC_SDSR & FMC_SDSR_BUSY) + reg::FMC_Bank5_6->SDCMR = bank + | ((params_.init_auto_refresh - 1) << FMC_SDCMR_NRFS_Pos) + | FMC_SDCMR_MODE_AutoRefresh; + while (reg::FMC_Bank5_6->SDSR & FMC_SDSR_BUSY) ; uint32_t sdram_mode = SDRAM_MODE_BURST_LENGTH_1 @@ -116,19 +118,19 @@ Sdram::enable () : SDRAM_MODE_CAS_LATENCY_3) | SDRAM_MODE_OPERATING_MODE_STANDARD | SDRAM_MODE_WRITEBURST_MODE_SINGLE; - FMC_SDCMR = bank | (sdram_mode << FMC_SDCMR_MRD_SHIFT) - | FMC_SDCMR_MODE_LOAD_MODE_REGISTER; + reg::FMC_Bank5_6->SDCMR = bank | (sdram_mode << FMC_SDCMR_MRD_Pos) + | FMC_SDCMR_MODE_LoadModeRegister; // Set refresh rate. int refresh_interval_ns = params_.tref_ms * 1000000 / (1 << params_.row_bits); - FMC_SDRTR = refresh_interval_ns / clock_ns - 20; + reg::FMC_Bank5_6->SDRTR = refresh_interval_ns / clock_ns - 20; } void * Sdram::addr () const { - return params_.bank == 1 ? reinterpret_cast (FMC_BANK7_BASE) - : reinterpret_cast (FMC_BANK8_BASE); + return params_.bank == 1 ? reinterpret_cast (FMC_BANK5_BASE) + : reinterpret_cast (FMC_BANK6_BASE); } int diff --git a/ucoo/hal/sdram/sdram.stm32f4.hh b/ucoo/hal/sdram/sdram.stm32f4.hh index fbeefe0..aac82e0 100644 --- a/ucoo/hal/sdram/sdram.stm32f4.hh +++ b/ucoo/hal/sdram/sdram.stm32f4.hh @@ -23,6 +23,7 @@ // DEALINGS IN THE SOFTWARE. // // }}} +#include "ucoo/hal/gpio/gpio.hh" #include "ucoo/common.hh" #include @@ -34,7 +35,7 @@ class Sdram { public: struct Io { - uint32_t gpio; + GpioPort &port; uint16_t pins; }; struct Param { diff --git a/ucoo/hal/sdram/test/test_sdram.stm32f4.cc b/ucoo/hal/sdram/test/test_sdram.stm32f4.cc index 841d486..17ed1b4 100644 --- a/ucoo/hal/sdram/test/test_sdram.stm32f4.cc +++ b/ucoo/hal/sdram/test/test_sdram.stm32f4.cc @@ -25,9 +25,7 @@ #include "ucoo/arch/arch.hh" #include "ucoo/base/test/test.hh" - -#include -#include +#include "ucoo/utils/bits.hh" #include #include @@ -65,24 +63,25 @@ main (int argc, const char **argv) { ucoo::arch_init (argc, argv); ucoo::test_stream_setup (); - rcc_periph_clock_enable (RCC_GPIOD); - rcc_periph_clock_enable (RCC_GPIOE); - rcc_periph_clock_enable (RCC_GPIOF); - rcc_periph_clock_enable (RCC_GPIOG); - rcc_periph_clock_enable (RCC_GPIOH); - rcc_periph_clock_enable (RCC_GPIOI); + ucoo::GPIOD.enable (); + ucoo::GPIOE.enable (); + ucoo::GPIOF.enable (); + ucoo::GPIOG.enable (); + ucoo::GPIOH.enable (); + ucoo::GPIOI.enable (); std::initializer_list sdram_ios { - { GPIOD, GPIO0 | GPIO1 | GPIO8 | GPIO9 | GPIO10 | GPIO14 | GPIO15 }, - { GPIOE, GPIO0 | GPIO1 | GPIO7 | GPIO8 | GPIO9 | GPIO10 | GPIO11 - | GPIO12 | GPIO13 | GPIO14 | GPIO15 }, - { GPIOF, GPIO0 | GPIO1 | GPIO2 | GPIO3 | GPIO4 | GPIO5 | GPIO11 - | GPIO12 | GPIO13 | GPIO14 | GPIO15 }, - { GPIOG, GPIO0 | GPIO1 | GPIO2 | GPIO3 | GPIO4 | GPIO5 | GPIO8 - | GPIO15 }, - { GPIOH, GPIO2 | GPIO3 | GPIO5 | GPIO8 | GPIO9 | GPIO10 | GPIO11 - | GPIO12 | GPIO13 | GPIO14 | GPIO15 }, - { GPIOI, GPIO0 | GPIO1 | GPIO2 | GPIO3 | GPIO4 | GPIO5 | GPIO6 | GPIO7 - | GPIO9 | GPIO10 }, + { ucoo::GPIOD, ucoo::bits ( + 0, 1, 8, 9, 10, 14, 15) }, + { ucoo::GPIOE, ucoo::bits ( + 0, 1, 7, 8, 9, 10, 11, 12, 13, 14, 15) }, + { ucoo::GPIOF, ucoo::bits ( + 0, 1, 2, 3, 4, 5, 11, 12, 13, 14, 15) }, + { ucoo::GPIOG, ucoo::bits ( + 0, 1, 2, 3, 4, 5, 8, 15) }, + { ucoo::GPIOH, ucoo::bits ( + 2, 3, 5, 8, 9, 10, 11, 12, 13, 14, 15) }, + { ucoo::GPIOI, ucoo::bits ( + 0, 1, 2, 3, 4, 5, 6, 7, 9, 10) }, }; ucoo::Sdram::Param sdram_param { .bank = 1, diff --git a/ucoo/hal/self_programming/self_programming.stm32f1.cc b/ucoo/hal/self_programming/self_programming.stm32f1.cc index 8cbbb24..8cebd6b 100644 --- a/ucoo/hal/self_programming/self_programming.stm32f1.cc +++ b/ucoo/hal/self_programming/self_programming.stm32f1.cc @@ -22,17 +22,14 @@ // // }}} #include "ucoo/hal/self_programming/self_programming.hh" - -#include -#include -#include +#include "ucoo/arch/reg.hh" namespace ucoo { int self_programming_flash_size () { - return DESIG_FLASH_SIZE * 1024; + return reg::DESIG->FLASH_SIZE * 1024; } int @@ -40,7 +37,7 @@ self_programming_erase_size (uint32_t addr) { assert (static_cast (addr - FLASH_BASE) <= self_programming_flash_size ()); - uint32_t idcode = DBGMCU_IDCODE & DBGMCU_IDCODE_DEV_ID_MASK; + uint32_t idcode = reg::DBGMCU->IDCODE & DBGMCU_IDCODE_DEV_ID_Msk; int page_size = idcode >= 0x414 ? 2048 : 1024; assert ((addr & (page_size - 1)) == 0); return page_size; @@ -53,14 +50,23 @@ self_programming_erase (uint32_t addr, int count) <= self_programming_flash_size ()); int page_size = self_programming_erase_size (addr); assert ((count & (page_size - 1)) == 0); + reg::FLASH->CR = FLASH_CR_LOCK; + reg::FLASH->KEYR = FLASH_KEYR_KEY1; + reg::FLASH->KEYR = FLASH_KEYR_KEY2; + while (reg::FLASH->SR & FLASH_SR_BSY) + ; while (count) { - flash_unlock (); - flash_erase_page (addr); - flash_lock (); + reg::FLASH->CR = FLASH_CR_PER; + reg::FLASH->AR = addr; + reg::FLASH->CR |= FLASH_CR_STRT; + while (reg::FLASH->SR & FLASH_SR_BSY) + ; + reg::FLASH->CR = 0; addr += page_size; count -= page_size; } + reg::FLASH->CR = FLASH_CR_LOCK; assert (count == 0); } @@ -72,11 +78,21 @@ self_programming_write (uint32_t addr, const char *buf, int count) assert (count % 2 == 0); assert (static_cast (addr - FLASH_BASE + count) <= self_programming_flash_size ()); - flash_unlock (); + reg::FLASH->CR = FLASH_CR_LOCK; + reg::FLASH->KEYR = FLASH_KEYR_KEY1; + reg::FLASH->KEYR = FLASH_KEYR_KEY2; + while (reg::FLASH->SR & FLASH_SR_BSY) + ; for (int i = 0; i < count; i += 2) - flash_program_half_word ( - addr + i, *reinterpret_cast (buf + i)); - flash_lock (); + { + reg::FLASH->CR = FLASH_CR_PG; + *reinterpret_cast (addr + i) + = *reinterpret_cast (buf + i); + while (reg::FLASH->SR & FLASH_SR_BSY) + ; + reg::FLASH->CR = 0; + } + reg::FLASH->CR = FLASH_CR_LOCK; } } // namespace ucoo diff --git a/ucoo/hal/self_programming/self_programming.stm32f4.cc b/ucoo/hal/self_programming/self_programming.stm32f4.cc index 3956b1e..4d8418f 100644 --- a/ucoo/hal/self_programming/self_programming.stm32f4.cc +++ b/ucoo/hal/self_programming/self_programming.stm32f4.cc @@ -22,9 +22,7 @@ // // }}} #include "ucoo/hal/self_programming/self_programming.hh" - -#include -#include +#include "ucoo/arch/reg.hh" namespace ucoo { @@ -49,7 +47,7 @@ static const uint32_t sector_addr[] = { int self_programming_flash_size () { - return DESIG_FLASH_SIZE * 1024; + return reg::DESIG->FLASH_SIZE * 1024; } int @@ -69,19 +67,28 @@ self_programming_erase (uint32_t addr, int count) assert (static_cast (addr - sector_addr[0] + count) <= self_programming_flash_size ()); int sector; + reg::FLASH->CR = FLASH_CR_LOCK; + reg::FLASH->KEYR = FLASH_KEYR_KEY1; + reg::FLASH->KEYR = FLASH_KEYR_KEY2; + while (reg::FLASH->SR & FLASH_SR_BSY) + ; + reg::FLASH->CR = FLASH_CR_PSIZE_x32; for (sector = 0; count && sector < lengthof (sector_addr); sector++) { if (addr == sector_addr[sector]) { int snb = sector >= 12 ? sector + 16 - 12 : sector; - flash_unlock (); - flash_erase_sector (snb, FLASH_CR_PROGRAM_X32); - flash_lock (); + reg::FLASH->CR = FLASH_CR_PSIZE_x32 | (snb * FLASH_CR_SNB_0) + | FLASH_CR_SER; + reg::FLASH->CR |= FLASH_CR_STRT; + while (reg::FLASH->SR & FLASH_SR_BSY) + ; int sector_size = sector_addr[sector + 1] - addr; addr += sector_size; count -= sector_size; } } + reg::FLASH->CR = FLASH_CR_LOCK; assert (count == 0); } @@ -93,11 +100,20 @@ self_programming_write (uint32_t addr, const char *buf, int count) assert (count % 4 == 0); assert (static_cast (addr - sector_addr[0] + count) <= self_programming_flash_size ()); - flash_unlock (); + reg::FLASH->CR = FLASH_CR_LOCK; + reg::FLASH->KEYR = FLASH_KEYR_KEY1; + reg::FLASH->KEYR = FLASH_KEYR_KEY2; + while (reg::FLASH->SR & FLASH_SR_BSY) + ; + reg::FLASH->CR = FLASH_CR_PSIZE_x32 | FLASH_CR_PG; for (int i = 0; i < count; i += 4) - flash_program_word (addr + i, - *reinterpret_cast (buf + i)); - flash_lock (); + { + *reinterpret_cast (addr + i) = + *reinterpret_cast (buf + i); + while (reg::FLASH->SR & FLASH_SR_BSY) + ; + } + reg::FLASH->CR = FLASH_CR_LOCK; } } // namespace ucoo diff --git a/ucoo/hal/spi/spi_hard.stm32.cc b/ucoo/hal/spi/spi_hard.stm32.cc index 264647d..898a565 100644 --- a/ucoo/hal/spi/spi_hard.stm32.cc +++ b/ucoo/hal/spi/spi_hard.stm32.cc @@ -22,10 +22,7 @@ // // }}} #include "ucoo/hal/spi/spi_hard.stm32.hh" - -#include -#include - +#include "ucoo/arch/rcc.stm32.hh" #include "ucoo/common.hh" namespace ucoo { @@ -34,34 +31,33 @@ namespace ucoo { struct spi_hardware_t { /// SPI base address. - uint32_t base; - /// APB number. - int apb; - /// Clock enable identifier. - enum rcc_periph_clken clken; + SPI_TypeDef *base; + /// APB bus. + Bus bus; + /// RCC identifier. + Rcc rcc; }; /// Information on SPI hardware array, this is zero indexed. static const spi_hardware_t spi_hardware[] = { - { SPI1, 2, RCC_SPI1 }, - { SPI2, 1, RCC_SPI2 }, - { SPI3, 1, RCC_SPI3 }, + { reg::SPI1, Bus::APB2, Rcc::SPI1 }, + { reg::SPI2, Bus::APB1, Rcc::SPI2 }, + { reg::SPI3, Bus::APB1, Rcc::SPI3 }, #ifdef SPI4_BASE - { SPI4, 2, RCC_SPI4 }, + { reg::SPI4, Bus::APB2, Rcc::SPI4 }, #endif #ifdef SPI5_BASE - { SPI5, 2, RCC_SPI5 }, + { reg::SPI5, Bus::APB2, Rcc::SPI5 }, #endif #ifdef SPI6_BASE - { SPI6, 2, RCC_SPI6 }, + { reg::SPI6, Bus::APB2, Rcc::SPI6 }, #endif }; -SpiHardMaster::SpiHardMaster (int n) - : n_ (n), enabled_ (false) +SpiHardMaster::SpiHardMaster (SpiHardMaster::Instance inst) + : n_ (static_cast (inst)), enabled_ (false) { - assert (n < lengthof (spi_hardware)); } SpiHardMaster::~SpiHardMaster () @@ -73,24 +69,24 @@ void SpiHardMaster::enable (int speed_hz, SpiMode mode) { enabled_ = true; - uint32_t base = spi_hardware[n_].base; + auto base = spi_hardware[n_].base; // Turn on. - rcc_periph_clock_enable (spi_hardware[n_].clken); + rcc_peripheral_clock_enable (spi_hardware[n_].rcc); // Compute speed, rounded down. - int apb_freq = spi_hardware[n_].apb == 1 ? rcc_apb1_frequency - : rcc_apb2_frequency; - int freq = apb_freq / 2; + int apb_freq_hz = spi_hardware[n_].bus == Bus::APB1 ? rcc_apb1_freq_hz + : rcc_apb2_freq_hz; + int freq_hz = apb_freq_hz / 2; int br = 0; - while (freq > speed_hz) + while (freq_hz > speed_hz) { - freq = freq / 2; + freq_hz = freq_hz / 2; br++; - assert (br <= SPI_CR1_BR_FPCLK_DIV_256); + assert (br <= 7); } // Set parameters and enable. - SPI_CR2 (base) = 0; - SPI_CR1 (base) = SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | (br << 3) - | SPI_CR1_MSTR | mode; + base->CR2 = 0; + base->CR1 = SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE + | (br << SPI_CR1_BR_Pos) | SPI_CR1_MSTR | mode; } void @@ -99,11 +95,11 @@ SpiHardMaster::disable () if (enabled_) { enabled_ = false; - uint32_t base = spi_hardware[n_].base; + auto base = spi_hardware[n_].base; // Stop SPI. - SPI_CR1 (base) = 0; + base->CR1 = 0; // Turn off. - rcc_periph_clock_disable (spi_hardware[n_].clken); + rcc_peripheral_clock_disable (spi_hardware[n_].rcc); } } @@ -111,13 +107,13 @@ void SpiHardMaster::send_and_recv (const char *tx_buf, char *rx_buf, int count) { assert (enabled_); - uint32_t base = spi_hardware[n_].base; + auto base = spi_hardware[n_].base; while (count--) { - SPI_DR (base) = *tx_buf++; - while (!(SPI_SR (base) & SPI_SR_RXNE)) + base->DR = *tx_buf++; + while (!(base->SR & SPI_SR_RXNE)) ; - *rx_buf++ = SPI_DR (base); + *rx_buf++ = base->DR; } } @@ -125,31 +121,31 @@ void SpiHardMaster::send (const char *tx_buf, int count) { assert (enabled_); - uint32_t base = spi_hardware[n_].base; + auto base = spi_hardware[n_].base; while (count--) { - SPI_DR (base) = *tx_buf++; - while (!(SPI_SR (base) & SPI_SR_TXE)) + base->DR = *tx_buf++; + while (!(base->SR & SPI_SR_TXE)) ; } // Wait for end of transfer. - while (SPI_SR (base) & SPI_SR_BSY) + while (base->SR & SPI_SR_BSY) ; // Clear RXNE. - (void) SPI_DR (base); + (void) base->DR; } void SpiHardMaster::recv (char *rx_buf, int count) { assert (enabled_); - uint32_t base = spi_hardware[n_].base; + auto base = spi_hardware[n_].base; while (count--) { - SPI_DR (base) = 0; - while (!(SPI_SR (base) & SPI_SR_RXNE)) + base->DR = 0; + while (!(base->SR & SPI_SR_RXNE)) ; - *rx_buf++ = SPI_DR (base); + *rx_buf++ = base->DR; } } diff --git a/ucoo/hal/spi/spi_hard.stm32.hh b/ucoo/hal/spi/spi_hard.stm32.hh index 7576be5..13d24d3 100644 --- a/ucoo/hal/spi/spi_hard.stm32.hh +++ b/ucoo/hal/spi/spi_hard.stm32.hh @@ -24,6 +24,7 @@ // // }}} #include "ucoo/intf/spi_master.hh" +#include "ucoo/arch/reg.hh" namespace ucoo { @@ -31,8 +32,24 @@ namespace ucoo { class SpiHardMaster : public SpiMaster { public: - /// Constructor for the Nth SPI. - SpiHardMaster (int n); + enum class Instance + { + SPI1, + SPI2, + SPI3, +#ifdef SPI4_BASE + SPI4, +#endif +#ifdef SPI5_BASE + SPI5, +#endif +#ifdef SPI6_BASE + SPI6, +#endif + }; + public: + /// Constructor for an SPI instance. + SpiHardMaster (Instance inst); /// Destructor, disable. ~SpiHardMaster (); /// See SpiMaster::enable. diff --git a/ucoo/hal/spi/test/test_spi.cc b/ucoo/hal/spi/test/test_spi.cc index de155b7..73225ce 100644 --- a/ucoo/hal/spi/test/test_spi.cc +++ b/ucoo/hal/spi/test/test_spi.cc @@ -29,8 +29,6 @@ #include "ucoo/arch/arch.hh" #include "ucoo/base/test/test.hh" -#include - #include #include @@ -41,15 +39,16 @@ main (int argc, const char **argv) ucoo::Stream &ts = ucoo::test_stream (); // Use connection to LIS302DL device on discovery board revision MB997B. // Revision MB997C uses a different device. - rcc_periph_clock_enable (RCC_GPIOA); - rcc_periph_clock_enable (RCC_GPIOE); - ucoo::Gpio ss (GPIOE, 3); + ucoo::GPIOA.enable (); + ucoo::GPIOE.enable (); + ucoo::Gpio ss = ucoo::GPIOE[3]; ss.set (); ss.output (); - ucoo::Gpio sck (GPIOA, 5), mosi (GPIOA, 7), miso (GPIOA, 6); + ucoo::Gpio sck = ucoo::GPIOA[5], mosi = ucoo::GPIOA[7], + miso = ucoo::GPIOA[6]; ucoo::SpiSoftMaster spis (sck, mosi, miso); spis.enable (1000000, ucoo::SPI_MODE_3); - ucoo::SpiHardMaster spih (0); + ucoo::SpiHardMaster spih (ucoo::SpiHardMaster::Instance::SPI1); ucoo::SpiMaster *spi = &spis; // Loop with simple IU. char buf[64]; @@ -115,9 +114,9 @@ main (int argc, const char **argv) case 'h': spi->disable (); spi = &spih; - gpio_mode_setup (GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, - GPIO5 | GPIO6 | GPIO7); - gpio_set_af (GPIOA, GPIO_AF5, GPIO5 | GPIO6 | GPIO7); + miso.af (5); + mosi.af (5); + sck.af (5); spi->enable (1000000, ucoo::SPI_MODE_3); break; case 's': diff --git a/ucoo/hal/timer/test/Makefile b/ucoo/hal/timer/test/Makefile index 284ad18..c0d1b49 100644 --- a/ucoo/hal/timer/test/Makefile +++ b/ucoo/hal/timer/test/Makefile @@ -4,6 +4,6 @@ TARGETS = stm32f4 PROGS = test_timer test_timer_SOURCES = test_timer.cc -MODULES = ucoo/base/test ucoo/hal/timer ucoo/hal/usb ucoo/utils +MODULES = ucoo/base/test ucoo/hal/timer ucoo/hal/usb ucoo/hal/gpio ucoo/utils include $(BASE)/build/top.mk diff --git a/ucoo/hal/timer/test/test_timer.cc b/ucoo/hal/timer/test/test_timer.cc index e02e92f..f3b47e8 100644 --- a/ucoo/hal/timer/test/test_timer.cc +++ b/ucoo/hal/timer/test/test_timer.cc @@ -24,9 +24,7 @@ #include "ucoo/base/test/test.hh" #include "ucoo/arch/arch.hh" #include "ucoo/hal/timer/timer.hh" - -#include -#include +#include "ucoo/hal/gpio/gpio.hh" #include @@ -39,17 +37,19 @@ main (int argc, const char **argv) // PD15 (T4_CH4). ucoo::arch_init (argc, argv); ucoo::test_stream_setup (); - using Timer = ucoo::TimerHard; - using TimerRef = ucoo::TimerHard; + using Timer = ucoo::TimerHard; + using TimerRef = ucoo::TimerHard; Timer timer; TimerRef timerref; // AF setup. - rcc_periph_clock_enable (RCC_GPIOA); - rcc_periph_clock_enable (RCC_GPIOD); - gpio_mode_setup (GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLDOWN, GPIO1); - gpio_set_af (GPIOA, GPIO_AF1, GPIO1); - gpio_mode_setup (GPIOD, GPIO_MODE_AF, GPIO_PUPD_PULLDOWN, GPIO13 | GPIO15); - gpio_set_af (GPIOD, GPIO_AF2, GPIO13 | GPIO15); + ucoo::GPIOA.enable (); + ucoo::GPIOD.enable (); + ucoo::GPIOA[1].af (1); + ucoo::GPIOA[1].pull (ucoo::Gpio::Pull::DOWN); + ucoo::GPIOD[13].af (2); + ucoo::GPIOD[13].pull (ucoo::Gpio::Pull::DOWN); + ucoo::GPIOD[15].af (2); + ucoo::GPIOD[15].pull (ucoo::Gpio::Pull::DOWN); // Timers setup. timerref.enable, TimerRef::OptionOutputCompare<2> > (2000); diff --git a/ucoo/hal/timer/timer.stm32.hh b/ucoo/hal/timer/timer.stm32.hh index 85941e4..6b2f2dd 100644 --- a/ucoo/hal/timer/timer.stm32.hh +++ b/ucoo/hal/timer/timer.stm32.hh @@ -27,8 +27,23 @@ namespace ucoo { +/// Available instances. +enum class TimerInstance +{ + TIM1, + TIM2, + TIM3, + TIM4, + TIM5, + TIM10, + TIM11, +}; + +template +struct TimerHardware { }; + /// STM32 timer very basic support. -template +template class TimerHard { public: @@ -39,7 +54,7 @@ class TimerHard ~TimerHard (); /// Enable timer. template - void enable (int freq, bool start = true); + void enable (int freq_hz, bool start = true); /// Disable timer. void disable (); /// Manually start timer. @@ -57,7 +72,7 @@ class TimerHard /// Wait until a new input capture value is available. void wait_input_capture (int ch) const; /// Get timer frequency. - int get_freq () const { return freq_; } + int get_freq_hz () const { return freq_hz_; } /// Enable interrupts on update event. void enable_interrupt (); /// Disable interrupts on update event. @@ -95,11 +110,11 @@ class TimerHard { public: /// Disable updates. - UpdateDisabled (TimerHard &timer); + UpdateDisabled (TimerHard &timer); /// Enable updates. ~UpdateDisabled (); private: - TimerHard &timer_; + TimerHard &timer_; }; friend class UpdateDisabled; /// Base class for options. @@ -129,7 +144,9 @@ class TimerHard struct CombinedOptions; private: /// When enabled, programmed frequency. - int freq_ = 0; + int freq_hz_ = 0; + /// Hardware characteristics. + using Hard = TimerHardware; }; } // namespace ucoo diff --git a/ucoo/hal/timer/timer.stm32.tcc b/ucoo/hal/timer/timer.stm32.tcc index 9adc8d2..3a0b4ef 100644 --- a/ucoo/hal/timer/timer.stm32.tcc +++ b/ucoo/hal/timer/timer.stm32.tcc @@ -23,22 +23,19 @@ // DEALINGS IN THE SOFTWARE. // // }}} -#include -#include +#include "ucoo/arch/rcc.stm32.hh" +#include "ucoo/arch/reg.hh" namespace ucoo { -template -struct TimerHardware { }; - // TODO add more timers. -// TODO timer uses a double frequency only if APB prescaler is not 1. template<> -struct TimerHardware +struct TimerHardware { - static const enum rcc_periph_clken clken = RCC_TIM1; - static int freq () { return 2 * rcc_apb2_frequency; } + static constexpr TIM_TypeDef *base = reg::TIM1; + static const Rcc rcc = Rcc::TIM1; + static int freq_hz () { return rcc_apb2_timer_freq_hz; } static const unsigned int max = 0xffff; static const bool advanced = true; static const bool slave = true; @@ -47,10 +44,11 @@ struct TimerHardware }; template<> -struct TimerHardware +struct TimerHardware { - static const enum rcc_periph_clken clken = RCC_TIM2; - static int freq () { return 2 * rcc_apb1_frequency; } + static constexpr TIM_TypeDef *base = reg::TIM2; + static const Rcc rcc = Rcc::TIM2; + static int freq_hz () { return rcc_apb1_timer_freq_hz; } #if defined TARGET_stm32f4 static const unsigned int max = 0xffffffff; #else @@ -63,10 +61,11 @@ struct TimerHardware }; template<> -struct TimerHardware +struct TimerHardware { - static const enum rcc_periph_clken clken = RCC_TIM3; - static int freq () { return 2 * rcc_apb1_frequency; } + static constexpr TIM_TypeDef *base = reg::TIM3; + static const Rcc rcc = Rcc::TIM3; + static int freq_hz () { return rcc_apb1_timer_freq_hz; } static const unsigned int max = 0xffff; static const bool advanced = false; static const bool slave = true; @@ -75,10 +74,11 @@ struct TimerHardware }; template<> -struct TimerHardware +struct TimerHardware { - static const enum rcc_periph_clken clken = RCC_TIM4; - static int freq () { return 2 * rcc_apb1_frequency; } + static constexpr TIM_TypeDef *base = reg::TIM4; + static const Rcc rcc = Rcc::TIM4; + static int freq_hz () { return rcc_apb1_timer_freq_hz; } static const unsigned int max = 0xffff; static const bool advanced = false; static const bool slave = true; @@ -87,10 +87,11 @@ struct TimerHardware }; template<> -struct TimerHardware +struct TimerHardware { - static const enum rcc_periph_clken clken = RCC_TIM5; - static int freq () { return 2 * rcc_apb1_frequency; } + static constexpr TIM_TypeDef *base = reg::TIM5; + static const Rcc rcc = Rcc::TIM5; + static int freq_hz () { return rcc_apb1_timer_freq_hz; } #if defined TARGET_stm32f4 static const unsigned int max = 0xffffffff; #else @@ -103,10 +104,11 @@ struct TimerHardware }; template<> -struct TimerHardware +struct TimerHardware { - static const enum rcc_periph_clken clken = RCC_TIM10; - static int freq () { return 2 * rcc_apb2_frequency; } + static constexpr TIM_TypeDef *base = reg::TIM10; + static const Rcc rcc = Rcc::TIM10; + static int freq_hz () { return rcc_apb2_timer_freq_hz; } static const unsigned int max = 0xffff; static const bool advanced = false; static const bool slave = false; @@ -115,10 +117,11 @@ struct TimerHardware }; template<> -struct TimerHardware +struct TimerHardware { - static const enum rcc_periph_clken clken = RCC_TIM10; - static int freq () { return 2 * rcc_apb2_frequency; } + static constexpr TIM_TypeDef *base = reg::TIM11; + static const Rcc rcc = Rcc::TIM10; + static int freq_hz () { return rcc_apb2_timer_freq_hz; } static const unsigned int max = 0xffff; static const bool advanced = false; static const bool slave = false; @@ -126,175 +129,174 @@ struct TimerHardware static const int channels = 1; }; -template -const unsigned int TimerHard::max = TimerHardware::max; +template +const unsigned int TimerHard::max = TimerHardware::max; -template -TimerHard::~TimerHard () +template +TimerHard::~TimerHard () { disable (); } -template +template template void -TimerHard::enable (int freq, bool start) -{ - using Hard = TimerHardware; - rcc_periph_clock_enable (Hard::clken); - int in_freq = Hard::freq (); - int div = in_freq / freq; - assert (div <= 0x10000 && in_freq / div == freq); - freq_ = freq; +TimerHard::enable (int freq_hz, bool start) +{ + rcc_peripheral_clock_enable (Hard::rcc); + int in_freq_hz = Hard::freq_hz (); + int div = in_freq_hz / freq_hz; + assert (div <= 0x10000 && in_freq_hz / div == freq_hz); + freq_hz_ = freq_hz; using OptionsCombined = CombinedOptions; - TIM_CR1 (Base) = TIM_CR1_ARPE | OptionsCombined::cr1; - TIM_CR2 (Base) = 0; + Hard::base->CR1 = TIM_CR1_ARPE | OptionsCombined::cr1; + Hard::base->CR2 = 0; if (Hard::slave) - TIM_SMCR (Base) = OptionsCombined::smcr; + Hard::base->SMCR = OptionsCombined::smcr; if (Hard::channels > 0) { - TIM_CCER (Base) = 0; - TIM_CCMR1 (Base) = OptionsCombined::ccmr1; + Hard::base->CCER = 0; + Hard::base->CCMR1 = OptionsCombined::ccmr1; if (Hard::channels > 2) - TIM_CCMR2 (Base) = OptionsCombined::ccmr2; - TIM_CCER (Base) = OptionsCombined::ccer; - TIM_CCR1 (Base) = OptionsCombined::ccr1; + Hard::base->CCMR2 = OptionsCombined::ccmr2; + Hard::base->CCER = OptionsCombined::ccer; + Hard::base->CCR1 = OptionsCombined::ccr1; if (Hard::channels >= 2) - TIM_CCR2 (Base) = OptionsCombined::ccr2; + Hard::base->CCR2 = OptionsCombined::ccr2; if (Hard::channels >= 3) - TIM_CCR3 (Base) = OptionsCombined::ccr3; + Hard::base->CCR3 = OptionsCombined::ccr3; if (Hard::channels >= 4) - TIM_CCR4 (Base) = OptionsCombined::ccr4; + Hard::base->CCR4 = OptionsCombined::ccr4; } - TIM_PSC (Base) = div - 1; - TIM_ARR (Base) = OptionsCombined::arr ? OptionsCombined::arr : Hard::max; + Hard::base->PSC = div - 1; + Hard::base->ARR = OptionsCombined::arr ? OptionsCombined::arr : Hard::max; if (Hard::advanced) - TIM_BDTR (Base) = TIM_BDTR_MOE; - TIM_EGR (Base) = TIM_EGR_UG; + Hard::base->BDTR = TIM_BDTR_MOE; + Hard::base->EGR = TIM_EGR_UG; if (start) - TIM_CR1 (Base) = TIM_CR1_ARPE | OptionsCombined::cr1 | TIM_CR1_CEN; + Hard::base->CR1 = TIM_CR1_ARPE | OptionsCombined::cr1 | TIM_CR1_CEN; } -template +template void -TimerHard::disable () +TimerHard::disable () { - TIM_CR1 (Base) = 0; - rcc_periph_clock_disable (TimerHardware::clken); - freq_ = 0; + Hard::base->CR1 = 0; + rcc_peripheral_clock_disable (TimerHardware::rcc); + freq_hz_ = 0; } -template +template void -TimerHard::start () +TimerHard::start () { - TIM_CR1 (Base) |= TIM_CR1_CEN; + Hard::base->CR1 |= TIM_CR1_CEN; } -template +template void -TimerHard::set_reload (unsigned int value) +TimerHard::set_reload (unsigned int value) { - assert (value <= TimerHardware::max); - TIM_ARR (Base) = value; + assert (value <= Hard::max); + Hard::base->ARR = value; } -template +template void -TimerHard::set_output_compare (int ch, unsigned int value) +TimerHard::set_output_compare (int ch, unsigned int value) { - assert (ch > 0 && ch <= TimerHardware::channels); - assert (value <= TimerHardware::max); - *(&TIM_CCR1 (Base) + ch - 1) = value; + assert (ch > 0 && ch <= Hard::channels); + assert (value <= Hard::max); + *(&Hard::base->CCR1 + ch - 1) = value; } -template +template unsigned int -TimerHard::get_value () +TimerHard::get_value () { - return TIM_CNT (Base); + return Hard::base->CNT; } -template +template unsigned int -TimerHard::get_input_capture (int ch) const +TimerHard::get_input_capture (int ch) const { - assert (ch > 0 && ch <= TimerHardware::channels); - return *(&TIM_CCR1 (Base) + ch - 1); + assert (ch > 0 && ch <= Hard::channels); + return *(&Hard::base->CCR1 + ch - 1); } -template +template void -TimerHard::wait_update () const +TimerHard::wait_update () const { unsigned int mask = TIM_SR_UIF; - while (!(TIM_SR (Base) & mask)) + while (!(Hard::base->SR & mask)) ; - TIM_SR (Base) = ~mask; + Hard::base->SR = ~mask; } -template +template void -TimerHard::wait_input_capture (int ch) const +TimerHard::wait_input_capture (int ch) const { unsigned int mask = TIM_SR_CC1IF >> 1 << ch; - while (!(TIM_SR (Base) & mask)) + while (!(Hard::base->SR & mask)) ; - TIM_SR (Base) = ~mask; + Hard::base->SR = ~mask; } -template +template void -TimerHard::enable_interrupt () +TimerHard::enable_interrupt () { - TIM_DIER (Base) |= TIM_DIER_UIE; + Hard::base->DIER |= TIM_DIER_UIE; } -template +template void -TimerHard::disable_interrupt () +TimerHard::disable_interrupt () { - TIM_DIER (Base) &= ~TIM_DIER_UIE; + Hard::base->DIER &= ~TIM_DIER_UIE; } -template +template void -TimerHard::clear_interrupt () +TimerHard::clear_interrupt () { - TIM_SR (Base) = ~TIM_SR_UIF; + Hard::base->SR = ~TIM_SR_UIF; } -template +template void -TimerHard::enable_updates () +TimerHard::enable_updates () { - TIM_CR1 (Base) &= ~TIM_CR1_UDIS; + Hard::base->CR1 &= ~TIM_CR1_UDIS; } -template +template void -TimerHard::disable_updates () +TimerHard::disable_updates () { - TIM_CR1 (Base) |= TIM_CR1_UDIS; + Hard::base->CR1 |= TIM_CR1_UDIS; } -template -TimerHard::UpdateDisabled::UpdateDisabled (TimerHard &timer) +template +TimerHard::UpdateDisabled::UpdateDisabled (TimerHard &timer) : timer_ (timer) { timer_.disable_updates (); } -template -TimerHard::UpdateDisabled::~UpdateDisabled () +template +TimerHard::UpdateDisabled::~UpdateDisabled () { timer_.enable_updates (); } -template -struct TimerHard::Option +template +struct TimerHard::Option { - static const uint32_t base = Base; + static const TimerInstance opinst = inst; static const unsigned cr1 = 0; static const unsigned smcr = 0; static const unsigned ccmr1 = 0; @@ -307,214 +309,214 @@ struct TimerHard::Option static const unsigned ccr4 = 0; }; -template +template template -struct TimerHard::OptionReloadValue : public TimerHard::Option +struct TimerHard::OptionReloadValue : public TimerHard::Option { - static_assert (value <= TimerHardware::max, "value too large"); + static_assert (value <= Hard::max, "value too large"); static const unsigned arr = value; }; -template -struct TimerHard::OptionOnePulse : public TimerHard::Option +template +struct TimerHard::OptionOnePulse : public TimerHard::Option { - static_assert (TimerHardware::one_pulse, "no one pulse mode"); + static_assert (Hard::one_pulse, "no one pulse mode"); static const unsigned cr1 = TIM_CR1_OPM; }; -template +template template -struct TimerHard::OptionTrigger : public TimerHard::Option +struct TimerHard::OptionTrigger : public TimerHard::Option { - static_assert (TimerHardware::slave, "no external trigger"); + static_assert (Hard::slave, "no external trigger"); static_assert (timer_input >= 1 && timer_input <= 2, "no such input"); static const unsigned smcr = - (timer_input == 1 ? TIM_SMCR_TS_IT1FP1 : TIM_SMCR_TS_IT1FP2) + (timer_input == 1 ? TIM_SMCR_TS_TI1FP1 : TIM_SMCR_TS_TI1FP2) | TIM_SMCR_SMS_TM; }; -template -template::Filter filter, - typename TimerHard::Map map, - typename TimerHard::Polarity polarity> -struct TimerHard::OptionInputCapture +template +template::Filter filter, + typename TimerHard::Map map, + typename TimerHard::Polarity polarity> +struct TimerHard::OptionInputCapture { static_assert (channel == 1, "no such channel"); // Always activate input capture (through Polarity values), even when only // used as a trigger, no harm is done. }; -template -template::Filter filter, - typename TimerHard::Map map, - typename TimerHard::Polarity polarity> -struct TimerHard::OptionInputCapture<1, filter, map, polarity> - : public TimerHard::Option +template +template::Filter filter, + typename TimerHard::Map map, + typename TimerHard::Polarity polarity> +struct TimerHard::OptionInputCapture<1, filter, map, polarity> + : public TimerHard::Option { - static_assert (1 <= TimerHardware::channels, "no such channel"); + static_assert (1 <= TimerHard::Hard::channels, "no such channel"); static const unsigned ccmr1 = (static_cast (filter) - * (TIM_CCMR1_IC1F_MASK & ~(TIM_CCMR1_IC1F_MASK - 1))) + * (TIM_CCMR1_IC1F & ~(TIM_CCMR1_IC1F - 1))) | (static_cast (map) - * (TIM_CCMR1_CC1S_MASK & ~(TIM_CCMR1_CC1S_MASK - 1))); + * (TIM_CCMR1_CC1S & ~(TIM_CCMR1_CC1S - 1))); static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC1E; }; -template -template::Filter filter, - typename TimerHard::Map map, - typename TimerHard::Polarity polarity> -struct TimerHard::OptionInputCapture<2, filter, map, polarity> - : public TimerHard::Option +template +template::Filter filter, + typename TimerHard::Map map, + typename TimerHard::Polarity polarity> +struct TimerHard::OptionInputCapture<2, filter, map, polarity> + : public TimerHard::Option { - static_assert (2 <= TimerHardware::channels, "no such channel"); + static_assert (2 <= TimerHard::Hard::channels, "no such channel"); static const unsigned ccmr1 = (static_cast (filter) - * (TIM_CCMR1_IC2F_MASK & ~(TIM_CCMR1_IC2F_MASK - 1))) + * (TIM_CCMR1_IC2F & ~(TIM_CCMR1_IC2F - 1))) | (static_cast (map) - * (TIM_CCMR1_CC2S_MASK & ~(TIM_CCMR1_CC2S_MASK - 1))); + * (TIM_CCMR1_CC2S & ~(TIM_CCMR1_CC2S - 1))); static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC2E; }; -template -template::Filter filter, - typename TimerHard::Map map, - typename TimerHard::Polarity polarity> -struct TimerHard::OptionInputCapture<3, filter, map, polarity> - : public TimerHard::Option +template +template::Filter filter, + typename TimerHard::Map map, + typename TimerHard::Polarity polarity> +struct TimerHard::OptionInputCapture<3, filter, map, polarity> + : public TimerHard::Option { - static_assert (3 <= TimerHardware::channels, "no such channel"); + static_assert (3 <= TimerHard::Hard::channels, "no such channel"); static const unsigned ccmr2 = (static_cast (filter) - * (TIM_CCMR2_IC3F_MASK & ~(TIM_CCMR2_IC3F_MASK - 1))) + * (TIM_CCMR2_IC3F & ~(TIM_CCMR2_IC3F - 1))) | (static_cast (map) - * (TIM_CCMR2_CC3S_MASK & ~(TIM_CCMR2_CC3S_MASK - 1))); + * (TIM_CCMR2_CC3S & ~(TIM_CCMR2_CC3S - 1))); static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC3E; }; -template -template::Filter filter, - typename TimerHard::Map map, - typename TimerHard::Polarity polarity> -struct TimerHard::OptionInputCapture<4, filter, map, polarity> - : public TimerHard::Option +template +template::Filter filter, + typename TimerHard::Map map, + typename TimerHard::Polarity polarity> +struct TimerHard::OptionInputCapture<4, filter, map, polarity> + : public TimerHard::Option { - static_assert (4 <= TimerHardware::channels, "no such channel"); + static_assert (4 <= TimerHard::Hard::channels, "no such channel"); static const unsigned ccmr2 = (static_cast (filter) - * (TIM_CCMR2_IC4F_MASK & ~(TIM_CCMR2_IC4F_MASK - 1))) + * (TIM_CCMR2_IC4F & ~(TIM_CCMR2_IC4F - 1))) | (static_cast (map) - * (TIM_CCMR2_CC4S_MASK & ~(TIM_CCMR2_CC4S_MASK - 1))); + * (TIM_CCMR2_CC4S & ~(TIM_CCMR2_CC4S - 1))); static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC4E; }; -template -template::Polarity polarity> -struct TimerHard::OptionOutputCompare +template +template::Polarity polarity> +struct TimerHard::OptionOutputCompare { static_assert (channel == 1, "no such channel"); }; -template -template::Polarity polarity> -struct TimerHard::OptionOutputCompare<1, polarity> - : public TimerHard::Option +template +template::Polarity polarity> +struct TimerHard::OptionOutputCompare<1, polarity> + : public TimerHard::Option { - static_assert (1 <= TimerHardware::channels, "no such channel"); + static_assert (1 <= TimerHard::Hard::channels, "no such channel"); static const unsigned ccmr1 = TIM_CCMR1_OC1M_PWM1 | TIM_CCMR1_OC1PE; static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC1E; }; -template -template::Polarity polarity> -struct TimerHard::OptionOutputCompare<2, polarity> - : public TimerHard::Option +template +template::Polarity polarity> +struct TimerHard::OptionOutputCompare<2, polarity> + : public TimerHard::Option { - static_assert (2 <= TimerHardware::channels, "no such channel"); + static_assert (2 <= TimerHard::Hard::channels, "no such channel"); static const unsigned ccmr1 = TIM_CCMR1_OC2M_PWM1 | TIM_CCMR1_OC2PE; static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC2E; }; -template -template::Polarity polarity> -struct TimerHard::OptionOutputCompare<3, polarity> - : public TimerHard::Option +template +template::Polarity polarity> +struct TimerHard::OptionOutputCompare<3, polarity> + : public TimerHard::Option { - static_assert (3 <= TimerHardware::channels, "no such channel"); + static_assert (3 <= TimerHard::Hard::channels, "no such channel"); static const unsigned ccmr2 = TIM_CCMR2_OC3M_PWM1 | TIM_CCMR2_OC3PE; static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC3E; }; -template -template::Polarity polarity> -struct TimerHard::OptionOutputCompare<4, polarity> - : public TimerHard::Option +template +template::Polarity polarity> +struct TimerHard::OptionOutputCompare<4, polarity> + : public TimerHard::Option { - static_assert (4 <= TimerHardware::channels, "no such channel"); + static_assert (4 <= TimerHard::Hard::channels, "no such channel"); static const unsigned ccmr2 = TIM_CCMR2_OC4M_PWM1 | TIM_CCMR2_OC4PE; static const unsigned ccer = static_cast (polarity) * TIM_CCER_CC4E; }; -template +template template -struct TimerHard::OptionOutputCompareValue - : public TimerHard::Option +struct TimerHard::OptionOutputCompareValue + : public TimerHard::Option { static_assert (channel == 1, "no such channel"); }; -template +template template -struct TimerHard::OptionOutputCompareValue<1, value> - : public TimerHard::Option +struct TimerHard::OptionOutputCompareValue<1, value> + : public TimerHard::Option { - static_assert (1 <= TimerHardware::channels, "no such channel"); - static_assert (value <= TimerHardware::max, "value too large"); + static_assert (1 <= TimerHard::Hard::channels, "no such channel"); + static_assert (value <= TimerHard::Hard::max, "value too large"); static const unsigned ccr1 = value; }; -template +template template -struct TimerHard::OptionOutputCompareValue<2, value> - : public TimerHard::Option +struct TimerHard::OptionOutputCompareValue<2, value> + : public TimerHard::Option { - static_assert (2 <= TimerHardware::channels, "no such channel"); - static_assert (value <= TimerHardware::max, "value too large"); + static_assert (2 <= TimerHard::Hard::channels, "no such channel"); + static_assert (value <= TimerHard::Hard::max, "value too large"); static const unsigned ccr2 = value; }; -template +template template -struct TimerHard::OptionOutputCompareValue<3, value> - : public TimerHard::Option +struct TimerHard::OptionOutputCompareValue<3, value> + : public TimerHard::Option { - static_assert (3 <= TimerHardware::channels, "no such channel"); - static_assert (value <= TimerHardware::max, "value too large"); + static_assert (3 <= TimerHard::Hard::channels, "no such channel"); + static_assert (value <= TimerHard::Hard::max, "value too large"); static const unsigned ccr3 = value; }; -template +template template -struct TimerHard::OptionOutputCompareValue<4, value> - : public TimerHard::Option +struct TimerHard::OptionOutputCompareValue<4, value> + : public TimerHard::Option { - static_assert (4 <= TimerHardware::channels, "no such channel"); - static_assert (value <= TimerHardware::max, "value too large"); + static_assert (4 <= TimerHard::Hard::channels, "no such channel"); + static_assert (value <= TimerHard::Hard::max, "value too large"); static const unsigned ccr4 = value; }; -template +template template -struct TimerHard::CombinedOptions : public TimerHard::Option +struct TimerHard::CombinedOptions : public TimerHard::Option { }; -template +template template -struct TimerHard::CombinedOptions +struct TimerHard::CombinedOptions { - static_assert (Base == Option::base, "option for another timer"); - static const uint32_t base = Base; + static_assert (inst == Option::opinst, "option for another timer"); + static const TimerInstance opinst = inst; static const unsigned cr1 = Option::cr1 | CombinedOptions::cr1; static const unsigned smcr = Option::smcr diff --git a/ucoo/hal/uart/test/Makefile b/ucoo/hal/uart/test/Makefile index b1e9c71..4e00795 100644 --- a/ucoo/hal/uart/test/Makefile +++ b/ucoo/hal/uart/test/Makefile @@ -6,7 +6,7 @@ stm32f4_PROGS = test_uart_disc test_uart_SOURCES = test_uart.cc test_uart_disc_SOURCES = test_uart_disc.cc -MODULES = ucoo/hal/uart +MODULES = ucoo/hal/uart ucoo/hal/gpio test_uart_disc_MODULES = $(MODULES) ucoo/base/test ucoo/hal/usb include $(BASE)/build/top.mk diff --git a/ucoo/hal/uart/test/test_uart.cc b/ucoo/hal/uart/test/test_uart.cc index 6236938..b43b797 100644 --- a/ucoo/hal/uart/test/test_uart.cc +++ b/ucoo/hal/uart/test/test_uart.cc @@ -26,7 +26,6 @@ #include "ucoo/arch/arch.hh" #if defined (TARGET_stm32) -# include # include "ucoo/hal/gpio/gpio.hh" #endif @@ -41,14 +40,14 @@ main (int argc, const char **argv) #elif defined (TARGET_stm32) // D8, D9: UART3 // C12, D2: UART5 - rcc_periph_clock_enable (RCC_GPIOC); - rcc_periph_clock_enable (RCC_GPIOD); - gpio_mode_setup (GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO12); - gpio_mode_setup (GPIOD, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO2 | GPIO8 | GPIO9); - gpio_set_af (GPIOC, GPIO_AF8, GPIO12); - gpio_set_af (GPIOD, GPIO_AF8, GPIO2); - gpio_set_af (GPIOD, GPIO_AF7, GPIO8 | GPIO9); - ucoo::Uart u0 (2), u1 (4); + ucoo::GPIOC.enable (); + ucoo::GPIOD.enable (); + ucoo::GPIOC[12].af (8); + ucoo::GPIOD[2].af (8); + ucoo::GPIOD[8].af (7); + ucoo::GPIOD[9].af (7); + ucoo::Uart u0 (ucoo::Uart::Instance::USART3); + ucoo::Uart u1 (ucoo::Uart::Instance::UART5); u0.enable (38400, ucoo::Uart::Parity::EVEN, 1); u1.enable (38400, ucoo::Uart::Parity::EVEN, 1); #endif diff --git a/ucoo/hal/uart/test/test_uart_disc.cc b/ucoo/hal/uart/test/test_uart_disc.cc index 3893f7e..029b28d 100644 --- a/ucoo/hal/uart/test/test_uart_disc.cc +++ b/ucoo/hal/uart/test/test_uart_disc.cc @@ -27,8 +27,6 @@ #include "ucoo/hal/gpio/gpio.hh" #include "ucoo/base/test/test.hh" -#include - static void check_act (ucoo::Stream &ts, ucoo::Stream &u, char n) { @@ -53,23 +51,20 @@ main (int argc, const char **argv) { ucoo::arch_init (argc, argv); ucoo::Stream &ts = ucoo::test_stream (); - ucoo::Uart u1 (0); - ucoo::Uart u3 (2); - ucoo::Uart u4 (3); + ucoo::Uart u1 (ucoo::Uart::Instance::USART1); + ucoo::Uart u3 (ucoo::Uart::Instance::USART3); + ucoo::Uart u4 (ucoo::Uart::Instance::UART4); u1.enable (38400, ucoo::Uart::Parity::EVEN, 1); u3.enable (38400, ucoo::Uart::Parity::EVEN, 1); u4.enable (38400, ucoo::Uart::Parity::EVEN, 1); // For this test, shorten B6 & B7 to have a loopback on UART1, shorten C10 // & C11 to connect UART3 to UART4. - rcc_periph_clock_enable (RCC_GPIOB); - rcc_periph_clock_enable (RCC_GPIOC); - gpio_mode_setup (GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, - GPIO6 | GPIO7); - gpio_set_af (GPIOB, GPIO_AF7, GPIO6 | GPIO7); - gpio_mode_setup (GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, - GPIO10 | GPIO11); - gpio_set_af (GPIOC, GPIO_AF7, GPIO10); - gpio_set_af (GPIOC, GPIO_AF8, GPIO11); + ucoo::GPIOB.enable (); + ucoo::GPIOC.enable (); + ucoo::GPIOB[6].af (7); + ucoo::GPIOB[7].af (7); + ucoo::GPIOC[10].af (7); + ucoo::GPIOC[11].af (8); // Loop to report any activity on ports and provide a simple UI. char buf[64]; int buf_i = 0; diff --git a/ucoo/hal/uart/uart.stm32.cc b/ucoo/hal/uart/uart.stm32.cc index 9fdeee9..b6efbb4 100644 --- a/ucoo/hal/uart/uart.stm32.cc +++ b/ucoo/hal/uart/uart.stm32.cc @@ -21,11 +21,9 @@ // DEALINGS IN THE SOFTWARE. // // }}} -#include "uart.stm32.hh" - -#include -#include -#include +#include "ucoo/hal/uart/uart.stm32.hh" +#include "ucoo/arch/interrupt.arm.hh" +#include "ucoo/arch/rcc.stm32.hh" namespace ucoo { @@ -33,95 +31,96 @@ namespace ucoo { struct uart_hardware_t { /// UART base address. - uint32_t base; + USART_TypeDef *base; /// APB number. - int apb; - /// Clock enable identifier. - enum rcc_periph_clken clken; + Bus apb; + /// RCC identifier, to enable clock. + Rcc rcc; /// Corresponding IRQ. - int irq; + Irq irq; }; /// Information on UART hardware array, this is zero indexed, USART1 is at /// index 0. static const uart_hardware_t uart_hardware[] = { - { USART1, 2, RCC_USART1, NVIC_USART1_IRQ }, - { USART2, 1, RCC_USART2, NVIC_USART2_IRQ }, - { USART3, 1, RCC_USART3, NVIC_USART3_IRQ }, - { UART4, 1, RCC_UART4, NVIC_UART4_IRQ }, - { UART5, 1, RCC_UART5, NVIC_UART5_IRQ }, -#ifdef USART6 - { USART6, 2, RCC_USART6, NVIC_USART6_IRQ }, + { reg::USART1, Bus::APB2, Rcc::USART1, Irq::USART1 }, + { reg::USART2, Bus::APB1, Rcc::USART2, Irq::USART2 }, + { reg::USART3, Bus::APB1, Rcc::USART3, Irq::USART3 }, + { reg::UART4, Bus::APB1, Rcc::UART4, Irq::UART4 }, + { reg::UART5, Bus::APB1, Rcc::UART5, Irq::UART5 }, +#ifdef USART6_BASE + { reg::USART6, Bus::APB2, Rcc::USART6, Irq::USART6 }, #endif }; static Uart *uart_instances[lengthof (uart_hardware)]; -} // namespace ucoo - -extern "C" { - -void usart1_isr () { ucoo::Uart::isr (0); } - -void usart2_isr () { ucoo::Uart::isr (1); } +template<> +void interrupt () { Uart::isr (0); } -void usart3_isr () { ucoo::Uart::isr (2); } +template<> +void interrupt () { Uart::isr (1); } -void uart4_isr () { ucoo::Uart::isr (3); } +template<> +void interrupt () { Uart::isr (2); } -void uart5_isr () { ucoo::Uart::isr (4); } +template<> +void interrupt () { Uart::isr (3); } -void usart6_isr () { ucoo::Uart::isr (5); } +template<> +void interrupt () { Uart::isr (4); } -} - -namespace ucoo { +#ifdef USART6_BASE +template<> +void interrupt () { Uart::isr (5); } +#endif -Uart::Uart (int n) - : n_ (n), error_char_ (default_error_char), enabled_ (false) +Uart::Uart (Uart::Instance inst) + : n_ (static_cast (inst)), + error_char_ (default_error_char), enabled_ (false) { - assert (n < lengthof (uart_instances)); - assert (!uart_instances[n]); - uart_instances[n] = this; + assert (!uart_instances[n_]); + uart_instances[n_] = this; } Uart::~Uart () { disable (); - uart_instances[n_] = 0; + uart_instances[n_] = nullptr; } void Uart::enable (int speed, Parity parity, int stop_bits) { enabled_ = true; - uint32_t base = uart_hardware[n_].base; + auto base = uart_hardware[n_].base; // Turn on. - rcc_periph_clock_enable (uart_hardware[n_].clken); + rcc_peripheral_clock_enable (uart_hardware[n_].rcc); // Set speed, rounded to nearest. - int apb_freq = uart_hardware[n_].apb == 1 ? rcc_apb1_frequency - : rcc_apb2_frequency; - USART_BRR (base) = (2 * apb_freq + speed) / (2 * speed); + int apb_freq = uart_hardware[n_].apb == Bus::APB1 ? rcc_apb1_freq_hz + : rcc_apb2_freq_hz; + base->BRR = (2 * apb_freq + speed) / (2 * speed); // Set parameters and enable. if (stop_bits == 1) - USART_CR2 (base) = USART_CR2_STOPBITS_1; + base->CR2 = USART_CR2_STOP_Bits_1; else if (stop_bits == 2) - USART_CR2 (base) = USART_CR2_STOPBITS_2; + base->CR2 = USART_CR2_STOP_Bits_2; else assert_unreachable (); - USART_CR3 (base) = 0; - uint32_t cr1 = USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_TE | USART_CR1_RE; + base->CR3 = 0; + uint32_t cr1 = USART_CR1_UE | USART_CR1_RXNEIE | USART_CR1_TE + | USART_CR1_RE; if (parity != Parity::NONE) cr1 |= USART_CR1_M | USART_CR1_PCE; if (parity == Parity::ODD) cr1 |= USART_CR1_PS; - USART_CR1 (base) = cr1; + base->CR1 = cr1; // Reset status. - (void) USART_SR (base); - (void) USART_DR (base); + (void) base->SR; + (void) base->DR; // Enable interrupts. - nvic_enable_irq (uart_hardware[n_].irq); + interrupt_enable (uart_hardware[n_].irq); } void @@ -130,12 +129,12 @@ Uart::disable () if (enabled_) { enabled_ = false; - uint32_t base = uart_hardware[n_].base; + auto base = uart_hardware[n_].base; // Stop UART. - nvic_disable_irq (uart_hardware[n_].irq); - USART_CR1 (base) = 0; + interrupt_disable (uart_hardware[n_].irq); + base->CR1 = 0; // Turn off. - rcc_periph_clock_disable (uart_hardware[n_].clken); + rcc_peripheral_clock_disable (uart_hardware[n_].rcc); } } @@ -165,7 +164,7 @@ Uart::write (const char *buf, int count) int r = tx_fifo_.write (buf, left); if (r) { - USART_CR1 (uart_hardware[n_].base) |= USART_CR1_TXEIE; + uart_hardware[n_].base->CR1 |= USART_CR1_TXEIE; buf += r; left -= r; } @@ -184,12 +183,12 @@ Uart::poll () void Uart::isr (int n) { - uint32_t base = uart_hardware[n].base; - uint32_t sr = USART_SR (base); - uint32_t dr = USART_DR (base); + auto base = uart_hardware[n].base; + uint32_t sr = base->SR; + uint32_t dr = base->DR; assert (uart_instances[n]); Uart &uart = *uart_instances[n]; - if (sr & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) + if (sr & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE)) { dr = uart.error_char_; // Datasheet is not really clear about this when error bits are set. @@ -209,9 +208,9 @@ Uart::isr (int n) { bool was_full = uart.tx_fifo_.full (); if (!uart.tx_fifo_.empty ()) - USART_DR (base) = static_cast (uart.tx_fifo_.pop ()); + base->DR = static_cast (uart.tx_fifo_.pop ()); if (uart.tx_fifo_.empty ()) - USART_CR1 (base) &= ~USART_CR1_TXEIE; + base->CR1 &= ~USART_CR1_TXEIE; if (was_full && uart.handler_) uart.handler_ (false); } diff --git a/ucoo/hal/uart/uart.stm32.hh b/ucoo/hal/uart/uart.stm32.hh index a38d4d6..bf73302 100644 --- a/ucoo/hal/uart/uart.stm32.hh +++ b/ucoo/hal/uart/uart.stm32.hh @@ -29,6 +29,8 @@ #include "config/ucoo/hal/uart.hh" +#include "ucoo/arch/reg.hh" + namespace ucoo { /// Universal asynchronous receiver transmitter (UART). @@ -42,9 +44,21 @@ class Uart : public Stream enum class Parity { ODD, EVEN, NONE }; /// Default error character. static const char default_error_char = '~'; + /// Available UARTS. + enum class Instance + { + USART1, + USART2, + USART3, + UART4, + UART5, +#ifdef USART6_BASE + USART6, +#endif + }; public: - /// Constructor for the Nth UART. - Uart (int n); + /// Constructor for an UART instance. + Uart (Instance inst); /// Shutdown UART. ~Uart (); /// Enable and setup UART. diff --git a/ucoo/hal/usb/Config b/ucoo/hal/usb/Config index 249a0ac..e0cd973 100644 --- a/ucoo/hal/usb/Config +++ b/ucoo/hal/usb/Config @@ -2,15 +2,12 @@ # Theses are APBTeam IDs, given by Openmoko! vendor_id = 0x1d50 product_id = 0x6052 -# Declare as a CDC ACM device. -cdc_acm = true -# Number of streams, interfaces, or pair of endpoints. -stream_nb = 1 -# Set to 0 if powered from USB cable, 1 if device has its own power supply. -self_powered = 0 +# Set to false if powered from USB cable, true if device has its own power +# supply. +self_powered = false # Maximum power consumed from USB cable (mA). max_power = 100 # End point size, you should use 64. ep_size = 64 -# Use HS driver instead of FS -driver_hs = false +# Enable trace buffer. +trace = false diff --git a/ucoo/hal/usb/Module b/ucoo/hal/usb/Module index e47e0e8..b879961 100644 --- a/ucoo/hal/usb/Module +++ b/ucoo/hal/usb/Module @@ -1 +1,5 @@ -ucoo_hal_usb_SOURCES = usb.stm32.cc usb_desc.stm32.c +ucoo_hal_usb_SOURCES = \ + usb_driver.cc \ + usb_application.cc \ + usb_cdc.cc \ + usb_dwc_otg.stm32.cc diff --git a/ucoo/hal/usb/test/Config b/ucoo/hal/usb/test/Config index a29fd20..908a0df 100644 --- a/ucoo/hal/usb/test/Config +++ b/ucoo/hal/usb/test/Config @@ -1,2 +1,2 @@ [ucoo/hal/usb] -stream_nb = 1 +trace = true diff --git a/ucoo/hal/usb/test/Makefile b/ucoo/hal/usb/test/Makefile index 95e4648..d4849df 100644 --- a/ucoo/hal/usb/test/Makefile +++ b/ucoo/hal/usb/test/Makefile @@ -4,6 +4,6 @@ TARGETS = stm32f4 PROGS = test_usb test_usb_SOURCES = test_usb.cc -MODULES = ucoo/hal/usb +MODULES = ucoo/hal/usb ucoo/hal/gpio include $(BASE)/build/top.mk diff --git a/ucoo/hal/usb/test/test_usb.cc b/ucoo/hal/usb/test/test_usb.cc index eb2eb12..ff730a4 100644 --- a/ucoo/hal/usb/test/test_usb.cc +++ b/ucoo/hal/usb/test/test_usb.cc @@ -22,53 +22,34 @@ // // }}} #include "ucoo/hal/usb/usb.hh" +#include "ucoo/hal/usb/usb_cdc.hh" +#include "ucoo/hal/gpio/gpio.hh" #include "ucoo/arch/arch.hh" +static const auto string_descs_pack = ucoo::usb_descs_pack ( + ucoo::usb_string_desc (ucoo::USB_LANGUAGE_EN_US), + ucoo::usb_string_desc (u"APBTeam"), + ucoo::usb_string_desc (u"test")); + +static const auto string_descs = ucoo::usb_descs (string_descs_pack); + int main (int argc, const char **argv) { ucoo::arch_init (argc, argv); - ucoo::UsbStreamControl usc ("APBTeam", "USB test"); - usc.enable (); - ucoo::UsbStream us[] = { - ucoo::UsbStream (usc, 0), -#if CONFIG_UCOO_HAL_USB_STREAM_NB >= 2 - ucoo::UsbStream (usc, 1), -#endif -#if CONFIG_UCOO_HAL_USB_STREAM_NB >= 3 - ucoo::UsbStream (usc, 2), -#endif - }; - if (CONFIG_UCOO_HAL_USB_STREAM_NB > 1) - { - for (int i = 0; i < CONFIG_UCOO_HAL_USB_STREAM_NB; i++) - us[i].block (false); - } - char buf[6]; + ucoo::UsbDriverDwcOtg driver (ucoo::UsbDriverDwcOtg::Instance::OTG_FS, + ucoo::usb_cdc_default_device_desc (), + ucoo::usb_cdc_default_configuration_desc (), + string_descs); + ucoo::UsbApplicationCdcAcm cdc (driver); + driver.enable (); + cdc.write ("hello", 5); while (1) { - for (int i = 0; i < CONFIG_UCOO_HAL_USB_STREAM_NB; i++) - { - int len = us[i].read (buf + 2, sizeof (buf) - 2); - if (len) - { - buf[0] = i + '0'; - buf[1] = '>'; - len += 2; - if (CONFIG_UCOO_HAL_USB_STREAM_NB == 1) - us[i].write (buf, len); - else - { - const char *p = buf; - while (len) - { - int r = us[i].write (p, len); - p += r; - len -= r; - } - } - } - } + char buf[64]; + int r = cdc.read (buf, sizeof (buf)); + if (r) + cdc.write (buf, r); } } diff --git a/ucoo/hal/usb/usb.hh b/ucoo/hal/usb/usb.hh index 5324058..8115bc7 100644 --- a/ucoo/hal/usb/usb.hh +++ b/ucoo/hal/usb/usb.hh @@ -24,8 +24,10 @@ // // }}} +#include "usb_driver.hh" +#include "usb_application.hh" #ifdef TARGET_stm32 -# include "usb.stm32.hh" +# include "usb_dwc_otg.stm32.hh" #else # error "not implemented for this target" #endif diff --git a/ucoo/hal/usb/usb.stm32.cc b/ucoo/hal/usb/usb.stm32.cc deleted file mode 100644 index 5ed6b27..0000000 --- a/ucoo/hal/usb/usb.stm32.cc +++ /dev/null @@ -1,311 +0,0 @@ -// ucoolib - Microcontroller object oriented library. {{{ -// -// Copyright (C) 2012 Nicolas Schodet -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// }}} -#include "usb.stm32.hh" -#include - -#include -#include -#include -#include -#include - -#include "usb_desc.stm32.h" - -#if CONFIG_UCOO_HAL_USB_CDC_ACM -# include -#endif - -#if defined (TARGET_stm32f4) -# if CONFIG_UCOO_HAL_USB_DRIVER_HS -# define usb_isr otg_hs_isr -# define usb_driver otghs_usb_driver -# else -# define usb_isr otg_fs_isr -# define usb_driver otgfs_usb_driver -# endif -#elif defined (TARGET_stm32f1) -# define usb_isr otg_fs_isr -# define usb_driver otgfs_usb_driver -#else -# error "not implemented for this target" -#endif - -static usbd_device *usbdev; - -// Buffer for control requests. -static uint8_t usb_control_buffer[128]; - -extern "C" { - -void -usb_isr () -{ - usbd_poll (usbdev); -} - -} - -namespace ucoo { - -UsbStreamControl *UsbStreamControl::instance_; - -const char *strings[] = { - NULL, - NULL -}; - -#if CONFIG_UCOO_HAL_USB_CDC_ACM - -static void -usb_cdc_acm_send_serial_state (usbd_device *usbdev, bool active) -{ - struct serial_state_notification - { - struct usb_cdc_notification notification; - uint16_t state; - } n; - n.notification.bmRequestType = 0xa1; - n.notification.bNotification = USB_CDC_NOTIFY_SERIAL_STATE; - n.notification.wValue = 0; - n.notification.wIndex = 0; - n.notification.wLength = 2; - n.state = active ? 3 : 0; - usbd_ep_write_packet (usbdev, 0x82, reinterpret_cast (&n), - sizeof (n)); -} - -static int -usb_cdc_acm_control_request ( - usbd_device *usbdev, - struct usb_setup_data *req, uint8_t **buf, uint16_t *len, - void (**complete) (usbd_device *usbdev, struct usb_setup_data *req)) -{ - switch (req->bRequest) - { - case USB_CDC_REQ_SET_CONTROL_LINE_STATE: - usb_cdc_acm_send_serial_state (usbdev, true); - return USBD_REQ_HANDLED; - case USB_CDC_REQ_SET_LINE_CODING: - if (*len < sizeof (struct usb_cdc_line_coding)) - return USBD_REQ_NOTSUPP; - return USBD_REQ_HANDLED; - } - return 0; -} - -#endif /* CONFIG_UCOO_HAL_USB_CDC_ACM */ - -UsbStreamControl::RxBuffer::RxBuffer (void) - : size (0), offset (0) -{ -} - -UsbStreamControl::UsbStreamControl (const char *vendor, const char *product) - : enabled_ (false), configured_ (false) -{ - assert (!instance_); - instance_ = this; - strings[0] = vendor; - strings[1] = product; -} - -UsbStreamControl::~UsbStreamControl () -{ - disable (); -} - -void -UsbStreamControl::enable () -{ - if (!enabled_) - { -#if defined (TARGET_stm32f4) -# if CONFIG_UCOO_HAL_USB_DRIVER_HS - rcc_periph_clock_enable (RCC_OTGHS); - rcc_periph_clock_enable (RCC_GPIOB); - gpio_mode_setup (GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO14 | GPIO15); - gpio_set_af (GPIOB, GPIO_AF12, GPIO14 | GPIO15); -# else - rcc_periph_clock_enable (RCC_OTGFS); - rcc_periph_clock_enable (RCC_GPIOA); - gpio_mode_setup (GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO11 | GPIO12); - gpio_set_af (GPIOA, GPIO_AF10, GPIO11 | GPIO12); -# endif -#elif defined (TARGET_stm32f1) - rcc_periph_clock_enable (RCC_OTGFS); - rcc_periph_clock_enable (RCC_GPIOA); -#endif - usbdev = usbd_init (&usb_driver, &usb_desc_dev, &usb_desc_config, - strings, lengthof (strings), - usb_control_buffer, sizeof (usb_control_buffer)); -#if CONFIG_UCOO_HAL_USB_DRIVER_HS - if (OTG_HS_CID == 0x2000) - { - // Different registers in F479. - OTG_HS_GCCFG = (1 << 21) | (1 << 16); - OTG_HS_DCTL = 0; - } -#else - if (OTG_FS_CID == 0x2000) - { - // Different registers in F479. - OTG_FS_GCCFG = (1 << 21) | (1 << 16); - OTG_FS_DCTL = 0; - } -#endif - usbd_register_set_config_callback (usbdev, set_config); -#if CONFIG_UCOO_HAL_USB_DRIVER_HS - nvic_enable_irq (NVIC_OTG_HS_IRQ); -#else - nvic_enable_irq (NVIC_OTG_FS_IRQ); -#endif - enabled_ = true; - } -} - -void -UsbStreamControl::disable () -{ - if (enabled_) - { - enabled_ = false; - configured_ = false; -#if CONFIG_UCOO_HAL_USB_DRIVER_HS - nvic_disable_irq (NVIC_OTG_HS_IRQ); -#else - nvic_disable_irq (NVIC_OTG_FS_IRQ); -#endif - usbd_disconnect (usbdev, true); -#if defined (TARGET_stm32f4) -# if CONFIG_UCOO_HAL_USB_DRIVER_HS - rcc_periph_clock_disable (RCC_OTGHS); - gpio_mode_setup (GPIOB, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO14 | GPIO15); -# else - rcc_periph_clock_disable (RCC_OTGFS); - gpio_mode_setup (GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_NONE, GPIO11 | GPIO12); -# endif -#elif defined (TARGET_stm32f1) - rcc_periph_clock_disable (RCC_OTGFS); -#endif - } -} - -void -UsbStreamControl::set_config (usbd_device *usbdev, uint16_t configured) -{ - instance_->configured_ = configured; - if (configured) - { - for (int i = 0; i < stream_nb_; i++) - { - usbd_ep_setup (usbdev, 0x01 + i, USB_ENDPOINT_ATTR_BULK, ep_size_, - rx_callback); - usbd_ep_setup (usbdev, 0x81 + i, USB_ENDPOINT_ATTR_BULK, ep_size_, - NULL); - } -#if CONFIG_UCOO_HAL_USB_CDC_ACM - usbd_ep_setup (usbdev, 0x82, USB_ENDPOINT_ATTR_INTERRUPT, 16, NULL); - usbd_register_control_callback ( - usbdev, - USB_REQ_TYPE_CLASS | USB_REQ_TYPE_INTERFACE, - USB_REQ_TYPE_TYPE | USB_REQ_TYPE_RECIPIENT, - usb_cdc_acm_control_request); - usb_cdc_acm_send_serial_state (usbdev, true); -#endif - } -} - -void -UsbStreamControl::rx_callback (usbd_device *usbdev, uint8_t ep) -{ - assert (ep > 0 && ep <= stream_nb_); - int num = ep - 1; - RxBuffer &rb = instance_->rx_buffer_[num]; - assert (rb.size == 0 && rb.offset == 0); - usbd_ep_nak_set (usbdev, ep, 1); - rb.size = usbd_ep_read_packet (usbdev, ep, rb.buf, ep_size_); -} - -UsbStream::UsbStream (UsbStreamControl &control, int num) - : control_ (control), num_ (num) -{ - assert (num < UsbStreamControl::stream_nb_); -} - -int -UsbStream::read (char *buf, int count) -{ - UsbStreamControl::RxBuffer &rb = control_.rx_buffer_[num_]; - /* Wait for reception. */ - if (!rb.size && !block_) - return 0; - while (!rb.size) - barrier (); - /* Copy to provided buffer. */ - int len = std::min (count, rb.size - rb.offset); - buf = std::copy (rb.buf + rb.offset, rb.buf + rb.offset + len, buf); - rb.offset += len; - /* Reload buffer? */ - if (rb.offset == rb.size) - { - rb.offset = rb.size = 0; - barrier (); - usbd_ep_nak_set (usbdev, num_ + 1, 0); - } - /* Done. */ - return len; -} - -int -UsbStream::write (const char *buf, int count) -{ - int left = count; - while (left) - { - if (control_.configured_) - { - // Do the FIFO write with IRQ locked, as the USB IP does not - // tolerate any interruption while the FIFO is being filled. - int len = std::min (left, UsbStreamControl::ep_size_); - irq_flags_t f = irq_lock (); - len = usbd_ep_write_packet (usbdev, num_ + 0x81, buf, len); - irq_restore (f); - buf += len; - left -= len; - } - if (!block_) - break; - barrier (); - } - return count - left; -} - -int -UsbStream::poll () -{ - UsbStreamControl::RxBuffer &rb = control_.rx_buffer_[num_]; - return rb.size - rb.offset; -} - -} // namespace ucoo diff --git a/ucoo/hal/usb/usb.stm32.hh b/ucoo/hal/usb/usb.stm32.hh deleted file mode 100644 index 5c44e2f..0000000 --- a/ucoo/hal/usb/usb.stm32.hh +++ /dev/null @@ -1,106 +0,0 @@ -#ifndef ucoo_hal_usb_usb_stm32_hh -#define ucoo_hal_usb_usb_stm32_hh -// ucoolib - Microcontroller object oriented library. {{{ -// -// Copyright (C) 2012 Nicolas Schodet -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the "Software"), -// to deal in the Software without restriction, including without limitation -// the rights to use, copy, modify, merge, publish, distribute, sublicense, -// and/or sell copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. -// -// }}} -#include "ucoo/intf/stream.hh" -#include "ucoo/common.hh" - -#include "config/ucoo/hal/usb.hh" - -#include - -namespace ucoo { - -class UsbStream; - -/// Top control class for an USB device. This is a limited implementation -/// which fulfill most of our needs: one or more interfaces, each of them is -/// handled as a character stream. This should be instantiated only once. -class UsbStreamControl -{ - public: - /// Construct a new USB control object, with given VENDOR and PRODUCT - /// strings. - UsbStreamControl (const char *vendor, const char *product); - /// Destruct and disable. - ~UsbStreamControl (); - /// Enable device. - void enable (); - /// Disable device. - void disable (); - /// Return true if the device is configured. This means that the - /// connection is done with the host and data can be exchanged. - bool is_configured () const { return configured_; } - private: - /// Called by USB stack when device is configured. This is expected to - /// setup endpoints. - static void set_config (usbd_device *usbdev, uint16_t wValue); - /// Called by USB stack when a frame is received on a OUT endpoint. - static void rx_callback (usbd_device *usbdev, uint8_t ep); - private: - /// Size of endpoints. - static const int ep_size_ = CONFIG_UCOO_HAL_USB_EP_SIZE; - /// Number of streams (also interfaces or pair of endpoints). - static const int stream_nb_ = CONFIG_UCOO_HAL_USB_STREAM_NB; - /// Pointer to the one and only instance. - static UsbStreamControl *instance_; - /// Is currently enabled? - bool enabled_; - /// Whether device is currently configured. - bool configured_; - /// Internal RX buffer type. - struct RxBuffer - { - char buf[ep_size_]; - int size, offset; - RxBuffer (void); - }; - /// Internal RX buffer, one per stream. - RxBuffer rx_buffer_[stream_nb_]; - friend class UsbStream; -}; - -/// One USB stream, instantiated for each stream, interface, or pair of -/// endpoints. -class UsbStream : public Stream -{ - public: - /// Construct from control object and stream index. - UsbStream (UsbStreamControl &control, int num); - /// See Stream::read. - int read (char *buf, int count); - /// See Stream::write. - int write (const char *buf, int count); - /// See Stream::poll. - int poll (); - private: - /// Reference to control object. - UsbStreamControl &control_; - /// Stream index. - int num_; -}; - -} // namespace ucoo - -#endif // ucoo_hal_usb_usb_stm32_hh diff --git a/ucoo/hal/usb/usb_application.cc b/ucoo/hal/usb/usb_application.cc new file mode 100644 index 0000000..3ae6bd5 --- /dev/null +++ b/ucoo/hal/usb/usb_application.cc @@ -0,0 +1,222 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_application.hh" +#include "ucoo/hal/usb/usb_driver.hh" +#include "ucoo/hal/usb/usb_def.hh" + +namespace ucoo { + +UsbApplication::UsbApplication (UsbDriver &driver) + : driver_ (driver) +{ + driver.register_application (*this); +} + +void +UsbApplicationWithSetup::ep_handle_out (uint8_t ep_address, int bcnt) +{ + assert (ep_address == 0); + if (setup_stage_ == SetupStage::HostToDeviceOUT) + { + if (bcnt <= data_size_) + { + if (bcnt) + { + driver_.ep_read (0, data_, bcnt); + data_ += bcnt; + data_size_ -= bcnt; + } + if (bcnt < USB_CONTROL_END_POINT_SIZE) + recv_done (); + } + else + { + driver_.ep_stall (0); + setup_stage_ = SetupStage::Idle; + } + } + else if (setup_stage_ == SetupStage::DeviceToHostStatusOUT && bcnt == 0) + { + // Status received, done. + setup_stage_ = SetupStage::Idle; + } + else + { + driver_.ep_stall (0); + setup_stage_ = SetupStage::Idle; + } +} + +void +UsbApplicationWithSetup::ep_handle_in (uint8_t ep_address) +{ + assert (ep_address == 0); + if (setup_stage_ == SetupStage::DeviceToHostIN) + { + if (data_) + { + int r = driver_.ep_write (0, data_, data_size_); + if (data_size_ < USB_CONTROL_END_POINT_SIZE) + data_ = nullptr; + else + { + data_ += r; + data_size_ -= r; + } + } + else + { + driver_.ep_read_ready (0, USB_CONTROL_END_POINT_SIZE); + setup_stage_ = SetupStage::DeviceToHostStatusOUT; + } + } + else if (setup_stage_ == SetupStage::HostToDeviceStatusIN) + { + // Status sent, done. + setup_stage_ = SetupStage::Idle; + } + else + { + driver_.ep_stall (0); + setup_stage_ = SetupStage::Idle; + } +} + +void +UsbApplicationWithSetup::send_status () +{ + driver_.ep_write (0, nullptr, 0); + setup_stage_ = SetupStage::HostToDeviceStatusIN; +} + +void +UsbApplicationWithSetup::send (const char *data, int data_size) +{ + int r = driver_.ep_write (0, data, data_size); + if (data_size < USB_CONTROL_END_POINT_SIZE) + data_ = nullptr; + else + { + data_ = const_cast (data) + r; + data_size_ = data_size - r; + } + setup_stage_ = SetupStage::DeviceToHostIN; +} + +void +UsbApplicationWithSetup::recv (char *data, int data_size) +{ + data_ = data; + data_size_ = data_size; + driver_.ep_read_ready (0, USB_CONTROL_END_POINT_SIZE); + setup_stage_ = SetupStage::HostToDeviceOUT; +} + +void +UsbApplicationWithSetup::recv_done () +{ + // Must be overrided if used. + assert_unreachable (); +} + +UsbApplicationBasic::SetupResult +UsbApplicationBasic::handle_setup (uint16_t request_n_type, + uint16_t value, uint16_t index, + uint16_t length) +{ + SetupResult res = SetupResult::NotHandled; + switch (request_n_type) + { + case USB_REQ_SET_ADDRESS: + if (index == 0 && length == 0) + { + usb_trace ("setup set address %d", value); + driver_.set_address (value); + send_status (); + res = SetupResult::Handled; + } + break; + case USB_REQ_GET_DESCRIPTOR: + usb_trace ("setup get descriptor 0x%04x", value); + res = send_descriptor (value >> 8, value & 0xff, index, length); + break; + case USB_REQ_SET_CONFIGURATION: + if (index == 0 && length == 0) + { + usb_trace ("setup set configuration %d", value); + driver_.set_configuration (value); + send_status (); + res = SetupResult::Handled; + } + break; + case USB_REQ_SET_INTERFACE: + if (length == 0) + { + usb_trace ("setup set interface %d", value); + // TODO + send_status (); + res = SetupResult::Handled; + } + break; + } + return res; +} + +void +UsbApplicationBasic::handle_configuration_set (uint8_t configuration) +{ +} + +UsbApplicationBasic::SetupResult +UsbApplicationBasic::send_descriptor (uint8_t desc_type, uint8_t desc_index, + uint16_t language, uint16_t length) +{ + const details::UsbDesc *desc = nullptr; + switch (desc_type) + { + case 1: // Device. + if (desc_index == 0 || language == 0) + desc = &device_desc_; + break; + case 2: // Configuration. + if (desc_index == 0 || language == 0) + desc = &configuration_desc_; + break; + case 3: // String. + if ((desc_index == 0 && language == 0) + || (desc_index != 0 && desc_index < string_descs_nb_ + && language == ucoo::USB_LANGUAGE_EN_US)) + desc = &string_descs_[desc_index]; + } + if (desc) + { + send (desc->desc, + desc->desc_size < length ? desc->desc_size : length); + return SetupResult::Handled; + } + else + return SetupResult::NotHandled; +} + +} // namespace ucoo diff --git a/ucoo/hal/usb/usb_application.hh b/ucoo/hal/usb/usb_application.hh new file mode 100644 index 0000000..ac1ea2d --- /dev/null +++ b/ucoo/hal/usb/usb_application.hh @@ -0,0 +1,158 @@ +#ifndef ucoo_hal_usb_usb_application_hh +#define ucoo_hal_usb_usb_application_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_desc.hh" + +#include "ucoo/common.hh" + +namespace ucoo { + +class UsbDriver; + +/// Class which handles application specific controls and data exchanges. +class UsbApplication +{ + public: + /// Response from a setup handler. + enum class SetupResult + { + /// Setup not handled, should continue with next registered + /// application. + NotHandled, + /// Setup handled, this application is now responsible for any + /// following IN or OUT. + Handled, + /// Setup error, STALL the control end point. + Error + }; + public: + /// Handle setup message received on control end point. If message is + /// handled, this application is automatically made responsible for IN and + /// OUT on this end point. It can use ep_write right now (if it does not, + /// it will not receive any ep_handle_in event). + /// On error the control end point is STALLed. + virtual SetupResult handle_setup (uint16_t request_n_type, + uint16_t value, uint16_t index, + uint16_t length) = 0; + /// Handle configuration set, should setup any used end points. + virtual void handle_configuration_set (uint8_t configuration) = 0; + /// Handle data received on an OUT end point. The application must use + /// ep_read once to receive data. + virtual void ep_handle_out (uint8_t ep_address, int bcnt) = 0; + /// Handle data needed to be transmitted on an IN end point. The + /// application can use ep_write once to transmit data. Else the end + /// point is left in a NAK state. + virtual void ep_handle_in (uint8_t ep_address) = 0; + protected: + /// Register application. + UsbApplication (UsbDriver &driver); + protected: + UsbDriver &driver_; +}; + +/// Helper for SETUP handling. +class UsbApplicationWithSetup : public UsbApplication +{ + public: + /// SETUP stage, to be used as a SETUP FSM. + enum class SetupStage + { + /// Idle, waiting for a SETUP frame. + Idle, + /// Host to device, waiting for an OUT frame. + HostToDeviceOUT, + /// Host to device, status IN frame ready to send. + HostToDeviceStatusIN, + /// Device to host, IN frame ready to send. + DeviceToHostIN, + /// Device to host, waiting for status OUT frame. + DeviceToHostStatusOUT, + + }; + public: + UsbApplicationWithSetup (UsbDriver &driver) + : UsbApplication (driver) { } + void ep_handle_out (uint8_t ep_address, int bcnt) override; + void ep_handle_in (uint8_t ep_address) override; + protected: + /// Send status IN. + void send_status (); + /// Enqueue data to send in response to SETUP. + void send (const char *data, int data_size); + /// Prepare to receive data after SETUP. + void recv (char *data, int data_size); + /// Data reception done after SETUP. + virtual void recv_done (); + private: + SetupStage setup_stage_ = SetupStage::Idle; + char *data_ = nullptr; + int data_size_ = 0; +}; + +/// Handle basic USB protocol. +class UsbApplicationBasic : public UsbApplicationWithSetup +{ + public: + template + UsbApplicationBasic (UsbDriver &driver, + const DeviceDesc &device_desc, + const ConfigurationDesc &configuration_desc, + const details::UsbDescs &string_descs); + SetupResult handle_setup (uint16_t request_n_type, + uint16_t value, uint16_t index, + uint16_t length) override; + void handle_configuration_set (uint8_t configuration) override; + private: + /// Send a descriptor. + SetupResult send_descriptor (uint8_t desc_type, uint8_t desc_index, + uint16_t language, uint16_t length); + private: + const details::UsbDesc device_desc_; + const details::UsbDesc configuration_desc_; + const details::UsbDesc *string_descs_; + const int string_descs_nb_; + SetupStage setup_stage_ = SetupStage::Idle; + const char *to_send_ = nullptr; + int to_send_size_ = 0; +}; + +template +UsbApplicationBasic::UsbApplicationBasic ( + UsbDriver &driver, + const DeviceDesc &device_desc, + const ConfigurationDesc &configuration_desc, + const details::UsbDescs &string_descs) + : UsbApplicationWithSetup (driver), + device_desc_ (device_desc), configuration_desc_ (configuration_desc), + string_descs_ (reinterpret_cast (&string_descs)), + string_descs_nb_ (sizeof (string_descs) / sizeof (details::UsbDesc)) +{ +} + +} // namespace ucoo + +#endif // ucoo_hal_usb_usb_application_hh diff --git a/ucoo/hal/usb/usb_cdc.cc b/ucoo/hal/usb/usb_cdc.cc new file mode 100644 index 0000000..36d0889 --- /dev/null +++ b/ucoo/hal/usb/usb_cdc.cc @@ -0,0 +1,235 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_cdc.hh" +#include "ucoo/hal/usb/usb_cdc_desc.hh" +#include "ucoo/hal/usb/usb_driver.hh" + +#include "ucoo/utils/irq_locked.hh" + +namespace ucoo { + +enum +{ + END_POINT_RX = 0x01, + END_POINT_TX = 0x81, + END_POINT_NOTIF = 0x82, +}; + +static const auto device_desc = usb_device_desc ( + 0x0200, USB_CLASS_CDC, 0, 0, USB_CONTROL_END_POINT_SIZE, + CONFIG_UCOO_HAL_USB_VENDOR_ID, + CONFIG_UCOO_HAL_USB_PRODUCT_ID, + 0x0100, 1, 2, 0, 1); + +static const auto configuration_desc = usb_configuration_desc ( + 2, 1, 0, + 0x80 | (CONFIG_UCOO_HAL_USB_SELF_POWERED ? 0x40 : 0), + CONFIG_UCOO_HAL_USB_MAX_POWER / 2, + usb_interface_desc ( + 0, 0, 1, + USB_CLASS_CDC, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTOCOL_AT, 0, + usb_endpoint_desc (END_POINT_NOTIF, 0x03, 16, 128), + usb_cdc_header_desc (0x0110), + usb_cdc_call_management_desc (0, 1), + usb_cdc_abstract_control_management_desc (2), + usb_cdc_union_desc (0, 1)), + usb_interface_desc ( + 1, 0, 2, + USB_CLASS_DATA, 0, 0, 0, + usb_endpoint_desc_bulk (END_POINT_RX, CONFIG_UCOO_HAL_USB_EP_SIZE), + usb_endpoint_desc_bulk (END_POINT_TX, CONFIG_UCOO_HAL_USB_EP_SIZE))); + +details::UsbDesc +usb_cdc_default_device_desc () +{ + return device_desc; +} + +details::UsbDesc +usb_cdc_default_configuration_desc () +{ + return configuration_desc; +} + +UsbApplicationCdcAcm::SetupResult +UsbApplicationCdcAcm::handle_setup (uint16_t request_n_type, uint16_t value, + uint16_t index, uint16_t length) +{ + SetupResult res = SetupResult::NotHandled; + switch (request_n_type) + { + case USB_CDC_REQ_SET_CONTROL_LINE_STATE: + usb_trace ("setup set control line state"); + send_status (); + send_serial_state_ = true; + driver_.ep_write_ready (END_POINT_NOTIF); + res = SetupResult::Handled; + break; + case USB_CDC_REQ_SET_LINE_CODING: + usb_trace ("setup set line coding"); + recv (reinterpret_cast (&line_coding_), + sizeof (line_coding_)); + res = SetupResult::Handled; + break; + } + return res; +} + +void +UsbApplicationCdcAcm::handle_configuration_set (uint8_t configuration) +{ + driver_.ep_setup (END_POINT_RX, UsbDriver::EpType::Bulk, + CONFIG_UCOO_HAL_USB_EP_SIZE, *this); + driver_.ep_setup (END_POINT_TX, UsbDriver::EpType::Bulk, + CONFIG_UCOO_HAL_USB_EP_SIZE, *this); + driver_.ep_setup (END_POINT_NOTIF, UsbDriver::EpType::Interrupt, + 16, *this); + if (!rx_buffer_.full ()) + driver_.ep_read_ready (END_POINT_RX, rx_buffer_.room ()); + if (!tx_buffer_.empty ()) + driver_.ep_write_ready (END_POINT_TX); + send_serial_state_ = true; + driver_.ep_write_ready (END_POINT_NOTIF); + configured_ = true; +} + +void +UsbApplicationCdcAcm::ep_handle_out (uint8_t ep_address, int bcnt) +{ + if (ep_address == 0) + UsbApplicationWithSetup::ep_handle_out (0, bcnt); + else if (ep_address == END_POINT_RX) + { + int r = driver_.ep_read (END_POINT_RX, rx_buffer_.write (), + rx_buffer_.room ()); + rx_buffer_.written (r); + driver_.ep_read_ready (END_POINT_RX, rx_buffer_.room ()); + } + else + assert_unreachable (); +} + +void +UsbApplicationCdcAcm::ep_handle_in (uint8_t ep_address) +{ + if (ep_address == 0) + UsbApplicationWithSetup::ep_handle_in (0); + else if (ep_address == END_POINT_TX) + { + if (!tx_buffer_.empty ()) + { + int r = driver_.ep_write (END_POINT_TX, tx_buffer_.read (), + tx_buffer_.size ()); + tx_buffer_.drop (r); + tx_buffer_.rewind (); + } + } + else if (ep_address == END_POINT_NOTIF) + { + if (send_serial_state_) + { + struct + { + UsbCdcNotification notification; + uint16_t state; + } n; + n.notification.bmRequestType = USB_REQ (INTERFACE, CLASS, IN, 0); + n.notification.bNotification = USB_CDC_NOTIFICATION_SERIAL_STATE; + n.notification.wValue = 0; + n.notification.wIndex = 0; + n.notification.wLength = 2; + n.state = active_ ? 3 : 0; + driver_.ep_write (END_POINT_NOTIF, + reinterpret_cast (&n), sizeof (n)); + send_serial_state_ = false; + } + } + else + assert_unreachable (); +} + +int +UsbApplicationCdcAcm::read (char *buf, int count) +{ + while (1) + { + { + IrqLocked flags; + if (!rx_buffer_.empty ()) + { + int r = std::min (rx_buffer_.size (), count); + const char *f = rx_buffer_.read (); + std::copy (f, f + r, buf); + rx_buffer_.drop (r); + rx_buffer_.rewind (); + if (configured_) + driver_.ep_read_ready (END_POINT_RX, rx_buffer_.room ()); + return r; + } + else if (!block_) + return 0; + } + yield (); + } +} + +int +UsbApplicationCdcAcm::write (const char *buf, int count) +{ + int left = count; + while (left) + { + { + IrqLocked flags; + if (!tx_buffer_.full ()) + { + int r = std::min (tx_buffer_.room (), left); + std::copy (buf, buf + r, tx_buffer_.write (r)); + if (configured_) + driver_.ep_write_ready (END_POINT_TX); + buf += r; + left -= r; + } + } + if (!block_) + break; + yield (); + } + return count - left; +} + +int +UsbApplicationCdcAcm::poll () +{ + return rx_buffer_.size (); +} + +void +UsbApplicationCdcAcm::recv_done () +{ + // Do not actually use the line coding information. + send_status (); +} + +} // namespace ucoo diff --git a/ucoo/hal/usb/usb_cdc.hh b/ucoo/hal/usb/usb_cdc.hh new file mode 100644 index 0000000..7dd5aad --- /dev/null +++ b/ucoo/hal/usb/usb_cdc.hh @@ -0,0 +1,81 @@ +#ifndef ucoo_hal_usb_usb_cdc_hh +#define ucoo_hal_usb_usb_cdc_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_application.hh" +#include "ucoo/hal/usb/usb_desc.hh" +#include "ucoo/hal/usb/usb_cdc_def.hh" +#include "ucoo/intf/stream.hh" +#include "ucoo/utils/buffer.hh" + +#include "config/ucoo/hal/usb.hh" + +namespace ucoo { + +/// Return a default device descriptor for single CDC ACM device. +details::UsbDesc +usb_cdc_default_device_desc (); + +/// Return a default configuration descriptor for single CDC ACM device. +details::UsbDesc +usb_cdc_default_configuration_desc (); + +/// Handle a CDC ACM device, in our case a simple serial port. +class UsbApplicationCdcAcm : public UsbApplicationWithSetup, public Stream +{ + public: + UsbApplicationCdcAcm (UsbDriver &driver) + : UsbApplicationWithSetup (driver) { } + SetupResult handle_setup (uint16_t request_n_type, + uint16_t value, uint16_t index, + uint16_t length) override; + void handle_configuration_set (uint8_t configuration) override; + void ep_handle_out (uint8_t ep_address, int bcnt) override; + void ep_handle_in (uint8_t ep_address) override; + int read (char *buf, int count) override; + int write (const char *buf, int count) override; + int poll () override; + protected: + void recv_done () override; + private: + /// Send serial state over notification end point. + void send_serial_state (bool active); + private: + /// Line coding being received. + UsbCdcLineCoding line_coding_; + /// Whether serial port is active (always the case now). + static const bool active_ = true; + /// RX buffer. + Buffer rx_buffer_; + /// TX buffer. + Buffer tx_buffer_; + /// Is ready to exchange data? + bool configured_ = false; + /// Was serial state requested? + bool send_serial_state_ = false; +}; + +} // namespace ucoo + +#endif // ucoo_hal_usb_usb_cdc_hh diff --git a/ucoo/hal/usb/usb_cdc_def.hh b/ucoo/hal/usb/usb_cdc_def.hh new file mode 100644 index 0000000..6ad93fa --- /dev/null +++ b/ucoo/hal/usb/usb_cdc_def.hh @@ -0,0 +1,75 @@ +#ifndef ucoo_hal_usb_usb_cdc_def_hh +#define ucoo_hal_usb_usb_cdc_def_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_def.hh" + +namespace ucoo { + +enum +{ + USB_CDC_NOTIFICATION_SERIAL_STATE = 0x20, +}; + +/// Agglomeration of bmRequestType and bRequest. +enum +{ + USB_CDC_REQ_SET_CONTROL_LINE_STATE = USB_REQ (INTERFACE, CLASS, OUT, 0x22), + USB_CDC_REQ_SET_LINE_CODING = USB_REQ (INTERFACE, CLASS, OUT, 0x20), +}; + +/// Line coding structure. +struct __attribute__ ((packed, aligned (1))) UsbCdcLineCoding +{ + /// Data terminal rate in bits per second. + uint32_t dwDTERate; + /// Stop bits: + /// - 0: 1 stop bit + /// - 1: 1.5 stop bits + /// - 2: 2 stop bits + uint8_t bCharFormat; + /// Parity: + /// - 0: None + /// - 1: Odd + /// - 2: Even + /// - 3: Mark + /// - 4: Space + uint8_t bParityType; + /// Data bits, 5, 6, 7, 8 or 16. + uint8_t bDataBits; +}; + +/// Notification structure. +struct __attribute__ ((packed, aligned (1))) UsbCdcNotification +{ + uint8_t bmRequestType; + uint8_t bNotification; + uint16_t wValue; + uint16_t wIndex; + uint16_t wLength; +}; + +} // namespace ucoo + +#endif // ucoo_hal_usb_usb_cdc_def_hh diff --git a/ucoo/hal/usb/usb_cdc_desc.hh b/ucoo/hal/usb/usb_cdc_desc.hh new file mode 100644 index 0000000..5c1fac2 --- /dev/null +++ b/ucoo/hal/usb/usb_cdc_desc.hh @@ -0,0 +1,174 @@ +#ifndef ucoo_hal_usb_usb_desc_cdc_hh +#define ucoo_hal_usb_usb_desc_cdc_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_desc.hh" + +namespace ucoo { + +enum +{ + USB_CLASS_CDC = 0x02, + USB_CLASS_DATA = 0x0a, + USB_CDC_SUBCLASS_ACM = 0x02, + USB_CDC_PROTOCOL_AT = 0x01, +}; + +/// CDC header descriptor. +struct __attribute__ ((packed, aligned (1))) UsbCdcHeaderDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// Descriptor type (CS_INTERFACE: 0x24). + uint8_t bDescriptorType; + /// Descriptor sub type (header functional descriptor: 0). + uint8_t bDescriptorSubtype; + /// USB CDC release number (0x0110 for USB CDC 1.1). + uint16_t bcdCDC; +}; + +/// Make a header descriptor. +constexpr UsbCdcHeaderDesc +usb_cdc_header_desc (uint16_t bcdCDC) +{ + return { sizeof (UsbCdcHeaderDesc), 0x24, 0, bcdCDC }; +} + +/// CDC call management descriptor. +struct __attribute__ ((packed, aligned (1))) UsbCdcCallManagementDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// Descriptor type (CS_INTERFACE: 0x24). + uint8_t bDescriptorType; + /// Descriptor sub type (header functional descriptor: 1). + uint8_t bDescriptorSubtype; + /// Capabilities: + /// - bit 0: device handles call management itself + /// - bit 1: device can send/receive call management information over + /// data class interface + uint8_t bmCapabilities; + /// Interface number of data class interface used for call management. + uint8_t bDataInterface; +}; + +/// Make a call management descriptor. +constexpr UsbCdcCallManagementDesc +usb_cdc_call_management_desc ( + uint8_t bmCapabilities, + uint8_t bDataInterface) +{ + return { sizeof (UsbCdcCallManagementDesc), 0x24, 1, bmCapabilities, + bDataInterface }; +} + +/// CDC abstract control management descriptor. +struct __attribute__ ((packed, aligned (1))) + UsbCdcAbstractControlManagementDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// Descriptor type (CS_INTERFACE: 0x24). + uint8_t bDescriptorType; + /// Descriptor sub type (header functional descriptor: 2). + uint8_t bDescriptorSubtype; + /// Capabilities: + /// - bit 0: support Set_Comm_Feature, Clear_Comm_Feature and + /// Get_Comm_Feature + /// - bit 1: support Set_Line_Coding, Set_Control_Line_State, + /// Get_Line_Coding and Serial_State notification + /// - bit 2: support Send_Break + /// - bit 3: support Network_Connection notification + uint8_t bmCapabilities; +}; + +/// Make an abstract control management descriptor. +constexpr UsbCdcAbstractControlManagementDesc +usb_cdc_abstract_control_management_desc ( + uint8_t bmCapabilities) +{ + return { sizeof (UsbCdcAbstractControlManagementDesc), 0x24, 2, + bmCapabilities }; +} + +/// CDC union descriptor. +struct __attribute__ ((packed, aligned (1))) UsbCdcUnionDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// Descriptor type (CS_INTERFACE: 0x24). + uint8_t bDescriptorType; + /// Descriptor sub type (header functional descriptor: 6). + uint8_t bDescriptorSubtype; + /// Interface number of the communication or data class interface + /// designated as the controlling interface for the union. + uint8_t bControlInterface; + /// Interface number of the first subordinated interface in the union. + uint8_t bSubordinateInterface0; + /// Other subordinated interface number follows... +}; + +namespace details { // {{{ + +template +constexpr Desc +usb_cdc_union_desc (Desc desc) +{ + return desc; +} + +template +constexpr auto +usb_cdc_union_desc (Desc desc, uint8_t bSubordinateInterfaceN, + Rest... rest) + -> decltype (usb_cdc_union_desc ( + Pack (desc, 0), rest...)) +{ + return usb_cdc_union_desc ( + Pack (desc, bSubordinateInterfaceN), + rest...); +} + +} // namespace details }}} + +/// Make an union descriptor. +template +constexpr auto +usb_cdc_union_desc ( + uint8_t bControlInterface, + uint8_t bSubordinateInterface0, + SubInterfaces... sub_interfaces) + -> decltype (details::usb_cdc_union_desc ( + (UsbCdcUnionDesc) { 0, 0, 0, 0, 0 }, sub_interfaces...)) +{ + using Ret = decltype (details::usb_cdc_union_desc ( + (UsbCdcUnionDesc) { 0, 0, 0, 0, 0 }, sub_interfaces...)); + return details::usb_cdc_union_desc ( + (UsbCdcUnionDesc) { sizeof (Ret), 0x24, 6, bControlInterface, + bSubordinateInterface0 }, sub_interfaces...); +} + +} // namespace ucoo + +#endif // ucoo_hal_usb_usb_desc_cdc_hh diff --git a/ucoo/hal/usb/usb_def.hh b/ucoo/hal/usb/usb_def.hh new file mode 100644 index 0000000..f02ba74 --- /dev/null +++ b/ucoo/hal/usb/usb_def.hh @@ -0,0 +1,71 @@ +#ifndef ucoo_hal_usb_usb_def_hh +#define ucoo_hal_usb_usb_def_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +namespace ucoo { + +enum +{ + /// Only support one control end point size. + USB_CONTROL_END_POINT_SIZE = 64, +}; + +/// Agglomeration of bmRequestType and bRequest. +enum +{ + USB_REQ_TYPE_DEVICE = 0x0000, + USB_REQ_TYPE_INTERFACE = 0x0001, + USB_REQ_TYPE_END_POINT = 0x0002, + USB_REQ_TYPE_OTHER = 0x0003, + USB_REQ_TYPE_STANDARD = 0x0000, + USB_REQ_TYPE_CLASS = 0x0020, + USB_REQ_TYPE_VENDOR = 0x0040, + USB_REQ_TYPE_OUT = 0x0000, + USB_REQ_TYPE_IN = 0x0080, +#define USB_REQ(rec, type, dir, code) \ + (USB_REQ_TYPE_ ## rec | USB_REQ_TYPE_ ## type | USB_REQ_TYPE_ ## dir \ + | ((code) << 8)) + USB_REQ_CLEAR_FEATURE_DEVICE = USB_REQ (DEVICE, STANDARD, OUT, 0x01), + USB_REQ_CLEAR_FEATURE_INTERFACE = USB_REQ (INTERFACE, STANDARD, OUT, 0x01), + USB_REQ_CLEAR_FEATURE_END_POINT = USB_REQ (END_POINT, STANDARD, OUT, 0x01), + USB_REQ_GET_CONFIGURATION = USB_REQ (DEVICE, STANDARD, IN, 0x08), + USB_REQ_GET_DESCRIPTOR = USB_REQ (DEVICE, STANDARD, IN, 0x06), + USB_REQ_GET_INTERFACE = USB_REQ (INTERFACE, STANDARD, IN, 0x0a), + USB_REQ_GET_STATUS_DEVICE = USB_REQ (DEVICE, STANDARD, IN, 0x00), + USB_REQ_GET_STATUS_INTERFACE = USB_REQ (INTERFACE, STANDARD, IN, 0x00), + USB_REQ_GET_STATUS_END_POINT = USB_REQ (END_POINT, STANDARD, IN, 0x00), + USB_REQ_SET_ADDRESS = USB_REQ (DEVICE, STANDARD, OUT, 0x05), + USB_REQ_SET_CONFIGURATION = USB_REQ (DEVICE, STANDARD, OUT, 0x09), + USB_REQ_SET_DESCRIPTOR = USB_REQ (DEVICE, STANDARD, OUT, 0x07), + USB_REQ_SET_FEATURE_DEVICE = USB_REQ (DEVICE, STANDARD, OUT, 0x03), + USB_REQ_SET_FEATURE_INTERFACE = USB_REQ (INTERFACE, STANDARD, OUT, 0x03), + USB_REQ_SET_FEATURE_END_POINT = USB_REQ (END_POINT, STANDARD, OUT, 0x03), + USB_REQ_SET_INTERFACE = USB_REQ (INTERFACE, STANDARD, OUT, 0x0b), + USB_REQ_SYNCH_FRAME = USB_REQ (END_POINT, STANDARD, IN, 0x0c), +}; + +} // namespace ucoo + +#endif // ucoo_hal_usb_usb_def_hh diff --git a/ucoo/hal/usb/usb_desc.hh b/ucoo/hal/usb/usb_desc.hh new file mode 100644 index 0000000..e23af04 --- /dev/null +++ b/ucoo/hal/usb/usb_desc.hh @@ -0,0 +1,406 @@ +#ifndef ucoo_hal_usb_usb_desc_hh +#define ucoo_hal_usb_usb_desc_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/common.hh" + +namespace ucoo { + +namespace details { // {{{ + +/// Pack several elements in a new types (std::tuple constructor is not +/// constexpr in C++11). +template +struct Pack; + +template +struct __attribute__ ((packed, aligned (1))) Pack +{ + Head head; + Pack tail; + constexpr Pack (Head head_, Tail... tail_) + : head (head_), tail (tail_...) { } +}; + +template +struct __attribute__ ((packed, aligned (1))) Pack +{ + Head head; + constexpr Pack (Head head_) : head (head_) { } +}; + +/// Take a pack, unpack the Nth value. +template +struct Unpack; + +template +struct Unpack<0, Pack> +{ + static constexpr const Head &unpack (const Pack &pack) + { + return pack.head; + } +}; + +template +struct Unpack> +{ + static constexpr auto unpack (const Pack &pack) + -> decltype (Unpack>::unpack (pack.tail)) + { + return Unpack>::unpack (pack.tail); + } +}; + +/// Type whose only purpose is to have a sequence of integer as template +/// parameter. +template +struct Range +{ +}; + +template +struct RangeGen +{ + using Type = typename RangeGen::Type; +}; + +template +struct RangeGen<0, Next...> +{ + using Type = Range; +}; + +} // namespace details }}} + +/// Device descriptor. +struct __attribute__ ((packed, aligned (1))) UsbDeviceDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// Device descriptor type (1). + uint8_t bDescriptorType; + /// USB spec release number (0x0200 for USB 2.0). + uint16_t bcdUSB; + /// Class code assigned by USB-IF (0x00: each interface defines its own + /// class, 0xff: vendor defined). + uint8_t bDeviceClass; + /// SubClass code assigned by USB-IF. + uint8_t bDeviceSubClass; + /// Protocol code assigned by USB-IF. + uint8_t bDeviceProtocol; + /// Max packet size for endpoint 0 (8, 16, 32 or 64). + uint8_t bMaxPacketSize0; + /// Vendor ID. + uint16_t idVendor; + /// Product ID. + uint16_t idProduct; + /// Device release number. + uint16_t bcdDevice; + /// Index of string descriptor describing manufacturer (0 if no string). + uint8_t iManufacturer; + /// Index of string descriptor describing product (0 if no string). + uint8_t iProduct; + /// Index of string descriptor describing device serial number (0 if no + /// string). + uint8_t iSerialNumber; + /// Number of possible configurations. + uint8_t bNumConfigurations; +}; + +/// Make a device descriptor. +constexpr UsbDeviceDesc +usb_device_desc ( + uint16_t bcdUSB, + uint8_t bDeviceClass, + uint8_t bDeviceSubClass, + uint8_t bDeviceProtocol, + uint8_t bMaxPacketSize0, + uint16_t idVendor, + uint16_t idProduct, + uint16_t bcdDevice, + uint8_t iManufacturer, + uint8_t iProduct, + uint8_t iSerialNumber, + uint8_t bNumConfigurations) +{ + return { sizeof (UsbDeviceDesc), 1, bcdUSB, bDeviceClass, bDeviceSubClass, + bDeviceProtocol, bMaxPacketSize0, idVendor, idProduct, bcdDevice, + iManufacturer, iProduct, iSerialNumber, bNumConfigurations }; +} + +/// Configuration descriptor. +struct __attribute__ ((packed, aligned (1))) UsbConfigurationDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// Configuration descriptor type (2). + uint8_t bDescriptorType; + /// Total number of bytes in this descriptor and all the following + /// descriptors. + uint16_t wTotalLength; + /// Number of interfaces supported by this configuration. + uint8_t bNumInterfaces; + /// Value used by Set Configuration to select this configuration. + uint8_t bConfigurationValue; + /// Index of string descriptor describing configuration (0 if no string). + uint8_t iConfiguration; + /// Attributes: + /// - bit 7: must be set + /// - bit 6: self-powered + /// - bit 5: remote wakeup + /// - others: must be 0 + uint8_t bmAttributes; + /// Maximum current drawn by device in this configuration in units of + /// 2 mA. + uint8_t bMaxPower; +}; + +/// Make a configuration descriptor. +template +constexpr details::Pack +usb_configuration_desc ( + uint8_t bNumInterfaces, + uint8_t bConfigurationValue, + uint8_t iConfiguration, + uint8_t bmAttributes, + uint8_t bMaxPower, + SubDesc... sub_descs) +{ + return details::Pack ( + { sizeof (UsbConfigurationDesc), 2, + sizeof (details::Pack), + bNumInterfaces, bConfigurationValue, iConfiguration, bmAttributes, + bMaxPower }, sub_descs...); +} + +/// Interface descriptor. +struct __attribute__ ((packed, aligned (1))) UsbInterfaceDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// Interface descriptor type (4). + uint8_t bDescriptorType; + /// Number identifying this interface (start at 0). + uint8_t bInterfaceNumber; + /// Value used to select this alternate setting for this interface (0 for + /// first and default setting). + uint8_t bAlternateSetting; + /// Number of endpoints used by this interface. + uint8_t bNumEndpoints; + /// Class code assigned by USB-IF. + uint8_t bInterfaceClass; + /// SubClass Code assigned by USB-IF. + uint8_t bInterfaceSubClass; + /// Protocol Code assigned by USB-IF. + uint8_t bInterfaceProtocol; + /// Index of string descriptor describing interface (0 if no string). + uint8_t iInterface; +}; + +/// Make an interface descriptor. +template +constexpr details::Pack +usb_interface_desc ( + uint8_t bInterfaceNumber, + uint8_t bAlternateSetting, + uint8_t bNumEndpoints, + uint8_t bInterfaceClass, + uint8_t bInterfaceSubClass, + uint8_t bInterfaceProtocol, + uint8_t iInterface, + SubDesc... sub_descs) +{ + return details::Pack ( + { sizeof (UsbInterfaceDesc), 4, bInterfaceNumber, bAlternateSetting, + bNumEndpoints, bInterfaceClass, bInterfaceSubClass, + bInterfaceProtocol, iInterface }, sub_descs...); +} + +/// Endpoint descriptor. +struct __attribute__ ((packed, aligned (1))) UsbEndpointDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// Endpoint descriptor type (5). + uint8_t bDescriptorType; + /// The address of this endpoint within the device. + /// - bit 7: direction: 0: OUT, 1: IN + /// - bit 3-0: endpoint number + uint8_t bEndpointAddress; + /// Attributes: + /// - bit 7-6: must be 0 + /// - bit 5-4: usage type: + /// - 00: data endpoint + /// - 01: feedback endpoint + /// - 10: implicit feedback data endpoint + /// - bit 3-2: synchronisation type + /// - 00: none + /// - 01: asynchronous + /// - 10: adaptive + /// - 11: synchronous + /// - bit 1-0: transfer type + /// - 00: control + /// - 01: isochronous + /// - 10: bulk + /// - 11: interrupt + uint8_t bmAttributes; + /// Maximum packet size this endpoint can send or receive when this + /// configuration is selected. + uint16_t wMaxPacketSize; + /// Interval for polling endpoint for data transfers. Expressed in frames + /// (ms) for low/full speed. + uint8_t bInterval; +}; + +/// Make an endpoint descriptor. +template +constexpr details::Pack +usb_endpoint_desc ( + uint8_t bEndpointAddress, + uint8_t bmAttributes, + uint16_t wMaxPacketSize, + uint8_t bInterval, + SubDesc... sub_descs) +{ + return details::Pack ( + { sizeof (UsbEndpointDesc), 5, bEndpointAddress, bmAttributes, + wMaxPacketSize, bInterval }, sub_descs...); +} + +/// Make a bulk endpoint descriptor, shortcut. +template +constexpr details::Pack +usb_endpoint_desc_bulk ( + uint8_t bEndpointAddress, + uint16_t wMaxPacketSize, + SubDesc... sub_descs) +{ + return usb_endpoint_desc (bEndpointAddress, 0x02, wMaxPacketSize, 0, + sub_descs...); +} + +enum +{ + USB_LANGUAGE_EN_US = 0x0409, +}; + +/// String descriptor. +template +struct __attribute__ ((packed, aligned (1))) UsbStringDesc +{ + /// Size of this descriptor in bytes. + uint8_t bLength; + /// String descriptor type (3). + uint8_t bDescriptorType; + /// Unicode encoded string. + char16_t bString[Length]; +}; + +namespace details { // {{{ + +template +constexpr UsbStringDesc +usb_string_desc (const char16_t (&string)[LengthWithNull], + const Range &) +{ + return { sizeof (UsbStringDesc), 3, + { string[Rng]... } }; +} + +} // namespace details }}} + +/// Make a string descriptor. +template +constexpr UsbStringDesc +usb_string_desc (const char16_t (&string)[LengthWithNull]) +{ + return details::usb_string_desc ( + string, typename details::RangeGen::Type ()); +} + +/// Make the first string descriptor (language descriptor). +template +constexpr UsbStringDesc +usb_string_desc (Languages... languages) +{ + return { sizeof (UsbStringDesc), 3, + { static_cast (languages)... } }; +} + +/// Make a descriptors pack. +template +constexpr details::Pack +usb_descs_pack (Descs... descs) +{ + return details::Pack (descs...); +} + +namespace details { + +/// Descriptor with its size. +struct UsbDesc +{ + const char *desc; + int desc_size; + template + constexpr UsbDesc (const Desc &desc_) + : desc (reinterpret_cast (&desc_)), + desc_size (sizeof (desc_)) { } +}; + +/// Descriptors with their size. +template +struct UsbDescs; + +template +struct UsbDescs +{ + UsbDesc head; + constexpr UsbDescs (const Pack &pack) + : head (pack.head) { } +}; + +template +struct UsbDescs +{ + UsbDesc head; + UsbDescs tail; + constexpr UsbDescs (const Pack &pack) + : head (pack.head), tail (pack.tail) { } +}; + +} // namespace details }}} + +/// Make an object with descriptors and their sizes. +template +constexpr details::UsbDescs +usb_descs (const details::Pack &descs_pack) +{ + return descs_pack; +} + +} // namespace ucoo + +#endif // ucoo_hal_usb_usb_desc_hh diff --git a/ucoo/hal/usb/usb_desc.stm32.c b/ucoo/hal/usb/usb_desc.stm32.c deleted file mode 100644 index aecc4da..0000000 --- a/ucoo/hal/usb/usb_desc.stm32.c +++ /dev/null @@ -1,303 +0,0 @@ -/* ucoolib - Microcontroller object oriented library. {{{ - * - * Copyright (C) 2012 Nicolas Schodet - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * }}} */ -#include "usb_desc.stm32.h" -#include -#include - -#include "config/ucoo/hal/usb.hh" - -const struct usb_device_descriptor usb_desc_dev = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = CONFIG_UCOO_HAL_USB_CDC_ACM ? USB_CLASS_CDC : 0xff, - .bDeviceSubClass = 0, - .bDeviceProtocol = 0, - .bMaxPacketSize0 = 64, - .idVendor = CONFIG_UCOO_HAL_USB_VENDOR_ID, - .idProduct = CONFIG_UCOO_HAL_USB_PRODUCT_ID, - .bcdDevice = 0x0000, - .iManufacturer = 1, - .iProduct = 2, - .iSerialNumber = 0, - .bNumConfigurations = 1, -}; - -static const struct usb_endpoint_descriptor usb_desc_endp_1[] = { - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x01, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = CONFIG_UCOO_HAL_USB_EP_SIZE, - .bInterval = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x81, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = CONFIG_UCOO_HAL_USB_EP_SIZE, - .bInterval = 0, - }, -}; - -#if !CONFIG_UCOO_HAL_USB_CDC_ACM - -static const struct usb_interface_descriptor usb_desc_iface_1[] = { - { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 0, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0, - - .endpoint = usb_desc_endp_1, - }, -}; - -#else /* CONFIG_UCOO_HAL_USB_CDC_ACM */ - -static const struct usb_interface_descriptor usb_desc_iface_1[] = { - { - .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 = usb_desc_endp_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)) usb_desc_func_desc_1 = { - .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 = 2, /* SET_LINE_CODING, SET_CONTROL_LINE_STATE, - GET_LINE_CODING, and SERIAL_STATE - notification. */ - }, - .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_endpoint_descriptor usb_desc_endp_1_notif[] = { - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x82, - .bmAttributes = USB_ENDPOINT_ATTR_INTERRUPT, - .wMaxPacketSize = 16, - .bInterval = 128, - }, -}; - -static const struct usb_interface_descriptor usb_desc_iface_1_notif[] = { - { - .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 = usb_desc_endp_1_notif, - - .extra = &usb_desc_func_desc_1, - .extralen = sizeof (usb_desc_func_desc_1), - }, -}; - -#endif /* CONFIG_UCOO_HAL_USB_CDC_ACM */ - -#if CONFIG_UCOO_HAL_USB_STREAM_NB >= 2 -# if CONFIG_UCOO_HAL_USB_CDC_ACM -# error "no enough endpoints to implement CDC ACM" -# endif - -static const struct usb_endpoint_descriptor usb_desc_endp_2[] = { - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x02, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = CONFIG_UCOO_HAL_USB_EP_SIZE, - .bInterval = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x82, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = CONFIG_UCOO_HAL_USB_EP_SIZE, - .bInterval = 0, - }, -}; - -static const struct usb_interface_descriptor usb_desc_iface_2[] = { - { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 1, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0, - - .endpoint = usb_desc_endp_2, - }, -}; - -#endif /* CONFIG_UCOO_HAL_USB_STREAM_NB >= 2 */ - -#if CONFIG_UCOO_HAL_USB_STREAM_NB >= 3 - -static const struct usb_endpoint_descriptor usb_desc_endp_3[] = { - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x03, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = CONFIG_UCOO_HAL_USB_EP_SIZE, - .bInterval = 0, - }, - { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x83, - .bmAttributes = USB_ENDPOINT_ATTR_BULK, - .wMaxPacketSize = CONFIG_UCOO_HAL_USB_EP_SIZE, - .bInterval = 0, - }, -}; - -static const struct usb_interface_descriptor usb_desc_iface_3[] = { - { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = 2, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = 0xff, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 0, - .iInterface = 0, - - .endpoint = usb_desc_endp_3, - }, -}; - -#endif /* CONFIG_UCOO_HAL_USB_STREAM_NB >= 3 */ - -#if CONFIG_UCOO_HAL_USB_STREAM_NB > 3 -# error "too many streams requested" -#endif - -#if !CONFIG_UCOO_HAL_USB_CDC_ACM - -static const struct usb_interface usb_desc_ifaces[] = { - { - .num_altsetting = 1, - .altsetting = usb_desc_iface_1, - }, -#if CONFIG_UCOO_HAL_USB_STREAM_NB >= 2 - { - .num_altsetting = 1, - .altsetting = usb_desc_iface_2, - }, -#endif -#if CONFIG_UCOO_HAL_USB_STREAM_NB >= 3 - { - .num_altsetting = 1, - .altsetting = usb_desc_iface_3, - }, -#endif -}; - -#else /* CONFIG_UCOO_HAL_USB_CDC_ACM */ - -static const struct usb_interface usb_desc_ifaces[] = { - { - .num_altsetting = 1, - .altsetting = usb_desc_iface_1_notif, - }, - { - .num_altsetting = 1, - .altsetting = usb_desc_iface_1, - }, -}; - -#endif /* CONFIG_UCOO_HAL_USB_CDC_ACM */ - -const struct usb_config_descriptor usb_desc_config = { - .bLength = USB_DT_CONFIGURATION_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0, - .bNumInterfaces = CONFIG_UCOO_HAL_USB_CDC_ACM ? 2 : CONFIG_UCOO_HAL_USB_STREAM_NB, - .bConfigurationValue = 1, - .iConfiguration = 0, - .bmAttributes = 0x80 | (CONFIG_UCOO_HAL_USB_SELF_POWERED ? 0x40 : 0), - .bMaxPower = CONFIG_UCOO_HAL_USB_MAX_POWER / 2, - - .interface = usb_desc_ifaces, -}; - diff --git a/ucoo/hal/usb/usb_desc.stm32.h b/ucoo/hal/usb/usb_desc.stm32.h deleted file mode 100644 index dca84b7..0000000 --- a/ucoo/hal/usb/usb_desc.stm32.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef ucoo_hal_usb_usb_desc_stm32_h -#define ucoo_hal_usb_usb_desc_stm32_h -/* ucoolib - Microcontroller object oriented library. {{{ - * - * Copyright (C) 2012 Nicolas Schodet - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * }}} */ -#include - -/** USB device descriptor. */ -extern const struct usb_device_descriptor usb_desc_dev; -/** USB configuration descriptor. */ -extern const struct usb_config_descriptor usb_desc_config; - -#endif /* ucoo_hal_usb_usb_desc_stm32_h */ diff --git a/ucoo/hal/usb/usb_driver.cc b/ucoo/hal/usb/usb_driver.cc new file mode 100644 index 0000000..efd8e6c --- /dev/null +++ b/ucoo/hal/usb/usb_driver.cc @@ -0,0 +1,130 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_driver.hh" +#include "ucoo/hal/usb/usb_def.hh" + +namespace ucoo { + +Trace usb_trace; + +void +UsbDriver::register_application (UsbApplication &app) +{ + for (auto &ap : applications_) + { + if (!ap) + { + ap = &app; + return; + } + } + assert_unreachable (); +} + +void +UsbDriver::set_configuration (int configuration) +{ + if (configuration == current_configuration_) + return; + assert (current_configuration_ == -1); // TODO: handle change. + for (auto app : applications_) + { + if (!app) + break; + app->handle_configuration_set (configuration); + } + current_configuration_ = configuration; +} + +void +UsbDriver::ep_setup (uint8_t address, EpType type, int size, + UsbApplication &app) +{ + bool in = address & 0x80; + int ep = address & 0x7f; + assert (ep < END_POINT_MAX); + if (in) + in_handlers_[ep] = &app; + else + out_handlers_[ep] = &app; + ep_setup (address, type, size); +} + +void +UsbDriver::handle_reset () +{ + ep_setup (0, EpType::Control, USB_CONTROL_END_POINT_SIZE); +} + +void +UsbDriver::handle_setup (uint32_t *msg) +{ + uint16_t bmRequestType_n_bRequest = (msg[0] >> 0) & 0xffff; + uint16_t wValue = (msg[0] >> 16) & 0xffff; + uint16_t wIndex = (msg[1] >> 0) & 0xffff; + uint16_t wLength = (msg[1] >> 16) & 0xffff; + bool ok = false; + out_handlers_[0] = nullptr; + in_handlers_[0] = nullptr; + for (auto app : applications_) + { + if (!app) + break; + UsbApplication::SetupResult r = app->handle_setup ( + bmRequestType_n_bRequest, wValue, wIndex, wLength); + if (r == UsbApplication::SetupResult::Handled) + { + out_handlers_[0] = app; + in_handlers_[0] = app; + ok = true; + break; + } + else if (r == UsbApplication::SetupResult::Error) + break; + } + if (!ok) + ep_stall (0); +} + +void +UsbDriver::ep_handle_out (uint8_t address, int bcnt) +{ + int ep_num = address; + if (out_handlers_[ep_num]) + out_handlers_[ep_num]->ep_handle_out (address, bcnt); + else + ep_stall (address); +} + +void +UsbDriver::ep_handle_in (uint8_t address) +{ + int ep_num = address & 0x7f; + if (in_handlers_[ep_num]) + in_handlers_[ep_num]->ep_handle_in (address); + else + ep_stall (address); +} + +} // namespace ucoo diff --git a/ucoo/hal/usb/usb_driver.hh b/ucoo/hal/usb/usb_driver.hh new file mode 100644 index 0000000..3f36b1d --- /dev/null +++ b/ucoo/hal/usb/usb_driver.hh @@ -0,0 +1,120 @@ +#ifndef ucoo_hal_usb_usb_driver_hh +#define ucoo_hal_usb_usb_driver_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_application.hh" + +#include "ucoo/utils/trace.hh" +#include "config/ucoo/hal/usb.hh" + +namespace ucoo { + +extern Trace usb_trace; + +/// Low level USB driver. +class UsbDriver +{ + public: + enum class EpType + { + Control, + Isochronous, + Bulk, + Interrupt, + }; + public: + /// Constructor. + template + UsbDriver (const DeviceDesc &device_desc, + const ConfigurationDesc &configuration_desc, + const details::UsbDescs &string_descs); + /// No copy constructor. + UsbDriver (const UsbDriver &) = delete; + /// Register an application, typically done in application constructor. + void register_application (UsbApplication &app); + /// Enable hardware. + virtual void enable () = 0; + /// Disable hardware. + virtual void disable () = 0; + /// Write data to an end point. Return the number of written bytes, + /// following bytes will have to be written once there is room in TX FIFO. + virtual int ep_write (uint8_t address, const char *data, int size) = 0; + /// Signal application have some data to send. + virtual void ep_write_ready (uint8_t address) = 0; + /// Read data from an end point. Should only be done when data is + /// signaled to be available. Return the number of read bytes. + virtual int ep_read (uint8_t address, char *data, int size) = 0; + /// Signal application can read data (with maximum buffer size). + virtual void ep_read_ready (uint8_t address, int size) = 0; + /// Stall an end point. + virtual void ep_stall (uint8_t address, bool stall = true) = 0; + /// Set device address. + virtual void set_address (uint8_t address) = 0; + /// Set configuration. + void set_configuration (int configuration); + /// Setup an end point, make an application responsible for it. + void ep_setup (uint8_t address, EpType type, int size, UsbApplication &app); + protected: + /// Setup an end point. + virtual void ep_setup (uint8_t address, EpType type, int size) = 0; + /// Handle reset, called by driver. + void handle_reset (); + /// Handle SETUP, called by driver when received. + void handle_setup (uint32_t *msg); + /// Handle OUT, called by driver when received. + void ep_handle_out (uint8_t address, int bcnt); + /// Handle IN, called by driver when transmission can be done. + void ep_handle_in (uint8_t address); + private: + enum { + APPLICATION_MAX = 3, + END_POINT_MAX = 4, + }; + /// Registered applications. + UsbApplication *applications_[APPLICATION_MAX]; + /// Handlers of OUT events. + UsbApplication *out_handlers_[END_POINT_MAX]; + /// Handlers of IN events. + UsbApplication *in_handlers_[END_POINT_MAX]; + /// Basic USB protocol application. + UsbApplicationBasic app_basic_; + /// Current configuration or -1 if not configured. + int current_configuration_ = -1; +}; + +template +UsbDriver::UsbDriver (const DeviceDesc &device_desc, + const ConfigurationDesc &configuration_desc, + const details::UsbDescs &string_descs) + : applications_ { nullptr }, out_handlers_ { nullptr }, + in_handlers_ { nullptr }, + app_basic_ (*this, device_desc, configuration_desc, string_descs) +{ +} + +} // namespace ucoo + +#endif // ucoo_hal_usb_usb_driver_hh diff --git a/ucoo/hal/usb/usb_dwc_otg.stm32.cc b/ucoo/hal/usb/usb_dwc_otg.stm32.cc new file mode 100644 index 0000000..a19fca7 --- /dev/null +++ b/ucoo/hal/usb/usb_dwc_otg.stm32.cc @@ -0,0 +1,412 @@ +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_dwc_otg.stm32.hh" +#include "ucoo/hal/usb/usb_def.hh" + +#include "ucoo/hal/gpio/gpio.hh" +#include "ucoo/arch/reg.hh" +#include "ucoo/arch/interrupt.arm.hh" +#include "ucoo/arch/rcc.stm32.hh" + +namespace ucoo { + +/// Information on USB hardware. +struct UsbHardware +{ + /// Base address. + USB_OTG_TypeDef *base; + /// RCC identifier. + Rcc rcc; + /// Corresponding IRQ. + Irq irq; + /// Used GPIO port + GpioPort &gpio_port; +#ifdef TARGET_stm32f4 + /// D+ and D- pins. + int gpio_dp, gpio_dm; + /// GPIO alternate function. + int gpio_af; +#endif +}; + +static const UsbHardware usb_hardware[] = +{ + { reg::USB_OTG_FS, Rcc::OTGFS, Irq::OTG_FS, GPIOA, +#ifdef TARGET_stm32f4 + 12, 11, 10 +#endif + }, +#ifdef USB_OTG_HS_BASE + { reg::USB_OTG_HS, Rcc::OTGHS, Irq::OTG_HS, GPIOB, 15, 14, 12 }, +#endif +}; + +static UsbDriverDwcOtg *usb_instance; + +template<> +void interrupt () +{ + ucoo::assert (usb_instance); + usb_instance->isr (); +} + +#ifdef USB_OTG_HS_BASE +template<> +void interrupt () +{ + ucoo::assert (usb_instance); + usb_instance->isr (); +} +#endif + +void +UsbDriverDwcOtg::enable () +{ + assert (!usb_instance); + usb_trace ("enable"); + enabled_ = true; + usb_instance = this; + const auto &hard = usb_hardware[static_cast (inst_)]; + /// Setup GPIO (this is an exception of the global scheme where the user + /// do this). + hard.gpio_port.enable (); +#ifdef TARGET_stm32f4 + hard.gpio_port[hard.gpio_dp].af (hard.gpio_af); + hard.gpio_port[hard.gpio_dm].af (hard.gpio_af); +#endif + /// Enable clock. + rcc_peripheral_clock_enable (hard.rcc); + asm volatile ("nop"); // TODO: Do not know why this is needed. + asm volatile ("nop"); + asm volatile ("nop"); + // Reset. + while (!(hard.base->global.GRSTCTL & USB_OTG_GRSTCTL_AHBIDL)) + ; + hard.base->global.GRSTCTL = USB_OTG_GRSTCTL_CSRST; + while (hard.base->global.GRSTCTL & USB_OTG_GRSTCTL_CSRST) + ; + while (!(hard.base->global.GRSTCTL & USB_OTG_GRSTCTL_AHBIDL)) + ; + // Setup USB core. + assert (rcc_ahb_freq_hz > 32000000); // Or else, should change TRDT. + hard.base->global.GUSBCFG = USB_OTG_GUSBCFG_FDMOD + | (6 * USB_OTG_GUSBCFG_TRDT_0) | USB_OTG_GUSBCFG_PHYSEL; + hard.base->global.GAHBCFG |= USB_OTG_GAHBCFG_GINT; + hard.base->global.GINTMSK |= USB_OTG_GINTMSK_RXFLVLM + | USB_OTG_GINTMSK_USBSUSPM | USB_OTG_GINTMSK_USBRST + | USB_OTG_GINTMSK_ENUMDNEM | USB_OTG_GINTMSK_IEPINT + | USB_OTG_GINTMSK_SRQIM /* ? */ + | USB_OTG_GINTMSK_WUIM; + // TODO: USB_OTG_GCCFG_VBDEN for F479. + hard.base->global.GCCFG |= USB_OTG_GCCFG_VBUSBSEN | USB_OTG_GCCFG_PWRDWN; + hard.base->device.DCFG |= USB_OTG_DCFG_NZLSOHSK | USB_OTG_DCFG_DSPD; + hard.base->device.DCTL = 0; + hard.base->device.DIEPMSK = USB_OTG_DIEPMSK_XFRCM; + hard.base->device.DAINTMSK = USB_OTG_DAINTMSK_IEPM; + // Enable interrupts. + interrupt_enable (hard.irq); +} + +void +UsbDriverDwcOtg::disable () +{ + if (enabled_) + { + usb_trace ("disable"); + const auto &hard = usb_hardware[static_cast (inst_)]; + hard.base->device.DCTL = USB_OTG_DCTL_SDIS; + interrupt_disable (hard.irq); + rcc_peripheral_clock_disable (hard.rcc); +#ifdef TARGET_stm32f4 + hard.gpio_port[hard.gpio_dp].input (); + hard.gpio_port[hard.gpio_dm].input (); +#endif + usb_instance = nullptr; + } +} + +int +UsbDriverDwcOtg::ep_write (uint8_t address, const char *data, int size) +{ + int ep = address & 0x7f; + if (size > ep_in_size_[ep]) + size = ep_in_size_[ep]; + usb_trace ("write %d %d", ep, size); + const auto &hard = usb_hardware[static_cast (inst_)]; + assert (!(hard.base->ep_in[ep].DIEPCTL & USB_OTG_DIEPCTL_EPENA)); + hard.base->ep_in[ep].DIEPTSIZ = (1 << USB_OTG_DIEPTSIZ_PKTCNT_Pos) | size; + hard.base->ep_in[ep].DIEPCTL |= + USB_OTG_DIEPCTL_EPENA | USB_OTG_DIEPCTL_CNAK; + const uint32_t *data32 = reinterpret_cast (data); + for (int i = 0; i < size; i += 4) + hard.base->fifo[ep].FIFO = *data32++; + return size; +} + +void +UsbDriverDwcOtg::ep_write_ready (uint8_t address) +{ + int ep = address & 0x7f; + usb_trace ("write ready %d", ep); + const auto &hard = usb_hardware[static_cast (inst_)]; + irq_flags_t flags = irq_lock (); + // Before the interrupt is locked, an interrupt could have been triggered. + // This code might call ep_handle_in in this case, but only if no data was + // written by the application. + if (hard.base->ep_in[ep].DIEPCTL & USB_OTG_DIEPCTL_EPENA) + // End point is enabled, will trigger an interrupt when done. + ; + else if (hard.base->ep_in[ep].DIEPINT & USB_OTG_DIEPINT_XFRC) + // Interrupt triggered, waiting to deliver the interrupt. + ; + else + // Restart data flow. + ep_handle_in (address); + irq_restore (flags); +} + +int +UsbDriverDwcOtg::ep_read (uint8_t address, char *data, int size) +{ + int ep = address; + assert (ep == rx_ep_); + if (size > rx_length_) + size = rx_length_; + usb_trace ("read %d %d", ep, size); + const auto &hard = usb_hardware[static_cast (inst_)]; + uint32_t *data32 = reinterpret_cast (data); + for (int i = 0; i < size / 4; i++) + *data32++ = hard.base->fifo[0].FIFO; + if (size % 4) + { + uint32_t w = hard.base->fifo[0].FIFO; + uint8_t *data8 = reinterpret_cast (data32); + for (int i = 0; i < size % 4; i++) + { + *data8++ = w & 0xff; + w >>= 8; + } + } + rx_length_ -= size; + if (rx_length_ == 0) + rx_ep_ = -1; + return size; +} + +void +UsbDriverDwcOtg::ep_read_ready (uint8_t address, int size) +{ + int ep = address; + usb_trace ("read ready %d %d", ep, size); + if (size >= ep_out_size_[ep]) + { + const auto &hard = usb_hardware[static_cast (inst_)]; + irq_flags_t flags = irq_lock (); + if (!(hard.base->ep_out[ep].DOEPCTL & USB_OTG_DOEPCTL_EPENA)) + { + hard.base->ep_out[ep].DOEPTSIZ = ep_out_doeptsiz_[ep]; + hard.base->ep_out[ep].DOEPCTL |= USB_OTG_DOEPCTL_EPENA + | USB_OTG_DOEPCTL_CNAK; + usb_trace ("read ready done"); + } + irq_restore (flags); + } +} + +void +UsbDriverDwcOtg::ep_stall (uint8_t address, bool stall) +{ + assert (address == 0 && stall == true); + usb_trace ("ep stall %d %d", address, stall); + const auto &hard = usb_hardware[static_cast (inst_)]; + hard.base->ep_in[0].DIEPCTL |= USB_OTG_DIEPCTL_STALL; +} + +void +UsbDriverDwcOtg::set_address (uint8_t address) +{ + const auto &hard = usb_hardware[static_cast (inst_)]; + hard.base->device.DCFG |= USB_OTG_DCFG_DAD_0 * address; +} + +void +UsbDriverDwcOtg::ep_setup (uint8_t address, EpType type, int size) +{ + const auto &hard = usb_hardware[static_cast (inst_)]; + if (address == 0) + { + usb_trace ("ep setup 0"); + assert (type == EpType::Control + && size == USB_CONTROL_END_POINT_SIZE); + hard.base->global.GRXFSIZ = rx_fifo_size_ / 4; + fifo_used_ = rx_fifo_size_; + hard.base->global.DIEPTXF0_HNPTXFSIZ = + (USB_CONTROL_END_POINT_SIZE / 4) << 16 | fifo_used_ / 4; + fifo_used_ += USB_CONTROL_END_POINT_SIZE; + ep_in_size_[0] = USB_CONTROL_END_POINT_SIZE; + ep_out_size_[0] = USB_CONTROL_END_POINT_SIZE; + ep_out_doeptsiz_[0] = USB_OTG_DOEPTSIZ_STUPCNT + | (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) | USB_CONTROL_END_POINT_SIZE; + hard.base->ep_out[0].DOEPTSIZ = ep_out_doeptsiz_[0]; + hard.base->ep_out[0].DOEPCTL = USB_OTG_DOEPCTL_SNAK + | USB_OTG_DOEPCTL_USBAEP; + hard.base->ep_in[0].DIEPCTL = USB_OTG_DIEPCTL_SNAK + | USB_OTG_DIEPCTL_USBAEP; + } + else + { + int ep = address & 0x7f; + uint32_t epctl = + USB_OTG_DOEPCTL_SD0PID_SEVNFRM + | USB_OTG_DOEPCTL_SNAK + | (type == EpType::Control ? 0 : + type == EpType::Isochronous ? USB_OTG_DOEPCTL_EPTYP_0 : + type == EpType::Bulk ? USB_OTG_DOEPCTL_EPTYP_1 : + USB_OTG_DOEPCTL_EPTYP_1 | USB_OTG_DOEPCTL_EPTYP_0) + | USB_OTG_DOEPCTL_USBAEP + | size; + if (address & 0x80) + { + usb_trace ("ep setup in %d %d %d", ep, static_cast (type), + size); + hard.base->global.DIEPTXF[ep - 1] = (size / 4) << 16 + | fifo_used_ / 4; + fifo_used_ += size; + ep_in_size_[ep] = size; + hard.base->ep_in[ep].DIEPCTL = epctl + | (ep * USB_OTG_DIEPCTL_TXFNUM_0); + } + else + { + usb_trace ("ep setup out %d %d %d", ep, static_cast (type), + size); + ep_out_size_[ep] = size; + ep_out_doeptsiz_[ep] = (1 << USB_OTG_DOEPTSIZ_PKTCNT_Pos) | size; + hard.base->ep_out[ep].DOEPCTL = epctl; + } + } +} + +void +UsbDriverDwcOtg::isr () +{ + const auto &hard = usb_hardware[static_cast (inst_)]; + uint32_t gintsts = hard.base->global.GINTSTS; + if (gintsts & USB_OTG_GINTSTS_USBRST) + { + /* Reset. */ + usb_trace ("isr reset"); + hard.base->device.DCFG &= ~USB_OTG_DCFG_DAD; + for (int i = 0; i < END_POINT_NB; i++) + hard.base->ep_out[i].DOEPCTL = USB_OTG_DOEPCTL_SNAK; + handle_reset (); + hard.base->global.GINTSTS = USB_OTG_GINTSTS_USBRST; + } + if (gintsts & USB_OTG_GINTSTS_ENUMDNE) + { + /* Enumeration done. */ + usb_trace ("isr enumeration done"); + hard.base->global.GINTSTS = USB_OTG_GINTSTS_ENUMDNE; + } + if (gintsts & USB_OTG_GINTSTS_RXFLVL) + { + /* RX FIFO not empty. */ + isr_rx_fifo (); + } + if (gintsts & USB_OTG_GINTSTS_USBSUSP) + { + /* USB suspend. */ + usb_trace ("isr suspend"); + hard.base->global.GINTSTS = USB_OTG_GINTSTS_USBSUSP; + } + if (gintsts & USB_OTG_GINTSTS_IEPINT) + { + /* IN end point interrupt. */ + uint32_t daint = hard.base->device.DAINT; + for (int i = 0; i < END_POINT_NB; i++) + { + if (daint & (1 << i)) + { + usb_trace ("isr in xfrc %d", i); + ep_handle_in (i ? 0x80 | i : 0); + hard.base->ep_in[i].DIEPINT = USB_OTG_DIEPINT_XFRC; + } + } + } + if (gintsts & USB_OTG_GINTSTS_DISCINT) + { + /* Disconnect. */ + usb_trace ("isr disconnect"); + hard.base->global.GINTSTS = USB_OTG_GINTSTS_DISCINT; + } + if (gintsts & USB_OTG_GINTSTS_SRQINT) + { + /* New session. */ + usb_trace ("isr new session"); + hard.base->global.GINTSTS = USB_OTG_GINTSTS_SRQINT; + } + if (gintsts & USB_OTG_GINTSTS_WKUINT) + { + /* Wake up. */ + usb_trace ("isr wake up"); + hard.base->global.GINTSTS = USB_OTG_GINTSTS_WKUINT; + } +} + +void +UsbDriverDwcOtg::isr_rx_fifo () +{ + const auto &hard = usb_hardware[static_cast (inst_)]; + uint32_t rx = hard.base->global.GRXSTSP; + uint32_t pkt_status = rx & USB_OTG_GRXSTSP_PKTSTS; + int ep = rx & USB_OTG_GRXSTSP_EPNUM; + int bcnt = (rx & USB_OTG_GRXSTSP_BCNT) >> USB_OTG_GRXSTSP_BCNT_Pos; + if (pkt_status == USB_OTG_GRXSTSP_PKTSTS_Setup) + { + usb_trace ("isr rx setup"); + assert (ep == 0 && bcnt == 8); + setup_buf_[0] = hard.base->fifo[0].FIFO; + setup_buf_[1] = hard.base->fifo[0].FIFO; + } + else if (pkt_status == USB_OTG_GRXSTSP_PKTSTS_SetupCompleted) + { + usb_trace ("isr rx setup completed"); + handle_setup (&setup_buf_[0]); + } + else if (pkt_status == USB_OTG_GRXSTSP_PKTSTS_Out) + { + usb_trace ("isr rx out %d %d", ep, bcnt); + rx_ep_ = ep; + rx_length_ = bcnt; + ep_handle_out (ep, bcnt); + assert (rx_length_ == 0); + } + else if (pkt_status == USB_OTG_GRXSTSP_PKTSTS_OutCompleted) + usb_trace ("isr rx out completed"); + else + usb_trace ("isr rx other %d", + pkt_status >> USB_OTG_GRXSTSP_PKTSTS_Pos); +} + +} // namespace ucoo diff --git a/ucoo/hal/usb/usb_dwc_otg.stm32.hh b/ucoo/hal/usb/usb_dwc_otg.stm32.hh new file mode 100644 index 0000000..dec71ec --- /dev/null +++ b/ucoo/hal/usb/usb_dwc_otg.stm32.hh @@ -0,0 +1,94 @@ +#ifndef ucoo_hal_usb_usb_dwc_otg_stm32_hh +#define ucoo_hal_usb_usb_dwc_otg_stm32_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/hal/usb/usb_driver.hh" + +namespace ucoo { + +/// Low level USB driver. +class UsbDriverDwcOtg : public UsbDriver +{ + public: + enum + { + END_POINT_NB = 4, + }; + enum class Instance + { + OTG_FS, + OTG_HS, + }; + public: + /// Constructor for a USB OTG instance. + template + UsbDriverDwcOtg (Instance inst, + const DeviceDesc &device_desc, + const ConfigurationDesc &configuration_desc, + const details::UsbDescs &string_descs); + /// Destructor, disable. + ~UsbDriverDwcOtg () { disable (); } + void enable () override; + void disable () override; + int ep_write (uint8_t address, const char *data, int size) override; + void ep_write_ready (uint8_t address) override; + int ep_read (uint8_t address, char *data, int size) override; + void ep_read_ready (uint8_t address, int size) override; + void ep_stall (uint8_t address, bool stall = true) override; + void set_address (uint8_t address) override; + protected: + void ep_setup (uint8_t address, EpType type, int size) override; + public: + void isr (); + private: + void isr_rx_fifo (); + private: + Instance inst_; + bool enabled_ = false; + const int rx_fifo_size_ = 512; + int rx_ep_ = -1; + int rx_length_ = -1; + int ep_out_size_[END_POINT_NB]; + int ep_in_size_[END_POINT_NB]; + uint32_t ep_out_doeptsiz_[END_POINT_NB]; + int fifo_used_ = 0; + uint32_t setup_buf_[2]; +}; + +template +UsbDriverDwcOtg::UsbDriverDwcOtg ( + Instance inst, + const DeviceDesc &device_desc, + const ConfigurationDesc &configuration_desc, + const details::UsbDescs &string_descs) + : UsbDriver (device_desc, configuration_desc, string_descs), + inst_ (inst) +{ +} + +} // namespace ucoo + +#endif // ucoo_hal_usb_usb_dwc_otg_stm32_hh diff --git a/ucoo/math/test/Makefile b/ucoo/math/test/Makefile index 353bc10..c0a17ee 100644 --- a/ucoo/math/test/Makefile +++ b/ucoo/math/test/Makefile @@ -4,6 +4,6 @@ TARGETS = host stm32f4 PROGS = test_math test_math_SOURCES = test_math.cc -MODULES = ucoo/math ucoo/utils ucoo/base/test ucoo/hal/usb +MODULES = ucoo/math ucoo/utils ucoo/base/test ucoo/hal/usb ucoo/hal/gpio include $(BASE)/build/top.mk diff --git a/ucoo/utils/bits.hh b/ucoo/utils/bits.hh new file mode 100644 index 0000000..3c1916b --- /dev/null +++ b/ucoo/utils/bits.hh @@ -0,0 +1,45 @@ +#ifndef ucoo_utils_bits_hh +#define ucoo_utils_bits_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +namespace ucoo { + +template +T +bits (int index) +{ + return 1 << index; +} + +template +T +bits (int index, Indices... indices) +{ + return bits (index) | bits (indices...); +} + +} // namespace ucoo + +#endif // ucoo_utils_bits_hh diff --git a/ucoo/utils/buffer.hh b/ucoo/utils/buffer.hh new file mode 100644 index 0000000..b665d25 --- /dev/null +++ b/ucoo/utils/buffer.hh @@ -0,0 +1,74 @@ +#ifndef ucoo_utils_buffer_hh +#define ucoo_utils_buffer_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} + +namespace ucoo { + +/// Buffer with linear reads and writes. Looks like a fifo but without thread +/// safety and with poorer performance, but access is linear. +/// +/// Warning: no safety net, make sure not to read non present elements or to +/// write more elements than available space. +template +class Buffer +{ + public: + /// Constructor, empty buffer. + Buffer (); + /// Test whether the buffer is empty. + bool empty () const; + /// Test whether the buffer is full (no element can be written, but see + /// rewind()). + bool full () const; + /// Return the number of available elements. + int size () const; + /// Return the available space for elements. + int room () const; + /// Return pointer to readable elements. + const T *read () const; + /// Drop a number of elements after they have been read (or not). + void drop (int n); + /// Return pointer to readable elements and drop them (remember: not + /// thread safe). + const T *read (int n); + /// Return pointer to writable space. + T *write (); + /// Increase the number of available elements (just written). + void written (int n); + /// Return pointer to writable space and increase the number of available + /// elements (remember: not thread safe). + T *write (int n); + /// Move data to begin of buffer to make room after it. + void rewind (); + private: + T data_[max_size]; + T *begin_, *end_; +}; + +} // namespace ucoo + +#include "buffer.tcc" + +#endif // ucoo_utils_buffer_hh diff --git a/ucoo/utils/buffer.tcc b/ucoo/utils/buffer.tcc new file mode 100644 index 0000000..bc25bb7 --- /dev/null +++ b/ucoo/utils/buffer.tcc @@ -0,0 +1,125 @@ +#ifndef ucoo_utils_buffer_tcc +#define ucoo_utils_buffer_tcc +// buffer.tcc +// {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// 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 2 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, write to the Free Software +// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +// +// Contact : +// Web: http://ni.fr.eu.org/ +// Email: +// }}} +#include + +namespace ucoo { + +template +Buffer::Buffer () + : begin_ (data_), end_ (data_) +{ +} + +template +bool +Buffer::empty () const +{ + return begin_ == end_; +} + +template +bool +Buffer::full () const +{ + return end_ == &data_[max_size]; +} + +template +int +Buffer::size () const +{ + return end_ - begin_; +} + +template +int +Buffer::room () const +{ + return &data_[max_size] - end_; +} + +template +const T * +Buffer::read () const +{ + return begin_; +} + +template +void +Buffer::drop (int n) +{ + begin_ += n; + if (empty ()) + begin_ = end_ = &data_[0]; +} + +template +const T * +Buffer::read (int n) +{ + drop (n); + return read (); +} + +template +T * +Buffer::write () +{ + return end_; +} + +template +void +Buffer::written (int n) +{ + end_ += n; +} + +template +T * +Buffer::write (int n) +{ + T *r = write (); + written (n); + return r; +} + +template +void +Buffer::rewind () +{ + if (begin_ != &data_[0]) + { + std::copy (begin_, end_, &data_[0]); + end_ -= begin_ - &data_[0]; + begin_ = &data_[0]; + } +} + +} // namespace ucoo + +#endif // ucoo_utils_buffer_tcc diff --git a/ucoo/utils/delay.arm.cc b/ucoo/utils/delay.arm.cc index 244d521..086ca4c 100644 --- a/ucoo/utils/delay.arm.cc +++ b/ucoo/utils/delay.arm.cc @@ -22,12 +22,11 @@ // // }}} #include "delay.arm.hh" +#include "ucoo/arch/reg.hh" +#include "ucoo/arch/rcc.stm32.hh" #include -#include -#include - namespace ucoo { void @@ -35,21 +34,21 @@ delay_us (int us) { // Suppose that frequency is a multiple of 8 MHz (to avoid 64 bit // division). - int systick_mhz = rcc_ahb_frequency / (8 * 1000000); + int systick_mhz = rcc_ahb_freq_hz / (8 * 1000000); int cycles = systick_mhz * us; - STK_CSR = 0; + reg::SysTick->CTRL = 0; // Loop several times if cycles is too big for the systick timer. Some // nanoseconds are lost every second, I can live with that, it simplifies // code. while (cycles) { int loop_cycles = std::min (1 << 24, cycles); - STK_RVR = loop_cycles - 1; - STK_CVR = 0; - STK_CSR = STK_CSR_ENABLE; - while (!(STK_CSR & STK_CSR_COUNTFLAG)) + reg::SysTick->LOAD = loop_cycles - 1; + reg::SysTick->VAL = 0; + reg::SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; + while (!(reg::SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)) ; - STK_CSR = 0; + reg::SysTick->CTRL = 0; cycles -= loop_cycles; } } @@ -59,16 +58,16 @@ delay_ns (int ns) { // Suppose that frequency is a multiple of 1 MHz (to avoid 64 bit // division). - int hclock_mhz = rcc_ahb_frequency / 1000000; + int hclock_mhz = rcc_ahb_freq_hz / 1000000; int cycles = (hclock_mhz * ns + 999) / 1000; - STK_CSR = 0; + reg::SysTick->CTRL = 0; // Loop once, ns is supposed to be small. - STK_RVR = cycles - 1; - STK_CVR = 0; - STK_CSR = STK_CSR_CLKSOURCE_AHB | STK_CSR_ENABLE; - while (!(STK_CSR & STK_CSR_COUNTFLAG)) + reg::SysTick->LOAD = cycles - 1; + reg::SysTick->VAL = 0; + reg::SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; + while (!(reg::SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)) ; - STK_CSR = 0; + reg::SysTick->CTRL = 0; } } // namespace ucoo diff --git a/ucoo/utils/irq_locked.hh b/ucoo/utils/irq_locked.hh new file mode 100644 index 0000000..5f4a1a0 --- /dev/null +++ b/ucoo/utils/irq_locked.hh @@ -0,0 +1,45 @@ +#ifndef ucoo_utils_irq_locked_hh +#define ucoo_utils_irq_locked_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/common.hh" + +namespace ucoo { + +/// Make sure interrupts are unlocked when scope is exited. +class IrqLocked +{ + irq_flags_t flags_; + public: + /// Constructor, lock interrupts. + IrqLocked () { flags_ = irq_lock (); } + /// Destructor, unlock. + ~IrqLocked () { irq_restore (flags_); } + /// No copy constructor. + IrqLocked (const IrqLocked &) = delete; +}; + +} // namespace ucoo + +#endif // ucoo_utils_irq_locked_hh diff --git a/ucoo/utils/rate_limit.tcc b/ucoo/utils/rate_limit.tcc index 0d034e3..938c20f 100644 --- a/ucoo/utils/rate_limit.tcc +++ b/ucoo/utils/rate_limit.tcc @@ -46,7 +46,7 @@ template void RateLimit::set_limit (int rate_num, int rate_denum) { - int freq = timer_.get_freq (); + int freq = timer_.get_freq_hz (); interval_ = freq * rate_denum / rate_num; ucoo::assert (interval_ <= Timer::max / 2 + 1); next_ = timer_.get_value (); diff --git a/ucoo/utils/table_lookup.hh b/ucoo/utils/table_lookup.hh new file mode 100644 index 0000000..ffd8e3d --- /dev/null +++ b/ucoo/utils/table_lookup.hh @@ -0,0 +1,51 @@ +#ifndef ucoo_utils_table_lookup_hh +#define ucoo_utils_table_lookup_hh +// ucoolib - Microcontroller object oriented library. {{{ +// +// Copyright (C) 2016 Nicolas Schodet +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// }}} +#include "ucoo/common.hh" + +namespace ucoo { + +template +struct LookupTable +{ + K key; + V val; +}; + +template +V +simple_table_lookup (const LookupTable (&t)[N], K key) +{ + for (int i = 0; i < N; i++) + { + if (t[i].key == key) + return t[i].val; + } + assert_unreachable (); +} + +} // namespace ucoo + +#endif // ucoo_utils_table_lookup_hh diff --git a/ucoo/utils/test/Makefile b/ucoo/utils/test/Makefile index 6f2094e..1255cae 100644 --- a/ucoo/utils/test/Makefile +++ b/ucoo/utils/test/Makefile @@ -9,6 +9,6 @@ test_crc_SOURCES = test_crc.cc test_function_SOURCES = test_function.cc test_pool_SOURCES = test_pool.cc -MODULES = ucoo/utils ucoo/base/test ucoo/hal/usb +MODULES = ucoo/utils ucoo/base/test ucoo/hal/usb ucoo/hal/gpio include $(BASE)/build/top.mk diff --git a/ucoo/utils/test/test_delay.cc b/ucoo/utils/test/test_delay.cc index b56c7c1..87945d3 100644 --- a/ucoo/utils/test/test_delay.cc +++ b/ucoo/utils/test/test_delay.cc @@ -23,39 +23,47 @@ // }}} #include "ucoo/utils/delay.hh" #include "ucoo/arch/arch.hh" - -#include -#include +#include "ucoo/hal/gpio/gpio.hh" +#include "ucoo/common.hh" int main (int argc, const char **argv) { ucoo::arch_init (argc, argv); - rcc_periph_clock_enable (RCC_GPIOD); - gpio_mode_setup (GPIOD, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, - GPIO12 | GPIO13 | GPIO14 | GPIO15); - gpio_clear (GPIOD, GPIO12 | GPIO13 | GPIO14 | GPIO15); + ucoo::GPIOD.enable (); + ucoo::Gpio leds[] = + { + ucoo::GPIOD[12], + ucoo::GPIOD[13], + ucoo::GPIOD[14], + ucoo::GPIOD[15], + }; + for (int i = 0; i < ucoo::lengthof (leds); i++) + { + leds[i].output (); + leds[i].reset (); + } int i, j; while (1) { for (i = 0; i < 4; i++) { - gpio_toggle (GPIOD, GPIO12 << (i % 4)); + leds[i % 4].toggle (); ucoo::delay (1); } for (i = 0; i < 16; i++) { - gpio_toggle (GPIOD, GPIO12 << (i % 4)); + leds[i % 4].toggle (); ucoo::delay_ms (250); } for (i = 0; i < 16000; i++) { - gpio_toggle (GPIOD, GPIO12 << (i % 4)); + leds[i % 4].toggle (); ucoo::delay_us (250); } for (i = 0; i < 16; i++) { - gpio_toggle (GPIOD, GPIO12 << (i % 4)); + leds[i % 4].toggle (); for (j = 0; j < 1000; j++) ucoo::delay_us (250); } diff --git a/ucoo/utils/trace.hh b/ucoo/utils/trace.hh index dbbf442..7cae33d 100644 --- a/ucoo/utils/trace.hh +++ b/ucoo/utils/trace.hh @@ -42,7 +42,7 @@ class TraceBuffer /// Maximum number of arguments. static const int args_nb = 4; /// Maximum number of trace entries. - static const int entries_nb = 32; + static const int entries_nb = 512; public: /// Constructor. TraceBuffer (const Timestamp ×tamp = Timestamp ()); diff --git a/ucoo/utils/trace.tcc b/ucoo/utils/trace.tcc index e48c5d8..8d1fc29 100644 --- a/ucoo/utils/trace.tcc +++ b/ucoo/utils/trace.tcc @@ -68,7 +68,9 @@ TraceBuffer::operator() (const char *str, int a0, int a1, int a2) { timestamp_ (entries[index]); entries[index].str = str; - entries[index].args = { a0, a1, a2 }; + entries[index].args[0] = a0; + entries[index].args[1] = a1; + entries[index].args[2] = a2; index = (index + 1) % entries_nb; } -- cgit v1.2.3