Switch to cmake build system

* use tinyprintf
* ability to configure project via ccmake

Signed-off-by: Amir Hammad <amir.hammad@hotmail.com>
This commit is contained in:
Amir Hammad 2016-09-09 18:36:38 +02:00
parent 28ccd32608
commit ed70a1efa3
19 changed files with 1001 additions and 370 deletions

94
CMakeLists.txt Normal file
View file

@ -0,0 +1,94 @@
cmake_minimum_required (VERSION 2.6)
# initialize compiler
include (cmake/toolchain.cmake)
# initialize flashing
include (cmake/openocd_flash.cmake)
# initilize doc
include (cmake/doc.cmake)
project (libusbhost C)
# Declare cached variables
set (USE_STM32F4_FS TRUE CACHE BOOL "Use USB full speed (FS) host periphery")
set (USE_STM32F4_HS TRUE CACHE BOOL "Use USB high speed (HS) host periphery")
set (USE_USART_DEBUG TRUE CACHE BOOL "Use debug uart output")
# Set compiler and linker flags
set (FP_FLAGS
"-mfloat-abi=hard -mfpu=fpv4-sp-d16 -mfp16-format=alternative"
)
set (ARCH_FLAGS
"-mthumb -mcpu=cortex-m4 ${FP_FLAGS}"
)
set (COMMON_FLAGS
"-O2 -g -Wextra -Wshadow -Wredundant-decls -fno-common -ffunction-sections -fdata-sections"
)
set (CMAKE_C_FLAGS
"${COMMON_FLAGS} ${ARCH_FLAGS} -Wstrict-prototypes -Wmissing-prototypes -Wimplicit-function-declaration"
)
set (CMAKE_CXX_FLAGS
"${COMMON_FLAGS} ${ARCH_FLAGS} -Weffc++"
)
# C preprocessor flags
set (CPP_FLAGS
" -MD -Wall -Wundef"
)
add_definitions (${CPP_FLAGS})
# set platform
add_definitions (-DSTM32F4)
set (CMAKE_EXE_LINKER_FLAGS
"--static -nostartfiles -T${CMAKE_SOURCE_DIR}/libusbhost_stm32f4.ld -Wl,-Map=FIXME_ONE.map -Wl,--gc-sections -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group"
)
include_directories (${CMAKE_SOURCE_DIR}/include)
function (init_libopencm3)
include_directories (${CMAKE_SOURCE_DIR}/libopencm3/include)
link_directories (${CMAKE_SOURCE_DIR}/libopencm3/lib)
set (LIBOPENCM3_LIB opencm3_stm32f4 PARENT_SCOPE)
execute_process (
COMMAND sh "${CMAKE_SOURCE_DIR}/initRepo.sh"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_QUIET
)
endfunction (init_libopencm3)
message (STATUS "Initializing repository")
init_libopencm3 ()
message (STATUS "Repository initialized")
# Process cached varibles
message (STATUS "Setuping build")
if (USE_STM32F4_FS)
message (STATUS "... Using USB full speed (FS) host periphery")
add_definitions (-DUSE_STM32F4_USBH_DRIVER_FS)
endif (USE_STM32F4_FS)
if (USE_STM32F4_HS)
message (STATUS "... Using USB high speed (HS) host periphery")
add_definitions (-DUSE_STM32F4_USBH_DRIVER_HS)
endif (USE_STM32F4_HS)
if (USE_USART_DEBUG)
message (STATUS "... Using debug uart output")
add_definitions (-DUSART_DEBUG)
endif (USE_USART_DEBUG)
message (STATUS "Setup done")
add_custom_target (README.md
SOURCES README.md
)
add_subdirectory (src)

View file

@ -1,3 +0,0 @@
TODO
See usbh_driver*.c in src directory for example of device drivers.

267
Makefile
View file

