blinkenlights working w sonar

master
Ondřej Hruška 9 years ago
parent 11764cbd7a
commit d01e40e174
  1. 5
      Makefile
  2. 46
      f103.pro
  3. 223
      f103.pro.user
  4. 157
      openocd.cfg
  5. 21
      parsemap.php
  6. 159
      project/bus/event_handler.c
  7. 48
      project/bus/event_handler.h
  8. 66
      project/bus/event_queue.c
  9. 75
      project/bus/event_queue.h
  10. 66
      project/colorled.c
  11. 79
      project/colorled.h
  12. 96
      project/com/com_fileio.c
  13. 23
      project/com/com_fileio.h
  14. 179
      project/com/com_iface.c
  15. 168
      project/com/com_iface.h
  16. 48
      project/com/datalink.c
  17. 26
      project/com/datalink.h
  18. 108
      project/com/debug.c
  19. 73
      project/com/debug.h
  20. 91
      project/com/iface_noop.c
  21. 14
      project/com/iface_noop.h
  22. 328
      project/com/iface_usart.c
  23. 19
      project/com/iface_usart.h
  24. 94
      project/display.c
  25. 11
      project/display.h
  26. 125
      project/hw_init.c
  27. 6
      project/hw_init.h
  28. 70
      project/main.c
  29. 18
      project/main.h
  30. 40
      project/malloc_safe.c
  31. 17
      project/malloc_safe.h
  32. 24
      project/spl_assert.c
  33. 2
      project/stm32f10x_it.c
  34. 32
      project/syscalls.c
  35. 207
      project/system_stm32f10x.c
  36. 161
      project/utils/circbuf.c
  37. 93
      project/utils/circbuf.h
  38. 171
      project/utils/debounce.c
  39. 62
      project/utils/debounce.h
  40. 33
      project/utils/matcher.c
  41. 39
      project/utils/matcher.h
  42. 70
      project/utils/meanbuf.c
  43. 33
      project/utils/meanbuf.h
  44. 4
      project/utils/minmax.h
  45. 286
      project/utils/str_utils.c
  46. 75
      project/utils/str_utils.h
  47. 331
      project/utils/timebase.c
  48. 159
      project/utils/timebase.h

@ -4,7 +4,7 @@ ARCH_FLAGS = -msoft-float -mfloat-abi=soft
ARCH_FLAGS += -mthumb -mcpu=cortex-m3
# Clock speed constants
DEFS += -DF_CPU=8000000UL
DEFS += -DF_CPU=72000000UL
DEFS += -DSTM32F10X_MD
DEFS += -DARM_MATH_CM3
@ -113,7 +113,7 @@ LIB_CFLAGS = -Wno-shadow -Wno-float-equal -Wno-inline -Wno-unused-parameter -W
###############################################################################
# Linker flags
LDFLAGS += --static -lm -lc -nostartfiles
LDFLAGS += --static -lm -lc -nostartfiles -specs=nano.specs
LDFLAGS += -Llib
LDFLAGS += -T$(LDSCRIPT)
LDFLAGS += -Wl,-Map=$(*).map
@ -181,6 +181,7 @@ size: $(BINARY).elf
%.elf %.map: libcheck $(OBJS) $(HEADERS)
$(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(*).elf
$(Q)./parsemap.php
$(Q)$(SIZE) $(*).elf
%.o: %.c

@ -11,7 +11,7 @@ INCLUDEPATH += \
/usr/arm-none-eabi/include \
/usr/lib/gcc/arm-none-eabi/5.3.0/include/
DEFINES += F_CPU=8000000UL \
DEFINES += F_CPU=72000000UL \
STM32F10X_MD \
USE_STDPERIPH_DRIVER \
__null=0 \
@ -61,7 +61,27 @@ HEADERS += \
project/stm32f10x_conf.h \
project/stm32f10x_it.h \
project/system_stm32f10x.h \
project/sbmp_config.h
project/sbmp_config.h \
project/com/com_fileio.h \
project/com/com_iface.h \
project/com/datalink.h \
project/com/debug.h \
project/com/iface_noop.h \
project/com/iface_usart.h \
project/utils/circbuf.h \
project/utils/minmax.h \
project/utils/timebase.h \
project/colorled.h \
project/malloc_safe.h \
project/hw_init.h \
project/utils/debounce.h \
project/bus/event_handler.h \
project/bus/event_queue.h \
project/utils/str_utils.h \
project/main.h \
project/utils/matcher.h \
project/utils/meanbuf.h \
project/display.h
SOURCES += \
lib/cmsis/core_cm3.c \
@ -98,7 +118,27 @@ SOURCES += \
lib/spl/src/stm32f10x_wwdg.c \
project/main.c \
project/stm32f10x_it.c \
project/system_stm32f10x.c
project/system_stm32f10x.c \
project/com/com_fileio.c \
project/com/com_iface.c \
project/com/datalink.c \
project/com/debug.c \
project/com/iface_noop.c \
project/com/iface_usart.c \
project/utils/circbuf.c \
project/utils/debounce.c \
project/utils/timebase.c \
project/colorled.c \
project/hw_init.c \
project/malloc_safe.c \
project/spl_assert.c \
project/syscalls.c \
project/bus/event_handler.c \
project/bus/event_queue.c \
project/utils/str_utils.c \
project/utils/matcher.c \
project/utils/meanbuf.c \
project/display.c
DISTFILES += \
style.astylerc \

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 3.6.1, 2016-05-11T19:11:14. -->
<!-- Written by QtCreator 3.6.1, 2016-05-11T22:50:05. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
@ -53,7 +53,12 @@
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="ClangProjectSettings">
<value type="QString" key="CustomPchFile"></value>
<value type="int" key="PchUsage">1</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
@ -63,22 +68,11 @@
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{15262c8f-05de-48ee-9452-3d289b21ba3e}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">-1</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">2</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/ondra/devel/build-f103-STLINK-Debug</value>
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/ondra/devel/f103</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
@ -88,10 +82,10 @@
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">-B -j 4</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
@ -119,132 +113,12 @@
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Debug</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Main</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">2</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.1">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/ondra/devel/build-f103-STLINK-Release</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">false</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Release</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.2">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/ondra/devel/build-f103-STLINK-Profile</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">qmake</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">QtProjectManager.QMakeBuildStep</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.LinkQmlDebuggingLibrary">true</value>
<value type="QString" key="QtProjectManager.QMakeBuildStep.QMakeArguments"></value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.QMakeForced">false</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.SeparateDebugInfo">true</value>
<value type="bool" key="QtProjectManager.QMakeBuildStep.UseQtQuickCompiler">false</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.1">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">false</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments"></value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">2</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Build</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.MakeStep</value>
<valuelist type="QVariantList" key="Qt4ProjectManager.MakeStep.AutomaticallyAddedMakeArguments">
<value type="QString">-w</value>
<value type="QString">-r</value>
</valuelist>
<value type="bool" key="Qt4ProjectManager.MakeStep.Clean">true</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeArguments">clean</value>
<value type="QString" key="Qt4ProjectManager.MakeStep.MakeCommand"></value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Clean</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Profile</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">Qt4ProjectManager.Qt4BuildConfiguration</value>
<value type="int" key="Qt4ProjectManager.Qt4BuildConfiguration.BuildConfiguration">0</value>
<value type="bool" key="Qt4ProjectManager.Qt4BuildConfiguration.UseShadowBuild">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">3</value>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
@ -288,11 +162,11 @@
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Arguments"></value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable"></value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Arguments">flash</value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">/usr/bin/make</value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory">%{buildDir}</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Custom Executable</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Run /usr/bin/make</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">make flash</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
@ -340,13 +214,66 @@
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="QString" key="BareMetal.CustomRunConfig.Executable"></value>
<value type="QString" key="BareMetal.RunConfig.WorkingDirectory"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Run on GDB server or hardware debugger</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">f103 (via GDB server or hardware debugger)</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">BareMetal.CustomRunConfig</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">BareMetal/home/ondra/devel/f103/f103.pro</value>
<value type="QString" key="Qt4ProjectManager.MaemoRunConfiguration.Arguments"></value>
<value type="QString" key="Qt4ProjectManager.MaemoRunConfiguration.ProFile"></value>
<value type="QString" key="Qt4ProjectManager.MaemoRunConfiguration.ProFile">f103.pro</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseMultiProcess">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.2">
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
<value type="int">0</value>
<value type="int">1</value>
<value type="int">2</value>
<value type="int">3</value>
<value type="int">4</value>
<value type="int">5</value>
<value type="int">6</value>
<value type="int">7</value>
<value type="int">8</value>
<value type="int">9</value>
<value type="int">10</value>
<value type="int">11</value>
<value type="int">12</value>
<value type="int">13</value>
<value type="int">14</value>
</valuelist>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Arguments"></value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.Executable">/home/ondra/devel/f103/main.elf</value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory">/home/ondra/devel/f103</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Run /home/ondra/devel/f103/main.elf</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="uint" key="RunConfiguration.QmlDebugServerPort">3768</value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
@ -354,7 +281,7 @@
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">2</value>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">3</value>
</valuemap>
</data>
<data>

@ -0,0 +1,157 @@
#daemon configuration
telnet_port 4444
gdb_port 3333
#interface
#cat /usr/lib/openocd/interface/olimex-arm-usb-ocd.cfg >> openocd.cfg
#
# STMicroelectronics ST-LINK/V2 in-circuit debugger/programmer
#
interface hla
hla_layout stlink
hla_device_desc "ST-LINK/V2"
hla_vid_pid 0x0483 0x3748
# Optionally specify the serial number of ST-LINK/V2 usb device. ST-LINK/V2
# devices seem to have serial numbers with unreadable characters. ST-LINK/V2
# firmware version >= V2.J21.S4 recommended to avoid issues with adapter serial
# number reset issues.
# eg.
#hla_serial "\xaa\xbc\x6e\x06\x50\x75\xff\x55\x17\x42\x19\x3f"
# target configuration
# cat /usr/lib/openocd/target/stm32.cfg >> openocd.cfg
# script for stm32f3x family
#
# stm32 devices support both JTAG and SWD transports.
#
source [find target/swj-dp.tcl]
source [find mem_helper.tcl]
if { [info exists CHIPNAME] } {
set _CHIPNAME $CHIPNAME
} else {
set _CHIPNAME stm32f3x
}
set _ENDIAN little
# Work-area is a space in RAM used for flash programming
# By default use 16kB
if { [info exists WORKAREASIZE] } {
set _WORKAREASIZE $WORKAREASIZE
} else {
set _WORKAREASIZE 0x4000
}
# JTAG speed should be <= F_CPU/6. F_CPU after reset is 8MHz, so use F_JTAG = 1MHz
#
# Since we may be running of an RC oscilator, we crank down the speed a
# bit more to be on the safe side. Perhaps superstition, but if are
# running off a crystal, we can run closer to the limit. Note
# that there can be a pretty wide band where things are more or less stable.
adapter_khz 1000
adapter_nsrst_delay 100
if {[using_jtag]} {
jtag_ntrst_delay 100
}
#jtag scan chain
if { [info exists CPUTAPID] } {
set _CPUTAPID $CPUTAPID
} else {
if { [using_jtag] } {
# See STM Document RM0316
# Section 29.6.3 - corresponds to Cortex-M4 r0p1
set _CPUTAPID 0x4ba00477
} {
set _CPUTAPID 0x2ba01477
}
}
swj_newdap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
if { [info exists BSTAPID] } {
set _BSTAPID $BSTAPID
} else {
# STM Document RM0316 rev 5 for STM32F302/303 B/C size
set _BSTAPID1 0x06422041
# STM Document RM0313 rev 3 for STM32F37x
set _BSTAPID2 0x06432041
# STM Document RM364 rev 1 for STM32F334
set _BSTAPID3 0x06438041
# STM Document RM316 rev 5 for STM32F303 6/8 size
# STM Document RM365 rev 3 for STM32F302 6/8 size
# STM Document RM366 rev 2 for STM32F301 6/8 size
set _BSTAPID4 0x06439041
# STM Document RM016 rev 5 for STM32F303 D/E size
set _BSTAPID5 0x06446041
}
if {[using_jtag]} {
swj_newdap $_CHIPNAME bs -irlen 5 -expected-id $_BSTAPID1 \
-expected-id $_BSTAPID2 -expected-id $_BSTAPID3 \
-expected-id $_BSTAPID4 -expected-id $_BSTAPID5
}
set _TARGETNAME $_CHIPNAME.cpu
target create $_TARGETNAME cortex_m -endian $_ENDIAN -chain-position $_TARGETNAME
$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
set _FLASHNAME $_CHIPNAME.flash
flash bank $_FLASHNAME stm32f1x 0 0 0 0 $_TARGETNAME
reset_config srst_nogate
if {![using_hla]} {
# if srst is not fitted use SYSRESETREQ to
# perform a soft reset
cortex_m reset_config sysresetreq
}
proc stm32f3x_default_reset_start {} {
# Reset clock is HSI (8 MHz)
adapter_khz 1000
}
proc stm32f3x_default_examine_end {} {
# Enable debug during low power modes (uses more power)
mmw 0xe0042004 0x00000007 0 ;# DBGMCU_CR |= DBG_STANDBY | DBG_STOP | DBG_SLEEP
# Stop watchdog counters during halt
mww 0xe0042008 0x00001800 ;# DBGMCU_APB1_FZ = DBG_IWDG_STOP | DBG_WWDG_STOP
}
proc stm32f3x_default_reset_init {} {
# Configure PLL to boost clock to HSI x 8 (64 MHz)
mww 0x40021004 0x00380400 ;# RCC_CFGR = PLLMUL[3:1] | PPRE1[2]
mmw 0x40021000 0x01000000 0 ;# RCC_CR |= PLLON
mww 0x40022000 0x00000012 ;# FLASH_ACR = PRFTBE | LATENCY[1]
sleep 10 ;# Wait for PLL to lock
mmw 0x40021004 0x00000002 0 ;# RCC_CFGR |= SW[1]
# Boost JTAG frequency
adapter_khz 8000
}
# Default hooks
$_TARGETNAME configure -event examine-end { stm32f3x_default_examine_end }
$_TARGETNAME configure -event reset-start { stm32f3x_default_reset_start }
$_TARGETNAME configure -event reset-init { stm32f3x_default_reset_init }
$_TARGETNAME configure -event trace-config {
# Set TRACE_IOEN; TRACE_MODE is set to async; when using sync
# change this value accordingly to configure trace pins
# assignment
mmw 0xe0042004 0x00000020 0
}

@ -0,0 +1,21 @@
#!/usr/bin/env php
<?php
$map = file_get_contents('main.map');
$at_flash = strpos($map, '.text 0x0000000008000000');
$at_ram = strpos($map, '.data 0x0000000020000000');
$at_dbg = strpos($map, '.memory_b1_text');
$discard = substr($map, 0, $at_flash);
$flash = substr($map, $at_flash, $at_ram - $at_flash);
$ram = substr($map, $at_ram, $at_dbg - $at_ram);
$flash = str_replace("lib/gcc/arm-none-eabi/5.3.0/../../../../", "", $flash);
file_put_contents("main.flash.map", $flash);
file_put_contents("main.ram.map", $ram);
//preg_match("/\*\(\.eh_frame\)\n\s*0x00000000(2[0-9a-f]+)\s*\. = ALIGN \(0x4\)/i", $ram, $m);
//echo "Free RAM: " . (0x20010000 - hexdec($m[1])) . " B\n";

@ -0,0 +1,159 @@
#include "event_handler.h"
#include "com/debug.h"
typedef struct {
uint32_t handler_id;
uint32_t chained_handler; // if not 0, that handler is removed together with this handler.
EventType type; // event type
EventHandlerCallback handler; // returns True if event was handled.
bool used; // this slot is currently used
void *user_data;
} EventHandlerSlot;
#define EH_SLOT_COUNT 6
static EventHandlerSlot eh_slots[EH_SLOT_COUNT];
static uint32_t next_slot_pid = 1; // 0 is reserved
/** Get a valid free PID for a new handler slot. */
static uint32_t make_pid(void)
{
uint32_t pid = next_slot_pid++;
// make sure no task is given PID 0
if (next_slot_pid == 0) {
next_slot_pid++;
}
return pid;
}
/**
* @brief Register an event handler for event type
* @param type : handled event type
* @param handler : the handler func
* @return handler ID. Can be used to remove the handler.
*/
uint32_t register_event_handler(EventType type, EventHandlerCallback handler, void *user_data)
{
for (int i = 0; i < EH_SLOT_COUNT; i++) {
if (eh_slots[i].used) continue;
// Free slot found
EventHandlerSlot *slot = &eh_slots[i];
slot->handler = handler;
slot->type = type;
slot->handler_id = make_pid();
slot->used = true;
slot->chained_handler = 0;
slot->user_data = user_data;
return slot->handler_id;
}
error("Failed to register event handler for type %d", type);
return 0; // fail
}
/** Chain for common destruction */
bool chain_event_handler(uint32_t from, uint32_t to, bool reci)
{
uint8_t cnt = 0;
for (int i = 0; i < EH_SLOT_COUNT; i++) {
EventHandlerSlot *slot = &eh_slots[i];
if (!slot->used) continue;
if (slot->handler_id == from) {
slot->chained_handler = to;
cnt++;
}
// link back in two-handler reciprocal link
if (reci && slot->handler_id == to) {
slot->chained_handler = from;
cnt++;
}
if (cnt == (reci ? 2 : 1)) {
return true;
}
}
return false;
}
/**
* @brief check if exists
*/
bool event_handler_exists(uint32_t handler_id)
{
for (int i = 0; i < EH_SLOT_COUNT; i++) {
EventHandlerSlot *slot = &eh_slots[i];
if (!slot->used) continue;
if (slot->handler_id == handler_id) {
return true;
}
}
return false;
}
/**
* @brief Remove event handler by handler ID
* @param handler_id : handler ID, obtained when registering or in the callback.
* @return number of removed handlers
*/
int remove_event_handler(uint32_t handler_id)
{
int cnt = 0;
while (handler_id != 0) { // outer loop because of chained handlers
bool suc = false;
for (int i = 0; i < EH_SLOT_COUNT; i++) {
if (!eh_slots[i].used) {
continue; // skip empty slot
}
// Free slot found
EventHandlerSlot *slot = &eh_slots[i];
if (slot->handler_id == handler_id) {
slot->used = false;
slot->user_data = NULL;
suc = true;
cnt++;
handler_id = slot->chained_handler; // continue with the chained handler.
break;
}
}
if (!suc) break;
}
return cnt;
}
/** Handle an event */
void run_event_handler(Event *evt)
{
bool handled = false;
for (int i = 0; i < EH_SLOT_COUNT; i++) {
EventHandlerSlot *slot = &eh_slots[i];
if (!slot->used) continue; // unused
if (slot->type != evt->type) continue; // wrong type
handled = slot->handler(slot->handler_id, evt, &slot->user_data);
if (handled) break;
}
if (!handled) {
warn("Unhandled event, type %d", evt->type);
}
}

@ -0,0 +1,48 @@
#pragma once
#include "main.h"
#include "event_queue.h"
typedef bool (*EventHandlerCallback) (uint32_t hdlr_id, Event *evt, void **user_data);
/**
* @brief Register an event handler for event type
* @param type : handled event type
* @param handler : the handler func
* @return handler ID. Can be used to remove the handler.
*/
uint32_t register_event_handler(EventType type, EventHandlerCallback handler, void *user_data);
/**
* @brief Remove event handler by handler ID
* @param handler_id : handler ID, obtained when registering or in the callback.
* @return number of removed handlers
*/
int remove_event_handler(uint32_t handler_id);
/**
* @brief Handle an event
* @param event : pointer to the event to handle
*/
void run_event_handler(Event *event);
/**
* @brief Check if hansler exists
* @param handler_id : handler
* @return exists
*/
bool event_handler_exists(uint32_t handler_id);
/**
* @brief Create a link between two handlers (one direction).
*
* If handler A is linked to handler B, and handler A is removed,
* both handlers will perish.
*
* Make a circle if you need to chain more than two handlers.
*
* @param from : handler A
* @param to : handler B
* @param reciprocal : link also from B to A
* @return
*/
bool chain_event_handler(uint32_t from, uint32_t to, bool reciprocal);

@ -0,0 +1,66 @@
#include "event_queue.h"
#include "com/debug.h"
/** Task queue */
static CircBuf *tq;
/** Event queue */
static CircBuf *eq;
void queues_init(size_t tq_size, size_t eq_size)
{
tq = cbuf_create(tq_size, sizeof(QueuedTask));
eq = cbuf_create(eq_size, sizeof(Event));
}
bool tq_post(void (*handler)(void*), void *arg)
{
QueuedTask task;
task.handler = handler;
task.arg = arg;
bool suc = cbuf_append(tq, &task);
if (!suc) error("TQ overflow");
return suc;
}
bool eq_post(const Event *event)
{
bool suc = cbuf_append(eq, event);
if (!suc) {
error("EQ overflow, evt %d", event->type);
}
return suc;
}
bool tq_poll_one(void)
{
QueuedTask task;
// serve all tasks
bool suc = cbuf_pop(tq, &task);
if (suc) {
task.handler(task.arg);
}
return suc;
}
void tq_poll(void)
{
// serve all tasks
while (tq_poll_one());
}
bool eq_take(Event *dest)
{
bool suc = cbuf_pop(eq, dest);
return suc;
}

@ -0,0 +1,75 @@
#pragma once
#include "main.h"
#include "utils/circbuf.h"
#define TASK_QUEUE_SIZE 64
#define EVENT_QUEUE_SIZE 64
/** Application events */
typedef enum {
EVENT_ONE // placeholder
} EventType;
/** Event Queue entry */
typedef struct {
EventType type;
void *data;
} Event;
typedef struct {
void (*handler)(void*);
void* arg;
} QueuedTask;
/**
* @brief Set up the task and event queues
* @param tq_size : number of slots in the task queue
* @param eq_size : number of slots in the event queue
*/
void queues_init(size_t tq_size, size_t eq_size);
/**
* @brief Post a task on the task queue, with arg.
*
* @see tq_post()
*
* @param handler : task function
* @param arg : argument for the handler
* @return success
*/
bool tq_post(void (*handler)(void *), void *arg);
/**
* @brief Post an event on the event queue
* @param event : pointer to an event to post; will be copied.
* @return success
*/
bool eq_post(const Event *event);
/**
* @brief Run all pending tasks on the task queue
*/
void tq_poll(void);
/**
* @brief Run one pending task on the task queue
* @return true if a task was run.
*/
bool tq_poll_one(void);
/**
* @brief Take one event off the event queue.
* @param dest : pointer to a destination event variable.
* @return success
*/
bool eq_take(Event *dest);

@ -0,0 +1,66 @@
#include "colorled.h"
#include "utils/timebase.h"
#define LONG_DELAY() for (volatile uint32_t __j = 4; __j > 0; __j--)
#define SHORT_DELAY() for (volatile uint32_t __j = 1; __j > 0; __j--)
static inline
__attribute__((always_inline))
void colorled_byte(uint8_t b)
{
for (register volatile uint8_t i = 0; i < 8; i++) {
COLORLED_GPIO->BSRR = COLORLED_PIN; // set pin high
// duty cycle determines bit value
if (b & 0x80) {
LONG_DELAY();
COLORLED_GPIO->BRR = COLORLED_PIN; // set pin low
SHORT_DELAY();
} else {
SHORT_DELAY();
COLORLED_GPIO->BRR = COLORLED_PIN; // set pin low
LONG_DELAY();
}
b <<= 1; // shift to next bit
}
}
/** Set one RGB LED color */
void colorled_set(uint32_t rgb)
{
__disable_irq(); // SysTick interrupt when sending data would break the timing
colorled_byte((rgb & 0x00FF00) >> 8);
colorled_byte((rgb & 0xFF0000) >> 16);
colorled_byte(rgb & 0x0000FF);
__enable_irq();
delay_us(50); // show
}
/** Set many RGBs */
void colorled_set_many(uint32_t *rgbs, int count)
{
__disable_irq();
for (int i = 0; i < count; i++) {
uint32_t rgb = *rgbs++;
colorled_byte((rgb & 0x00FF00) >> 8);
colorled_byte((rgb & 0xFF0000) >> 16);
colorled_byte(rgb & 0x0000FF);
}
__enable_irq();
delay_us(50); // show
}
void colorled_off(void)
{
colorled_set(RGB_BLACK);
}

@ -0,0 +1,79 @@
#pragma once
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
// PB8 - WS2812B data line
#define COLORLED_GPIO GPIOB
#define COLORLED_PIN GPIO_Pin_12
#define RGB_RED rgb(255, 0, 0)
#define RGB_ORANGE rgb(255, 110, 0)
#define RGB_YELLOW rgb(255, 255, 0)
#define RGB_LIME rgb(160, 255, 0)
#define RGB_GREEN rgb( 0, 255, 0)
#define RGB_CYAN rgb( 0, 255, 120)
#define RGB_BLUE rgb( 0, 0, 255)
#define RGB_MAGENTA rgb(255, 0, 255)
#define RGB_WHITE rgb(255, 255, 255)
#define RGB_BLACK rgb( 0, 0, 0)
/**
* @brief Struct for easy manipulation of RGB colors.
*
* Set components in the xrgb.r (etc.) and you will get
* the hex in xrgb.num.
*/
typedef union {
/** Struct for access to individual color components */
struct __attribute__((packed)) {
uint8_t b;
uint8_t g;
uint8_t r;
};
/** RGB color as a single uint32_t */
uint32_t num;
} ws2812_rgb_t;
/* Exported macros -----------------------------------------------------------*/
/**
* @brief Compose an RGB color.
* @param r, g, b - components 0xFF
* @returns integer 0xRRGGBB
*/
#define rgb(r, g, b) (((0xFF & (r)) << 16) | ((0xFF & (g)) << 8) | (0xFF & (b)))
/* Get components */
#define rgb_r(rgb) (((rgb) >> 16) & 0xFF)
#define rgb_g(rgb) (((rgb) >> 8) & 0xFF)
#define rgb_b(rgb) ((rgb) & 0xFF)
/* Exported functions --------------------------------------------------------*/
/**
* @brief Turn OFF the rgb LED
*/
void colorled_off(void);
/**
* @brief Set color of a WS2812B
* @param rgb - color 0xRRGGBB
*/
void colorled_set(uint32_t rgb);
/**
* @brief Set color of multiple chained RGB leds
* @param rgbs - array of colors (0xRRGGBB)
* @param count - number of LEDs
*/
void colorled_set_many(uint32_t *rgbs, int count);

@ -0,0 +1,96 @@
#include "main.h"
#include "com_fileio.h"
#include "com_iface.h"
#include "utils/str_utils.h"
// Holding fields for ifaces
ComIface *debug_iface = NULL;
ComIface *data_iface = NULL;
// --- File descriptor names ------------------------------
struct name_fd {
const char *name;
const int fd;
};
#define NAME_FD_MAP_LEN 1
/** pre-assigned file descriptors for names */
static const struct name_fd name_fd_map[NAME_FD_MAP_LEN] = {
{FNAME_DLNK, FD_DLNK}
};
// --- Syscalls -------------------------------------------
/**
* @brief Write to a file by file descriptor.
*
* @param fd : open file descriptor
* @param buf : data to write
* @param len : buffer size
* @return number of written bytes
*/
int _write(int fd, const char *buf, int len)
{
switch (fd) {
case FD_STDOUT: return (int)com_tx_block(debug_iface, buf, (size_t)len);
case FD_STDERR: return (int)com_tx_block(debug_iface, buf, (size_t)len);
case FD_DLNK: return (int)com_tx_block(data_iface, buf, (size_t)len);
default:
return 0;
}
}
// For some reason, reading does not work.
#if 0
/**
* @brief Read from a file descriptor
*
* @param fd : file descriptor
* @param buf : destination buffer
* @param len : number of bytes to read
* @return actual number of read bytes
*/
int _read(int fd, char *buf, int len)
{
switch (fd) {
case FD_STDIN: return com_rx_block(debug_iface, buf, len);
case FD_ESP: return com_rx_block(esp_iface, buf, len);
default:
return 0;
}
}
#endif
/**
* @brief Open a file by name.
*
* This stub is called by newlib when fopen is used.
* It returns a pre-assigned file descriptor based
* on the name.
*
* @note
* stdout, stderr, stdin are open implicitly
*
* @param name : file name
* @param flags : file flags (ignored)
* @return file descriptor
*/
int _open(const char *name, int flags, ...)
{
(void)flags;
for (int i = 0; i < NAME_FD_MAP_LEN; i++) {
if (streq(name_fd_map[i].name, name)) {
return name_fd_map[i].fd;
}
}
return -1;
}

@ -0,0 +1,23 @@
#pragma once
#include "com_iface.h"
/** Debug USART iface */
extern ComIface *debug_iface;
/** ESP8266 com iface */
extern ComIface *data_iface;
/** Do-nothing iface */
extern ComIface *com_iface_noop;
/** File descriptors for use with built-in "files" */
enum {
FD_STDIN = 0,
FD_STDOUT = 1,
FD_STDERR = 2,
FD_DLNK = 3,
};
#define FNAME_DLNK "dlnk"

@ -0,0 +1,179 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include "com_iface.h"
#include "utils/timebase.h"
// ---- accessor methods ----------------------
bool com_rx_rdy(ComIface *iface)
{
return iface->rx_rdy(iface);
}
bool com_tx_rdy(ComIface *iface)
{
return iface->tx_rdy(iface);
}
bool com_tx_done(ComIface *iface)
{
return iface->tx_done(iface);
}
bool com_tx(ComIface *iface, uint8_t b)
{
return iface->tx(iface, b);
}
bool com_rx(ComIface *iface, uint8_t *b)
{
return iface->rx(iface, (uint8_t*) b);
}
bool com_unrx(ComIface *iface, uint8_t b)
{
if (!iface->unrx) {
return false; // not all may have it implemented
}
return iface->unrx(iface, b);
}
size_t com_tx_block(ComIface *iface, const void *blob, size_t size)
{
return iface->txb(iface, blob, size);
}
size_t com_rx_block(ComIface *iface, void *buf, size_t length)
{
return iface->rxb(iface, buf, length);
}
void com_poll(ComIface *iface)
{
iface->poll(iface);
}
bool com_rx_char(ComIface *iface, char * c)
{
return iface->rx(iface, (uint8_t*) c);
}
bool com_tx_char(ComIface *iface, const char c)
{
return iface->tx(iface, (uint8_t)c);
}
/** Read string, terminate with \0. Returns real read size. */
size_t com_rx_str(ComIface *iface, char* buf, size_t buflen)
{
size_t len = iface->rxb(iface, buf, buflen);
buf[len] = 0; // zero terminator
return len;
}
size_t com_tx_str(ComIface *iface, const char * string)
{
return iface->txb(iface, string, strlen(string));
}
/** Wait for incoming byte */
bool com_rx_wait(ComIface *iface, uint16_t timeout)
{
until_timeout(timeout) {
if (iface->rx_rdy(iface)) return true;
}
return false;
}
/** Wait for tx buf ready */
bool com_tx_rdy_wait(ComIface *iface, uint16_t timeout)
{
until_timeout(timeout) {
if (iface->tx_rdy(iface)) return true;
}
return false;
}
/** Wait for tx complete */
bool com_tx_done_wait(ComIface *iface, uint16_t timeout)
{
until_timeout(timeout) {
if (iface->tx_done(iface)) return true;
}
return false;
}
void com_printf(ComIface *iface, const char *fmt, ...)
{
if (iface->file == NULL) {
com_tx_str(iface, "com_printf(), no iface file!\r\n");
return;
}
// use the file descriptor attached
va_list va;
va_start(va, fmt);
vfprintf(iface->file, fmt, va);
va_end(va);
}
void com_vprintf(ComIface *iface, const char *fmt, va_list va)
{
if (iface->file == NULL) {
com_tx_str(iface, "com_vprintf(), no iface file!\r\n");
com_tx_str(iface, fmt); // poor mans fallback
return;
}
// use the file descriptor attached
vfprintf(iface->file, fmt, va);
}
void com_v100_attr_(ComIface *iface, uint8_t count, ...)
{
va_list va;
va_start(va, count);
com_tx_char(iface, 27);
com_tx_char(iface, '[');
for (int i = 0; i < count; i++) {
int attr = va_arg(va, int);
// comma
if (i > 0) com_tx_char(iface, ';');
// number
com_printf(iface, "%d", attr);
}
com_tx_char(iface, 'm');
va_end(va);
}

@ -0,0 +1,168 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
// Implementation-independent UI (command handler)
typedef struct ComIface_struct ComIface;
struct ComIface_struct {
// --- Variables ---
/** Implementation-specific data object */
void *opts;
/** File descriptor for this stream */
FILE *file;
/**
* User callback for data ready, triggered on poll().
* Can be null.
*/
void (*rx_callback)(ComIface *iface);
// --- Interface methods ---
/** Data ready to be read (RX buffer Not Empty) */
bool (*rx_rdy)(ComIface *iface);
/** Free space in TX buffer */
bool (*tx_rdy)(ComIface *iface);
/** Everything sent */
bool (*tx_done)(ComIface *iface);
/** Send 1 byte. Blocking, timeout after some time. */
bool (*tx)(ComIface *iface, uint8_t b);
/** Read one byte. False on failure. Blocking, timeout after some time. */
bool (*rx)(ComIface *iface, uint8_t *b);
/** Unreceive one byte. False on failure. */
bool (*unrx)(ComIface *iface, uint8_t b);
/** Send a binary block. False on failure. */
size_t (*txb)(ComIface *iface, const void *blob, size_t size);
/** Read a binary block. Returns real read length. Blocking, timeout after some time. */
size_t (*rxb)(ComIface *iface, void *buf, size_t length);
/** Poll for changes */
void (*poll)(ComIface *iface);
};
// ---- Functions for working with iface --------------
/** Wait for incoming byte */
bool com_rx_wait(ComIface *iface, uint16_t timeout);
/** Wait for free space in tx buffer */
bool com_tx_rdy_wait(ComIface *iface, uint16_t timeout);
/** Wait for tx complete */
bool com_tx_done_wait(ComIface *iface, uint16_t timeout);
/** Check if there's data in the receive buffer */
bool com_rx_rdy(ComIface *iface);
/** Check if transmit buffer can accept data */
bool com_tx_rdy(ComIface *iface);
/** Check if transmit buffer is empty */
bool com_tx_done(ComIface *iface);
/** Send 1 byte */
bool com_tx(ComIface *iface, uint8_t b);
/** Read one byte. False on failure. Fails on timeout. */
bool com_rx(ComIface *iface, uint8_t *b);
/** Unrx one byte. False on failure. */
bool com_unrx(ComIface *iface, uint8_t b);
/** Send a binary blob. False on failure. Fails on timeout. */
size_t com_tx_block(ComIface *iface, const void *blob, size_t size);
/** Read a blob. Returns real read length. Stops on timeout. */
size_t com_rx_block(ComIface *iface, void *buf, size_t length);
/** Poll for changes */
void com_poll(ComIface *iface);
/** Send 1 char */
bool com_tx_char(ComIface *iface, char c);
/** Read one char. False on failure. Fails on timeout. */
bool com_rx_char(ComIface *iface, char *c);
/** Send a string. False on failure. */
size_t com_tx_str(ComIface *iface, const char *str);
/** Read a string. Returns real read length. Stops on timeout. */
size_t com_rx_str(ComIface *iface, char *buf, size_t length);
/**
* Printf to a serial interface.
* Max length is 255 chars.
*/
void com_printf(ComIface *iface, const char *fmt, ...)
__attribute__ ((format (printf, 2, 3)));
/**
* Printf to a serial interface, with va_list.
*/
void com_vprintf(ComIface *iface, const char *fmt, va_list va);
// ---- VT100/ANSI color support -------------
/** ANSI formatting attributes */
typedef enum {
// Non-colour Attributes
FMT_RESET = 0, // Reset all attributes
FMT_BRIGHT = 1, // Bright
FMT_DIM = 2, // Dim
FMT_UNDER = 4, // Underscore
FMT_BLINK = 5, // Blink
FMT_INVERS = 7, // Reverse
FMT_HIDDEN = 8, // Hidden
FMT_ITALIC = 16, // Italic font
FMT_FAINT = 32, // Faint color
// Foreground Colours
FMT_BLACK = 30, // Black
FMT_RED = 31, // Red
FMT_GREEN = 32, // Green
FMT_YELLOW = 33, // Yellow
FMT_BLUE = 34, // Blue
FMT_MAGENTA = 35, // Magenta
FMT_CYAN = 36, // Cyan
FMT_WHITE = 37, // White
// Background Colours
FMT_BLACK_BG = 40, // Black
FMT_RED_BG = 41, // Red
FMT_GREEN_BG = 42, // Green
FMT_YELLOW_BG = 43, // Yellow
FMT_BLUE_BG = 44, // Blue
FMT_MAGENTA_BG = 45, // Magenta
FMT_CYAN_BG = 46, // Cyan
FMT_WHITE_BG = 47, // White
} ANSI_attr_t;
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N
#define com_v100_attr(iface, ...) com_v100_attr_(iface, VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
/**
* Send formatting code to a com interface
*/
void com_v100_attr_(ComIface *iface, uint8_t count, ...);

@ -0,0 +1,48 @@
#include "datalink.h"
#include "com_fileio.h"
#include "debug.h"
#include "com_fileio.h"
SBMP_Endpoint *dlnk_ep;
static void dlnk_tx(uint8_t b);
static void dlnk_rx_bridge(ComIface *iface);
/** Set up the datalink */
void dlnk_init(void)
{
// Allocate & setup the endpoint
dlnk_ep = sbmp_ep_init(NULL, NULL, 100, dlnk_rx, dlnk_tx);
sbmp_ep_seed_session(dlnk_ep, 0x3FFF); // just in case arbitration fails
sbmp_ep_enable(dlnk_ep, true);
sbmp_ep_init_listeners(dlnk_ep, NULL, 4); // really don' need many here..
data_iface->rx_callback = dlnk_rx_bridge;
}
/**
* Bridge between USART subsystem's Rx buffer, and the SBMP driver
*
* Called if bytes are received in the USART buffer,
* and the USART subsystem is polled.
*/
static void dlnk_rx_bridge(ComIface *iface)
{
uint8_t b;
while (com_rx(iface, &b)) {
SBMP_RxStatus st = sbmp_ep_receive(dlnk_ep, b);
// If byte was not accepted, try to put it back into the buffer
if (st == SBMP_RX_BUSY || st == SBMP_RX_DISABLED) {
com_unrx(iface, b);
break;
}
}
}
/** Datalink Tx func */
static void dlnk_tx(uint8_t b)
{
com_tx(data_iface, b);
}

@ -0,0 +1,26 @@
#pragma once
/**
* SBMP setup & funcs
*/
#include "main.h"
#include <sbmp.h>
#define DG_REQUEST_RAW 40 // request raw vector. Sample count [u16], Frequency [u32]
#define DG_REQUEST_FFT 41 // request fft vector. Sample count [u16], Frequency [u32]. Result - count/2 bins. Count must be 2^n, 16..2048
#define DG_REQUEST_STORE_REF 42 // calculate signal signature & store for comparing
#define DG_REQUEST_COMPARE_REF 43
// wifi status & control
#define DG_SETMODE_AP 44 // request AP mode (AP button pressed)
#define DG_WPS_START 45 // start WPS
#define DG_WIFI_STATUS 46 // WiFi status report
#define DG_REQUEST_STM_VERSION 47 // Get acquisition module firmware version
extern SBMP_Endpoint *dlnk_ep;
void dlnk_init(void);
/** Received datagram handler */
extern void dlnk_rx(SBMP_Datagram *dg);

@ -0,0 +1,108 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include "com_iface.h"
#include "utils/timebase.h"
#include "debug.h"
void dbg_printf(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
com_vprintf(debug_iface, fmt, va); // vsnprintf(strbuf, DBG_BUF_LEN, fmt, va);
va_end(va);
}
void dbg_va_base(const char *fmt, const char *tag, va_list va)
{
ms_time_t now = ms_now();
uint32_t secs = now / 1000;
uint32_t ms = now % 1000;
com_printf(debug_iface, "%4"PRIu32".%03"PRIu32" ", secs, ms);
dbg_raw(tag);
com_vprintf(debug_iface, fmt, va);
dbg_raw(DEBUG_EOL);
}
/** Print a log message with an INFO tag and newline (ONLY FOR BANNER - always shown) */
void banner_info(const char *fmt, ...)
{
com_v100_attr(debug_iface, FMT_GREEN);
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_INFO, va);
va_end(va);
com_v100_attr(debug_iface, FMT_RESET);
}
#if VERBOSE_LOGGING
/** Print a log message with a DEBUG tag and newline */
void dbg(const char *fmt, ...)
{
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_BASE, va);
va_end(va);
}
/** Print a log message with an INFO tag and newline */
void info(const char *fmt, ...) __attribute__((alias("banner_info")));
#endif
/** Print a log message with an INFO tag and newline */
void banner(const char *fmt, ...)
{
com_v100_attr(debug_iface, FMT_GREEN, FMT_BRIGHT);
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_INFO, va);
va_end(va);
com_v100_attr(debug_iface, FMT_RESET);
}
/** Print a log message with a warning tag and newline */
void warn(const char *fmt, ...)
{
com_v100_attr(debug_iface, FMT_YELLOW, FMT_BRIGHT);
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_WARN, va);
va_end(va);
com_v100_attr(debug_iface, FMT_RESET);
}
/** Print a log message with an ERROR tag and newline */
void error(const char *fmt, ...)
{
com_v100_attr(debug_iface, FMT_RED, FMT_BRIGHT);
va_list va;
va_start(va, fmt);
dbg_va_base(fmt, DEBUG_TAG_ERROR, va);
va_end(va);
com_v100_attr(debug_iface, FMT_RESET);
}