@ -1,267 +0,0 @@
##
## This file is part of the libusbhost project.
## Imported and adopted from libopencm3 project.
##
## Copyright (C) 2009 Uwe Hermann <uwe@hermann-uwe.de>
## Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
## Copyright (C) 2013 Frantisek Burian <BuFran@seznam.cz>
## Copyright (C) 2014 Amir Hammad <amir.hammad@hotmail.com>
##
## This library is free software: you can redistribute it and/or modify
## it under the terms of the GNU Lesser General Public License as published by
## the Free Software Foundation, either version 3 of the License, or
## (at your option) any later version.
##
## This library is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU Lesser General Public License for more details.
##
## You should have received a copy of the GNU Lesser General Public License
## along with this library. If not, see <http://www.gnu.org/licenses/>.
##
BINARY = demo
BINARY := $(addprefix build/, demo)
LIBUSBHOSTNAME = usbhost
LIBUSBHOST := $(addprefix build/lib, $(LIBUSBHOSTNAME))
LIBNAME = opencm3_stm32f4
DEFS = -DSTM32F4
# load user config
include config.mk
DEFS += $(USER_CONFIG)
ifdef USART_DEBUG
DEFS += -DUSART_DEBUG
endif
DEFS += -Iinclude
LDSCRIPT = lib$(LIBNAME).ld
SRCDIR = src
OPENCM3_DIR ?= ./libopencm3
FP_FLAGS ?= -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mfp16-format=alternative
ARCH_FLAGS = -mthumb -mcpu=cortex-m4 $(FP_FLAGS)
################################################################################
# OpenOCD specific variables
OOCD ?= openocd
OOCD_INTERFACE ?= stlink-v2
OOCD_BOARD ?= stm32f4discovery
################################################################################
# Black Magic Probe specific variables
# Set the BMP_PORT to a serial port and then BMP is used for flashing
BMP_PORT ?=
################################################################################
# texane/stlink specific variables
#STLINK_PORT ?= :4242
# Be silent per default, but 'make V=1' will show all compiler calls.
ifneq ($(V),1)
Q := @
NULL := 2>/dev/null
endif
###############################################################################
# Executables
PREFIX ?= arm-none-eabi
CC := $(PREFIX)-gcc
CXX := $(PREFIX)-g++
LD := $(PREFIX)-gcc
AR := $(PREFIX)-ar
AS := $(PREFIX)-as
OBJCOPY := $(PREFIX)-objcopy
OBJDUMP := $(PREFIX)-objdump
GDB := $(PREFIX)-gdb
STFLASH = $(shell which st-flash)
STYLECHECK := /checkpatch.pl
STYLECHECKFLAGS := --no-tree -f --terse --mailback
STYLECHECKFILES := $(shell find . -name '*.[ch]')
###############################################################################
# Source files
LDSCRIPT ?= $(BINARY).ld
SRCS := $(sort $(notdir $(wildcard $(SRCDIR)/*.c)))
OBJSDEMO := $(patsubst %.c, build/%.o ,$(SRCS))
SRCS := $(sort $(notdir $(wildcard $(SRCDIR)/*.cpp)))
OBJSDEMO += $(patsubst %.cpp, build/%.o ,$(SRCS))
OBJS = $(filter-out $(BINARY).o, $(OBJSDEMO))
ifndef USART_DEBUG
OBJS := $(filter-out build/usart_helpers.o, $(OBJS))
OBJSDEMO := $(filter-out build/usart_helpers.o, $(OBJSDEMO))
else
$(info compiling with DEBUG functions)
endif
INCLUDE_DIR = $(OPENCM3_DIR)/include
LIB_DIR = $(OPENCM3_DIR)/lib
SCRIPT_DIR = $(OPENCM3_DIR)/scripts
###############################################################################
# C flags
CFLAGS += -Ofast -g
CFLAGS += -Wextra -Wshadow -Wimplicit-function-declaration
CFLAGS += -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes
CFLAGS += -fno-common -ffunction-sections -fdata-sections
###############################################################################
# C++ flags
CXXFLAGS += -Ofast -g
CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++
CXXFLAGS += -fno-common -ffunction-sections -fdata-sections
###############################################################################
# C & C++ preprocessor common flags
CPPFLAGS += -MD
CPPFLAGS += -Wall -Wundef
CPPFLAGS += -I$(INCLUDE_DIR) $(DEFS)
###############################################################################
# Linker flags
LDFLAGS += --static -nostartfiles
LDFLAGS += -L$(LIB_DIR)
LDFLAGS += -T$(LDSCRIPT)
LDFLAGS += -Wl,-Map=build/$*.map
LDFLAGS += -Wl,--gc-sections
ifeq ($(V),99)
LDFLAGS += -Wl,--print-gc-sections
endif
###############################################################################
# Used libraries
LDLIBS += -l$(LIBNAME)
LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group
###############################################################################
###############################################################################
###############################################################################
.SUFFIXES: .elf .bin .hex .srec .list .map .images
.SECONDEXPANSION:
.SECONDARY:
all: elf bin lib
doc:
doxygen
elf: $(BINARY).elf
bin: $(BINARY).bin
hex: $(BINARY).hex
srec: $(BINARY).srec
list: $(BINARY).list
lib: $(LIBUSBHOST).a
images: $(BINARY).images
flash: $(BINARY).flash
%.images: %.bin %.hex %.srec %.list %.map
@#printf "*** $* images generated ***\n"
%.bin: %.elf
@printf " OBJCOPY $(*).bin\n"
$(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin
%.hex: %.elf
@#printf " OBJCOPY $(*).hex\n"
$(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex
%.srec: %.elf
@#printf " OBJCOPY $(*).srec\n"
$(Q)$(OBJCOPY) -Osrec $(*).elf $(*).srec
%.list: %.elf
@#printf " OBJDUMP $(*).list\n"
$(Q)$(OBJDUMP) -S $(*).elf > $(*).list
-include $(OBJSDEMO:.o=.d)
build/%.elf build/%.map: $(OBJSDEMO) $(LDSCRIPT)
@printf " LD $(*).elf\n"
$(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJSDEMO) $(LDLIBS) -o build/$*.elf
build/%.o:$(SRCDIR)/%.c
@printf " CC $(*).c\n"
$(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $@ -c $(SRCDIR)/$*.c
build/%.o: $(SRCDIR)/%.cxx
@printf " CXX $(*).cxx\n"
$(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $@ -c $(SRCDIR)/$(*).cxx
build/%.o: $(SRCDIR)/%.cpp
@printf " CXX $(*).cpp\n"
$(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $@ -c $(SRCDIR)/$(*).cpp
$(LIB_DIR)/lib$(LIBNAME).a:
clean:
@#printf " CLEAN\n"
@rm -f build/*
%.stlink-flash: %.bin
@printf " FLASH $<\n"
$(Q)$(STFLASH) write $(*).bin 0x8000000
ifeq ($(STLINK_PORT),)
ifeq ($(BMP_PORT),)
ifeq ($(OOCD_SERIAL),)
%.flash: %.hex
@printf " FLASH $<\n"
@# IMPORTANT: Don't use "resume", only "reset" will work correctly!
$(Q)$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \
-f board/$(OOCD_BOARD).cfg \
-c "init" -c "reset init" \
-c "flash write_image erase $(*).hex" \
-c "reset" \
-c "shutdown" $(NULL)
else
%.flash: %.hex
@printf " FLASH $<\n"
@# IMPORTANT: Don't use "resume", only "reset" will work correctly!
$(Q)$(OOCD) -f interface/$(OOCD_INTERFACE).cfg \
-f board/$(OOCD_BOARD).cfg \
-c "ft2232_serial $(OOCD_SERIAL)" \
-c "init" -c "reset init" \
-c "flash write_image erase $(*).hex" \
-c "reset" \
-c "shutdown" $(NULL)
endif
else
%.flash: %.elf
@printf " GDB $(*).elf (flash)\n"
$(Q)$(GDB) --batch \
-ex 'target extended-remote $(BMP_PORT)' \
-x $(SCRIPT_DIR)/black_magic_probe_flash.scr \
$(*).elf
endif
else
%.flash: %.elf
@printf " GDB $(*).elf (flash)\n"
$(Q)$(GDB) --batch \
-ex 'target extended-remote $(STLINK_PORT)' \
-x $(SCRIPT_DIR)/stlink_flash.scr \
$(*).elf
endif
.PHONY: images clean stylecheck styleclean elf bin hex srec list testing doc
-include $(OBJS:.o=.d)
build/lib$(LIBUSBHOSTNAME).a: $(OBJS)
@printf " LIB $@\n"
$(Q)$(AR) rcs $@ $(OBJS)

137
README.md
View file

@ -1,83 +1,104 @@
###General Information
##General Information
[Link to the official repository](http://github.com/libusbhost/libusbhost)
**This library is in an active development.**
**WARNING**: None of its features are considered stable !
###Objectives
This library implement usb host driver allowing users use
or write device drivers, which functionality
is abstracted of low level implementation.
Main objectives are:
- provide open-source(Lesser GPL3) usb host library for embedded devices
- execution speed: This library doesn't use blocking sleep,
- execution speed. This library doesn't use blocking sleep,
making low overhead on runtime performance
- uses static allocation for all its buffers,
so no allocation and reallocation is affecting performance
(possibility of memory fragmentation. execution time indeterminism),
so no malloc(), realloc(), free().
- written in C, with the support to use it with C++.
- does not depend on any Operating System. Library libopencm3 is used for testing purposes and to get proper defines.
So no runtime dependency is on this library.
- use static allocation for all of its buffers.
This means no allocation and reallocation is affecting performance
(possibility of memory fragmentation. execution time indeterminism). No malloc(), realloc(), free()
- do not depend on any operating system
### Supported hardware
- stm32f4discovery
Currently supported devices (yet tested) are:
* stm32f407 (stm32f4 Discovery)
### Supported device drivers
Native device drivers (mostly for demonstration purposes):
- HUB
- Gamepad - XBox compatible Controller
- mouse (draft: only displays raw data)
- USB MIDI devices (raw data + note on/off)
###Practical info
## Steps to compile library and demo
### Prerequisities
Make sure the following prerequisities are installed to be able to compile this library
- **git** for libopencm3 submodule fetch
- **gcc-arm-none-eabi** toolchain for cross compilation
- **cmake**
- **ccmake** (optional)
- **openocd** (optional)
!!! Do not forget to invoke "make clean" before new build when defines change(_TODO: remove this warning and fix the Makefile_)
### Basic setup
1. go to build directory located in the root of the project
> cd build
2. compile demo and the library with the default options set
> cmake .. && make
**How to initialize repository**
Executable demo is placed into `build/demo.hex`.
Library is placed into `build/src/libusbhost.a`.
> ./initRepo.sh
### Advanced setup
*cmake* initial cache variables
<table>
<tr>
<th>Cache variable</th><th>Value</th><th>Description</th>
</tr>
<tr>
<td>USE_STM32F4_FS</td><td>TRUE</td><td>Enable STM32F4 Full Speed USB host peripheral</td>
</tr>
<tr>
<td>USE_STM32F4_HS</td><td>TRUE</td><td>Enable STM32F4 High Speed USB host peripheral</td>
</tr>
<tr>
<td>USE_USART_DEBUG</td><td>TRUE</td><td>Enable writing of the debug information to USART6</td>
</tr>
<tr>
<td>OOCD_INTERFACE</td><td>"stlink-v2"</td><td>Interface configuration file used by the openocd</td>
</tr>
<tr>
<td>OOCD_BOARD</td><td>"stm32f4discovery"</td><td>Board configuration file used by the openocd</td>
</tr>
</table>
You can alter these by issuing the following commands in the build directory
fetch libopencm3 submodule and compile needed libraries
- Graphical user interface
> ccmake ..
**How to generate documentation**
- Command line interface
> cmake .. -D{VARIABLE}={VALUE}
> make doc
### Flashing
If the *openocd* is installed, `make flash` executed in the build directory
flashes the `build/demo.hex` to the stm32f4discovery board.
**How to compile demo**
### Reading debug output
The following table represents the configuration of the debug output
<table>
<tr>
<th>GPIO</th><td>GPIOC6</td>
</tr>
<tr>
<th>USART periphery</th><td>USART6</td>
</tr>
<tr>
<th>Function</th><td>UART TX</td>
</tr>
<tr>
<th>Baud rate</th><td>921600</td>
</tr>
<tr>
<th>Uart mode</th><td>8N1</td>
</tr>
</table>
Edit usbh_config.h to configure the library (By default Full speed OTG periphery on stm32f4 is supported)
## License
The libusbhost code is released under the terms of the GNU Lesser General
Public License (LGPL), version 3 or later.
> ./compileDemo.sh
compiles demo, that can be flashed into stm32f4 Discovery platform and debug by USART
**How to upload firmware (FLASH) to stm32f4 Discovery**
> sudo make flash
**How to view debug data**
connect uart to USART6 pins on gpios: GPIOC6(TX - data), GPIOC7(RX - not used)
configure uart baud on PC side to 921600 with 1 stop bit, no parity, 8bit data, no handshake
**How to compile library only**
> make lib
**libusbhost.a** is built without usart debug support
(check compileDemo.sh for hint on how to compile with debug)
###Contact
Amir Hammad - *amir.hammad@hotmail.com*
**Library is maintained there**
> http://github.com/libusbhost/libusbhost
See COPYING.GPL3 and COPYING.LGPL3 for details.

1
build/.gitignore vendored
View file

@ -0,0 +1 @@
*

6
cmake/doc.cmake Normal file
View file

@ -0,0 +1,6 @@
add_custom_target (doc
COMMAND doxygen
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "output is generted in the ${CMAKE_SOURCE_DIR}/doc"
SOURCES Doxyfile
)

18
cmake/openocd_flash.cmake Normal file
View file

@ -0,0 +1,18 @@
find_program (OOCD openocd DOC "openocd executable")
set (OOCD_INTERFACE stlink-v2 CACHE STRING "interface config file used for openocd flashing")
set (OOCD_BOARD stm32f4discovery CACHE STRING "board config file used for openocd flashing")
if (OOCD)
message (STATUS "OpenOCD found: ${OOCD}")
message (STATUS "... interface: ${OOCD_INTERFACE}")
message (STATUS "... board: ${OOCD_BOARD}")
add_custom_target (flash
COMMAND sh -c '${OOCD} -f interface/${OOCD_INTERFACE}.cfg
-f board/${OOCD_BOARD}.cfg
-c "init" -c "reset init"
-c "flash write_image erase $<TARGET_FILE:demo>"
-c "reset"
-c "shutdown" '
DEPENDS demo
)
endif (OOCD)

15
cmake/toolchain.cmake Normal file
View file

@ -0,0 +1,15 @@
set (_CMAKE_TOOLCHAIN_PREFIX "arm-none-eabi-" CACHE STRING "toolchain prefix")
set (_CMAKE_TOOLCHAIN_LOCATION "" CACHE STRING "toolchain location hint")
set (CMAKE_SYSTEM_NAME Generic)
set (CMAKE_C_COMPILER_WORKS 1)
set (CMAKE_CXX_COMPILER_WORKS 1)
set (CMAKE_C_FLAGS "")
set (CMAKE_CXX_FLAGS "")
set (BUILD_SHARED_LIBS OFF)
find_program (CMAKE_C_COMPILER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}gcc HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
find_program (CMAKE_C_COMPILER NAMES ${_CMAKE_TOOLCHAIN_PREFIX}g++ HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
find_program (CMAKE_OBJCOPY NAMES ${_CMAKE_TOOLCHAIN_PREFIX}objcopy HINTS ${_CMAKE_TOOLCHAIN_LOCATION})
find_program (CMAKE_SIZE NAMES ${_CMAKE_TOOLCHAIN_PREFIX}size HINTS ${_CMAKE_TOOLCHAIN_LOCATION})

View file

@ -1,2 +0,0 @@
#!/bin/sh
USART_DEBUG=1 OPENCM3_DIR=libopencm3 make all

View file

@ -1,4 +0,0 @@
USER_CONFIG =

View file

@ -240,7 +240,6 @@ void device_xfer_control_read(void *data, uint16_t datalen, usbh_packet_callback
void device_xfer_control_write_setup(const void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev);
void device_xfer_control_write_data(const void *data, uint16_t datalen, usbh_packet_callback_t callback, usbh_device_t *dev);
END_DECLS
#endif

View file

@ -59,10 +59,4 @@
#error USBH_MAX_DEVICES > 127
#endif
// Uncomment to enable OTG_HS support - low level driver
// #define USE_STM32F4_USBH_DRIVER_HS
// Uncomment to enable OTG_FS support - low level driver
#define USE_STM32F4_USBH_DRIVER_FS
#endif

View file

@ -28,5 +28,5 @@ MEMORY
}
/* Include the common ld script. */
INCLUDE ./libopencm3/lib/libopencm3_stm32f4.ld
INCLUDE libopencm3_stm32f4.ld

61
src/CMakeLists.txt Normal file
View file

@ -0,0 +1,61 @@
if (USE_USART_DEBUG)
set (USART_HELPERS
usart_helpers.c
tinyprintf.c
)
else (USE_USART_DEBUG)
set (USART_HELPERS "")
endif (USE_USART_DEBUG)
set (inc ${CMAKE_SOURCE_DIR}/include)
add_library (usbhost
${USART_HELPERS}
${inc}/usbh_core.h
${inc}/usbh_driver_ac_midi.h
${inc}/usbh_driver_gp_xbox.h
${inc}/usbh_driver_hid_mouse.h
${inc}/usbh_driver_hub.h
${inc}/usbh_lld_stm32f4.h
${inc}/driver/usbh_device_driver.h
usbh_core.c
usbh_driver_ac_midi.c
usbh_driver_ac_midi_private.h
usbh_driver_gp_xbox.c
usbh_driver_hid_mouse.c
usbh_driver_hub.c
usbh_driver_hub_private.h
usbh_lld_stm32f4.c
)
target_link_libraries (usbhost
${LIBOPENCM3_LIB}
)
add_executable (demo
demo.c
)
target_link_libraries (demo
usbhost
)
add_custom_command (TARGET demo
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -Oihex $<TARGET_FILE:demo> ${CMAKE_BINARY_DIR}/demo.hex
COMMENT "Generating output files: ${CMAKE_BINARY_DIR}/demo.hex"
)
add_custom_command (TARGET demo
POST_BUILD
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:demo>
COMMENT "Calculating size of the binary"
)
add_custom_command (TARGET usbhost
POST_BUILD
COMMENT "Calculating size of the library"
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:usbhost>
)

View file

@ -215,8 +215,13 @@ int main(void)
* Pass array of supported device drivers
*/
const void *lld_drivers[] = {
#ifdef USE_STM32F4_USBH_DRIVER_FS
usbh_lld_stm32f4_driver_fs, // Make sure USE_STM32F4_USBH_DRIVER_FS is defined in usbh_config.h
// usbh_lld_stm32f4_driver_hs, // Make sure USE_STM32F4_USBH_DRIVER_HS is defined in usbh_config.h
#endif
#ifdef USE_STM32F4_USBH_DRIVER_HS
usbh_lld_stm32f4_driver_hs, // Make sure USE_STM32F4_USBH_DRIVER_HS is defined in usbh_config.h
#endif
0
};
usbh_init(lld_drivers, device_drivers);

521
src/tinyprintf.c Normal file
View file

@ -0,0 +1,521 @@
/*
File: tinyprintf.c
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "tinyprintf.h"
/*
* Configuration
*/
/* Enable long int support */
#define PRINTF_LONG_SUPPORT
/* Enable long long int support (implies long int support) */
#define PRINTF_LONG_LONG_SUPPORT
/* Enable %z (size_t) support */
#define PRINTF_SIZE_T_SUPPORT
/*
* Configuration adjustments
*/
#ifdef PRINTF_SIZE_T_SUPPORT
#include <sys/types.h>
#endif
#ifdef PRINTF_LONG_LONG_SUPPORT
# define PRINTF_LONG_SUPPORT
#endif
/* __SIZEOF_<type>__ defined at least by gcc */
#ifdef __SIZEOF_POINTER__
# define SIZEOF_POINTER __SIZEOF_POINTER__
#endif
#ifdef __SIZEOF_LONG_LONG__
# define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__
#endif
#ifdef __SIZEOF_LONG__
# define SIZEOF_LONG __SIZEOF_LONG__
#endif
#ifdef __SIZEOF_INT__
# define SIZEOF_INT __SIZEOF_INT__
#endif
#ifdef __GNUC__
# define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline))
#else
# define _TFP_GCC_NO_INLINE_
#endif
/*
* Implementation
*/
struct param {
char lz:1; /**< Leading zeros */
char alt:1; /**< alternate form */
char uc:1; /**< Upper case (for base16 only) */
char align_left:1; /**< 0 == align right (default), 1 == align left */
unsigned int width; /**< field width */
char sign; /**< The sign to display (if any) */
unsigned int base; /**< number base (e.g.: 8, 10, 16) */
char *bf; /**< Buffer to output */
};
#ifdef PRINTF_LONG_LONG_SUPPORT
static void _TFP_GCC_NO_INLINE_ ulli2a(
unsigned long long int num, struct param *p)
{
int n = 0;
unsigned long long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void lli2a(long long int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ulli2a(num, p);
}
#endif
#ifdef PRINTF_LONG_SUPPORT
static void uli2a(unsigned long int num, struct param *p)
{
int n = 0;
unsigned long int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void li2a(long num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
uli2a(num, p);
}
#endif
static void ui2a(unsigned int num, struct param *p)
{
int n = 0;
unsigned int d = 1;
char *bf = p->bf;
while (num / d >= p->base)
d *= p->base;
while (d != 0) {
int dgt = num / d;
num %= d;
d /= p->base;
if (n || dgt > 0 || d == 0) {
*bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10);
++n;
}
}
*bf = 0;
}
static void i2a(int num, struct param *p)
{
if (num < 0) {
num = -num;
p->sign = '-';
}
ui2a(num, p);
}
static int a2d(char ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (ch >= 'a' && ch <= 'f')
return ch - 'a' + 10;
else if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
else
return -1;
}
static char a2u(char ch, const char **src, int base, unsigned int *nump)
{
const char *p = *src;
unsigned int num = 0;
int digit;
while ((digit = a2d(ch)) >= 0) {
if (digit > base)
break;
num = num * base + digit;
ch = *p++;
}
*src = p;
*nump = num;
return ch;
}
static void putchw(void *putp, putcf putf, struct param *p)
{
char ch;
int n = p->width;
char *bf = p->bf;
/* Number of filling characters */
while (*bf++ && n > 0)
n--;
if (p->sign)
n--;
if (p->alt && p->base == 16)
n -= 2;
else if (p->alt && p->base == 8)
n--;
/* Fill with space to align to the right, before alternate or sign */
if (!p->lz && !p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
/* print sign */
if (p->sign)
putf(putp, p->sign);
/* Alternate */
if (p->alt && p->base == 16) {
putf(putp, '0');
putf(putp, (p->uc ? 'X' : 'x'));
} else if (p->alt && p->base == 8) {
putf(putp, '0');
}
/* Fill with zeros, after alternate or sign */
if (p->lz) {
while (n-- > 0)
putf(putp, '0');
}
/* Put actual buffer */
bf = p->bf;
while ((ch = *bf++))
putf(putp, ch);
/* Fill with space to align to the left, after string */
if (!p->lz && p->align_left) {
while (n-- > 0)
putf(putp, ' ');
}
}
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va)
{
struct param p;
#ifdef PRINTF_LONG_SUPPORT
char bf[23]; /* long = 64b on some architectures */
#else
char bf[12]; /* int = 32b on some architectures */
#endif
char ch;
p.bf = bf;
while ((ch = *(fmt++))) {
if (ch != '%') {
putf(putp, ch);
} else {
#ifdef PRINTF_LONG_SUPPORT
char lng = 0; /* 1 for long, 2 for long long */
#endif
/* Init parameter struct */
p.lz = 0;
p.alt = 0;
p.width = 0;
p.align_left = 0;
p.sign = 0;
/* Flags */
while ((ch = *(fmt++))) {
switch (ch) {
case '-':
p.align_left = 1;
continue;
case '0':
p.lz = 1;
continue;
case '#':
p.alt = 1;
continue;
default:
break;
}
break;
}
/* Width */
if (ch >= '0' && ch <= '9') {
ch = a2u(ch, &fmt, 10, &(p.width));
}
/* We accept 'x.y' format but don't support it completely:
* we ignore the 'y' digit => this ignores 0-fill
* size and makes it == width (ie. 'x') */
if (ch == '.') {
p.lz = 1; /* zero-padding */
/* ignore actual 0-fill size: */
do {
ch = *(fmt++);
} while ((ch >= '0') && (ch <= '9'));
}
#ifdef PRINTF_SIZE_T_SUPPORT
# ifdef PRINTF_LONG_SUPPORT
if (ch == 'z') {
ch = *(fmt++);
if (sizeof(size_t) == sizeof(unsigned long int))
lng = 1;
# ifdef PRINTF_LONG_LONG_SUPPORT
else if (sizeof(size_t) == sizeof(unsigned long long int))
lng = 2;
# endif
} else
# endif
#endif
#ifdef PRINTF_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 1;
#ifdef PRINTF_LONG_LONG_SUPPORT
if (ch == 'l') {
ch = *(fmt++);
lng = 2;
}
#endif
}
#endif
switch (ch) {
case 0:
goto abort;
case 'u':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'd':
case 'i':
p.base = 10;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
lli2a(va_arg(va, long long int), &p);
else
#endif
if (1 == lng)
li2a(va_arg(va, long int), &p);
else
#endif
i2a(va_arg(va, int), &p);
putchw(putp, putf, &p);
break;
#ifdef SIZEOF_POINTER
case 'p':
p.alt = 1;
# if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT
lng = 0;
# elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG
lng = 1;
# elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG
lng = 2;
# endif
#endif
case 'x':
case 'X':
p.base = 16;
p.uc = (ch == 'X')?1:0;
#ifdef PRINTF_LONG_SUPPORT
#ifdef PRINTF_LONG_LONG_SUPPORT
if (2 == lng)
ulli2a(va_arg(va, unsigned long long int), &p);
else
#endif
if (1 == lng)
uli2a(va_arg(va, unsigned long int), &p);
else
#endif
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'o':
p.base = 8;
ui2a(va_arg(va, unsigned int), &p);
putchw(putp, putf, &p);
break;
case 'c':
putf(putp, (char)(va_arg(va, int)));
break;
case 's':
p.bf = va_arg(va, char *);
putchw(putp, putf, &p);
p.bf = bf;
break;
case '%':
putf(putp, ch);
default:
break;
}
}
}
abort:;
}
#if TINYPRINTF_DEFINE_TFP_PRINTF
static putcf stdout_putf;
static void *stdout_putp;
void init_printf(void *putp, putcf putf)
{
stdout_putf = putf;
stdout_putp = putp;
}
void tfp_printf(char *fmt, ...)
{
va_list va;
va_start(va, fmt);
tfp_format(stdout_putp, stdout_putf, fmt, va);
va_end(va);
}
#endif
#if TINYPRINTF_DEFINE_TFP_SPRINTF
struct _vsnprintf_putcf_data
{
size_t dest_capacity;
char *dest;
size_t num_chars;
};
static void _vsnprintf_putcf(void *p, char c)
{
struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p;
if (data->num_chars < data->dest_capacity)
data->dest[data->num_chars] = c;
data->num_chars ++;
}
int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap)
{
struct _vsnprintf_putcf_data data;
if (size < 1)
return 0;
data.dest = str;
data.dest_capacity = size-1;
data.num_chars = 0;
tfp_format(&data, _vsnprintf_putcf, format, ap);
if (data.num_chars < data.dest_capacity)
data.dest[data.num_chars] = '\0';
else
data.dest[data.dest_capacity] = '\0';
return data.num_chars;
}
int tfp_snprintf(char *str, size_t size, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsnprintf(str, size, format, ap);
va_end(ap);
return retval;
}
struct _vsprintf_putcf_data
{
char *dest;
size_t num_chars;
};
static void _vsprintf_putcf(void *p, char c)
{
struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p;
data->dest[data->num_chars++] = c;
}
int tfp_vsprintf(char *str, const char *format, va_list ap)
{
struct _vsprintf_putcf_data data;
data.dest = str;
data.num_chars = 0;
tfp_format(&data, _vsprintf_putcf, format, ap);
data.dest[data.num_chars] = '\0';
return data.num_chars;
}
int tfp_sprintf(char *str, const char *format, ...)
{
va_list ap;
int retval;
va_start(ap, format);
retval = tfp_vsprintf(str, format, ap);
va_end(ap);
return retval;
}
#endif

186
src/tinyprintf.h Normal file
View file

@ -0,0 +1,186 @@
/*
File: tinyprintf.h
Copyright (C) 2004 Kustaa Nyholm
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
This library is really just two files: 'tinyprintf.h' and 'tinyprintf.c'.
They provide a simple and small (+400 loc) printf functionality to
be used in embedded systems.
I've found them so useful in debugging that I do not bother with a
debugger at all.
They are distributed in source form, so to use them, just compile them
into your project.
Two printf variants are provided: printf and the 'sprintf' family of
functions ('snprintf', 'sprintf', 'vsnprintf', 'vsprintf').
The formats supported by this implementation are:
'c' 'd' 'i' 'o' 'p' 'u' 's' 'x' 'X'.
Zero padding and field width are also supported.
If the library is compiled with 'PRINTF_SUPPORT_LONG' defined, then
the long specifier is also supported. Note that this will pull in some
long math routines (pun intended!) and thus make your executable
noticeably longer. Likewise with 'PRINTF_LONG_LONG_SUPPORT' for the
long long specifier, and with 'PRINTF_SIZE_T_SUPPORT' for the size_t
specifier.
The memory footprint of course depends on the target CPU, compiler and
compiler options, but a rough guesstimate (based on a H8S target) is about
1.4 kB for code and some twenty 'int's and 'char's, say 60 bytes of stack space.
Not too bad. Your mileage may vary. By hacking the source code you can
get rid of some hundred bytes, I'm sure, but personally I feel the balance of
functionality and flexibility versus code size is close to optimal for
many embedded systems.
To use the printf, you need to supply your own character output function,
something like :
void putc ( void* p, char c)
{
while (!SERIAL_PORT_EMPTY) ;
SERIAL_PORT_TX_REGISTER = c;
}
Before you can call printf, you need to initialize it to use your
character output function with something like:
init_printf(NULL,putc);
Notice the 'NULL' in 'init_printf' and the parameter 'void* p' in 'putc',
the NULL (or any pointer) you pass into the 'init_printf' will eventually be
passed to your 'putc' routine. This allows you to pass some storage space (or
anything really) to the character output function, if necessary.
This is not often needed but it was implemented like that because it made
implementing the sprintf function so neat (look at the source code).
The code is re-entrant, except for the 'init_printf' function, so it is safe
to call it from interrupts too, although this may result in mixed output.
If you rely on re-entrancy, take care that your 'putc' function is re-entrant!
The printf and sprintf functions are actually macros that translate to
'tfp_printf' and 'tfp_sprintf' when 'TINYPRINTF_OVERRIDE_LIBC' is set
(default). Setting it to 0 makes it possible to use them along with
'stdio.h' printf's in a single source file. When
'TINYPRINTF_OVERRIDE_LIBC' is set, please note that printf/sprintf are
not function-like macros, so if you have variables or struct members
with these names, things will explode in your face. Without variadic
macros this is the best we can do to wrap these function. If it is a
problem, just give up the macros and use the functions directly, or
rename them.
It is also possible to avoid defining tfp_printf and/or tfp_sprintf by
clearing 'TINYPRINTF_DEFINE_TFP_PRINTF' and/or
'TINYPRINTF_DEFINE_TFP_SPRINTF' to 0. This allows for example to
export only tfp_format, which is at the core of all the other
functions.
For further details see source code.
regs Kusti, 23.10.2004
*/
#ifndef __TFP_PRINTF__
#define __TFP_PRINTF__
#include <stdarg.h>
/* Global configuration */
/* Set this to 0 if you do not want to provide tfp_printf */
#ifndef TINYPRINTF_DEFINE_TFP_PRINTF
# define TINYPRINTF_DEFINE_TFP_PRINTF 1
#endif
/* Set this to 0 if you do not want to provide
tfp_sprintf/snprintf/vsprintf/vsnprintf */
#ifndef TINYPRINTF_DEFINE_TFP_SPRINTF
# define TINYPRINTF_DEFINE_TFP_SPRINTF 1
#endif
/* Set this to 0 if you do not want tfp_printf and
tfp_{vsn,sn,vs,s}printf to be also available as
printf/{vsn,sn,vs,s}printf */
#ifndef TINYPRINTF_OVERRIDE_LIBC
# define TINYPRINTF_OVERRIDE_LIBC 1
#endif
/* Optional external types dependencies */
#if TINYPRINTF_DEFINE_TFP_SPRINTF
# include <sys/types.h> /* size_t */
#endif
/* Declarations */
#ifdef __GNUC__
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx) \
__attribute__((format (printf, fmt_idx, arg1_idx)))
#else
# define _TFP_SPECIFY_PRINTF_FMT(fmt_idx,arg1_idx)
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*putcf) (void *, char);
/*
'tfp_format' really is the central function for all tinyprintf. For
each output character after formatting, the 'putf' callback is
called with 2 args:
- an arbitrary void* 'putp' param defined by the user and
passed unmodified from 'tfp_format',
- the character.
The 'tfp_printf' and 'tfp_sprintf' functions simply define their own
callback and pass to it the right 'putp' it is expecting.
*/
void tfp_format(void *putp, putcf putf, const char *fmt, va_list va);
#if TINYPRINTF_DEFINE_TFP_SPRINTF
int tfp_vsnprintf(char *str, size_t size, const char *fmt, va_list ap);
int tfp_snprintf(char *str, size_t size, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(3, 4);
int tfp_vsprintf(char *str, const char *fmt, va_list ap);
int tfp_sprintf(char *str, const char *fmt, ...) \
_TFP_SPECIFY_PRINTF_FMT(2, 3);
# if TINYPRINTF_OVERRIDE_LIBC
# define vsnprintf tfp_vsnprintf
# define snprintf tfp_snprintf
# define vsprintf tfp_vsprintf
# define sprintf tfp_sprintf
# endif
#endif
#if TINYPRINTF_DEFINE_TFP_PRINTF
void init_printf(void *putp, putcf putf);
void tfp_printf(char *fmt, ...) _TFP_SPECIFY_PRINTF_FMT(1, 2);
# if TINYPRINTF_OVERRIDE_LIBC
# define printf tfp_printf
# endif
#endif
#ifdef __cplusplus
}
#endif
#endif