@ -0,0 +1,73 @@
#pragma once
#include "main.h"
#include "com_iface.h"
#include "com_fileio.h"
#include "bus/event_queue.h"
// helper to mark printf functions
#define PRINTF_LIKE __attribute__((format(printf, 1, 2)))
#define DBG_BUF_LEN 256
#define ESCAPE_DEBUG_MESSAGES 1
// formatting symbols
#define DEBUG_EOL "\r\n"
#define DEBUG_TAG_WARN "[W] "
#define DEBUG_TAG_ERROR "[E] "
#define DEBUG_TAG_BASE "[ ] "
#define DEBUG_TAG_INFO "[i] "
/** Print a log message with no tag and no newline */
void dbg_printf(const char *fmt, ...) PRINTF_LIKE;
/** Print via va_list */
void dbg_va_base(const char *fmt, const char *tag, va_list va);
/** Print a string to the debug interface (length not limited) */
static inline void dbg_raw(const char *str)
{
com_tx_str(debug_iface, str);
}
/** Print a char to the debug interface */
static inline void dbg_raw_c(char c)
{
com_tx(debug_iface, (uint8_t)c);
}
#if VERBOSE_LOGGING
/** Print a log message with a "debug" tag and newline */
void dbg(const char *fmt, ...) PRINTF_LIKE;
/** Print a log message with an "info" tag and newline */
void info(const char *fmt, ...) PRINTF_LIKE;
#else
#define dbg(fmt, ...)
#define info(fmt, ...)
#endif
/** Print a log message with an "info" tag and newline */
void banner_info(const char *fmt, ...) PRINTF_LIKE;
/** Print a log message with a "banner" tag and newline */
void banner(const char *fmt, ...) PRINTF_LIKE;
/** Print a log message with a "warning" tag and newline */
void warn(const char *fmt, ...) PRINTF_LIKE;
/** Print a log message with an "error" tag and newline */
void error(const char *fmt, ...) PRINTF_LIKE;

@ -0,0 +1,91 @@
#include <stdlib.h>
#include "iface_noop.h"
#include "com_fileio.h"
#include "malloc_safe.h"
// ==== NOOP com driver ====
static bool if_rx_rdy(ComIface *iface)
{
(void)iface;
return false;
}
static bool if_tx_rdy(ComIface *iface)
{
(void)iface;
return true;
}
static bool if_tx_done(ComIface *iface)
{
(void)iface;
return true;
}
static bool if_rx(ComIface *iface, uint8_t *b)
{
(void)iface;
(void)b;
return false;
}
static bool if_unrx(ComIface *iface, uint8_t b)
{
(void)iface;
(void)b;
return false;
}
static bool if_tx(ComIface *iface, uint8_t b)
{
(void)iface;
(void)b;
return true;
}
static size_t if_rxb(ComIface *iface, void *buf, size_t buflen)
{
(void)iface;
(void)buf;
(void)buflen;
return 0;
}
static size_t if_txb(ComIface *iface, const void *blob, size_t size)
{
(void)iface;
(void)blob;
return size;
}
static void if_poll(ComIface *iface)
{
(void)iface;
}
ComIface *com_noop_init(void)
{
ComIface *iface = malloc_s(sizeof(ComIface));
iface->rx_rdy = if_rx_rdy;
iface->tx_rdy = if_tx_rdy;
iface->tx_done = if_tx_done;
iface->rx = if_rx;
iface->unrx = if_unrx;
iface->tx = if_tx;
iface->rxb = if_rxb;
iface->txb = if_txb;
iface->poll = if_poll;
iface->rx_callback = NULL;
return iface;
}

@ -0,0 +1,14 @@
#pragma once
/**
* NOOP iface works like /dev/null
*/
#include "com_iface.h"
/**
* @brief Initialize a do-nothing interface.
* @param iface : iface pointer. If NULL, it'll be allocated.
* @return the iface pointer.
*/
ComIface *com_noop_init(void);

@ -0,0 +1,328 @@
#include "main.h"
#include "malloc_safe.h"
#include "iface_usart.h"
#include "utils/timebase.h"
#include "utils/circbuf.h"
#include "com/com_fileio.h"
#include "com/debug.h"
#include "bus/event_queue.h"
#define DEF_WAIT_TO_MS 5
typedef struct {
uint16_t wait_timeout;
USART_TypeDef *dev;
CircBuf *rxbuf; // allocated receive buffer
CircBuf *txbuf; // allocated transmit buffer, can be NULL -> no buffer
} usart_opts;
#define opts(iface) ((usart_opts *)(iface)->opts)
// ---- Instances ----
// (needed for async rx/tx with interrupts)
static ComIface *usart1_iface = NULL;
static ComIface *usart2_iface = NULL;
static ComIface *usart3_iface = NULL;
// -------------------
/** Check if data is ready for reading */
static bool if_rx_rdy(ComIface *iface)
{
CircBuf *buf = opts(iface)->rxbuf;
return !cbuf_empty(buf);
}
/** Check if sending is done */
static bool if_tx_rdy(ComIface *iface)
{
CircBuf *buf = opts(iface)->txbuf;
if (buf == NULL) {
// non-buffered mode
USART_TypeDef* USARTx = opts(iface)->dev;
return (USARTx->SR & USART_SR_TXE);
}
return !cbuf_full(buf);
}
/** Check if sending is done */
static bool if_tx_done(ComIface *iface)
{
CircBuf *buf = opts(iface)->txbuf;
USART_TypeDef* USARTx = opts(iface)->dev;
// NULL buffer is considered empty
return cbuf_empty(buf) && (USARTx->SR & USART_SR_TC);
}
/** Read one byte (wait for it). False on error. */
static bool if_rx(ComIface *iface, uint8_t *b)
{
// wait for data in the buffer
if (!com_rx_wait(iface, opts(iface)->wait_timeout) || b == NULL) {
return false;
}
// read from buffer
cbuf_pop(opts(iface)->rxbuf, b);
return true;
}
/** Try to unreceive a byte. False on error. */
static bool if_unrx(ComIface *iface, uint8_t b)
{
// push back
return cbuf_push(opts(iface)->rxbuf, &b);
}
/** Send one byte (waits for tx) */
static bool if_tx(ComIface *iface, uint8_t b)
{
usart_opts *uopts = opts(iface);
USART_TypeDef* USARTx = uopts->dev;
if (uopts->txbuf == NULL) {
// Blocking tx mode
until_timeout(uopts->wait_timeout) {
if (USARTx->SR & USART_SR_TXE) {
USARTx->DR = b;
return true; // success
}
}
return false;
} else {
// Buffered mode
// wait for free space in the buffer
bool ready = com_tx_rdy_wait(iface, uopts->wait_timeout);
if (ready) {
// append to the buffer
cbuf_append(uopts->txbuf, &b);
}
// regardless ready state, start Tx if there are bytes
// (should fix a bug where full buffer was never emptied)
// If TXE (send buffer empty), start sending the buffer
// Otherwise, IRQ chain is in progress.
if (USARTx->SR & USART_SR_TXE) {
USART_ITConfig(USARTx, USART_IT_TXE, ENABLE); // start tx chain
}
return ready;
}
}
/** Read a blob. Returns real read size */
static size_t if_rxb(ComIface *iface, void *buf, size_t buflen)
{
if (buf == NULL) return false;
//if (!com_rx_wait(iface, opts(iface)->wait_timeout)) return 0;
uint8_t* byteptr = (uint8_t*)buf;
for (size_t i = 0; i < buflen; i++) {
if (!if_rx(iface, byteptr++)) return i;
}
return buflen;
}
/** Send a binary blob. False on error. */
static size_t if_txb(ComIface *iface, const void *blob, size_t size)
{
if (blob == NULL) return false;
//if (!com_tx_rdy_wait(iface, opts(iface)->wait_timeout)) return false;
const uint8_t* byteptr = (const uint8_t*)blob;
for (size_t i = 0; i < size; i++) {
if (!if_tx(iface, *byteptr++)) return i;
}
return size;
}
/** Check for incoming data */
static void if_poll(ComIface *iface)
{
if (if_rx_rdy(iface)) {
// call user cb
if (iface->rx_callback) {
iface->rx_callback(iface);
}
}
}
// ---- Init interface ----
//void usart_iface_set_baudrate(ComIface *iface, uint32_t baud)
//{
// USART_TypeDef* USARTx = opts(iface)->dev;
//
// USART_SetBaudrate(USARTx, baud);
//}
/* public */
ComIface *usart_iface_init(USART_TypeDef* USARTx, uint32_t baud, size_t rxbuf_len, size_t txbuf_len)
{
assert_param(IS_USART_BAUDRATE(baud));
assert_param(rxbuf_len > 0);
ComIface *iface = malloc_s(sizeof(ComIface));
// --- setup device specific iface data ---
// Allocate the opts config object
iface->opts = malloc_s(sizeof(usart_opts));
// Set device ID
opts(iface)->dev = USARTx;
opts(iface)->wait_timeout = DEF_WAIT_TO_MS;
// Initialize the rx/tx buffers (malloc's the array)
opts(iface)->rxbuf = cbuf_create(rxbuf_len, 1);
// zero-length TX buffer -> blocking Tx
opts(iface)->txbuf = (txbuf_len == 0) ? NULL : cbuf_create(txbuf_len, 1);
// --- driver instance ---
iface->rx_rdy = if_rx_rdy;
iface->tx_rdy = if_tx_rdy;
iface->tx_done = if_tx_done;
iface->rx = if_rx;
iface->unrx = if_unrx;
iface->tx = if_tx;
iface->txb = if_txb;
iface->rxb = if_rxb;
iface->poll = if_poll;
iface->rx_callback = NULL; // user can replace
/* enable peripehral clock, assign to holder var, enable IRQ */
IRQn_Type irqn;
if (USARTx == USART1) {
// USART1
usart1_iface = iface;
irqn = USART1_IRQn;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
} else if (USARTx == USART2) {
// USART2
usart2_iface = iface;
irqn = USART2_IRQn;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
} else {
// USART3
usart3_iface = iface;
irqn = USART3_IRQn;
RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
}
// Enable IRQ
NVIC_EnableIRQ(irqn);
// lower priority than SysTick, but high
NVIC_SetPriority(irqn, 1); // function does the shifting
/* Setup UART parameters. */
USART_InitTypeDef usart;
USART_StructInit(&usart);
usart.USART_BaudRate = baud;
USART_Init(USARTx, &usart);
/* Enable */
USART_Cmd(USARTx, ENABLE);
// Enable Rx interrupt
USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE); // disable IRQ
return iface;
}
// ---- IRQ handlers for chained writing and rx ----
/**
* @brief Common USART irq handler
* @param iface the interface at which the event occured
*/
static void usart_iface_irq_base(ComIface* iface)
{
USART_TypeDef* USARTx = opts(iface)->dev;
if (USARTx->SR & USART_SR_RXNE) {
// Byte received.
uint8_t rxb = USARTx->DR & 0xFF;
if (!cbuf_append(opts(iface)->rxbuf, &rxb)) {
// Buffer overflow
// Can't print debug msg, can cause deadlock
}
}
if (USARTx->SR & USART_SR_TXE) {
// Byte sent.
uint8_t txb;
// Send next byte (if buffer is NULL, cbuf_pop also returns false)
if (cbuf_pop(opts(iface)->txbuf, &txb)) {
USARTx->DR = txb; // send data
} else {
USART_ITConfig(USARTx, USART_IT_TXE, DISABLE); // disable IRQ
}
}
// Clear ORE flag if set
USART_ClearFlag(USARTx, USART_FLAG_ORE);
}
void USART1_IRQHandler(void)
{
usart_iface_irq_base(usart1_iface);
}
void USART2_IRQHandler(void)
{
usart_iface_irq_base(usart2_iface);
}
void USART3_IRQHandler(void)
{
usart_iface_irq_base(usart3_iface);
}