View file

@ -22,14 +22,15 @@
#include "usart_helpers.h"
#define TINYPRINTF_OVERRIDE_LIBC 0
#define TINYPRINTF_DEFINE_TFP_SPRINTF 0
#include "tinyprintf.h"
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/gpio.h>
#define USART_FIFO_OUT_SIZE (4096)
uint8_t usart_fifo_out_data[USART_FIFO_OUT_SIZE];
@ -103,35 +104,22 @@ static void usart_fifo_in_push(uint8_t aData)
usart_fifo_in_len++;
}
static void usart_write(const char * data, uint32_t len)
static void putf(void *arg, char c)
{
uint32_t i;
for(i = 0; i < len; i++)
{
usart_fifo_push(data[i]);
}
//unused argument
(void)arg;
usart_fifo_push(c);
}
void usart_printf(const char *str, ...)
{
va_list va;
va_start(va, str);
usart_vprintf(str, va);
tfp_format(NULL, putf, str, va);
va_end(va);
}
void usart_vprintf(const char *str, va_list va)
{
char databuffer[128];
int i = vsnprintf(databuffer, 128, str, va);
if (i > 0) {
usart_write(databuffer, i);
}
}
void usart_init(uint32_t arg_usart, uint32_t baudrate)
{
usart_set_baudrate(arg_usart, baudrate);
@ -145,6 +133,7 @@ void usart_init(uint32_t arg_usart, uint32_t baudrate)
usart_enable(arg_usart);
usart = arg_usart;
}
void usart_interrupt(void)
{
if (usart_get_interrupt_source(usart, USART_SR_RXNE)) {
@ -230,9 +219,7 @@ void usart_call_cmd(struct usart_commands * commands)
LOG_PRINTF("#2");
return;
}
//~ for (i = 0; i < command_len; i++) {
//~ LOG_PRINTF("%c", command[i]);
//~ }
i=0;
while(commands[i].cmd != NULL) {
if (!strcmp((char*)command, (char*)commands[i].cmd)) {
@ -243,7 +230,7 @@ void usart_call_cmd(struct usart_commands * commands)
commands[i].callback(&command[command_argindex]);
}
}
usart_write("\n>>",4);
LOG_PRINTF("\n>>");
command_len = 0;
command_argindex = 0;
return;

View file

@ -38,7 +38,6 @@ struct usart_commands{
#ifdef USART_DEBUG
void usart_init(uint32_t usart, uint32_t baudrate);
void usart_printf(const char *str, ...);
void usart_vprintf(const char *str, va_list va);
void usart_fifo_send(void);
void usart_call_cmd(struct usart_commands * commands);