@ -0,0 +1,19 @@
#pragma once
#include "main.h"
#include "com_iface.h"
// Hardware USART.
/**
* @brief Init an USART interface. Caller must configure GPIO's & AF manually.
* @param USARTx device
* @param baud baud rate (ex.: 9600)
* @param rxbuf_len receive buffer size, must be > 0
* @param txbuf_len send buffer size. If 0, tx is blocking.
* @return the iface structure
*/
ComIface *usart_iface_init(USART_TypeDef* USARTx, uint32_t baud, size_t rxbuf_len, size_t txbuf_len);
///** Set baud rate */
//void usart_iface_set_baudrate(ComIface *iface, uint32_t baud);

@ -0,0 +1,94 @@
#include "display.h"
#include "com/debug.h"
#include "utils/timebase.h"
#include "utils/meanbuf.h"
#include <math.h>
#define PIXEL_COUNT 30
static ws2812_rgb_t pixels[PIXEL_COUNT] = {};
static MeanBuf *mb;
void display_show(void)
{
colorled_set_many((uint32_t*) pixels, PIXEL_COUNT);
}
static void handle_sonar_value(float mm)
{
for (int i = PIXEL_COUNT-1; i > 0; i--) {
pixels[i].num = pixels[i-1].num;
}
float x = mm/5.0f;
if (x>255) x = 255;
pixels[0].r = 255-x;
pixels[0].b = x;
display_show();
}
static void show(void*arg)
{
(void)arg;
handle_sonar_value(meanbuf_current(mb));
}
static void sonar(void* arg)
{
(void)arg;
info("Sonar");
GPIOB->BSRR = GPIO_Pin_13;
delay_us(10);
GPIOB->BRR = GPIO_Pin_13;
// wait for response
bool suc = false;
until_timeout(50) {
if((GPIOB->IDR & (1 << 14)) != 0) {
suc = true;
break;
}
}
if (!suc) {
dbg("Not suc");
return;
}
uint32_t cnt = 0;
until_timeout(50) {
if((GPIOB->IDR & (1 << 14)) == 0) break;
cnt++;
}
float t = cnt / 11.2f;
meanbuf_add(mb, t);
}
void display_init(void)
{
mb = meanbuf_create(10);
for (int i = 0; i < PIXEL_COUNT; i++) {
pixels[i].num = rgb(0, 0, 255);
}
display_show();
add_periodic_task(sonar, NULL, 50, true);
add_periodic_task(show, NULL, 50, true);
}

@ -0,0 +1,11 @@
#ifndef DISPLAY_H
#define DISPLAY_H
#include "main.h"
#include "colorled.h"
void display_show(void);
void display_init(void);
#endif // DISPLAY_H

@ -0,0 +1,125 @@
#include "hw_init.h"
#include "com/iface_usart.h"
#include "com/com_fileio.h"
#include "com/datalink.h"
#include "utils/debounce.h"
#include "utils/timebase.h"
#include "bus/event_queue.h"
// ---- Private prototypes --------
static void conf_gpio(void);
static void conf_usart(void);
static void conf_systick(void);
static void conf_subsystems(void);
static void conf_irq_prios(void);
// ---- Public functions ----------
/**
* @brief Initialize hardware resources
*/
void hw_init(void)
{
conf_gpio();
conf_usart();
conf_systick();
conf_irq_prios();
conf_subsystems();
}
// ---- Private functions ---------
static void conf_irq_prios(void)
{
NVIC_SetPriorityGrouping(0); // 0 bits for sub-priority
// SysTick - highest prio, used for timeouts
NVIC_SetPriority(SysTick_IRQn, 0); // SysTick - for timeouts
NVIC_SetPriority(USART2_IRQn, 6); // USART - datalink
NVIC_SetPriority(USART1_IRQn, 10); // USART - debug
// FIXME check , probably bad ports
}
/**
* @brief Configure SW subsystems
*/
static void conf_subsystems(void)
{
// task scheduler subsystem
timebase_init(15, 15);
// event and task queues
queues_init(15, 15);
// initialize SBMP for ESP8266
dlnk_init();
}
/**
* @brief Configure GPIOs
*/
static void conf_gpio(void)
{
GPIO_InitTypeDef gpio_cnf;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
// Red LED
gpio_cnf.GPIO_Pin = GPIO_Pin_13;
gpio_cnf.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cnf.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOC, &gpio_cnf);
// colorled | sonar trig
gpio_cnf.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13;
gpio_cnf.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cnf.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOB, &gpio_cnf);
gpio_cnf.GPIO_Pin = GPIO_Pin_14;
gpio_cnf.GPIO_Mode = GPIO_Mode_IPD;
gpio_cnf.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOB, &gpio_cnf);
// A0-sonar trig | UART2 - debug, UART1 - esp
gpio_cnf.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_9 | GPIO_Pin_10;
gpio_cnf.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &gpio_cnf);
}
/**
* @brief Configure USARTs
*/
static void conf_usart(void)
{
// Debug interface, working as stdout/stderr.
debug_iface = usart_iface_init(USART2, 115200, 256, 256);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
debug_iface->file = stdout;
// Datalink iface
data_iface = usart_iface_init(USART1, 460800, 256, 256);
}
/**
* @brief Configure 1 kHz SysTick w/ interrupt
*/
static void conf_systick(void)
{
SysTick_Config(F_CPU / 1000);
}

@ -0,0 +1,6 @@
#pragma once
#include "main.h"
void hw_init(void);

@ -1,25 +1,69 @@
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdio.h>
#include "main.h"
#include "hw_init.h"
#include "com/debug.h"
#include "com/com_fileio.h"
#include "com/com_iface.h"
#include "bus/event_queue.h"
#include "bus/event_handler.h"
#include "utils/timebase.h"
#include "colorled.h"
#include "display.h"
#include <math.h>
#include <sbmp.h>
void poll_subsystems(void)
{
// poll serial buffers (runs callback)
com_poll(debug_iface);
com_poll(data_iface);
// run queued tasks
tq_poll();
// handle queued events
Event evt;
until_timeout(2) { // take 2 ms max
if (eq_take(&evt)) {
run_event_handler(&evt);
} else {
break;
}
}
}
void blinky(void* arg)
{
(void)arg;
GPIOC->ODR ^= 1<<13;
}
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
hw_init();
display_init();
GPIO_InitTypeDef gpio_cnf;
gpio_cnf.GPIO_Pin = GPIO_Pin_13;
gpio_cnf.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_cnf.GPIO_Speed = GPIO_Speed_2MHz;
banner("*** STM32F103K8T6 RGB LED demo ***");
banner_info("(c) Ondrej Hruska, 2016");
banner_info("Katedra mereni K338, CVUT FEL");
GPIO_Init(GPIOC, &gpio_cnf);
add_periodic_task(blinky, NULL, 500, false);
while (1) {
GPIOC->ODR ^= GPIO_Pin_13;
// delay
for (int i = 0; i < 1000000; i++);
poll_subsystems();
}
}
void dlnk_rx(SBMP_Datagram *dg)
{
dbg("Rx dg type %d", dg->type);
}

@ -0,0 +1,18 @@
#ifndef MAIN_H
#define MAIN_H
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <stm32f10x.h>
#define MIN(a,b) ((a)>(b)?(b):(a))
#define MAX(a,b) ((a)<(b)?(b):(a))
#define STR_HELPER(x) #x
#define STR(x) STR_HELPER(x)
#endif // MAIN_H

@ -0,0 +1,40 @@
#include "com/debug.h"
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
static void reset_when_done(void)
{
for (uint32_t i = 0; i < 20000; i++) {
if (com_tx_done(debug_iface)) break;
}
NVIC_SystemReset();
}
void *malloc_safe_do(size_t size, const char* file, uint32_t line)
{
void *mem = malloc(size);
if (mem == NULL) {
// malloc failed
error("Malloc failed in file %s on line %"PRIu32, file, line);
reset_when_done();
}
return mem;
}
void *calloc_safe_do(size_t nmemb, size_t size, const char* file, uint32_t line)
{
void *mem = calloc(size, nmemb);
if (mem == NULL) {
// malloc failed
error("Malloc failed in file %s on line %"PRIu32, file, line);
reset_when_done();
}
return mem;
}

@ -0,0 +1,17 @@
#ifndef MALLOC_SAFE_H
#define MALLOC_SAFE_H
/**
* Malloc that prints error and restarts the system on failure.
*/
#include <stdlib.h>
#include <stdint.h>
void *malloc_safe_do(size_t size, const char* file, uint32_t line);
void *calloc_safe_do(size_t nmemb, size_t size, const char* file, uint32_t line);
#define malloc_s(size) malloc_safe_do(size, __FILE__, __LINE__)
#define calloc_s(nmemb, size) calloc_safe_do(nmemb, size, __FILE__, __LINE__)
#endif // MALLOC_SAFE_H

@ -0,0 +1,24 @@
#include "main.h"
#include "utils/timebase.h"
#include "com/debug.h"
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @return None
*/
void __attribute__((noreturn))
assert_failed(uint8_t* file, uint32_t line)
{
error("Assert failed in file %s, line %"PRIu32".", file, line);
/* Infinite loop */
while (1);
}
#endif

@ -23,6 +23,7 @@
/* Includes ------------------------------------------------------------------*/
#include "stm32f10x_it.h"
#include "utils/timebase.h"
/** @addtogroup STM32F10x_StdPeriph_Template
* @{
@ -138,6 +139,7 @@ void PendSV_Handler(void)
*/
void SysTick_Handler(void)
{
timebase_ms_cb();
}
/******************************************************************************/

@ -0,0 +1,32 @@
#include <errno.h>
#include <stdio.h>
register char * stack_ptr asm("sp");
caddr_t _sbrk(int incr)
{
extern char end __asm("end");
static char *heap_end;
char *prev_heap_end;
if (heap_end == 0)
heap_end = &end;
prev_heap_end = heap_end;
if (heap_end + incr > stack_ptr)
{
// write(1, "Heap and stack collision\n", 25);
// abort();
errno = ENOMEM;
return (caddr_t) -1;
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
// Other systcalls are defined in
// - com/com_fileio.c
// - hw_utils/reset.h

@ -164,7 +164,7 @@
uint32_t SystemCoreClock = HSI_VALUE; /*!< System Clock Frequency (Core Clock) */
#endif
__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
static __I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
/**
* @}
*/
@ -318,8 +318,7 @@ void SystemCoreClockUpdate (void)
/* Get SYSCLK source -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
switch (tmp) {
case 0x00: /* HSI used as system clock */
SystemCoreClock = HSI_VALUE;
break;
@ -335,25 +334,20 @@ void SystemCoreClockUpdate (void)
#ifndef STM32F10X_CL
pllmull = (pllmull >> 18) + 2;
if (pllsource == 0x00)
{
if (pllsource == 0x00) {
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
}
else
{
} else {
#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1;
/* HSE oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
#else
/* HSE selected as PLL clock entry */
if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (uint32_t)RESET)
{/* HSE oscillator clock divided by 2 */
if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (uint32_t)RESET) {
/* HSE oscillator clock divided by 2 */
SystemCoreClock = (HSE_VALUE >> 1) * pllmull;
}
else
{
} else {
SystemCoreClock = HSE_VALUE * pllmull;
}
#endif
@ -361,34 +355,28 @@ void SystemCoreClockUpdate (void)
#else
pllmull = pllmull >> 18;
if (pllmull != 0x0D)
{
if (pllmull != 0x0D) {
pllmull += 2;
}
else
{ /* PLL multiplication factor = PLL input clock * 6.5 */
} else {
/* PLL multiplication factor = PLL input clock * 6.5 */
pllmull = 13 / 2;
}
if (pllsource == 0x00)
{
if (pllsource == 0x00) {
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
}
else
{/* PREDIV1 selected as PLL clock entry */
} else {
/* PREDIV1 selected as PLL clock entry */
/* Get PREDIV1 clock source and division factor */
prediv1source = RCC->CFGR2 & RCC_CFGR2_PREDIV1SRC;
prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1;
if (prediv1source == 0)
{
if (prediv1source == 0) {
/* HSE oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
}
else
{/* PLL2 clock selected as PREDIV1 clock entry */
} else {
/* PLL2 clock selected as PREDIV1 clock entry */
/* Get PREDIV2 division factor and PLL2 multiplication factor */
prediv2factor = ((RCC->CFGR2 & RCC_CFGR2_PREDIV2) >> 4) + 1;
@ -506,23 +494,18 @@ static void SetSysClockToHSE(void)
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
do {
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
HSEStatus = (uint32_t)0x01;
}
else
{
} else {
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
if (HSEStatus == (uint32_t)0x01) {
#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL
/* Enable Prefetch Buffer */
@ -534,12 +517,9 @@ static void SetSysClockToHSE(void)
#ifndef STM32F10X_CL
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
#else
if (HSE_VALUE <= 24000000)
{
if (HSE_VALUE <= 24000000) {
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;
}
else
{
} else {
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_1;
}
#endif /* STM32F10X_CL */
@ -559,12 +539,10 @@ static void SetSysClockToHSE(void)
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE;
/* Wait till HSE is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04)
{
}
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x04) {
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
} else {
/* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
@ -585,23 +563,18 @@ static void SetSysClockTo24(void)
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
do {
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
HSEStatus = (uint32_t)0x01;
}
else
{
} else {
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
if (HSEStatus == (uint32_t)0x01) {
#if !defined STM32F10X_LD_VL && !defined STM32F10X_MD_VL && !defined STM32F10X_HD_VL
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
@ -637,8 +610,7 @@ static void SetSysClockTo24(void)
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
while ((RCC->CR & RCC_CR_PLL2RDY) == 0) {
}
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || defined (STM32F10X_HD_VL)
/* PLL configuration: = (HSE / 2) * 6 = 24 MHz */
@ -654,8 +626,7 @@ static void SetSysClockTo24(void)
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
}
/* Select PLL as system clock source */
@ -663,12 +634,10 @@ static void SetSysClockTo24(void)
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) {
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
} else {
/* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
@ -689,23 +658,18 @@ static void SetSysClockTo36(void)
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
do {
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
HSEStatus = (uint32_t)0x01;
}
else
{
} else {
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
if (HSEStatus == (uint32_t)0x01) {
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
@ -741,8 +705,7 @@ static void SetSysClockTo36(void)
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
while ((RCC->CR & RCC_CR_PLL2RDY) == 0) {
}
#else
@ -755,8 +718,7 @@ static void SetSysClockTo36(void)
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
}
/* Select PLL as system clock source */
@ -764,12 +726,10 @@ static void SetSysClockTo36(void)
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) {
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
} else {
/* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
@ -790,23 +750,18 @@ static void SetSysClockTo48(void)
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
do {
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
HSEStatus = (uint32_t)0x01;
}
else
{
} else {
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
if (HSEStatus == (uint32_t)0x01) {
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
@ -836,8 +791,7 @@ static void SetSysClockTo48(void)
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
while ((RCC->CR & RCC_CR_PLL2RDY) == 0) {
}
@ -855,8 +809,7 @@ static void SetSysClockTo48(void)
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
}
/* Select PLL as system clock source */
@ -864,12 +817,10 @@ static void SetSysClockTo48(void)
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) {
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
} else {
/* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
@ -891,23 +842,18 @@ static void SetSysClockTo56(void)
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
do {
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
HSEStatus = (uint32_t)0x01;
}
else
{
} else {
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
if (HSEStatus == (uint32_t)0x01) {
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
@ -937,8 +883,7 @@ static void SetSysClockTo56(void)
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
while ((RCC->CR & RCC_CR_PLL2RDY) == 0) {
}
@ -957,8 +902,7 @@ static void SetSysClockTo56(void)
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
}
/* Select PLL as system clock source */
@ -966,12 +910,10 @@ static void SetSysClockTo56(void)
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) {
}
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
} else {
/* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}
@ -993,23 +935,18 @@ static void SetSysClockTo72(void)
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* Wait till HSE is ready and if Time out is reached exit */
do
{
do {
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
} while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
HSEStatus = (uint32_t)0x01;
}
else
{
} else {
HSEStatus = (uint32_t)0x00;
}
if (HSEStatus == (uint32_t)0x01)
{
if (HSEStatus == (uint32_t)0x01) {
/* Enable Prefetch Buffer */
FLASH->ACR |= FLASH_ACR_PRFTBE;
@ -1040,8 +977,7 @@ static void SetSysClockTo72(void)
/* Enable PLL2 */
RCC->CR |= RCC_CR_PLL2ON;
/* Wait till PLL2 is ready */
while((RCC->CR & RCC_CR_PLL2RDY) == 0)
{
while ((RCC->CR & RCC_CR_PLL2RDY) == 0) {
}
@ -1060,8 +996,7 @@ static void SetSysClockTo72(void)
RCC->CR |= RCC_CR_PLLON;
/* Wait till PLL is ready */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
while ((RCC->CR & RCC_CR_PLLRDY) == 0) {
}
/* Select PLL as system clock source */
@ -1069,12 +1004,10 @@ static void SetSysClockTo72(void)
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/* Wait till PLL is used as system clock source */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) {
}
else
{ /* If HSE fails to start-up, the application will have wrong clock
} else {
/* If HSE fails to start-up, the application will have wrong clock
configuration. User can add here some code to deal with this error */
}
}

@ -0,0 +1,161 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <malloc.h>
#include "circbuf.h"
#include "malloc_safe.h"
// --- Circbuf data structure ----
/** Offset in void* buffer */
#define PV_OFFS(pvBuf, elem_size, index) ((uint8_t*)(pvBuf) + ((elem_size)*(index)))
// Instance structure
struct circbuf_struct {
void *buf;
size_t elem_size;
size_t cap;
size_t lr; // last read pos
size_t nw; // next write pos
};
/**
* @brief Write data to a CircBuf slot
* @param cb : circbuf
* @param index : slot index
* @param source : data source
*/
static void write_buffer(CircBuf *cb, size_t index, const void *source)
{
memcpy(PV_OFFS(cb->buf, cb->elem_size, index), source, cb->elem_size);
}
/**
* @brief Copy data from a CircBuf slot to a buffer
* @param cb : circbuf
* @param index : slot index
* @param dest : destination buffer
*/
static void read_buffer(const CircBuf *cb, size_t index, void *dest)
{
memcpy(dest, PV_OFFS(cb->buf, cb->elem_size, index), cb->elem_size);
}
/** Create a cbuf */
CircBuf *cbuf_create(size_t capacity, size_t elem_size)
{
// add one, because one is always unused.
capacity++;
// Allocate the structure
CircBuf *cb = malloc_s(sizeof(CircBuf));
// allocate the buffer
cb->buf = malloc_s(capacity * elem_size);
// set capacity, clear state
cb->elem_size = elem_size;
cb->cap = capacity;
cbuf_clear(cb);
return cb;
}
/** Release cbuf memory */
void cbuf_destroy(CircBuf *cb)
{
if (cb != NULL) {
if (cb->buf != NULL) {
free(cb->buf);
}
free(cb);
}
}
/** Check if cbuf is full */
bool cbuf_full(const CircBuf *cb)
{
if (cb == NULL) return false;
return (cb->lr == cb->nw);
}
/** Check if cbuf is empty */
bool cbuf_empty(const CircBuf *cb)
{
if (cb == NULL) return true;
return ((cb->lr + 1) % cb->cap) == cb->nw;
}
/** Write a byte to the buffer, if space left */
bool cbuf_append(CircBuf *cb, const void *source)
{
if (cb == NULL) return false;
if (source == NULL) return false;
if (cbuf_full(cb)) return false;
write_buffer(cb, cb->nw, source);
// increment
cb->nw++;
if (cb->nw == cb->cap) cb->nw = 0;
return true;
}
/** Push value to the end, like a stack. */
bool cbuf_push(CircBuf *cb, const void *source)
{
if (cb == NULL) return false;
if (source == NULL) return false;
if (cbuf_full(cb)) return false;
write_buffer(cb, cb->lr, source);
// move lr back
if (cb->lr == 0) {
cb->lr = cb->cap - 1; // wrap to the end
} else {
cb->lr--;
}
return true;
}
/** Read one byte, if not empty. */
bool cbuf_pop(CircBuf *cb, void *dest)
{
if (cb == NULL || dest == NULL) return false;
if (cbuf_empty(cb)) return false;
// increment
cb->lr++;
if (cb->lr == cb->cap) cb->lr = 0;
read_buffer(cb, cb->lr, dest);
return true;
}
/** Clear a cbuf */
void cbuf_clear(CircBuf *cb)
{
if (cb == NULL) return;
cb->lr = cb->cap - 1;
cb->nw = 0;
}

@ -0,0 +1,93 @@
/**
* @file circbuf.h
* @author Ondřej Hruška, 2016
*
* Circular buffer / queue / stack.
* Slots are pre-allocated, values are copied into the buffer.
*
* The buffer may be used as a stack, event queue or a simple buffer.
*
* -------------------------------------
*
* NW LR
* append -> [][][][] -> pop
* <- push
*
* NW - next write pointer (stack base)
* LR - last read position (stack top)
*
* -------------------------------------
*
* MIT license
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
typedef struct circbuf_struct CircBuf;
/**
* @brief Initialize a circular buffer. The buffer is malloc'd.
* @param capacity : buffer capacity
* @param elem_size : size of one element
* @return pointer to the buffer instance
*/
CircBuf *cbuf_create(size_t capacity, size_t elem_size);
/**
* @brief Destroy a buffer, freeing used memory.
*
* @attention
* If the buffer items have malloc'd members, you have
* to free them manually to avoid a memory leak.
*
* @param cb : buffer
*/
void cbuf_destroy(CircBuf *cb);
/** Test for full buffer */
bool cbuf_full(const CircBuf *cb);
/** Test for empty buffer */
bool cbuf_empty(const CircBuf *cb);
/**
* @brief Append a value to the buffer (FIFO)
* @param cb : buffer
* @param source : pointer to a value (will be copied)
* @return success
*/
bool cbuf_append(CircBuf *cb, const void *source);
/**
* @brief Push a value into the circbuf (LIFO).
*
* @param cb : buffer
* @param source : pointer to a value (will be copied)
* @return success
*/
bool cbuf_push(CircBuf *cb, const void *source);
/**
* @brief Read a value from the buffer, return susccess.
*
* @param cb : buffer
* @param dest : read destionation. If NULL, value is discarded.
* @return success
*/
bool cbuf_pop(CircBuf *cb, void *dest);
/** @brief Remove all data from buffer */
void cbuf_clear(CircBuf *cb);

@ -0,0 +1,171 @@
#include "main.h"
#include "debounce.h"
#include "timebase.h"
#include "malloc_safe.h"
// ms debounce time
#define DEF_DEBO_TIME 20
typedef struct {
GPIO_TypeDef *GPIOx; ///< GPIO base
uint16_t pin; ///< bit mask
bool state; ///< current state
bool invert; ///< invert pin
debo_id_t id; ///< pin ID
ms_time_t debo_time; ///< debouncing time (ms)
ms_time_t counter_0; ///< counter for falling edge (ms)
ms_time_t counter_1; ///< counter for rising edge (ms)
void (*falling_cb)(void);
void (*rising_cb)(void);
} debo_slot_t;
/** Number of allocated slots */
static size_t debo_slot_count = 0;
/** Slots array */
static debo_slot_t *debo_slots;
/** Next free pin ID for make_id() */
static debo_id_t next_pin_id = 1;
/**
* @brief Get a valid free pin ID for a new entry.
* @return the ID.
*/
static debo_id_t make_id(void)
{
debo_id_t id = next_pin_id++;
// make sure no task is given PID 0
if (next_pin_id == DEBO_PIN_NONE) {
next_pin_id++;
}
return id;
}
/** Init the debouncer */
void debounce_init(size_t slot_count)
{
debo_slots = calloc_s(slot_count, sizeof(debo_slot_t));
debo_slot_count = slot_count;
}
/** Register a pin */
debo_id_t debo_register_pin(debo_init_t *init)
{
assert_param(IS_GPIO_ALL_PERIPH(init->GPIOx));
assert_param(IS_GET_GPIO_PIN(init->pin));
for (size_t i = 0; i < debo_slot_count; i++) {
debo_slot_t *slot = &debo_slots[i];
if (slot->id != DEBO_PIN_NONE) continue; // slot is used
slot->GPIOx = init->GPIOx;
slot->pin = init->pin;
slot->falling_cb = init->falling_cb;
slot->rising_cb = init->rising_cb;
slot->invert = init->invert;
slot->counter_0 = 0;
slot->counter_1 = 0;
slot->debo_time = (init->debo_time == 0) ? DEF_DEBO_TIME : init->debo_time;
bool state = GPIO_ReadInputDataBit(slot->GPIOx, slot->pin);
if (slot->invert) state = !state;
slot->state = state;
slot->id = make_id();
return slot->id;
}
return DEBO_PIN_NONE;
}
/** Callback that must be called every 1 ms */
void debo_periodic_task(void)
{
for (size_t i = 0; i < debo_slot_count; i++) {
debo_slot_t *slot = &debo_slots[i];
if (slot->id == DEBO_PIN_NONE) continue; // unused
bool state = GPIO_ReadInputDataBit(slot->GPIOx, slot->pin);
if (slot->invert) state = !state;
if (slot->state != state) {
if (state == 0) {
// falling
if (slot->counter_0++ == slot->debo_time) {
slot->state = 0;
if (slot->falling_cb != NULL) {
slot->falling_cb();
}
}
} else {
// rising
if (slot->counter_1++ == slot->debo_time) {
slot->state = 1;
if (slot->rising_cb != NULL) {
slot->rising_cb();
}
}
}
} else {
// reset counters
slot->counter_0 = 0;
slot->counter_1 = 0;
}
}
}
/**
* @brief Check if a pin is high
* @param pin_id : Slot ID
* @return true if the pin is registered and is HIGH
*/
bool debo_pin_state(debo_id_t pin_id)
{
if (pin_id == DEBO_PIN_NONE) return false;
for (size_t i = 0; i < debo_slot_count; i++) {
debo_slot_t *slot = &debo_slots[i];
if (slot->id != pin_id) continue;
return slot->state;
}
return false;
}
/**
* @brief Remove a pin entry from the debouncer.
* @param pin_id : Slot ID
* @return true if task found & removed.
*/
bool debo_remove_pin(debo_id_t pin_id)
{
if (pin_id == DEBO_PIN_NONE) return false;
for (size_t i = 0; i < debo_slot_count; i++) {
debo_slot_t *slot = &debo_slots[i];
if (slot->id != pin_id) continue;
slot->id = DEBO_PIN_NONE;
return true;
}
return false;
}

@ -0,0 +1,62 @@
#pragma once
#include "main.h"
#include "utils/timebase.h"
// Debouncer requires that you setup SysTick first.
/** Debounced pin ID - used for state readout */
typedef uint32_t debo_id_t;
/** debo_id_t indicating unused slot */
#define DEBO_PIN_NONE 0
/**
* @brief Initialize the debouncer.
*
* You have to also register the periodic task to timebase.
*
* @param pin_count : number of pin slots to allocate
*/
void debounce_init(size_t pin_count);
/**
* @brief 1 ms periodic callback for debouncer. Must be registered to timebase.
*/
void debo_periodic_task(void);
typedef struct {
GPIO_TypeDef *GPIOx; ///< GPIO base
uint16_t pin; ///< pin mask
ms_time_t debo_time; ///< debounce time in ms, 0 = default (20 ms)
bool invert; ///< invert value read from GPIO (button to ground)
void (*rising_cb)(void); ///< callback when the pin goes HIGH
void (*falling_cb)(void); ///< callback when the pin goes LOW
} debo_init_t;
/**
* @brief Add a pin for debouncing.
*
* The pin state will be checked with the configured hysteresis
* and callbacks will be called when a state change is detected.
*/
debo_id_t debo_register_pin(debo_init_t *init_struct);
/**
* @brief Check if a pin is high
* @param pin_id : Slot ID
* @return true if the pin is registered and is HIGH
*/
bool debo_pin_state(debo_id_t pin_id);
/**
* @brief Remove a pin entry from the debouncer.
* @param pin_id : Slot ID
* @return true if task found & removed.
*/
bool debo_remove_pin(debo_id_t pin_id);

@ -0,0 +1,33 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include "matcher.h"
void matcher_reset(matcher_t *m)
{
m->cursor = 0;
}
/** Handle incoming char. Returns true if this char completed the match. */
bool matcher_test(matcher_t * m, uint8_t b)
{
// If mismatch, rewind (and check at 0)
if (m->pattern[m->cursor] != b) {
m->cursor = 0;
}
// Check for match
if (m->pattern[m->cursor] == b) {
// Good char
m->cursor++;
if (m->pattern[m->cursor] == 0) { // end of pattern
m->cursor = 0; // rewind
return true; // indicate success
}
}
return false;
}

@ -0,0 +1,39 @@
/**
* @file matcher.h
* @author Ondřej Hruška, 2016
*
* String matching utility.
*
* Matcher can be used for detecting a pattern in a stream of characters.
* With each incoming character, call matcher_test().
*
* It will return true if the character completed a match.
*
* MIT license
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
typedef struct {
const char *pattern;
size_t cursor;
} matcher_t;
/** reset match progress */
void matcher_reset(matcher_t *m);
/**
* Consume an incoming character.
* If this char was the last char of the pattern, returns true and resets matcher.
*
* If the char is not in the pattern, resets matcher.
*
* @returns true if the char concluded the expected pattern.
*/
bool matcher_test(matcher_t * mb, uint8_t b);

@ -0,0 +1,70 @@
#include <stdint.h>
#include <malloc.h>
#include "meanbuf.h"
#include "malloc_safe.h"
struct meanbuf_struct {
float * buf; // buffer (allocated at init)
size_t cap; // capacity
size_t nw; // next write index
float mean; // updated on write
};
/** Init a buffer */
MeanBuf *meanbuf_create(size_t size)
{
MeanBuf *mb = malloc_s(sizeof(MeanBuf));
if (size < 1) size = 1;
mb->buf = calloc_s(size, sizeof(float)); // calloc, so it starts with zeros.
mb->cap = size;
mb->nw = 0;
mb->mean = 0;
// clean buffer
for (uint16_t i = 0; i < size; i++) {
mb->buf[i] = 0;
}
return mb;
}
void meanbuf_destroy(MeanBuf *mb)
{
if (mb == NULL) return;
if (mb->buf != NULL) {
free(mb->buf);
}
free(mb);
}
/** Add a value to the buffer. Returns current mean. */
float meanbuf_add(MeanBuf *mb, float f)
{
// add sample
mb->buf[mb->nw++] = f;
if (mb->nw == mb->cap) mb->nw = 0;
// calculate average
float acc = 0;
for (size_t i = 0; i < mb->cap; i++) {
acc += mb->buf[i];
}
acc /= mb->cap;
return mb->mean = acc;
}
float meanbuf_current(MeanBuf *mb)
{
return mb->mean;
}

@ -0,0 +1,33 @@
/**
* @file meanbuf.h
* @author Ondřej Hruška, 2016
*
* Averaging float buffer. (You can adjust it to use doubles, if you prefer.)
*
* The meanbuf_create() function allocates a buffer.
*
* You can then call meanbuf_add() to add a new value into the buffer (and remove the oldest).
* This function returns the current average value.
*
* This buffer can be used for signal smoothing (such as from an analogue sensor).
*
* MIT license
*/
#pragma once
#include <stdlib.h>
#include <stdint.h>
typedef struct meanbuf_struct MeanBuf;
/** Init a buffer */
MeanBuf *meanbuf_create(size_t size);
/** Deinit a buffer (free buffer array) */
void meanbuf_destroy(MeanBuf *mb);
/** Add a value to the buffer. Returns current mean. */
float meanbuf_add(MeanBuf *mb, float f);
float meanbuf_current(MeanBuf *bm);

@ -0,0 +1,4 @@
#pragma once
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))

@ -0,0 +1,286 @@
#include "str_utils.h"
#include "matcher.h"
#include "malloc_safe.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
// Lot of this stuff is actually not needed anymore,
// it was written for the ESP AT firmware, which is no longer used.
/**
* Escape a char.
* @returns what to put after backslash, or '\0' for no escape.
*/
static char escape_char(char c)
{
switch (c) {
case '\r': return 'r';
case '\n': return 'n';
case '\t': return 't';
case '\\': return '\\';
default: return 0;
}
}
/**
* Escape string in place
*/
void str_escape_ip(char * buf, size_t buf_len)
{
size_t i = 0;
// string length (updated when escapes are performed)
size_t slen = strlen(buf);
for (; i < buf_len - 1 && buf[i] != 0; i++) {
char replace = escape_char(buf[i]);
// Escape, shift trailing chars
if (replace != 0) {
if (i >= buf_len - 2) {
break; // discard the char, escape wouldn't fit.
}
// (could be faster if moved starting at the end)
char m = buf[i + 1]; // remember next char
buf[i] = '\\';
buf[i + 1] = replace;
slen++; // account for the added backslash
// shift trailing chars
for (size_t j = i + 2; j <= slen; j++) {
char n = buf[j];
buf[j] = m;
m = n;
}
i++; // skip the insterted slash
}
}
buf[i] = 0; // add terminator (in case end of string was reached)
}
void str_escape(char *dest, const char *src, size_t dest_len)
{
size_t di = 0, si = 0;
for (; src[si] != 0 && di < dest_len - 1; si++) {
char orig = src[si];
char replace = escape_char(orig);
if (replace == 0) {
dest[di++] = orig;
} else {
if (di >= dest_len - 2) {
break; // out of space
}
dest[di++] = '\\';
dest[di++] = replace;
}
}
dest[di] = 0; // append terminator
}
int32_t strpos(const char *haystack, const char *needle)
{
const char *p = strstr(haystack, needle);
if (p) return (p - haystack);
return -1; // Not found = -1.
}
int32_t strpos_upto(const char *haystack, const char *needle, size_t limit)
{
if (limit <= 0) return strpos(haystack, needle);
matcher_t m = {needle, 0};
char c;
for (size_t i = 0; i < limit; i++, haystack++) {
c = *haystack;
if (c == 0) break;
if (matcher_test(&m, (uint8_t)c)) {
return i - strlen(needle) + 1; // match occured on the last needle char
}
}
return -1;
}
int32_t strpos_upto_match(const char *haystack, const char *needle, const char *endmatch)
{
if (endmatch == NULL) return strpos(haystack, needle);
matcher_t matcher_needle = {needle, 0};
matcher_t matcher_end = {endmatch, 0};
char c;
for (int i = 0;; i++, haystack++) {
c = *haystack;
if (c == 0) break;
// match
if (matcher_test(&matcher_needle, (uint8_t)c)) {
return i - strlen(needle) + 1; // match occured on the last needle char
}
// end
if (matcher_test(&matcher_end, (uint8_t)c)) {
return -1;
}
}
return -1;
}
size_t str_copy(char * dest, const char *src)
{
char c;
size_t i = 0;
while ((c = *src++) != 0) {
*dest++ = c;
i++;
}
return i;
}
/**
* Decode URL-encoded string in place.
*/
void urldecode_ip(char *str)
{
unsigned int x;
for (size_t i = 0; str[i] != 0; i++) {
char c = str[i];
if (c == '+') {
str[i] = ' ';
} else if (c == '%') {
// decode the byte
sscanf(&str[i + 1], "%02x", &x);
str[i] = (char)x;
// shift following chars
for (size_t a = i + 3, b = i + 1;; a++, b++) {
str[b] = str[a]; // move
if (str[a] == 0) break;
}
}
}
}
/**
* url-decode string, put output in a buffer.
*/
void urldecode(char *dest, const char *src)
{
unsigned int x;
size_t si = 0, di = 0;
for (; src[si] != 0; si++) {
char c = src[si];
if (c == '+') {
dest[di++] = ' ';
} else if (c == '%') {
// decode the byte
sscanf(&src[si + 1], "%02x", &x);
dest[di++] = (char)x;
si += 2;
} else {
dest[di++] = c;
}
}
// add terminator
dest[di] = 0;
}
/**
* url-decode string, put output in a buffer.
* Limit operation to N chars in input string
*/
void urldecode_n(char *dest, const char *src, size_t count)
{
unsigned int x;
size_t si = 0, di = 0;
for (; src[si] != 0 && si < count; si++) {
char c = src[si];
if (c == '+') {
dest[di++] = ' ';
} else if (c == '%') {
// decode the byte
sscanf(&src[si + 1], "%02x", &x);
dest[di++] = (char)x;
si += 2;
} else {
dest[di++] = c;
}
}
// add terminator
dest[di] = 0;
}
bool get_query_value(char *buffer, const char *querystring, const char *key, size_t buf_len)
{
bool retval;
size_t qs_len = strlen(querystring);
char *ptrn = malloc_s(strlen(key) + 3); // &key=\0
sprintf(ptrn, "&%s=", key);
matcher_t m = {ptrn, 1}; // pretend ampersand was already matched
for (size_t i = 0; i < qs_len; i++) {
char c = querystring[i];
if (matcher_test(&m, (uint8_t)c)) {
// found the match
i++; // advance past the equals sign
size_t seg_end = i;
while (seg_end < qs_len && querystring[seg_end] != '&') {
seg_end++;
}
if (seg_end - i > buf_len) seg_end = i + buf_len;
if (seg_end == i) {
buffer[0] = 0; // strncpy behaves strange with length 0
} else {
urldecode_n(buffer, querystring + i, seg_end - i);
}
retval = true;
goto done;
}
}
// not found
retval = false;
done:
free(ptrn);
return retval;
}

@ -0,0 +1,75 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define streq(a, b) (strcmp((a), (b)) == 0)
#define streqi(a, b) (strcasecmp((a), (b)) == 0)
/**
* Escape string, storing result in a buffer.
*/
void str_escape(char *dest, const char *src, size_t dest_len);
/**
* Escape special chars in a string, IN PLACE.
*
* If string is too long after escaping, last chars are dropped.
*
* @param buf the buffer, containing 0-terminated string.
* @param buflen buffer length
*/
void str_escape_ip(char *buf, size_t buf_len);
/**
* Get position of needle in a haystack.
* -1 if not found.
*/
int32_t strpos(const char *haystack, const char *needle);
/**
* Find substring position, ending at index 'limit'.
* Limit <= 0 means no limit.
* Returns index of the first character of needle in haystack.
*/
int32_t strpos_upto(const char *haystack, const char *needle, size_t limit);
/**
* Find substring position, ending when endmatch is encountered. (Substring within endmatch *can* be reported).
* Returns index of the first character of needle in haystack.
*/
int32_t strpos_upto_match(const char *haystack, const char *needle, const char *endmatch);
/**
* Like sprintf, except without formatting
*/
size_t str_copy(char * dest, const char *src);
/**
* Decode url-encoded string, store result in dest.
*/
void urldecode(char *dest, const char *src);
/**
* Decode url-encoded string in place.
*/
void urldecode_ip(char *str);
/**
* Retrieve & url-decode a query string value by name.
*
* @param buffer - target buffer
* @param querystring - string (foo=bar&baz=moi)
* @param key - key to retrieve
* @param buf_len - length of the target buffer
* @return true if found.
*/
bool get_query_value(char *buffer, const char *querystring, const char *key, size_t buf_len);

@ -0,0 +1,331 @@
#include "timebase.h"
#include "bus/event_queue.h"
#include "com/debug.h"
#include "malloc_safe.h"
// Time base
static volatile ms_time_t SystemTime_ms = 0;
typedef struct {
/** User callback with arg */
void (*callback)(void *);
/** Arg for the arg callback */
void *cb_arg;
/** Callback interval */
ms_time_t interval_ms;
/** Counter, when reaches interval_ms, is cleared and callback is called. */
ms_time_t countup;
/** Unique task ID (for cancelling / modification) */
task_pid_t pid;
/** Enable flag - disabled tasks still count, but CB is not run */
bool enabled;
/** Marks that the task is due to be run */
bool enqueue;
} periodic_task_t;
typedef struct {
/** User callback with arg */
void (*callback)(void *);
/** Arg for the arg callback */
void *cb_arg;
/** Counter, when reaches 0ms, callback is called and the task is removed */
ms_time_t countdown_ms;
/** Unique task ID (for cancelling / modification) */
task_pid_t pid;
/** Whether this task is long and needs posting on the queue */
bool enqueue;
} future_task_t;
static size_t periodic_slot_count = 0;
static size_t future_slot_count = 0;
static periodic_task_t *periodic_tasks;
static future_task_t *future_tasks;
/** Init timebase */
void timebase_init(size_t periodic, size_t future)
{
periodic_slot_count = periodic;
future_slot_count = future;
periodic_tasks = calloc_s(periodic, sizeof(periodic_task_t));
future_tasks = calloc_s(future, sizeof(future_task_t));
}
static task_pid_t next_task_pid = 1; // 0 (PID_NONE) is reserved
/** Get a valid free PID for a new task. */
static task_pid_t make_pid(void)
{
task_pid_t pid = next_task_pid++;
// make sure no task is given PID 0
if (next_task_pid == PID_NONE) {
next_task_pid++;
}
return pid;
}
/** Take an empty periodic task slot and populate the basics. */
static periodic_task_t* claim_periodic_task_slot(ms_time_t interval, bool enqueue)
{
for (size_t i = 0; i < periodic_slot_count; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != PID_NONE) continue; // task is used
task->countup = 0;
task->interval_ms = interval - 1;
task->enqueue = enqueue;
task->pid = make_pid();
task->enabled = true;
return task;
}
error("Periodic task table full.");
return NULL;
}
/** Take an empty future task slot and populate the basics. */
static future_task_t* claim_future_task_slot(ms_time_t delay, bool enqueue)
{
for (size_t i = 0; i < future_slot_count; i++) {
future_task_t *task = &future_tasks[i];
if (task->pid != PID_NONE) continue; // task is used
task->countdown_ms = delay;
task->enqueue = enqueue;
task->pid = make_pid();
return task;
}
error("Future task table full.");
return NULL;
}
/** Add a periodic task with an arg. */
task_pid_t add_periodic_task(void (*callback)(void*), void* arg, ms_time_t interval, bool enqueue)
{
periodic_task_t *task = claim_periodic_task_slot(interval, enqueue);
if (task == NULL) return PID_NONE;
task->callback = callback;
task->cb_arg = arg;
return task->pid;
}
/** Schedule a future task, with uint32_t argument. */
task_pid_t schedule_task(void (*callback)(void*), void *arg, ms_time_t delay, bool enqueue)
{
future_task_t *task = claim_future_task_slot(delay, enqueue);
if (task == NULL) return PID_NONE;
task->callback = callback;
task->cb_arg = arg;
return task->pid;
}
/** Enable or disable a periodic task. */
bool enable_periodic_task(task_pid_t pid, FunctionalState enable)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < periodic_slot_count; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
task->enabled = (enable == ENABLE);
return true;
}
return false;
}
/** Check if a periodic task is enabled */
bool is_periodic_task_enabled(task_pid_t pid)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < periodic_slot_count; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
return task->enabled;
}
return false;
}
bool reset_periodic_task(task_pid_t pid)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < periodic_slot_count; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
task->countup = 0;
return true;
}
return false;
}
/** Remove a periodic task. */
bool remove_periodic_task(task_pid_t pid)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < periodic_slot_count; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid != pid) continue;
task->pid = PID_NONE; // mark unused
return true;
}
return false;
}
/** Abort a scheduled task. */
bool abort_scheduled_task(task_pid_t pid)
{
if (pid == PID_NONE) return false;
for (size_t i = 0; i < future_slot_count; i++) {
future_task_t *task = &future_tasks[i];
if (task->pid != pid) continue;
task->pid = PID_NONE; // mark unused
return true;
}
return false;
}
/** Run a periodic task */
static void run_periodic_task(periodic_task_t *task)
{
if (!task->enabled) return;
if (task->enqueue) {
// queued task
tq_post(task->callback, task->cb_arg);
} else {
// immediate task
task->callback(task->cb_arg);
}
}
/** Run a future task */
static void run_future_task(future_task_t *task)
{
if (task->enqueue) {
// queued task
tq_post(task->callback, task->cb_arg);
} else {
// immediate task
task->callback(task->cb_arg);
}
}
/**
* @brief Millisecond callback, should be run in the SysTick handler.
*/
void timebase_ms_cb(void)
{
// increment global time
SystemTime_ms++;
// run periodic tasks
for (size_t i = 0; i < periodic_slot_count; i++) {
periodic_task_t *task = &periodic_tasks[i];
if (task->pid == PID_NONE) continue; // unused
if (task->countup++ >= task->interval_ms) {
// run if enabled
run_periodic_task(task);
// restart counter
task->countup = 0;
}
}
// run planned future tasks
for (size_t i = 0; i < future_slot_count; i++) {
future_task_t *task = &future_tasks[i];
if (task->pid == PID_NONE) continue; // unused
if (task->countdown_ms-- == 0) {
// run
run_future_task(task);
// release the slot
task->pid = PID_NONE;
}
}
}
/** Seconds delay */
void delay_s(uint32_t s)
{
while (s-- != 0) {
delay_ms(1000);
}
}
/** Delay N ms */
void delay_ms(ms_time_t ms)
{
ms_time_t start = SystemTime_ms;
while ((SystemTime_ms - start) < ms); // overrun solved by unsigned arithmetic
}
/** Get milliseconds elapsed since start timestamp */
ms_time_t ms_elapsed(ms_time_t start)
{
return SystemTime_ms - start;
}
/** Get current timestamp. */
ms_time_t ms_now(void)
{
return SystemTime_ms;
}
/** Helper for looping with periodic branches */
bool ms_loop_elapsed(ms_time_t *start, ms_time_t duration)
{
if (SystemTime_ms - *start >= duration) {
*start = SystemTime_ms;
return true;
}
return false;
}

@ -0,0 +1,159 @@
#pragma once
/**
* To use the Timebase functionality,
* set up SysTick to 1 kHz and call
* timebase_ms_cb() in the IRQ.
*
* If you plan to use pendable future tasks,
* also make sure you call run_pending_tasks()
* in your main loop.
*
* This is not needed for non-pendable tasks.
*/
#include "main.h"
/** Task PID. */
typedef uint32_t task_pid_t;
/** Time value in ms */
typedef uint32_t ms_time_t;
// PID value that can be used to indicate no task
#define PID_NONE 0
/** Loop until timeout - use in place of while() or for(). break and continue work too! */
#define until_timeout(to_ms) for(uint32_t _utmeo = ms_now(); ms_elapsed(_utmeo) < (to_ms);)
/** Retry a call until a timeout. Variable 'suc' is set to the return value. Must be defined. */
#define retry_TO(to_ms, call) \
until_timeout(to_ms) { \
suc = call; \
if (suc) break; \
}
/** Init timebase, allocate slots for tasks. */
void timebase_init(size_t periodic_count, size_t future_count);
/** Must be called every 1 ms */
void timebase_ms_cb(void);
// --- Periodic -----------------------------------------------
/**
* @brief Add a periodic task with an arg.
* @param callback : task callback
* @param arg : callback argument
* @param interval : task interval (ms)
* @param enqueue : put on the task queue when due
* @return task PID
*/
task_pid_t add_periodic_task(void (*callback)(void *), void *arg, ms_time_t interval, bool enqueue);
/** Destroy a periodic task. */
bool remove_periodic_task(task_pid_t pid);
/** Enable or disable a periodic task. Returns true on success. */
bool enable_periodic_task(task_pid_t pid, FunctionalState cmd);
/** Check if a periodic task exists and is enabled. */
bool is_periodic_task_enabled(task_pid_t pid);
/** Reset timer for a task */
bool reset_periodic_task(task_pid_t pid);
// --- Future -------------------------------------------------
/**
* @brief Schedule a future task, with uint32_t argument.
* @param callback : task callback
* @param arg : callback argument
* @param delay : task delay (ms)
* @param enqueue : put on the task queue when due
* @return task PID
*/
task_pid_t schedule_task(void (*callback_arg)(void *), void *arg, ms_time_t delay, bool enqueue);
/** Abort a scheduled task. */
bool abort_scheduled_task(task_pid_t pid);
// --- Waiting functions --------------------------------------
/** Get milliseconds elapsed since start timestamp */
ms_time_t ms_elapsed(ms_time_t start);
/** Get current timestamp. */
ms_time_t ms_now(void);
/** Delay using SysTick */
void delay_ms(ms_time_t ms);
/** Delay N seconds */
void delay_s(uint32_t s);
inline __attribute__((always_inline))
void delay_cycles(uint32_t n)
{
uint32_t l = n >> 2;
__asm volatile(
"0: mov r0,r0;"
"subs %[count], #1;"
"bne 0b;"
: [count] "+r"(l)
);
}
inline __attribute__((always_inline))
void delay_ns(uint32_t ns)
{
delay_cycles(ns / 24);
}
/**
* @brief Microsecond delay.
* @param us
*/
inline __attribute__((always_inline))
void delay_us(uint32_t us)
{
delay_ns(us * 1150);
}
/**
* @brief Check if time since `start` elapsed.
*
* If so, sets the *start variable to the current time.
*
* Example:
*
* ms_time_t s = ms_now();
*
* while(1) {
* if (ms_loop_elapsed(&s, 100)) {
* // this is called every 100 ms
* }
* // ... rest of the loop ...
* }
*
* @param start start time variable
* @param duration delay length
* @return delay elapsed; start was updated.
*/
bool ms_loop_elapsed(ms_time_t *start, ms_time_t duration);
Loading…
Cancel
Save