add copy of TSYS

This commit is contained in:
Edward Emelianov 2023-08-29 20:44:40 +03:00
parent e07260c9d0
commit 16dcbd52d9
37 changed files with 4775 additions and 0 deletions

View File

@ -0,0 +1,10 @@
BINARY := tsys01
# MCU code
MCU := F042x6
# change this linking script depending on particular MCU model,
LDSCRIPT := stm32f042x6.ld
DEFINES := -DUSARTNUM=1 -DI2CPINS=67
include ../makefile.f0
include ../../makefile.stm32

View File

@ -0,0 +1,158 @@
BINARY = tsys01
BOOTPORT ?= /dev/ttyUSB0
BOOTSPEED ?= 57600
# MCU FAMILY
FAMILY = F0
# MCU code
MCU = F042x6
# hardware definitions
DEFS := -DUSARTNUM=1 -DI2CPINS=67
#DEFS += -DCHECK_TMOUT
#DEFS += -DEBUG
# change this linking script depending on particular MCU model,
# for example, if you have STM32F103VBT6, you should write:
LDSCRIPT = stm32f042x6.ld
# autoincremental version & build date
VERSION_FILE := version.inc
NEXTVER := $(shell expr $$(awk '/#define BUILD_NUMBER/' $(VERSION_FILE) | tr -cd "[0-9]") + 1)
BUILDDATE := $(shell date +%Y-%m-%d)
INDEPENDENT_HEADERS=
FP_FLAGS ?= -msoft-float
ASM_FLAGS = -mthumb -mcpu=cortex-m0 -march=armv6-m -mtune=cortex-m0
ARCH_FLAGS = $(ASM_FLAGS) $(FP_FLAGS)
###############################################################################
# Executables
OPREFIX ?= /opt/bin/arm-none-eabi
#PREFIX ?= /usr/x86_64-pc-linux-gnu/arm-none-eabi/gcc-bin/7.3.0/arm-none-eabi
PREFIX ?= $(OPREFIX)
RM := rm -f
RMDIR := rmdir
CC := $(PREFIX)-gcc
LD := $(PREFIX)-gcc
AR := $(PREFIX)-ar
AS := $(PREFIX)-as
OBJCOPY := $(OPREFIX)-objcopy
OBJDUMP := $(OPREFIX)-objdump
GDB := $(OPREFIX)-gdb
STFLASH := $(shell which st-flash)
STBOOT := $(shell which stm32flash)
DFUUTIL := $(shell which dfu-util)
###############################################################################
# Source files
OBJDIR = mk
LDSCRIPT ?= $(BINARY).ld
SRC := $(wildcard *.c)
OBJS := $(addprefix $(OBJDIR)/, $(SRC:%.c=%.o))
STARTUP = $(OBJDIR)/startup.o
OBJS += $(STARTUP)
DEPS := $(OBJS:.o=.d)
INC_DIR ?= ../inc
INCLUDE := -I$(INC_DIR)/F0 -I$(INC_DIR)/cm
LIB_DIR := $(INC_DIR)/ld
###############################################################################
# C flags
CFLAGS += -O2 -g -D__thumb2__=1
CFLAGS += -Wall -Werror -Wextra -Wshadow -Wimplicit-function-declaration
CFLAGS += -Wredundant-decls $(INCLUDE)
# -Wmissing-prototypes -Wstrict-prototypes
CFLAGS += -fno-common -ffunction-sections -fdata-sections
###############################################################################
# Linker flags
LDFLAGS += --static -nostartfiles
#--specs=nano.specs
LDFLAGS += -L$(LIB_DIR)
LDFLAGS += -T$(LDSCRIPT)
LDFLAGS += -Wl,-Map=$(OBJDIR)/$(BINARY).map
LDFLAGS += -Wl,--gc-sections
###############################################################################
# Used libraries
#LDLIBS += -Wl,--start-group -lc -lgcc -Wl,--end-group
#LDLIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
DEFS += -DSTM32$(FAMILY) -DSTM32$(MCU)
#.SUFFIXES: .elf .bin .hex .srec .list .map .images
#.SECONDEXPANSION:
#.SECONDARY:
ELF := $(OBJDIR)/$(BINARY).elf
LIST := $(OBJDIR)/$(BINARY).list
BIN := $(BINARY).bin
HEX := $(BINARY).hex
all: bin list
elf: $(ELF)
bin: $(BIN)
hex: $(HEX)
list: $(LIST)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(OBJDIR):
mkdir $(OBJDIR)
$(STARTUP): $(INC_DIR)/startup/vector.c
$(CC) $(CFLAGS) $(DEFS) $(INCLUDE) $(ARCH_FLAGS) -o $@ -c $<
$(VERSION_FILE): *.[ch]
@echo " Generate version: $(NEXTVER) for date $(BUILDDATE)"
@sed -i "s/#define BUILD_NUMBER.*/#define BUILD_NUMBER \"$(NEXTVER)\"/" $(VERSION_FILE)
@sed -i "s/#define BUILDNO.*/#define BUILDNO $(NEXTVER)/" $(VERSION_FILE)
@sed -i "s/#define BUILD_DATE.*/#define BUILD_DATE \"$(BUILDDATE)\"/" $(VERSION_FILE)
$(OBJDIR)/proto.o: proto.c $(VERSION_FILE)
$(OBJDIR)/%.o: %.c
@make $(VERSION_FILE)
@echo " CC $<"
$(CC) $(CFLAGS) -MD $(DEFS) $(INCLUDE) $(ARCH_FLAGS) -o $@ -c $<
$(BIN): $(ELF)
@echo " OBJCOPY $(BIN)"
$(OBJCOPY) -Obinary $(ELF) $(BIN)
$(HEX): $(ELF)
@echo " OBJCOPY $(HEX)"
$(OBJCOPY) -Oihex $(ELF) $(HEX)
$(LIST): $(ELF)
@echo " OBJDUMP $(LIST)"
$(OBJDUMP) -S $(ELF) > $(LIST)
$(ELF): $(OBJDIR) $(OBJS)
@echo " LD $(ELF)"
$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(ELF)
@size $(ELF)
clean:
@echo " CLEAN"
$(RM) $(OBJS) $(DEPS) $(ELF) $(HEX) $(LIST) $(OBJDIR)/*.map *.d
@rmdir $(OBJDIR) 2>/dev/null || true
dfuboot: $(BIN)
@echo " LOAD $(BIN) THROUGH DFU"
$(DFUUTIL) -a0 -D $(BIN) -s 0x08000000
flash: $(BIN)
@echo " FLASH $(BIN)"
$(STFLASH) write $(BIN) 0x8000000
boot: $(BIN)
@echo " LOAD $(BIN) through bootloader"
$(STBOOT) -b$(BOOTSPEED) $(BOOTPORT) -w $(BIN)
.PHONY: clean flash boot dfuboot

View File

@ -0,0 +1,133 @@
# Firmware for controllers of thermal sensors
Make regular scan of 8 sensors' pairs.
USART speed 115200. Code for ../../kicad/stm32
## Serial interface commands (ends with '\n'), small letter for only local processing:
- **0...7** send message to Nth controller, not broadcast (after number should be CAN command)
- **@** set/reset debug mode
- **a** get raw ADC values
- **B** send dummy CAN messages to broadcast address
- **b** get/set CAN bus baudrate
- **c** show coefficients for all thermosensors
- **D** send dummy CAN messages to master (0) address
- **d** get current CAN address of device
- **Ee** end temperature scan
- **Ff** turn sensors off
- **g** group (sniffer) CAN mode (print to USB terminal all incoming CAN messages with alien IDs)
- **Hh** switch I2C to high speed (100kHz)
- **Ii** (re)init sensors
- **Jj** get MCU temperature
- **Kk** get values of U and I
- **Ll** switch I2C to low speed (default, 10kHz)
- **Mm** change master id to 0 (**m**) / broadcast (**M**)
- **N** get build number
- **Oo** turn onboard diagnostic LEDs **O**n or **o**ff (both commands are local!)
- **P** ping everyone over CAN
- **Qq** get system time
- **Rr** reinit I2C
- **s** send CAN message (format: ID data[0..8], dec, 0x - hex, 0b - binary)
- **Tt** start single temperature measurement
- **u** unique ID (default) CAN mode
- **Vv** very low speed
- **Xx** go into temperature scan mode
- **Yy** get sensors state over CAN (data format: 3 - state, 4,5 - presense mask [0,1], 6 - npresent, 7 - ntempmeasured
- **z** check CAN status for errors
The command **M** allows to temporaly change master ID of all
controllers to broadcast ID. So all data they sent will be
accessed @ any controller.
## PINOUT
- **I2C**: PB6 (SCL) & PB7 (SDA)
- **USART1**: PA9 (Tx) & PA10 (Rx)
- **CAN bus**: PB8 (Rx), PB9 (Tx)
- **USB bus**: PA11 (DM), PA12 (DP)
- **I2C multiplexer**: PB0..PB2 (0..2 address bits), PB12 (~EN)
- **sensors' power**: PB3 (in, overcurrent), PA8 (out, enable power)
- **signal LEDs**: PB10 (LED0), PB11 (LED1)
- **ADC inputs**: PA0 (V12/4.93), PA1 (V5/2), PA3 (I12 - 1V/A), PA6 (V3.3/2)
- **controller CAN address**: PA13..PA15 (0..2 bits), PB15 (3rd bit); 0 - master, other address - slave
## LEDS
- LED0 (nearest to sensors' connectors) - heartbeat
- LED1 (above LED0) - CAN bus OK
## CAN protocol
Variable data length: from 1 to 8 bytes.
First (number zero) byte of every sequence is command mark (0xA5) or data mark (0x5A).
## Commands
### Common commands
- `CMD_PING` (0) request for PONG cmd
- `CMD_START_MEASUREMENT` (1) start single temperature measurement
- `CMD_SENSORS_STATE` (2) get sensors state
- `CMD_START_SCAN` (3) run scan mode
- `CMD_STOP_SCAN` (4) stop scan mode
- `CMD_SENSORS_OFF` (5) turn off power of sensors
- `CMD_LOWEST_SPEED` (6) lowest I2C speed
- `CMD_LOW_SPEED` (7) low I2C speed (10kHz)
- `CMD_HIGH_SPEED` (8) high I2C speed (100kHz)
- `CMD_REINIT_I2C` (9) reinit I2C with current speed
- `CMD_CHANGE_MASTER_B` (10) change master id to broadcast
- `CMD_CHANGE_MASTER` (11) change master id to 0
- `CMD_GETMCUTEMP` (12) MCU temperature value
- `CMD_GETUIVAL` (13) request to get values of V12, V5, I12 and V3.3
- `CMD_GETUIVAL0` (14) answer with values of V12 and V5
- `CMD_GETUIVAL1` (15) answer with values of I12 and V3.3
- `CMD_REINIT_SENSORS` (16) (re)init all sensors (discover all and get calibrated data)
- `CMD_GETBUILDNO` (17) get by CAN firmware build number (uint32_t, littleendian, starting from byte #4)
- `CMD_SYSTIME` (18) get system time in ms (uint32_t, littleendian, starting from byte #4)
### Dummy commands for test purposes
- `CMD_DUMMY0` = 0xDA,
- `CMD_DUMMY1` = 0xAD
### Commands data format
- byte 1 - Controller number
- byte 2 - Command received
- bytes 3..7 - data
### Thermal data format
- byte 3 - Sensor number (10*N + M, where N is multiplexer number, M - number of sensor in pair, i.e. 0,1,10,11,20,21...70,71)
- byte 4 - thermal data H
- byte 5 - thermal data L
### Sensors state data format
- byte 3 - Sstate value:
- `[SENS_INITING]` = "init"
- `[SENS_RESETING]` = "reset"
- `[SENS_GET_COEFFS]` = "getcoeff"
- `[SENS_SLEEPING]` = "sleep"
- `[SENS_START_MSRMNT]` = "startmeasure"
- `[SENS_WAITING]` = "waitresults"
- `[SENS_GATHERING]` = "collectdata"
- `[SENS_OFF]` = "off"
- `[SENS_OVERCURNT]` = "overcurrent"
- `[SENS_OVERCURNT_OFF]` = "offbyovercurrent"
- byte 4 - `sens_present[0]` value
- byte 5 - `sens_present[1]` value
- byte 6 - `Nsens_present` value
- byte 7 - `Ntemp_measured` value
### MCU temperature data format
- byte 3 - data H
- byte 4 - data L
All temperature is in degrC/100!
### U and I data format
- byte 2 - type of data (`CMD_GETUIVAL0` - V12 and V5, `CMD_GETUIVAL1` - I12 and V3.3)
case CMD_GETUIVAL0
- bytes 3,4 - V12 H/L
- bytes 5,6 - V5 H/L
case CMD_GETUIVAL1
- bytes 3,4 - I12 H/L
- bytes 5,6 - V33 H/L
Voltage is in V/100, Current is in mA

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1,8 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42
#define FAMILY F0
#define MCU F042x6
#define STM32F0
#define STM32F042x6
#define USARTNUM 1
#define I2CPINS 67

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 8.0.2, 2023-08-29T20:44:10. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Big/Data/00__Electronics/STM32/F0-nolib/TSYS_controller</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</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">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</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.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1,198 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 4.6.2, 2019-09-09T09:34:18. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">2</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap"/>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/eddy/Docs/SAO/BTA/MIRROR_CONTROL_termo/Project/STM32src/TSYS_controller</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</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">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="GenericProjectManager.GenericMakeStep.Clean">false</value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeArguments"></value>
<value type="QString" key="GenericProjectManager.GenericMakeStep.MakeCommand"></value>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</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">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Установка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Конфигурация установки</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.PluginSettings"/>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
<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"></value>
<value type="QString" key="ProjectExplorer.CustomExecutableRunConfiguration.WorkingDirectory"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName"></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>
<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>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">18</value>
</data>
<data>
<variable>Version</variable>
<value type="int">18</value>
</data>
</qtcreator>

View File

@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 6.0.0, 2022-02-03T09:36:24. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">2</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">true</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">4</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/eddy/Docs/SAO/BTA/MIRROR_CONTROL_termo/Project/STM32src/TSYS_controller</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<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">Build</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">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</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">Clean</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.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,23 @@
adc.c
adc.h
usb.c
usb.h
usb_lib.h
usb_lib.c
usb_defs.h
can.c
can.h
can_process.c
can_process.h
hardware.c
hardware.h
i2c.c
i2c.h
main.c
proto.c
proto.h
sensors_manage.c
sensors_manage.h
usart.c
usart.h
version.inc

View File

@ -0,0 +1,4 @@
.
../inc
../inc/F0
../inc/cm

View File

@ -0,0 +1,167 @@
/*
* This file is part of the TSYS_controller project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "adc.h"
/**
* @brief ADC_array - array for ADC channels with median filtering:
* 0..3 - external channels
* 4 - internal Tsens
* 5 - Vref
*/
#define TSENS_CHAN (NUMBER_OF_ADC_CHANNELS-2)
#define VREF_CHAN (NUMBER_OF_ADC_CHANNELS-1)
static uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9];
/*
* ADC channels:
* IN0 - V12
* IN1 - V5
* IN3 - I12
* IN6 - V3.3
* IN16- temperature sensor
* IN17- vref
*/
void adc_setup(){
uint16_t ctr = 0; // 0xfff0 - more than 1.3ms
// Enable clocking
/* (1) Enable the peripheral clock of the ADC */
/* (2) Start HSI14 RC oscillator */
/* (3) Wait HSI14 is ready */
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; /* (1) */
RCC->CR2 |= RCC_CR2_HSI14ON; /* (2) */
while ((RCC->CR2 & RCC_CR2_HSI14RDY) == 0 && ++ctr < 0xfff0){}; /* (3) */
// calibration
/* (1) Ensure that ADEN = 0 */
/* (2) Clear ADEN */
/* (3) Launch the calibration by setting ADCAL */
/* (4) Wait until ADCAL=0 */
if ((ADC1->CR & ADC_CR_ADEN) != 0){ /* (1) */
ADC1->CR &= (uint32_t)(~ADC_CR_ADEN); /* (2) */
}
ADC1->CR |= ADC_CR_ADCAL; /* (3) */
ctr = 0; // ADC calibration time is 5.9us
while ((ADC1->CR & ADC_CR_ADCAL) != 0 && ++ctr < 0xfff0){}; /* (4) */
// enable ADC
ctr = 0;
do{
ADC1->CR |= ADC_CR_ADEN;
}while ((ADC1->ISR & ADC_ISR_ADRDY) == 0 && ++ctr < 0xfff0);
// configure ADC
/* (1) Select HSI14 by writing 00 in CKMODE (reset value) */
/* (2) Select the continuous mode */
/* (3) Select CHSEL0,1,3,6 - ADC inputs, 16,17 - t. sensor and vref */
/* (4) Select a sampling mode of 111 i.e. 239.5 ADC clk to be greater than 17.1us */
/* (5) Wake-up the VREFINT and Temperature sensor (only for VBAT, Temp sensor and VRefInt) */
// ADC1->CFGR2 &= ~ADC_CFGR2_CKMODE; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_CONT; /* (2)*/
ADC1->CHSELR = ADC_CHSELR_CHSEL0 | ADC_CHSELR_CHSEL1 | ADC_CHSELR_CHSEL3 |
ADC_CHSELR_CHSEL6 | ADC_CHSELR_CHSEL16 | ADC_CHSELR_CHSEL17; /* (3)*/
ADC1->SMPR |= ADC_SMPR_SMP_0 | ADC_SMPR_SMP_1 | ADC_SMPR_SMP_2; /* (4) */
ADC->CCR |= ADC_CCR_TSEN | ADC_CCR_VREFEN; /* (5) */
// configure DMA for ADC
// DMA for AIN
/* (1) Enable the peripheral clock on DMA */
/* (2) Enable DMA transfer on ADC and circular mode */
/* (3) Configure the peripheral data register address */
/* (4) Configure the memory address */
/* (5) Configure the number of DMA tranfer to be performs on DMA channel 1 */
/* (6) Configure increment, size, interrupts and circular mode */
/* (7) Enable DMA Channel 1 */
RCC->AHBENR |= RCC_AHBENR_DMA1EN; /* (1) */
ADC1->CFGR1 |= ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG; /* (2) */
DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR)); /* (3) */
DMA1_Channel1->CMAR = (uint32_t)(ADC_array); /* (4) */
DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9; /* (5) */
DMA1_Channel1->CCR |= DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0 | DMA_CCR_CIRC; /* (6) */
DMA1_Channel1->CCR |= DMA_CCR_EN; /* (7) */
ADC1->CR |= ADC_CR_ADSTART; /* start the ADC conversions */
}
/**
* @brief getADCval - calculate median value for `nch` channel
* @param nch - number of channel
* @return
*/
uint16_t getADCval(int nch){
int i, addr = nch;
register uint16_t temp;
#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
#define PIX_SWAP(a,b) { temp=(a);(a)=(b);(b)=temp; }
uint16_t p[9];
for(i = 0; i < 9; ++i, addr += NUMBER_OF_ADC_CHANNELS) // first we should prepare array for optmed
p[i] = ADC_array[addr];
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ;
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ;
PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ;
PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ;
PIX_SORT(p[4], p[2]) ;
return p[4];
#undef PIX_SORT
#undef PIX_SWAP
}
// return MCU temperature (degrees of celsius * 10)
int32_t getMCUtemp(){
getVdd();
// make correction on Vdd value
// int32_t temperature = (int32_t)ADC_array[4] * VddValue / 330;
int32_t ADval = getADCval(TSENS_CHAN);
int32_t temperature = (int32_t) *TEMP30_CAL_ADDR - ADval;
temperature *= (int32_t)(1100 - 300);
temperature /= (int32_t)(*TEMP30_CAL_ADDR - *TEMP110_CAL_ADDR);
temperature += 300;
return(temperature);
}
// return Vdd * 100 (V)
uint32_t getVdd(){
uint32_t vdd = ((uint32_t) *VREFINT_CAL_ADDR) * (uint32_t)330; // 3.3V
vdd /= getADCval(VREF_CHAN);
return vdd;
}
static inline uint32_t Ufromadu(uint8_t nch, uint32_t vdd){
uint32_t ADU = getADCval(nch);
ADU *= vdd;
ADU >>= 12; // /4096
return ADU;
}
/**
* @brief getUval - calculate U & I
* @return array with members:
* 0 - V12 * 100V (U12 = 12Vin/4.93)
* 1 - V5 * 100V (U5 = 5Vin /2)
* 2 - I12 mA (U = 1V/1A)
* 3 - V3.3* 100V (U3.3= 3.3Vin/2)
*/
uint16_t *getUval(){
static uint16_t Uval[4];
uint32_t vdd = getVdd();
uint32_t val = Ufromadu(0, vdd) * 493;
Uval[0] = (uint16_t)(val / 100);
Uval[1] = (uint16_t)(Ufromadu(1, vdd) << 1);
val = getADCval(2) * vdd * 10;
Uval[2] = (uint16_t)(val >> 12);
Uval[3] = (uint16_t)(Ufromadu(3, vdd) << 1);
return Uval;
}

View File

@ -0,0 +1,31 @@
/*
* This file is part of the TSYS_controller project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ADC_H
#define ADC_H
#include "stm32f0.h"
#define NUMBER_OF_ADC_CHANNELS (6)
int32_t getMCUtemp();
uint32_t getVdd();
uint16_t getADCval(int nch);
void adc_setup();
uint16_t *getUval();
#endif // ADC_H

View File

@ -0,0 +1,296 @@
/*
* geany_encoding=koi8-r
* can.c
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <string.h> // memcpy
#include "can.h"
#include "hardware.h"
#include "proto.h"
// incoming message buffer size
#define CAN_INMESSAGE_SIZE (32)
extern volatile uint32_t Tms;
// circular buffer for received messages
static CAN_message messages[CAN_INMESSAGE_SIZE];
static uint8_t first_free_idx = 0; // index of first empty cell
static int8_t first_nonfree_idx = -1; // index of first data cell
int8_t cansniffer = 0; // ==1 to listen all CAN ID's
uint16_t curcanspeed = CAN_SPEED_DEFAULT; // speed of last init
uint16_t CANID = 0xFFFF;
uint8_t Controller_address = 0;
static CAN_status can_status = CAN_STOP;
static void can_process_fifo(uint8_t fifo_num);
CAN_status CAN_get_status(){
CAN_status st = can_status;
// give overrun message only once
if(st == CAN_FIFO_OVERRUN) can_status = CAN_READY;
return st;
}
// push next message into buffer; return 1 if buffer overfull
static int CAN_messagebuf_push(CAN_message *msg){
MSG("Try to push\n");
if(first_free_idx == first_nonfree_idx) return 1; // no free space
if(first_nonfree_idx < 0) first_nonfree_idx = 0; // first message in empty buffer
memcpy(&messages[first_free_idx++], msg, sizeof(CAN_message));
// need to roll?
if(first_free_idx == CAN_INMESSAGE_SIZE) first_free_idx = 0;
return 0;
}
// pop message from buffer
CAN_message *CAN_messagebuf_pop(){
if(first_nonfree_idx < 0) return NULL;
CAN_message *msg = &messages[first_nonfree_idx++];
if(first_nonfree_idx == CAN_INMESSAGE_SIZE) first_nonfree_idx = 0;
if(first_nonfree_idx == first_free_idx){ // buffer is empty - refresh it
first_nonfree_idx = -1;
first_free_idx = 0;
}
return msg;
}
// get CAN address data from GPIO pins
void readCANID(){
uint8_t CAN_addr = READ_CAN_INV_ADDR();
Controller_address = ~CAN_addr & 0x0f;
CANID = (CAN_ID_PREFIX & CAN_ID_MASK) | Controller_address;
}
void CAN_setup(uint16_t speed){
if(speed == 0) speed = curcanspeed;
else if(speed < CAN_SPEED_MIN) speed = CAN_SPEED_MIN;
else if(speed > CAN_SPEED_MAX) speed = CAN_SPEED_MAX;
curcanspeed = speed;
readCANID();
CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2;
RCC->APB1RSTR |= RCC_APB1RSTR_CANRST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST;
// Configure GPIO: PB8 - CAN_Rx, PB9 - CAN_Tx
/* (1) Select AF mode (10) on PB8 and PB9 */
/* (2) AF4 for CAN signals */
GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER8 | GPIO_MODER_MODER9))
| (GPIO_MODER_MODER8_AF | GPIO_MODER_MODER9_AF); /* (1) */
GPIOB->AFR[1] = (GPIOB->AFR[1] &~ (GPIO_AFRH_AFRH0 | GPIO_AFRH_AFRH1))\
| (4 << (0 * 4)) | (4 << (1 * 4)); /* (2) */
/* Enable the peripheral clock CAN */
RCC->APB1ENR |= RCC_APB1ENR_CANEN;
/* Configure CAN */
/* (1) Enter CAN init mode to write the configuration */
/* (2) Wait the init mode entering */
/* (3) Exit sleep mode */
/* (4) Normal mode, set timing to 100kb/s: BS1 = 4, BS2 = 3, prescaler = 6000/speed */
/* (5) Leave init mode */
/* (6) Wait the init mode leaving */
/* (7) Enter filter init mode, (16-bit + mask, filter 0 for FIFO 0) */
/* (8) Acivate filter 0 (1,2) */
/* (9) Identifier mode for bank#0, mask mode for #1 and #2 */
/* (10) Set the Id list */
/* (11) Set the mask list */
/* (12) Leave filter init */
/* (13) Set error interrupts enable */
CAN->MCR |= CAN_MCR_INRQ; /* (1) */
uint32_t tmout = 16000000;
while((CAN->MSR & CAN_MSR_INAK)!=CAN_MSR_INAK){ /* (2) */
if(--tmout == 0) break;
}
CAN->MCR &=~ CAN_MCR_SLEEP; /* (3) */
CAN->MCR |= CAN_MCR_ABOM;
CAN->BTR |= 2 << 20 | 3 << 16 | (6000/speed - 1); /* (4) */
CAN->MCR &=~ CAN_MCR_INRQ; /* (5) */
tmout = 16000000;
while((CAN->MSR & CAN_MSR_INAK)==CAN_MSR_INAK){ /* (6) */
if(--tmout == 0) break;
}
CAN->FMR = CAN_FMR_FINIT; /* (7) */
CAN->FA1R = CAN_FA1R_FACT0; /* (8) */
CAN->FM1R = CAN_FM1R_FBM0; /* (9) */
CAN->sFilterRegister[0].FR1 = CANID << 5 | ((BCAST_ID << 5) << 16); /* (10) */
if(cansniffer){ /* (11) */
CAN->FA1R |= CAN_FA1R_FACT1 | CAN_FA1R_FACT2; // activate 1 & 2
CAN->sFilterRegister[1].FR1 = (1<<21)|(1<<5); // all odd IDs
CAN->sFilterRegister[2].FR1 = (1<<21); // all even IDs
CAN->FFA1R = 2; // filter 1 for FIFO1, filters 0&2 - for FIFO0
}
CAN->FMR &=~ CAN_FMR_FINIT; /* (12) */
CAN->IER |= CAN_IER_ERRIE | CAN_IER_FOVIE0 | CAN_IER_FOVIE1; /* (13) */
/* Configure IT */
/* (14) Set priority for CAN_IRQn */
/* (15) Enable CAN_IRQn */
NVIC_SetPriority(CEC_CAN_IRQn, 0); /* (14) */
NVIC_EnableIRQ(CEC_CAN_IRQn); /* (15) */
can_status = CAN_READY;
}
// add filters for ALL ID's
void CAN_listenall(){
cansniffer = 1;
CAN_setup(0);
}
// listen only packets to self & broadcast - delete filters 1&2
void CAN_listenone(){
cansniffer = 0;
CAN_setup(0);
}
void can_proc(){
// check for messages in FIFO0 & FIFO1
if(CAN->RF0R & CAN_RF0R_FMP0){
can_process_fifo(0);
}
if(CAN->RF1R & CAN_RF1R_FMP1){
can_process_fifo(1);
}
if(CAN->ESR & (CAN_ESR_BOFF | CAN_ESR_EPVF | CAN_ESR_EWGF)){ // much errors - restart CAN BUS
mesg("too much CAN errors, restart CAN");
MSG("bus-off, restarting\n");
// request abort for all mailboxes
CAN->TSR |= CAN_TSR_ABRQ0 | CAN_TSR_ABRQ1 | CAN_TSR_ABRQ2;
// reset CAN bus
RCC->APB1RSTR |= RCC_APB1RSTR_CANRST;
RCC->APB1RSTR &= ~RCC_APB1RSTR_CANRST;
can_status = CAN_ERROR;
}
}
CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id){
if(!noLED) LED_on(LED1); // turn ON LED1 at first data sent/receive
uint8_t mailbox = 0;
// check first free mailbox
if(CAN->TSR & (CAN_TSR_TME)){
mailbox = (CAN->TSR & CAN_TSR_CODE) >> 24;
}else{ // no free mailboxes
return CAN_BUSY;
}
CAN_TxMailBox_TypeDef *box = &CAN->sTxMailBox[mailbox];
uint32_t lb = 0, hb = 0;
switch(len){
case 8:
hb |= (uint32_t)msg[7] << 24;
__attribute__((fallthrough));
case 7:
hb |= (uint32_t)msg[6] << 16;
__attribute__((fallthrough));
case 6:
hb |= (uint32_t)msg[5] << 8;
__attribute__((fallthrough));
case 5:
hb |= (uint32_t)msg[4];
__attribute__((fallthrough));
case 4:
lb |= (uint32_t)msg[3] << 24;
__attribute__((fallthrough));
case 3:
lb |= (uint32_t)msg[2] << 16;
__attribute__((fallthrough));
case 2:
lb |= (uint32_t)msg[1] << 8;
__attribute__((fallthrough));
default:
lb |= (uint32_t)msg[0];
}
box->TDLR = lb;
box->TDHR = hb;
box->TDTR = len;
box->TIR = (target_id & 0x7FF) << 21 | CAN_TI0R_TXRQ;
return CAN_OK;
}
static void can_process_fifo(uint8_t fifo_num){
if(fifo_num > 1) return;
CAN_FIFOMailBox_TypeDef *box = &CAN->sFIFOMailBox[fifo_num];
volatile uint32_t *RFxR = (fifo_num) ? &CAN->RF1R : &CAN->RF0R;
if(!noLED) LED_on(LED1); // turn ON LED1 at first data sent/receive
// read all
while(*RFxR & CAN_RF0R_FMP0){ // amount of messages pending
// CAN_RDTxR: (16-31) - timestamp, (8-15) - filter match index, (0-3) - data length
/* TODO: check filter match index if more than one ID can receive */
CAN_message msg;
uint8_t *dat = msg.data;
{ // set all data to 0
uint32_t *dptr = (uint32_t*)msg.data;
dptr[0] = dptr[1] = 0;
}
uint8_t len = box->RDTR & 0x0f;
msg.length = len;
msg.ID = box->RIR >> 21;
if(len){ // message can be without data
uint32_t hb = box->RDHR, lb = box->RDLR;
switch(len){
case 8:
dat[7] = hb>>24;
__attribute__((fallthrough));
case 7:
dat[6] = (hb>>16) & 0xff;
__attribute__((fallthrough));
case 6:
dat[5] = (hb>>8) & 0xff;
__attribute__((fallthrough));
case 5:
dat[4] = hb & 0xff;
__attribute__((fallthrough));
case 4:
dat[3] = lb>>24;
__attribute__((fallthrough));
case 3:
dat[2] = (lb>>16) & 0xff;
__attribute__((fallthrough));
case 2:
dat[1] = (lb>>8) & 0xff;
__attribute__((fallthrough));
case 1:
dat[0] = lb & 0xff;
}
}
if(CAN_messagebuf_push(&msg)) return; // error: buffer is full, try later
*RFxR |= CAN_RF0R_RFOM0; // release fifo for access to next message
}
if(*RFxR & CAN_RF0R_FULL0) *RFxR &= ~CAN_RF0R_FULL0;
}
void cec_can_isr(){
if(CAN->RF0R & CAN_RF0R_FOVR0){ // FIFO overrun
CAN->RF0R &= ~CAN_RF0R_FOVR0;
can_status = CAN_FIFO_OVERRUN;
}
if(CAN->RF1R & CAN_RF1R_FOVR1){
CAN->RF1R &= ~CAN_RF1R_FOVR1;
can_status = CAN_FIFO_OVERRUN;
}
if(CAN->MSR & CAN_MSR_ERRI){ // Error
CAN->MSR &= ~CAN_MSR_ERRI;
// request abort for problem mailbox
if(CAN->TSR & CAN_TSR_TERR0) CAN->TSR |= CAN_TSR_ABRQ0;
if(CAN->TSR & CAN_TSR_TERR1) CAN->TSR |= CAN_TSR_ABRQ1;
if(CAN->TSR & CAN_TSR_TERR2) CAN->TSR |= CAN_TSR_ABRQ2;
}
}

View File

@ -0,0 +1,73 @@
/*
* geany_encoding=koi8-r
* can.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __CAN_H__
#define __CAN_H__
#include "hardware.h"
// identifier mask
#define CAN_ID_MASK ((uint16_t)0x7F0)
// prefix of identifiers
#define CAN_ID_PREFIX ((uint16_t)0xAAA)
// this is master - Controller_address==0
#define MASTER_ID (CAN_ID_PREFIX & CAN_ID_MASK)
// broadcasting to every slave
#define BCAST_ID ((uint16_t)0x7F7)
// send dummy message to this ID for testing CAN bus status
#define NOONE_ID ((uint16_t)0x7FF)
extern uint16_t curcanspeed;
extern uint16_t CANID;
extern int8_t cansniffer;
typedef struct{
uint8_t data[8];
uint8_t length;
uint16_t ID; // ID of receiver
} CAN_message;
typedef enum{
CAN_NOTMASTER, // can't send command - not a master
CAN_STOP, // CAN stopped
CAN_READY, // ready to send
CAN_BUSY, // bus is busy
CAN_OK, // all OK?
CAN_FIFO_OVERRUN, // FIFO overrun
CAN_ERROR // no recipients on bus or too many errors
} CAN_status;
CAN_status CAN_get_status();
void readCANID();
void CAN_setup(uint16_t speed);
void CAN_listenall();
void CAN_listenone();
void can_proc();
CAN_status can_send(uint8_t *msg, uint8_t len, uint16_t target_id);
CAN_message *CAN_messagebuf_pop();
#endif // __CAN_H__

View File

@ -0,0 +1,341 @@
/*
* geany_encoding=koi8-r
* can_process.c
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "adc.h"
#include "can.h"
#include "can_process.h"
#include "proto.h"
#include "sensors_manage.h"
#include "version.inc"
extern volatile uint32_t Tms; // timestamp data
// id of master - all data will be sent to it
static uint16_t master_id = MASTER_ID;
static inline void sendmcut(uint8_t *data){
uint8_t t[3];
uint16_t T = getMCUtemp();
t[0] = data[1]; // command itself
t[1] = (T >> 8) & 0xff; // H
t[2] = T & 0xff; // L
can_send_data(t,3);
}
static inline void senduival(){
uint8_t buf[5];
uint16_t *vals = getUval();
buf[0] = CMD_GETUIVAL0; // V12 and V5
buf[1] = vals[0] >> 8; // H
buf[2] = vals[0] & 0xff;// L
buf[3] = vals[1] >> 8; // -//-
buf[4] = vals[1] & 0xff;
can_send_data(buf, 5);
buf[0] = CMD_GETUIVAL1; // I12 and V3.3
buf[1] = vals[2] >> 8; // H
buf[2] = vals[2] & 0xff;// L
buf[3] = vals[3] >> 8; // -//-
buf[4] = vals[3] & 0xff;
can_send_data(buf, 5);
}
static inline void showui(char *v1, char *v2, uint8_t *data){
char N = '0' + data[1];
addtobuf(v1);
bufputchar(N);
bufputchar('=');
uint16_t v = data[3]<<8 | data[4];
printu(v);
newline();
addtobuf(v2);
bufputchar(N);
bufputchar('=');
v = data[5]<<8 | data[6];
printu(v);
}
void can_messages_proc(){
CAN_message *can_mesg = CAN_messagebuf_pop();
if(!can_mesg) return; // no data in buffer
uint8_t len = can_mesg->length;
IWDG->KR = IWDG_REFRESH;
#ifdef EBUG
SEND("got message, len: "); bufputchar('0' + len);
SEND(", data: ");
uint8_t ctr;
for(ctr = 0; ctr < len; ++ctr){
printuhex(can_mesg->data[ctr]);
bufputchar(' ');
}
newline();
#endif
uint8_t *data = can_mesg->data, b[6];
b[0] = data[1];
// show received message in sniffer mode
if(cansniffer){
printu(Tms);
SEND(" #");
printuhex(can_mesg->ID);
for(int ctr = 0; ctr < len; ++ctr){
SEND(" ");
printuhex(can_mesg->data[ctr]);
}
newline();
}
// don't process alien messages
if(can_mesg->ID != CANID && can_mesg->ID != BCAST_ID) return;
int16_t t;
uint32_t U32;
if(data[0] == COMMAND_MARK){ // process commands
if(len < 2) return;
// master shouldn't react to broadcast commands!
if(can_mesg->ID == BCAST_ID && CANID == MASTER_ID) return;
switch(data[1]){
case CMD_DUMMY0:
case CMD_DUMMY1:
SEND("DUMMY");
bufputchar('0' + (data[1]==CMD_DUMMY0 ? 0 : 1));
newline();
break;
case CMD_PING: // pong
can_send_data(b, 1);
break;
case CMD_SENSORS_STATE:
b[1] = Sstate;
b[2] = sens_present[0];
b[3] = sens_present[1];
b[4] = Nsens_present;
b[5] = Ntemp_measured;
can_send_data(b, 6);
break;
case CMD_START_MEASUREMENT:
sensors_start();
break;
case CMD_START_SCAN:
sensors_scan_mode = 1;
break;
case CMD_STOP_SCAN:
sensors_scan_mode = 0;
break;
case CMD_SENSORS_OFF:
sensors_off();
break;
case CMD_LOWEST_SPEED:
i2c_setup(VERYLOW_SPEED);
break;
case CMD_LOW_SPEED:
i2c_setup(LOW_SPEED);
break;
case CMD_HIGH_SPEED:
i2c_setup(HIGH_SPEED);
break;
case CMD_REINIT_I2C:
i2c_setup(CURRENT_SPEED);
break;
case CMD_CHANGE_MASTER_B:
master_id = BCAST_ID;
break;
case CMD_CHANGE_MASTER:
master_id = MASTER_ID;
break;
case CMD_GETMCUTEMP:
sendmcut(data);
break;
case CMD_GETUIVAL:
senduival();
break;
case CMD_REINIT_SENSORS:
sensors_init();
break;
case CMD_GETBUILDNO:
b[1] = 0;
*((uint32_t*)&b[2]) = BUILDNO;
can_send_data(b, 6);
break;
case CMD_SYSTIME:
b[1] = 0;
*((uint32_t*)&b[2]) = Tms;
can_send_data(b, 6);
break;
}
}else if(data[0] == DATA_MARK){ // process received data
char Ns = '0' + data[1];
if(len < 3) return;
switch(data[2]){
case CMD_PING:
mesg("CMD_PING");
SEND("PONG");
bufputchar(Ns);
break;
case CMD_SENSORS_STATE:
mesg("CMD_SENSORS_STATE");
SEND("SSTATE");
bufputchar(Ns);
bufputchar('=');
SEND(sensors_get_statename(data[3]));
SEND("\nNSENS");
bufputchar(Ns);
bufputchar('=');
printu(data[6]);
SEND("\nSENSPRESENT");
bufputchar(Ns);
bufputchar('=');
printu(data[4] | (data[5]<<8));
SEND("\nNTEMP");
bufputchar(Ns);
bufputchar('=');
printu(data[7]);
break;
case CMD_START_MEASUREMENT: // temperature
mesg("CMD_START_MEASUREMENT");
if(len != 6) return;
bufputchar('T');
bufputchar(Ns);
bufputchar('_');
printu(data[3]);
bufputchar('=');
t = data[4]<<8 | data[5];
if(t < 0){
t = -t;
bufputchar('-');
}
printu(t);
break;
case CMD_GETMCUTEMP:
mesg("CMD_GETMCUTEMP");
addtobuf("TMCU");
bufputchar(Ns);
bufputchar('=');
t = data[3]<<8 | data[4];
if(t < 0){
bufputchar('-');
t = -t;
}
printu(t);
break;
case CMD_GETUIVAL0: // V12 and V5
mesg("CMD_GETUIVAL0");
showui("V12_", "V5_", data);
break;
case CMD_GETUIVAL1: // I12 and V3.3
mesg("CMD_GETUIVAL1");
showui("I12_", "V33_", data);
break;
case CMD_GETBUILDNO:
mesg("CMD_GETBUILDNO");
addtobuf("BUILDNO");
bufputchar(Ns);
bufputchar('=');
U32 = *((uint32_t*)&data[4]);
printu(U32);
break;
case CMD_SYSTIME:
mesg("CMD_SYSTIME");
addtobuf("SYSTIME");
bufputchar(Ns);
bufputchar('=');
U32 = *((uint32_t*)&data[4]);
printu(U32);
break;
default:
SEND("UNKNOWN_DATA");
}
newline();
}
}
// try to send messages, wait no more than 100ms
static CAN_status try2send(uint8_t *buf, uint8_t len, uint16_t id){
uint32_t Tstart = Tms;
while(Tms - Tstart < SEND_TIMEOUT_MS){
if(CAN_OK == can_send(buf, len, id)) return CAN_OK;
IWDG->KR = IWDG_REFRESH;
}
SEND("CAN_BUSY\n");
return CAN_BUSY;
}
/**
* Send command over CAN bus (works only if controller number is 0 - master mode)
* @param targetID - target identifier
* @param cmd - command to send
*/
CAN_status can_send_cmd(uint16_t targetID, uint8_t cmd){
//if(Controller_address != 0 && cmd != CMD_DUMMY0 && cmd != CMD_DUMMY1) return CAN_NOTMASTER;
uint8_t buf[2];
buf[0] = COMMAND_MARK;
buf[1] = cmd;
return try2send(buf, 2, targetID);
}
// send data over CAN bus to MASTER_ID (not more than 6 bytes)
CAN_status can_send_data(uint8_t *data, uint8_t len){
if(len > 6) return CAN_OK;
uint8_t buf[8];
buf[0] = DATA_MARK;
buf[1] = Controller_address;
int i;
for(i = 0; i < len; ++i) buf[i+2] = *data++;
return try2send(buf, len+2, master_id);
}
/**
* send temperature data over CAN bus once per call
* @return next number or -1 if all data sent
*/
int8_t send_temperatures(int8_t N){
if(N < 0 || Controller_address == 0) return -1; // don't need to send Master's data over CAN bus
int a, p;
uint8_t can_data[4];
int8_t retn = N;
can_data[0] = CMD_START_MEASUREMENT;
a = N / 10;
p = N - a*10;
if(p == 2){ // next sensor
if(++a > MUL_MAX_ADDRESS) return -1;
p = 0;
}
do{
if(!(sens_present[p] & (1<<a))){
if(p == 0) p = 1;
else{
p = 0;
++a;
}
} else break;
}while(a <= MUL_MAX_ADDRESS);
if(a > MUL_MAX_ADDRESS) return -1; // done
retn = a*10 + p; // current temperature sensor number
can_data[1] = a*10 + p;
//char b[] = {'T', a+'0', p+'0', '=', '+'};
int16_t t = Temperatures[a][p];
if(t == BAD_TEMPERATURE || t == NO_SENSOR){ // don't send data if it's absent on current measurement
++retn;
}else{
can_data[2] = t>>8; // H byte
can_data[3] = t&0xff; // L byte
if(CAN_OK == can_send_data(can_data, 4)){ // OK, calculate next address
++retn;
}
}
return retn;
}

View File

@ -0,0 +1,61 @@
/*
* geany_encoding=koi8-r
* can_process.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "can.h"
// timeout for trying to send data
#define SEND_TIMEOUT_MS (10)
// mark first byte if command sent
#define COMMAND_MARK (0xA5)
// mark first byte if data sent
#define DATA_MARK (0x5A)
// 8-bit commands sent by master
typedef enum{
CMD_PING, // request for PONG cmd
CMD_START_MEASUREMENT, // start thermal measurement (and turn ON sensors if was OFF)
CMD_SENSORS_STATE, // reply data with sensors state (data: 0 - SState, 1,2 - sens_present0, 3 - Nsens_presend, 4 - Ntemp_measured)
CMD_START_SCAN, // run scan mode @ all controllers
CMD_STOP_SCAN, // stop scan mode
CMD_SENSORS_OFF, // turn off power of sensors
CMD_LOWEST_SPEED, // lowest I2C speed
CMD_LOW_SPEED, // low I2C speed (10kHz)
CMD_HIGH_SPEED, // high I2C speed (100kHz)
CMD_REINIT_I2C, // reinit I2C with current speed
CMD_CHANGE_MASTER_B, // change master id to broadcast
CMD_CHANGE_MASTER, // change master id to 0
CMD_GETMCUTEMP, // MCU temperature value
CMD_GETUIVAL, // request to get values of V12, V5, I12 and V3.3
CMD_GETUIVAL0, // answer with values of V12 and V5
CMD_GETUIVAL1, // answer with values of I12 and V3.3
CMD_REINIT_SENSORS, // (re)init sensors
CMD_GETBUILDNO, // request for firmware build number
CMD_SYSTIME, // get system time
// dummy commands for test purposes
CMD_DUMMY0 = 0xDA,
CMD_DUMMY1 = 0xAD
} CAN_commands;
void can_messages_proc();
CAN_status can_send_cmd(uint16_t targetID, uint8_t cmd);
CAN_status can_send_data(uint8_t *data, uint8_t len);
int8_t send_temperatures(int8_t N);

View File

@ -0,0 +1,99 @@
/*
* geany_encoding=koi8-r
* hardware.c - hardware-dependent macros & functions
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "hardware.h"
I2C_SPEED curI2Cspeed = LOW_SPEED;
void gpio_setup(void){
// here we turn on clocking for all periph.
RCC->AHBENR |= RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_DMAEN;
// Set LEDS (PB10/11) & multiplexer as output (PB0..2,12)
GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER10 | GPIO_MODER_MODER11 |
GPIO_MODER_MODER0 | GPIO_MODER_MODER1 |
GPIO_MODER_MODER2 | GPIO_MODER_MODER12 )
) |
GPIO_MODER_MODER10_O | GPIO_MODER_MODER11_O |
GPIO_MODER_MODER0_O | GPIO_MODER_MODER1_O |
GPIO_MODER_MODER2_O | GPIO_MODER_MODER12_O;
// multiplexer outputs are push-pull, GPIOB->OTYPER = 0
MUL_OFF();
// PA8 - power enable
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER8)) |
GPIO_MODER_MODER8_O;
// PA13..15 (low bits) + PB15 (high bit) - CAN address, pullup inputs
GPIOA->PUPDR = (GPIOA->PUPDR & ~(GPIO_PUPDR_PUPDR13 | GPIO_PUPDR_PUPDR14 |
GPIO_PUPDR_PUPDR15)
) |
GPIO_PUPDR_PUPDR13_0 | GPIO_PUPDR_PUPDR14_0 |
GPIO_PUPDR_PUPDR15_0;
GPIOB->PUPDR = (GPIOB->PUPDR & ~(GPIO_PUPDR_PUPDR15)) |
GPIO_PUPDR_PUPDR15_0;
pin_set(LED0_port, LED0_pin); // clear LEDs
pin_set(LED1_port, LED1_pin);
}
void i2c_setup(I2C_SPEED speed){
if(speed == CURRENT_SPEED){
speed = curI2Cspeed;
}else{
curI2Cspeed = speed;
}
I2C1->CR1 = 0;
#if I2CPINS == 910
/*
* GPIO Resources: I2C1_SCL - PA9, I2C1_SDA - PA10
* GPIOA->AFR[1]
*/
GPIOA->AFR[1] &= ~0xff0; // alternate function F4 for PA9/PA10
GPIOA->AFR[1] |= 0x440;
GPIOA->MODER &= ~(GPIO_MODER_MODER9 | GPIO_MODER_MODER10);
GPIOA->MODER |= GPIO_MODER_MODER9_AF | GPIO_MODER_MODER10_AF; // alternate function
GPIOA->OTYPER |= GPIO_OTYPER_OT_9 | GPIO_OTYPER_OT_10; // opendrain
//GPIOA->OTYPER |= GPIO_OTYPER_OT_10; // opendrain
#elif I2CPINS == 67
/*
* GPIO Resources: I2C1_SCL - PB6, I2C1_SDA - PB7 (AF1)
* GPIOB->AFR[0] -> 1<<6*4 | 1<<7*4 = 0x11000000
*/
GPIOB->AFR[0] = (GPIOB->AFR[0] & ~0xff000000) | 0x11000000;
GPIOB->MODER = (GPIOB->MODER & ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7)) |
GPIO_MODER_MODER6_AF | GPIO_MODER_MODER7_AF;
GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_7;
#else // undefined
#error "Not implemented"
#endif
// I2C
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // timing
RCC->CFGR3 |= RCC_CFGR3_I2C1SW; // use sysclock for timing
if(speed == LOW_SPEED){ // 10kHz
// PRESC=B, SCLDEL=4, SDADEL=2, SCLH=0xC3, SCLL=0xB0
I2C1->TIMINGR = (0xB<<28) | (4<<20) | (2<<16) | (0xC3<<8) | (0xB0);
}else if(speed == HIGH_SPEED){ // 100kHz
I2C1->TIMINGR = (0xB<<28) | (4<<20) | (2<<16) | (0x12<<8) | (0x11);
}else{ // VERYLOW_SPEED - the lowest speed by STM register: 5.8kHz (presc = 16-1 = 15; )
I2C1->TIMINGR = (0xf<<28) | (4<<20) | (2<<16) | (0xff<<8) | (0xff);
}
I2C1->CR1 = I2C_CR1_PE;// | I2C_CR1_RXIE; // Enable I2C & (interrupt on receive - not supported yet)
}

View File

@ -0,0 +1,97 @@
/*
* geany_encoding=koi8-r
* hardware.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __HARDWARE_H__
#define __HARDWARE_H__
#include "stm32f0.h"
#define CAN_SPEED_DEFAULT (250)
#define CAN_SPEED_MIN (12)
#define CAN_SPEED_MAX (1000)
// LED0
#define LED0_port GPIOB
#define LED0_pin (1<<10)
// LED1
#define LED1_port GPIOB
#define LED1_pin (1<<11)
#ifndef USARTNUM
#define USARTNUM 2
#endif
#define CONCAT(a,b) a ## b
#define STR_HELPER(s) #s
#define STR(s) STR_HELPER(s)
#define FORMUSART(X) CONCAT(USART, X)
#define USARTX FORMUSART(USARTNUM)
#ifndef I2CPINS
#define I2CPINS 910
#endif
#ifndef LED1_port
#define LED1_port LED0_port
#endif
#ifndef LED1_pin
#define LED1_pin LED0_pin
#endif
#define LED_blink(x) pin_toggle(x ## _port, x ## _pin)
#define LED_on(x) pin_clear(x ## _port, x ## _pin)
#define LED_off(x) pin_set(x ## _port, x ## _pin)
// set active channel number
#define MUL_ADDRESS(x) do{GPIOB->BSRR = (0x7 << 16) | (x);}while(0)
// address from 0 to 7
// WARNING!!! In current case all variables for sensors counting are uint8_t, so if
// MUL_MAX_ADDRESS would be greater than 7 you need to edit all codes!!!11111111111111111111
#define MUL_MAX_ADDRESS (7)
// turn multiplexer on/off (PB12 -> 1/0)
#define MUL_ON() pin_clear(GPIOB, (1<<12))
#define MUL_OFF() pin_set(GPIOB, (1<<12))
// turn on/off power of sensors (PA8-> 1/0)
#define SENSORS_ON() pin_set(GPIOA, (1<<8))
#define SENSORS_OFF() pin_clear(GPIOA, (1<<8))
// check overcurrent (PB3 == 0)
#define SENSORS_OVERCURNT() ((1<<3) != (GPIOB->IDR & (1<<3)))
// CAN address - PA13..PA15
#define READ_CAN_INV_ADDR() (((GPIOA->IDR & (0x7<<13))>>13) | ((GPIOB->IDR & (1<<15)) >> 12))
extern uint8_t Controller_address;
typedef enum{
VERYLOW_SPEED,
LOW_SPEED,
HIGH_SPEED,
CURRENT_SPEED
} I2C_SPEED;
extern I2C_SPEED curI2Cspeed;
void gpio_setup(void);
void i2c_setup(I2C_SPEED speed);
#endif // __HARDWARE_H__

View File

@ -0,0 +1,129 @@
/*
* geany_encoding=koi8-r
* i2c.c
*
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "hardware.h"
#include "i2c.h"
/**
* I2C for TSYS01
* Speed <= 400kHz (200)
* t_SCLH > 21ns
* t_SCLL > 21ns
* while reading, sends NACK
* after reading get 24bits of T value, we need upper 2 bytes: ADC16 = ADC>>8
* T = (-2) * k4 * 10^{-21} * ADC16^4
* + 4 * k3 * 10^{-16} * ADC16^3
* + (-2) * k2 * 10^{-11} * ADC16^2
* + 1 * k1 * 10^{-6} * ADC16
* +(-1.5)* k0 * 10^{-2}
* All coefficiens are in registers:
* k4 - 0xA2, k3 - 0xA4, k2 - 0xA6, k1 - 0xA8, k0 - 0xAA
*/
extern volatile uint32_t Tms;
static uint32_t cntr;
/**
* write command byte to I2C
* @param addr - device address (TSYS01_ADDR0 or TSYS01_ADDR1)
* @param data - byte to write
* @return 0 if error
*/
uint8_t write_i2c(uint8_t addr, uint8_t data){
cntr = Tms;
I2C1->ICR = 0x3f38; // clear all errors
while(I2C1->ISR & I2C_ISR_BUSY){
IWDG->KR = IWDG_REFRESH;
if(Tms - cntr > I2C_TIMEOUT){
return 0; // check busy
}}
cntr = Tms;
while(I2C1->CR2 & I2C_CR2_START){
IWDG->KR = IWDG_REFRESH;
if(Tms - cntr > I2C_TIMEOUT){
return 0; // check start
}}
//I2C1->ICR = 0x3f38; // clear all errors
I2C1->CR2 = 1<<16 | addr | I2C_CR2_AUTOEND; // 1 byte, autoend
// now start transfer
I2C1->CR2 |= I2C_CR2_START;
cntr = Tms;
while(!(I2C1->ISR & I2C_ISR_TXIS)){ // ready to transmit
IWDG->KR = IWDG_REFRESH;
if(I2C1->ISR & I2C_ISR_NACKF){
I2C1->ICR |= I2C_ICR_NACKCF;
return 0;
}
if(Tms - cntr > I2C_TIMEOUT){
return 0;
}
}
I2C1->TXDR = data; // send data
// wait for data gone
while(I2C1->ISR & I2C_ISR_BUSY){
IWDG->KR = IWDG_REFRESH;
if(Tms - cntr > I2C_TIMEOUT){break;}
}
return 1;
}
/**
* read nbytes (2 or 3) of data from I2C line
* @return 1 if all OK, 0 if NACK or no device found
*/
uint8_t read_i2c(uint8_t addr, uint32_t *data, uint8_t nbytes){
uint32_t result = 0;
cntr = Tms;
//MSG("read_i2c\n");
while(I2C1->ISR & I2C_ISR_BUSY){
IWDG->KR = IWDG_REFRESH;
if(Tms - cntr > I2C_TIMEOUT){
return 0; // check busy
}}
cntr = Tms;
while(I2C1->CR2 & I2C_CR2_START){
IWDG->KR = IWDG_REFRESH;
if(Tms - cntr > I2C_TIMEOUT){
return 0; // check start
}}
// I2C1->ICR = 0x3f38; // clear all errors
// read N bytes
I2C1->CR2 = (nbytes<<16) | addr | 1 | I2C_CR2_AUTOEND | I2C_CR2_RD_WRN;
I2C1->CR2 |= I2C_CR2_START;
uint8_t i;
cntr = Tms;
for(i = 0; i < nbytes; ++i){
while(!(I2C1->ISR & I2C_ISR_RXNE)){ // wait for data
IWDG->KR = IWDG_REFRESH;
if(I2C1->ISR & I2C_ISR_NACKF){
I2C1->ICR |= I2C_ICR_NACKCF;
return 0;
}
if(Tms - cntr > I2C_TIMEOUT){
return 0;
}
}
result = (result << 8) | I2C1->RXDR;
}
*data = result;
return 1;
}

View File

@ -0,0 +1,41 @@
/*
* geany_encoding=koi8-r
* i2c.h
*
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "stm32f0.h"
// timeout of I2C bus in ms
#define I2C_TIMEOUT (100)
// CSB=1, address 1110110
#define TSYS01_ADDR0 (0x76 << 1)
// CSB=0, address 1110111
#define TSYS01_ADDR1 (0x77 << 1)
// registers: reset, read ADC value, start converstion, start of PROM
#define TSYS01_RESET (0x1E)
#define TSYS01_ADC_READ (0x00)
#define TSYS01_START_CONV (0x48)
#define TSYS01_PROM_ADDR0 (0xA0)
// conversion time (with reserve)
#define CONV_TIME (15)
uint8_t read_i2c(uint8_t addr, uint32_t *data, uint8_t nbytes);
uint8_t write_i2c(uint8_t addr, uint8_t data);

View File

@ -0,0 +1,12 @@
/* Linker script for STM32F042x6, 32K flash, 6K RAM. */
/* Define memory regions. */
MEMORY
{
rom (rx) : ORIGIN = 0x08000000, LENGTH = 32K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 6K
}
/* Include the common ld script. */
INCLUDE stm32f0.ld

View File

@ -0,0 +1,135 @@
/*
* main.c
*
* Copyright 2017 Edward V. Emelianoff <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "adc.h"
#include "can.h"
#include "can_process.h"
#include "hardware.h"
#include "i2c.h"
#include "proto.h"
#include "sensors_manage.h"
#include "usart.h"
#include "usb.h"
#pragma message("USARTNUM=" STR(USARTNUM))
#pragma message("I2CPINS=" STR(I2CPINS))
#ifdef EBUG
#pragma message("Debug mode")
#else
#pragma message("Release mode")
#endif
volatile uint32_t Tms = 0;
volatile uint8_t canerror = 0;
/* Called when systick fires */
void sys_tick_handler(void){
++Tms;
}
static void iwdg_setup(){
/* Enable the peripheral clock RTC */
/* (1) Enable the LSI (40kHz) */
/* (2) Wait while it is not ready */
RCC->CSR |= RCC_CSR_LSION; /* (1) */
while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY); /* (2) */
/* Configure IWDG */
/* (1) Activate IWDG (not needed if done in option bytes) */
/* (2) Enable write access to IWDG registers */
/* (3) Set prescaler by 64 (1.6ms for each tick) */
/* (4) Set reload value to have a rollover each 2s */
/* (5) Check if flags are reset */
/* (6) Refresh counter */
IWDG->KR = IWDG_START; /* (1) */
IWDG->KR = IWDG_WRITE_ACCESS; /* (2) */
IWDG->PR = IWDG_PR_PR_1; /* (3) */
IWDG->RLR = 1250; /* (4) */
while(IWDG->SR); /* (5) */
IWDG->KR = IWDG_REFRESH; /* (6) */
}
int main(void){
uint32_t lastT = 0, lastS = 0, lastB = 0;
uint8_t gotmeasurement = 0;
char inbuf[256];
sysreset();
SysTick_Config(6000, 1);
gpio_setup();
adc_setup();
usart_setup();
i2c_setup(LOW_SPEED);
readCANID();
if(CANID == MASTER_ID) cansniffer = 1; // MASTER in sniffer mode by default
CAN_setup(0); // setup with default 250kbaud
RCC->CSR |= RCC_CSR_RMVF; // remove reset flags
USB_setup();
sensors_init();
iwdg_setup();
while (1){
IWDG->KR = IWDG_REFRESH; // refresh watchdog
if(lastT > Tms || Tms - lastT > 499){
if(!noLED) LED_blink(LED0);
lastT = Tms;
// send dummy command to noone to test CAN bus
//can_send_cmd(NOONE_ID, CMD_DUMMY0);
}
if(lastS != Tms){ // run sensors proc. once per 1ms
sensors_process();
lastS = Tms;
if(SENS_SLEEPING == Sstate){ // show temperature @ each sleeping occurence
if(!gotmeasurement){
gotmeasurement = 1;
showtemperature();
}
}else{
if(SENS_WAITING == Sstate) gotmeasurement = 0;
}
}
usb_proc();
can_proc();
CAN_status stat = CAN_get_status();
if(stat == CAN_FIFO_OVERRUN){
SEND("CAN bus fifo overrun occured!\n");
}else if(stat == CAN_ERROR){
if(!noLED) LED_off(LED1);
CAN_setup(0);
canerror = 1;
}
can_messages_proc();
IWDG->KR = IWDG_REFRESH;
uint8_t r = 0;
if((r = USB_receive(inbuf, 255))){
inbuf[r] = 0;
cmd_parser(inbuf, 1);
}
if(usartrx()){ // usart1 received data, store it in buffer
char *txt = NULL;
r = usart_getline(&txt);
txt[r] = 0;
cmd_parser(txt, 0);
}
if(lastB - Tms > 99){ // run `sendbuf` each 100ms
sendbuf();
lastB = Tms;
}
}
return 0;
}

View File

@ -0,0 +1,527 @@
/*
* geany_encoding=koi8-r
* proto.c
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <string.h>
#include "adc.h"
#include "can.h"
#include "can_process.h"
#include "hardware.h"
#include "proto.h"
#include "sensors_manage.h"
#include "usart.h"
#include "usb.h"
#include "version.inc"
extern volatile uint8_t canerror;
extern volatile uint32_t Tms;
static char buff[UARTBUFSZ+1], /* +1 - for USB send (it receive \0-terminated line) */ *bptr = buff;
static int blen = 0, USBcmd = 0, debugmode = 0;
// LEDs are OFF by default
uint8_t noLED =
#ifdef EBUG
0
#else
1
#endif
;
void sendbuf(){
IWDG->KR = IWDG_REFRESH;
if(blen == 0) return;
*bptr = 0;
if(USBcmd) USB_send(buff);
else while(LINE_BUSY == usart_send(buff, blen)){IWDG->KR = IWDG_REFRESH;}
bptr = buff;
blen = 0;
}
void addtobuf(const char *txt){
IWDG->KR = IWDG_REFRESH;
int l = strlen(txt);
if(l > UARTBUFSZ){
sendbuf(); // send prevoius data in buffer
if(USBcmd) USB_send(txt);
else while(LINE_BUSY == usart_send_blocking(txt, l)){IWDG->KR = IWDG_REFRESH;}
}else{
if(blen+l > UARTBUFSZ){
sendbuf();
}
strcpy(bptr, txt);
bptr += l;
}
*bptr = 0;
blen += l;
}
void bufputchar(char ch){
if(blen > UARTBUFSZ-1){
sendbuf();
}
*bptr++ = ch;
++blen;
}
static void CANsend(uint16_t targetID, uint8_t cmd, char echo){
if(CAN_OK == can_send_cmd(targetID, cmd)){
bufputchar(echo);
bufputchar('\n');
}
}
// show all ADC values
static inline void showADCvals(){
char msg[] = "ADCn=";
for(int n = 0; n < NUMBER_OF_ADC_CHANNELS; ++n){
msg[3] = n + '0';
addtobuf(msg);
printu(getADCval(n));
newline();
}
}
static inline void printmcut(){
SEND("MCUT=");
int32_t T = getMCUtemp();
if(T < 0){
bufputchar('-');
T = -T;
}
printu(T);
newline();
}
static inline void showUIvals(){
uint16_t *vals = getUval();
SEND("V12="); printu(vals[0]);
SEND("\nV5="); printu(vals[1]);
SEND("\nV33="); printu(vals[3]);
SEND("\nI12="); printu(vals[2]);
newline();
}
static char *omit_spaces(char *buf){
while(*buf){
if(*buf > ' ') break;
++buf;
}
return buf;
}
static inline void setCANbrate(char *str){
if(!str || !*str) return;
int32_t spd = 0;
str = omit_spaces(str);
char *e = getnum(str, &spd);
if(e == str){
SEND("BAUDRATE=");
printu(curcanspeed);
newline();
return;
}
if(spd < CAN_SPEED_MIN || spd > CAN_SPEED_MAX){
SEND("Wrong speed\n");
return;
}
CAN_setup(spd);
SEND("OK\n");
}
// parse `txt` to CAN_message
static CAN_message *parseCANmsg(char *txt){
static CAN_message canmsg;
int32_t N;
char *n;
int ctr = -1;
canmsg.ID = 0xffff;
do{
txt = omit_spaces(txt);
n = getnum(txt, &N);
if(txt == n) break;
txt = n;
if(ctr == -1){
if(N > 0x7ff){
SEND("ID should be 11-bit number!\n");
return NULL;
}
canmsg.ID = (uint16_t)(N&0x7ff);
ctr = 0;
continue;
}
if(ctr > 7){
SEND("ONLY 8 data bytes allowed!\n");
return NULL;
}
if(N > 0xff){
SEND("Every data portion is a byte!\n");
return NULL;
}
canmsg.data[ctr++] = (uint8_t)(N&0xff);
}while(1);
if(canmsg.ID == 0xffff){
SEND("NO ID given, send nothing!\n");
return NULL;
}
SEND("Message parsed OK\n");
canmsg.length = (uint8_t) ctr;
return &canmsg;
}
// send command, format: ID (hex/bin/dec) data bytes (up to 8 bytes, space-delimeted)
static void sendCANcommand(char *txt){
CAN_message *msg = parseCANmsg(txt);
if(!msg) return;
uint32_t N = 1000;
while(CAN_BUSY == can_send(msg->data, msg->length, msg->ID)){
if(--N == 0) break;
}
}
/**
* @brief cmd_parser - command parsing
* @param txt - buffer with commands & data
* @param isUSB - == 1 if data got from USB
*/
void cmd_parser(char *txt, uint8_t isUSB){
USBcmd = isUSB;
int16_t L = strlen(txt), ID = BCAST_ID;
char _1st = txt[0];
if(_1st >= '0' && _1st < '8'){ // send command to Nth controller, not broadcast
if(L == 3){ // with '\n' at end!
ID = (CAN_ID_PREFIX & CAN_ID_MASK) | (_1st - '0');
_1st = txt[1];
}else{
_1st = '?'; // show help
}
}
switch(_1st){
case '@':
debugmode = !debugmode;
SEND("DEBUG mode ");
if(debugmode) SEND("ON");
else SEND("OFF");
newline();
break;
case 'a':
showADCvals();
break;
case 'B':
CANsend(ID, CMD_DUMMY0, _1st);
break;
case 'b':
setCANbrate(txt + 1);
break;
case 'c':
showcoeffs();
break;
case 'D':
CANsend(MASTER_ID, CMD_DUMMY1, _1st);
break;
case 'd':
SEND("Can address: ");
printuhex(CANID);
newline();
break;
case 'E':
CANsend(ID, CMD_STOP_SCAN, _1st);
break;
case 'e':
sensors_scan_mode = 0;
break;
case 'F':
CANsend(ID, CMD_SENSORS_OFF, _1st);
break;
case 'f':
sensors_off();
break;
case 'g':
SEND("Group ID (sniffer) CAN mode\n");
CAN_listenall();
break;
case 'H':
CANsend(ID, CMD_HIGH_SPEED, _1st);
break;
case 'h':
i2c_setup(HIGH_SPEED);
break;
case 'I':
CANsend(ID, CMD_REINIT_SENSORS, _1st);
break;
case 'i':
sensors_init();
break;
case 'J':
CANsend(ID, CMD_GETMCUTEMP, _1st);
break;
case 'j':
printmcut();
break;
case 'K':
CANsend(ID, CMD_GETUIVAL, _1st);
break;
case 'k':
showUIvals();
break;
case 'L':
CANsend(ID, CMD_LOW_SPEED, _1st);
break;
case 'l':
i2c_setup(LOW_SPEED);
break;
case 'M':
CANsend(ID, CMD_CHANGE_MASTER_B, _1st);
break;
case 'm':
CANsend(ID, CMD_CHANGE_MASTER, _1st);
break;
case 'N':
CANsend(ID, CMD_GETBUILDNO, _1st);
break;
case 'O':
noLED = 0;
SEND("LED on\n");
break;
case 'o':
noLED = 1;
LED_off(LED0);
LED_off(LED1);
SEND("LED off\n");
break;
case 'P':
CANsend(ID, CMD_PING, _1st);
break;
case 'Q':
CANsend(ID, CMD_SYSTIME, _1st);
break;
case 'q':
SEND("SYSTIME0="); printu(Tms); newline();
break;
case 'R':
CANsend(ID, CMD_REINIT_I2C, _1st);
break;
case 'r':
i2c_setup(CURRENT_SPEED);
break;
case 's':
sendCANcommand(txt+1);
break;
case 'T':
CANsend(ID, CMD_START_MEASUREMENT, _1st);
break;
case 't':
if(!sensors_scan_mode) sensors_start();
break;
case 'u':
SEND("Unique ID CAN mode\n");
CAN_listenone();
break;
case 'V':
CANsend(ID, CMD_LOWEST_SPEED, _1st);
break;
case 'v':
i2c_setup(VERYLOW_SPEED);
break;
case 'X':
CANsend(ID, CMD_START_SCAN, _1st);
break;
case 'x':
sensors_scan_mode = 1;
break;
case 'Y':
CANsend(ID, CMD_SENSORS_STATE, _1st);
break;
case 'y':
SEND("SSTATE0=");
SEND(sensors_get_statename(Sstate));
SEND("\nNSENS0=");
printu(Nsens_present);
SEND("\nSENSPRESENT0=");
printu(sens_present[0] | (sens_present[1]<<8));
SEND("\nNTEMP0=");
printu(Ntemp_measured);
newline();
break;
case 'z':
SEND("CANERROR=");
if(canerror){
canerror = 0;
bufputchar('1');
}else bufputchar('0');
newline();
break;
default: // help
SEND("https://github.com/eddyem/tsys01/tree/master/STM32/TSYS_controller build#" BUILD_NUMBER " @ " BUILD_DATE "\n");
SEND(
"ALL little letters - without CAN messaging\n"
"0..7 - send command to given controller (0 - this) instead of broadcast\n"
"@ - set/reset debug mode\n"
"a - get raw ADC values\n"
"B - send broadcast CAN dummy message\n"
"b - get/set CAN bus baudrate\n"
"c - show coefficients (current)\n"
"d - get last CAN address\n"
"D - send CAN dummy message to master\n"
"Ee- end themperature scan\n"
"Ff- turn oFf sensors\n"
"g - group (sniffer) CAN mode\n"
"Hh- high I2C speed\n"
"Ii- (re)init sensors\n"
"Jj- get MCU temperature\n"
"Kk- get U/I values\n"
"Ll- low I2C speed\n"
"Mm- change master id to 0 (m) / broadcast (M)\n"
"N - get build number\n"
"Oo- turn onboard diagnostic LEDs *O*n or *o*ff (both commands are local)\n"
"P - ping everyone over CAN\n"
"Qq- get system time\n"
"Rr- reinit I2C\n"
"s - send CAN message\n"
"Tt- start temperature measurement\n"
"u - unique ID (default) CAN mode\n"
"Vv- very low I2C speed\n"
"Xx- Start themperature scan\n"
"Yy- get sensors state\n"
"z - check CAN status for errors\n"
);
break;
}
}
// print 32bit unsigned int
void printu(uint32_t val){
char buf[11], *bufptr = &buf[10];
*bufptr = 0;
if(!val){
*(--bufptr) = '0';
}else{
while(val){
register uint32_t o = val;
val /= 10;
*(--bufptr) = (o - 10*val) + '0';
}
}
addtobuf(bufptr);
}
// print 32bit unsigned int as hex
void printuhex(uint32_t val){
addtobuf("0x");
uint8_t *ptr = (uint8_t*)&val + 3;
int i, j, z = 1;
for(i = 0; i < 4; ++i, --ptr){
if(*ptr == 0){ // omit leading zeros
if(i == 3) z = 0;
if(z) continue;
}
else z = 0;
for(j = 1; j > -1; --j){
uint8_t half = (*ptr >> (4*j)) & 0x0f;
if(half < 10) bufputchar(half + '0');
else bufputchar(half - 10 + 'a');
}
}
}
// THERE'S NO OVERFLOW PROTECTION IN NUMBER READ PROCEDURES!
// read decimal number
static char *getdec(const char *buf, int32_t *N){
int32_t num = 0;
int positive = TRUE;
if(*buf == '-'){
positive = FALSE;
++buf;
}
while(*buf){
char c = *buf;
if(c < '0' || c > '9'){
break;
}
num *= 10;
num += c - '0';
++buf;
}
*N = (positive) ? num : -num;
return (char *)buf;
}
// read hexadecimal number (without 0x prefix!)
static char *gethex(const char *buf, int32_t *N){
uint32_t num = 0;
while(*buf){
char c = *buf;
uint8_t M = 0;
if(c >= '0' && c <= '9'){
M = '0';
}else if(c >= 'A' && c <= 'F'){
M = 'A' - 10;
}else if(c >= 'a' && c <= 'f'){
M = 'a' - 10;
}
if(M){
num <<= 4;
num += c - M;
}else{
break;
}
++buf;
}
*N = (int32_t)num;
return (char *)buf;
}
// read binary number (without 0b prefix!)
static char *getbin(const char *buf, int32_t *N){
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '1'){
break;
}
num <<= 1;
if(c == '1') num |= 1;
++buf;
}
*N = (int32_t)num;
return (char *)buf;
}
/**
* @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111)
* @param buf - buffer with number and so on
* @param N - the number read
* @return pointer to first non-number symbol in buf (if it is == buf, there's no number)
*/
char *getnum(char *txt, int32_t *N){
if(*txt == '0'){
if(txt[1] == 'x' || txt[1] == 'X') return gethex(txt+2, N);
if(txt[1] == 'b' || txt[1] == 'B') return getbin(txt+2, N);
}
return getdec(txt, N);
}
// show message in debug mode
void mesg(char *txt){
if(!debugmode) return;
addtobuf("[DBG] ");
addtobuf(txt);
bufputchar('\n');
}

View File

@ -0,0 +1,51 @@
/*
* geany_encoding=koi8-r
* proto.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __PROTO_H__
#define __PROTO_H__
#include "stm32f0.h"
#include "hardware.h"
// macro for static strings
#define SEND(str) do{addtobuf(str);}while(0)
#ifdef EBUG
#define MSG(str) do{addtobuf(__FILE__ " (L" STR(__LINE__) "): " str);}while(0)
#else
#define MSG(str)
#endif
#define newline() do{bufputchar('\n');}while(0)
extern uint8_t noLED;
void cmd_parser(char *buf, uint8_t isUSB);
void addtobuf(const char *txt);
void bufputchar(char ch);
void printu(uint32_t val);
void printuhex(uint32_t val);
void sendbuf();
char *getnum(char *txt, int32_t *N);
void mesg(char *txt);
#endif // __PROTO_H__

View File

@ -0,0 +1,442 @@
/*
* geany_encoding=koi8-r
* sensors_manage.c
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "sensors_manage.h"
#include "can_process.h"
#include "i2c.h"
#include "proto.h" // addtobuf, bufputchar, memcpy
extern volatile uint32_t Tms;
uint8_t sensors_scan_mode = 0; // infinite scan mode
static uint32_t lastSensT = 0;
SensorsState Sstate = SENS_OFF; // turn on sensors only by request
static uint8_t curr_mul_addr = 0; // current sensors pair address @ multiplexer
static uint8_t overcurnt_ctr = 0; // if this counter > 32 go to OFF state
uint8_t sens_present[2] = {0,0}; // bit flag: Nth bit == 1 if sensor[s] on given channel found
uint8_t Nsens_present = 0; // total amount of sensors found
uint8_t Ntemp_measured = 0; // total amount of themperatures measured
// 8 - amount of pairs, 2 - amount in pair, 5 - amount of Coef.
static uint16_t coefficients[MUL_MAX_ADDRESS+1][2][5]; // Coefficients for given sensors
// measured temperatures * 100
int16_t Temperatures[MUL_MAX_ADDRESS+1][2];
// pair addresses
static const uint8_t Taddr[2] = {TSYS01_ADDR0, TSYS01_ADDR1};
static const char *statenames[] = {
[SENS_INITING] = "init"
,[SENS_RESETING] = "reset"
,[SENS_GET_COEFFS] = "getcoeff"
,[SENS_SLEEPING] = "sleep"
,[SENS_START_MSRMNT] = "startmeasure"
,[SENS_WAITING] = "waitresults"
,[SENS_GATHERING] = "collectdata"
,[SENS_OFF] = "off"
,[SENS_OVERCURNT] = "overcurrent"
,[SENS_OVERCURNT_OFF] = "offbyovercurrent"
};
static uint8_t getcoeff(uint8_t i);
const char *sensors_get_statename(SensorsState x){
if(x >= SENS_STATE_CNT) return "wrongstate";
return statenames[x];
}
// TODO: check if we can convert double to float!
const double mul[5] = {-1.5e-2, 1., -2., 4., -2.};
/**
* Get temperature & calculate it by polinome
* T = (-2) * k4 * 10^{-21} * ADC16^4
* + 4 * k3 * 10^{-16} * ADC16^3
* + (-2) * k2 * 10^{-11} * ADC16^2
* + 1 * k1 * 10^{-6} * ADC16
* +(-1.5)* k0 * 10^{-2}
* k0*(-1.5e-2) + 1e-6*val*(k1 + 1e-5*val*(-2*k2 + 1e-5*val*(4*k3 + -2e-5*k4*val)))
*
* @param t - value from sensor
* @param i - number of sensor in pair
* @return -30000 if something wrong or T*100 if all OK
*/
static uint16_t calc_t(uint32_t t, int i){
uint16_t *coeff = coefficients[curr_mul_addr][i];
if(coeff[0] == 0){
if(!getcoeff(i)) return BAD_TEMPERATURE; // what is with coeffs?
}
if(t < 600000 || t > 30000000) return BAD_TEMPERATURE; // wrong value - too small or too large
int j;
double d = (double)t/256., tmp = 0.;
// k0*(-1.5e-2) + 0.1*1e-5*val*(1*k1 + 1e-5*val*(-2.*k2 + 1e-5*val*(4*k3 + 1e-5*val*(-2*k4))))
for(j = 4; j > 0; --j){
tmp += mul[j] * (double)coeff[j];
tmp *= 1e-5*d;
}
tmp = tmp * 10. + 100. * mul[0] * coeff[0];
return (uint16_t)tmp;
}
// turn off sensors' power
void sensors_off(){
mesg("Turn off sensors");
MUL_OFF(); // turn off multiplexers
SENSORS_OFF(); // turn off sensors' power
Sstate = SENS_OFF;
}
/**
* if all OK with current, turn ON sensors' power
*/
static int sensors_on(){
mesg("Turn on sensors");
curr_mul_addr = 0;
MUL_OFF();
if(SENSORS_OVERCURNT()){
mesg("OVERCURRENT!");
SENSORS_OFF();
Sstate = (++overcurnt_ctr > 32) ? SENS_OVERCURNT_OFF : SENS_OVERCURNT;
return FALSE;
}else{
mesg("Powered on");
SENSORS_ON();
return TRUE;
}
}
// init sensors
void sensors_init(){
sens_present[0] = sens_present[1] = 0;
overcurnt_ctr = 0;
Nsens_present = 0;
if(sensors_on()) Sstate = SENS_INITING;
}
/**
* start measurement if sensors are sleeping,
* turn ON if they were OFF
* do nothing if measurement processing
*/
void sensors_start(){
if(sensors_scan_mode) return;
switch(Sstate){
case SENS_SLEEPING:
Sstate = SENS_START_MSRMNT;
break;
case SENS_OFF:
overcurnt_ctr = 0;
if(sensors_on()) Sstate = SENS_START_MSRMNT;
break;
case SENS_OVERCURNT_OFF:
sensors_init();
break;
default:
break;
}
}
// count 1 bits in sens_present & set `Nsens_present` to this value
static void count_sensors(){
Nsens_present = 0;
uint16_t B = sens_present[0]<<8 | sens_present[1];
while(B){
++Nsens_present;
B &= (B - 1);
}
/*
SEND("count_sensors(): ");
printu(Nsens_present);
newline();
*/
}
/**
* All procedures return TRUE if all OK or FALSE if failed and need to start scan again
*/
// procedure call each time @ resetting
static uint8_t resetproc(){
uint8_t i;
for(i = 0; i < 2; ++i){
if(write_i2c(Taddr[i], TSYS01_RESET)){
sens_present[i] |= 1<<curr_mul_addr; // set bit - found
}else{ // not found
sens_present[i] &= ~(1<<curr_mul_addr); // reset bit - not found
}
}
return TRUE;
}
static uint8_t getcoeff(uint8_t i){
const uint8_t regs[5] = {0xAA, 0xA8, 0xA6, 0xA4, 0xA2}; // commands for coefficients
uint8_t err = 5;
uint16_t *coef = coefficients[curr_mul_addr][i];
for(uint8_t j = 0; j < 5; ++j){
uint32_t K;
if(write_i2c(Taddr[i], regs[j])){
if(read_i2c(Taddr[i], &K, 2)){
coef[j] = K;
--err;
}else break;
}else break;
}
if(err){ // restart all procedures if we can't get coeffs of present sensor
return FALSE;
}
return TRUE;
}
// procedure call each time @ getting coefficients
static uint8_t getcoefsproc(){
uint8_t r = TRUE;
for(uint8_t i = 0; i < 2; ++i){
if(!(sens_present[i] & (1<<curr_mul_addr))) continue; // no sensors @ given line
if(!getcoeff(i)) r = FALSE;
}
return r;
}
// procedure call each time @ start measurement
static uint8_t msrtempproc(){
uint8_t i, j;
for(i = 0; i < 2; ++i){
if(!(sens_present[i] & (1<<curr_mul_addr))){ // no sensors @ given line - try to find it
if(write_i2c(Taddr[i], TSYS01_RESET)){
sens_present[i] |= 1<<curr_mul_addr;
mesg("One sensor added");
count_sensors();
// if(getcoeff(i)) count_sensors();
}
continue;
}
for(j = 0; j < 5; ++j){
if(write_i2c(Taddr[i], TSYS01_START_CONV)) break;
//if(!write_i2c(Taddr[i], TSYS01_RESET)) i2c_setup(CURRENT_SPEED); // maybe I2C restart will solve the problem?
}
if(j == 5){
sens_present[i] &= ~(1<<curr_mul_addr); // clear presence flag
mesg("One sensor removed");
count_sensors();
}
}
return TRUE;
}
// procedure call each time @ read temp
static uint8_t gettempproc(){
uint8_t i;
for(i = 0; i < 2; ++i){
if(!(sens_present[i] & (1<<curr_mul_addr))) continue; // no sensors @ given line
Temperatures[curr_mul_addr][i] = NO_SENSOR;
uint8_t err = 1;
if(write_i2c(Taddr[i], TSYS01_ADC_READ)){
uint32_t t;
if(read_i2c(Taddr[i], &t, 3) && t){
if(BAD_TEMPERATURE != (Temperatures[curr_mul_addr][i] = calc_t(t, i))){
err = 0;
++Ntemp_measured;
}
}
}
if(err){
write_i2c(Taddr[i], TSYS01_RESET);
}
}
return TRUE;
}
/**
* 2 phase for each call: 1) set address & turn on mul.; 2) call function & turn off mul.
* Change address @ call function `procfn`
* @return TRUE if scan is over
*/
static uint8_t sensors_scan(uint8_t (* procfn)()){
static uint8_t callctr = 0;
if(callctr == 0){ // set address & turn on multiplexer
MUL_ADDRESS(curr_mul_addr);
MUL_ON();
callctr = 1;
}else{ // call procfn & turn off multiplexer
callctr = 0;
uint8_t s = procfn();
MUL_OFF();
if(!s){ // start scan again if error
curr_mul_addr = 0;
return FALSE;
}
if(++curr_mul_addr > MUL_MAX_ADDRESS){ // scan is over
curr_mul_addr = 0;
return TRUE;
}
}
return FALSE;
}
// print coefficients @debug console
void showcoeffs(){
int a, p, k;
if(Nsens_present == 0){
SEND("showcoeffs(): no sensors found\n");
return;
}
for(a = 0; a <= MUL_MAX_ADDRESS; ++a){
for(p = 0; p < 2; ++p){
if(!(sens_present[p] & (1<<a))) continue; // no sensor
for(k = 0; k < 5; ++k){
char b[] = {'K', a+'0', p+'0', '_', k+'0', '=', 0};
addtobuf(b);
printu(coefficients[a][p][k]);
newline();
}
}
}
}
// print temperatures @debug console
void showtemperature(){
int a, p;
if(Nsens_present == 0){
SEND("showtemperature(): no sensors found");
return;
}
if(Ntemp_measured == 0){
SEND("showtemperature(): no temperatures measured");
return;
}
for(a = 0; a <= MUL_MAX_ADDRESS; ++a){
for(p = 0; p < 2; ++p){
if(!(sens_present[p] & (1<<a))){
continue; // no sensor
}
bufputchar('T');
bufputchar('0' + Controller_address);
bufputchar('_');
printu(a*10+p);
bufputchar('=');
int16_t t = Temperatures[a][p];
if(t < 0){
t = -t;
bufputchar('-');
}
printu(t);
newline();
}
}
}
// finite state machine for sensors switching & checking
void sensors_process(){
static int8_t NsentOverCAN = -1; // number of T (N*10+p) sent over CAN bus; -1 - nothing to send
if(SENSORS_OVERCURNT()){
MUL_OFF();
SENSORS_OFF();
Sstate = (++overcurnt_ctr > 32) ? SENS_OVERCURNT_OFF : SENS_OVERCURNT;
return;
}
switch(Sstate){
case SENS_INITING: // initialisation (restart I2C)
mesg("SENS_INITING");
i2c_setup(CURRENT_SPEED);
Sstate = SENS_RESETING;
lastSensT = Tms;
NsentOverCAN = -1;
break;
case SENS_RESETING: // reset & discovery procedure
if(NsentOverCAN == -1){
mesg("SENS_RESETING");
NsentOverCAN = 0;
}
if(Tms - lastSensT > POWERUP_TIME){
if(sensors_scan(resetproc)){
count_sensors(); // get total amount of sensors
if(Nsens_present){
Sstate = SENS_GET_COEFFS;
}else{ // no sensors found
mesg("No sensors found -> off");
sensors_off();
}
}
}
break;
case SENS_GET_COEFFS: // get coefficients
mesg("SENS_GET_COEFFS");
if(sensors_scan(getcoefsproc)){
Sstate = SENS_SLEEPING; // sleep after got coefficients
}
break;
case SENS_START_MSRMNT: // send all sensors command to start measurements
mesg("SENS_START_MSRMNT");
if(sensors_scan(msrtempproc)){
lastSensT = Tms;
Sstate = SENS_WAITING;
Ntemp_measured = 0; // reset value of good measurements
}
break;
case SENS_WAITING: // wait for end of conversion
mesg("SENS_WAITING");
if(Tms - lastSensT > CONV_TIME){
NsentOverCAN = -1;
Sstate = SENS_GATHERING;
}
break;
case SENS_GATHERING: // scan all sensors, get thermal data & calculate temperature
if(NsentOverCAN < 0){
mesg("SENS_SLEEPING");
NsentOverCAN = 0;
}
if(sensors_scan(gettempproc)){
lastSensT = Tms;
NsentOverCAN = 0;
Sstate = SENS_SENDING_DATA;
}
break;
case SENS_SENDING_DATA:
mesg("SENS_SENDING_DATA");
NsentOverCAN = send_temperatures(NsentOverCAN); // call sending T process
if(NsentOverCAN < 0){ // all data sent -> sleep
Sstate = SENS_SLEEPING;
/*
if(Nsens_present != Ntemp_measured){ // restart sensors only after measurements sent
mesg("restart");
i2c_setup(CURRENT_SPEED);
sensors_on();
}*/
}
break;
case SENS_SLEEPING: // wait for `SLEEP_TIME` till next measurements in scan mode
if(NsentOverCAN < 0){
mesg("SENS_SLEEPING");
NsentOverCAN = 0;
}
if(sensors_scan_mode){ // sleep until next measurement start
if(Tms - lastSensT > SLEEP_TIME){
Sstate = SENS_START_MSRMNT;
}
}
break;
case SENS_OVERCURNT: // try to reinit all after overcurrent
mesg("SENS_OVERCURNT");
if(sensors_on()) Sstate = SENS_SLEEPING;
break;
default: // do nothing
break;
}
}

View File

@ -0,0 +1,69 @@
/*
* geany_encoding=koi8-r
* sensors_manage.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __SENSORS_MANAGE_H__
#define __SENSORS_MANAGE_H__
#include "hardware.h"
// time for power up procedure (500ms)
#define POWERUP_TIME (500)
// time between readings in scan mode (15sec)
#define SLEEP_TIME (15000)
// error in measurement == -300degrC
#define BAD_TEMPERATURE (-30000)
// no sensor on given channel
#define NO_SENSOR (-31000)
typedef enum{
SENS_INITING // 0 power on
,SENS_RESETING // 1 discovery sensors resetting them
,SENS_GET_COEFFS // 2 get coefficients from all sensors
,SENS_SLEEPING // 3 wait for a time to process measurements
,SENS_START_MSRMNT // 4 send command 2 start measurement
,SENS_WAITING // 5 wait for measurements end
,SENS_GATHERING // 6 collect information
,SENS_OFF // 7 sensors' power is off by external command
,SENS_OVERCURNT // 8 overcurrent detected @ any stage
,SENS_OVERCURNT_OFF // 9 sensors' power is off due to continuous overcurrent
,SENS_SENDING_DATA // A send data over CAN bus
,SENS_STATE_CNT
} SensorsState;
extern uint8_t sensors_scan_mode;
extern int16_t Temperatures[MUL_MAX_ADDRESS+1][2];
extern uint8_t sens_present[2];
extern SensorsState Sstate;
extern uint8_t Nsens_present;
extern uint8_t Ntemp_measured;
const char *sensors_get_statename(SensorsState x);
void sensors_process();
void sensors_off();
void sensors_init();
void sensors_start();
void showcoeffs();
void showtemperature();
#endif // __SENSORS_MANAGE_H__

Binary file not shown.

View File

@ -0,0 +1,216 @@
/*
* usart.c
*
* Copyright 2017 Edward V. Emelianoff <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <string.h>
#include "stm32f0.h"
#include "hardware.h"
#include "usart.h"
extern volatile uint32_t Tms;
static int datalen[2] = {0,0}; // received data line length (including '\n')
volatile int linerdy = 0, // received data ready
dlen = 0, // length of data (including '\n') in current buffer
bufovr = 0, // input buffer overfull
txrdy = 1 // transmission done
;
int rbufno = 0; // current rbuf number
static char rbuf[UARTINBUFSZ][2], tbuf[UARTBUFSZ]; // receive & transmit buffers
static char *recvdata = NULL;
/**
* return length of received data (without trailing zero
*/
int usart_getline(char **line){
if(bufovr){
bufovr = 0;
linerdy = 0;
return 0;
}
*line = recvdata;
linerdy = 0;
return dlen;
}
TXstatus usart_send(const char *str, int len){
if(!txrdy) return LINE_BUSY;
if(len > UARTBUFSZ) return STR_TOO_LONG;
txrdy = 0;
memcpy(tbuf, str, len);
#if USARTNUM == 2
DMA1_Channel4->CCR &= ~DMA_CCR_EN;
DMA1_Channel4->CNDTR = len;
DMA1_Channel4->CCR |= DMA_CCR_EN; // start transmission
#elif USARTNUM == 1
DMA1_Channel2->CCR &= ~DMA_CCR_EN;
DMA1_Channel2->CNDTR = len;
DMA1_Channel2->CCR |= DMA_CCR_EN;
#else
#error "Not implemented"
#endif
return ALL_OK;
}
TXstatus usart_send_blocking(const char *str, int len){
if(!txrdy) return LINE_BUSY;
int i;
bufovr = 0;
for(i = 0; i < len; ++i){
USARTX -> TDR = *str++;
while(!(USARTX->ISR & USART_ISR_TXE)){IWDG->KR = IWDG_REFRESH;};
}
return ALL_OK;
}
void usart_send_blck(const char *str){
while(!txrdy){IWDG->KR = IWDG_REFRESH;}
bufovr = 0;
while(*str){
USARTX -> TDR = *str++;
while(!(USARTX->ISR & USART_ISR_TXE)){IWDG->KR = IWDG_REFRESH;};
}
}
void usart_setup(){
// Nucleo's USART2 connected to VCP proxy of st-link
#if USARTNUM == 2
// setup pins: PA2 (Tx - AF1), PA15 (Rx - AF1)
// AF mode (AF1)
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER2|GPIO_MODER_MODER15))\
| (GPIO_MODER_MODER2_AF | GPIO_MODER_MODER15_AF);
GPIOA->AFR[0] = (GPIOA->AFR[0] &~GPIO_AFRH_AFRH2) | 1 << (2 * 4); // PA2
GPIOA->AFR[1] = (GPIOA->AFR[1] &~GPIO_AFRH_AFRH7) | 1 << (7 * 4); // PA15
// DMA: Tx - Ch4
DMA1_Channel4->CPAR = (uint32_t) &USART2->TDR; // periph
DMA1_Channel4->CMAR = (uint32_t) tbuf; // mem
DMA1_Channel4->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq
// Tx CNDTR set @ each transmission due to data size
NVIC_SetPriority(DMA1_Channel4_5_IRQn, 3);
NVIC_EnableIRQ(DMA1_Channel4_5_IRQn);
NVIC_SetPriority(USART2_IRQn, 0);
// setup usart2
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; // clock
// oversampling by16, 115200bps (fck=48mHz)
//USART2_BRR = 0x1a1; // 48000000 / 115200
USART2->BRR = 480000 / 1152;
USART2->CR3 = USART_CR3_DMAT; // enable DMA Tx
USART2->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART
while(!(USART2->ISR & USART_ISR_TC)); // polling idle frame Transmission
USART2->ICR |= USART_ICR_TCCF; // clear TC flag
USART2->CR1 |= USART_CR1_RXNEIE;
NVIC_EnableIRQ(USART2_IRQn);
// USART1 of main board
#elif USARTNUM == 1
// PA9 - Tx, PA10 - Rx (AF1)
GPIOA->MODER = (GPIOA->MODER & ~(GPIO_MODER_MODER9 | GPIO_MODER_MODER10))\
| (GPIO_MODER_MODER9_AF | GPIO_MODER_MODER10_AF);
GPIOA->AFR[1] = (GPIOA->AFR[1] & ~(GPIO_AFRH_AFRH1 | GPIO_AFRH_AFRH2)) |
1 << (1 * 4) | 1 << (2 * 4); // PA9, PA10
// USART1 Tx DMA - Channel2 (default value in SYSCFG_CFGR1)
DMA1_Channel2->CPAR = (uint32_t) &USART1->TDR; // periph
DMA1_Channel2->CMAR = (uint32_t) tbuf; // mem
DMA1_Channel2->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq
// Tx CNDTR set @ each transmission due to data size
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 3);
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
NVIC_SetPriority(USART1_IRQn, 0);
// setup usart1
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
USART1->BRR = 480000 / 1152;
USART1->CR3 = USART_CR3_DMAT; // enable DMA Tx
USART1->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART
while(!(USART1->ISR & USART_ISR_TC)); // polling idle frame Transmission
USART1->ICR |= USART_ICR_TCCF; // clear TC flag
USART1->CR1 |= USART_CR1_RXNEIE;
NVIC_EnableIRQ(USART1_IRQn);
#else
#error "Not implemented"
#endif
}
#if USARTNUM == 2
void usart2_isr(){
// USART1
#elif USARTNUM == 1
void usart1_isr(){
#else
#error "Not implemented"
#endif
#ifdef CHECK_TMOUT
static uint32_t tmout = 0;
#endif
if(USARTX->ISR & USART_ISR_RXNE){ // RX not emty - receive next char
#ifdef CHECK_TMOUT
if(tmout && Tms >= tmout){ // set overflow flag
bufovr = 1;
datalen[rbufno] = 0;
}
tmout = Tms + TIMEOUT_MS;
if(!tmout) tmout = 1; // prevent 0
#endif
// read RDR clears flag
uint8_t rb = USARTX->RDR;
if(datalen[rbufno] < UARTINBUFSZ){ // put next char into buf
rbuf[rbufno][datalen[rbufno]++] = rb;
if(rb == '\n'){ // got newline - line ready
linerdy = 1;
dlen = datalen[rbufno];
recvdata = rbuf[rbufno];
// prepare other buffer
rbufno = !rbufno;
datalen[rbufno] = 0;
#ifdef CHECK_TMOUT
// clear timeout at line end
tmout = 0;
#endif
}
}else{ // buffer overrun
bufovr = 1;
datalen[rbufno] = 0;
#ifdef CHECK_TMOUT
tmout = 0;
#endif
}
}
}
#if USARTNUM == 2
void dma1_channel4_5_isr(){
if(DMA1->ISR & DMA_ISR_TCIF4){ // Tx
DMA1->IFCR |= DMA_IFCR_CTCIF4; // clear TC flag
txrdy = 1;
}
}
// USART1
#elif USARTNUM == 1
void dma1_channel2_3_isr(){
if(DMA1->ISR & DMA_ISR_TCIF2){ // Tx
DMA1->IFCR |= DMA_IFCR_CTCIF2; // clear TC flag
txrdy = 1;
}
}
#else
#error "Not implemented"
#endif

View File

@ -0,0 +1,51 @@
/*
* usart.h
*
* Copyright 2017 Edward V. Emelianoff <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __USART_H__
#define __USART_H__
// output buffers size
#define UARTBUFSZ (512)
// input buffer size
#define UARTINBUFSZ (64)
// timeout between data bytes
#ifndef TIMEOUT_MS
#define TIMEOUT_MS (1500)
#endif
typedef enum{
ALL_OK,
LINE_BUSY,
STR_TOO_LONG
} TXstatus;
#define usartrx() (linerdy)
#define usartovr() (bufovr)
extern volatile int linerdy, bufovr, txrdy;
void usart_setup();
int usart_getline(char **line);
TXstatus usart_send(const char *str, int len);
TXstatus usart_send_blocking(const char *str, int len);
void usart_send_blck(const char *str);
#endif // __USART_H__

View File

@ -0,0 +1,183 @@
/*
* geany_encoding=koi8-r
* usb.c - base functions for different USB types
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <string.h>
#include "usart.h"
#include "usb.h"
#include "usb_lib.h"
// incoming buffer size
#define IDATASZ (256)
static uint8_t incoming_data[IDATASZ];
static uint8_t ovfl = 0;
static uint16_t idatalen = 0;
static int8_t usbON = 0; // ==1 when USB fully configured
static volatile uint8_t tx_succesfull = 0;
// interrupt IN handler (never used?)
static uint16_t EP1_Handler(ep_t ep){
uint8_t ep0buf[11];
if (ep.rx_flag){
EP_Read(1, ep0buf);
ep.status = SET_VALID_TX(ep.status);
ep.status = KEEP_STAT_RX(ep.status);
}else if (ep.tx_flag){
ep.status = SET_VALID_RX(ep.status);
ep.status = SET_STALL_TX(ep.status);
}
return ep.status;
}
// data IN/OUT handler
static uint16_t EP23_Handler(ep_t ep){
if(ep.rx_flag){
int rd = ep.rx_cnt, rest = IDATASZ - idatalen;
if(rd){
if(rd <= rest){
idatalen += EP_Read(2, &incoming_data[idatalen]);
ovfl = 0;
}else{
ep.status = SET_NAK_RX(ep.status);
ovfl = 1;
return ep.status;
}
}
ep.status = CLEAR_DTOG_RX(ep.status);
ep.status = CLEAR_DTOG_TX(ep.status);
ep.status = SET_STALL_TX(ep.status);
}else if (ep.tx_flag){
ep.status = KEEP_STAT_TX(ep.status);
tx_succesfull = 1;
}
ep.status = SET_VALID_RX(ep.status);
return ep.status;
}
void USB_setup(){
RCC->APB1ENR |= RCC_APB1ENR_CRSEN | RCC_APB1ENR_USBEN; // enable CRS (hsi48 sync) & USB
RCC->CFGR3 &= ~RCC_CFGR3_USBSW; // reset USB
RCC->CR2 |= RCC_CR2_HSI48ON; // turn ON HSI48
uint32_t tmout = 16000000;
while(!(RCC->CR2 & RCC_CR2_HSI48RDY)){if(--tmout == 0) break; IWDG->KR = IWDG_REFRESH;}
FLASH->ACR = FLASH_ACR_PRFTBE | FLASH_ACR_LATENCY;
CRS->CFGR &= ~CRS_CFGR_SYNCSRC;
CRS->CFGR |= CRS_CFGR_SYNCSRC_1; // USB SOF selected as sync source
CRS->CR |= CRS_CR_AUTOTRIMEN; // enable auto trim
CRS->CR |= CRS_CR_CEN; // enable freq counter & block CRS->CFGR as read-only
RCC->CFGR |= RCC_CFGR_SW;
// allow RESET and CTRM interrupts
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM;
// clear flags
USB->ISTR = 0;
// and activate pullup
USB->BCDR |= USB_BCDR_DPPU;
NVIC_EnableIRQ(USB_IRQn);
}
void usb_proc(){
if(USB_GetState() == USB_CONFIGURE_STATE){ // USB configured - activate other endpoints
if(!usbON){ // endpoints not activated
// make new BULK endpoint
// Buffer have 1024 bytes, but last 256 we use for CAN bus (30.2 of RM: USB main features)
EP_Init(1, EP_TYPE_INTERRUPT, 10, 0, EP1_Handler); // IN1 - transmit
EP_Init(2, EP_TYPE_BULK, 0, USB_RXBUFSZ, EP23_Handler); // OUT2 - receive data
EP_Init(3, EP_TYPE_BULK, USB_TXBUFSZ, 0, EP23_Handler); // IN3 - transmit data
usbON = 1;
}
}else{
usbON = 0;
}
}
void USB_send(const char *buf){
uint16_t l = 0, ctr = 0;
const char *p = buf;
while(*p++) ++l;
while(l){
IWDG->KR = IWDG_REFRESH;
uint16_t s = (l > USB_TXBUFSZ) ? USB_TXBUFSZ : l;
tx_succesfull = 0;
EP_Write(3, (uint8_t*)&buf[ctr], s);
uint32_t ctra = 1000000;
while(--ctra && tx_succesfull == 0){IWDG->KR = IWDG_REFRESH;}
l -= s;
ctr += s;
}
}
/**
* @brief USB_receive - read first received text string
* @param buf (i) - buffer for received data
* @param bufsize - its size
* @return amount of received bytes
*/
int USB_receive(char *buf, int bufsize){
if(bufsize<1 || !idatalen) return 0;
IWDG->KR = IWDG_REFRESH;
int stlen = 0, i;
for(i = 0; i < idatalen; ++i){
if(incoming_data[i] == '\n'){
stlen = i+1;
break;
}
}
if(i == idatalen || stlen == 0) return 0;
/*
char x[] = "USB got x:\n";
x[8] = '0' + stlen;
usart_send_blck(x);
usart_send_blck((char*)incoming_data);
usart_send_blck("\n");
*/
USB->CNTR = 0;
int sz = (stlen > bufsize) ? bufsize : stlen, rest = idatalen - sz;
memcpy(buf, incoming_data, sz);
buf[sz] = 0;
/*
usart_send_blck("buf:\n");
usart_send_blck((char*)buf);
usart_send_blck("\n");
*/
if(rest > 0){
memmove(incoming_data, &incoming_data[sz], rest);
idatalen = rest;
}else idatalen = 0;
if(ovfl){
EP23_Handler(endpoints[2]);
uint16_t epstatus = USB->EPnR[2];
epstatus = CLEAR_DTOG_RX(epstatus);
epstatus = SET_VALID_RX(epstatus);
USB->EPnR[2] = epstatus;
}
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM;
return sz;
}
/**
* @brief USB_configured
* @return 1 if USB is in configured state
*/
int USB_configured(){
return usbON;
}

View File

@ -0,0 +1,37 @@
/*
* geany_encoding=koi8-r
* usb.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __USB_H__
#define __USB_H__
#include "hardware.h"
#define BUFFSIZE (64)
void USB_setup();
void usb_proc();
void USB_send(const char *buf);
int USB_receive(char *buf, int bufsize);
int USB_configured();
#endif // __USB_H__

View File

@ -0,0 +1,106 @@
/*
* geany_encoding=koi8-r
* usb_defs.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __USB_DEFS_H__
#define __USB_DEFS_H__
#include <stm32f0xx.h>
/**
* Buffers size definition
**/
// !!! when working with CAN bus change USB_BTABLE_SIZE to 768 !!!
#define USB_BTABLE_SIZE 1024
// first 64 bytes of USB_BTABLE are registers!
#define USB_EP0_BASEADDR 64
// for USB FS EP0 buffers are from 8 to 64 bytes long (64 for PL2303)
#define USB_EP0_BUFSZ 64
// USB transmit buffer size (64 for PL2303)
#define USB_TXBUFSZ 64
// USB receive buffer size (64 for PL2303)
#define USB_RXBUFSZ 64
#define USB_BTABLE_BASE 0x40006000
#undef USB_BTABLE
#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE))
#define USB_ISTR_EPID 0x0000000F
#define USB_FNR_LSOF_0 0x00000800
#define USB_FNR_lSOF_1 0x00001000
#define USB_LPMCSR_BESL_0 0x00000010
#define USB_LPMCSR_BESL_1 0x00000020
#define USB_LPMCSR_BESL_2 0x00000040
#define USB_LPMCSR_BESL_3 0x00000080
#define USB_EPnR_CTR_RX 0x00008000
#define USB_EPnR_DTOG_RX 0x00004000
#define USB_EPnR_STAT_RX 0x00003000
#define USB_EPnR_STAT_RX_0 0x00001000
#define USB_EPnR_STAT_RX_1 0x00002000
#define USB_EPnR_SETUP 0x00000800
#define USB_EPnR_EP_TYPE 0x00000600
#define USB_EPnR_EP_TYPE_0 0x00000200
#define USB_EPnR_EP_TYPE_1 0x00000400
#define USB_EPnR_EP_KIND 0x00000100
#define USB_EPnR_CTR_TX 0x00000080
#define USB_EPnR_DTOG_TX 0x00000040
#define USB_EPnR_STAT_TX 0x00000030
#define USB_EPnR_STAT_TX_0 0x00000010
#define USB_EPnR_STAT_TX_1 0x00000020
#define USB_EPnR_EA 0x0000000F
#define USB_COUNTn_RX_BLSIZE 0x00008000
#define USB_COUNTn_NUM_BLOCK 0x00007C00
#define USB_COUNTn_RX 0x0000003F
#define USB_TypeDef USB_TypeDef_custom
typedef struct{
__IO uint32_t EPnR[8];
__IO uint32_t RESERVED1;
__IO uint32_t RESERVED2;
__IO uint32_t RESERVED3;
__IO uint32_t RESERVED4;
__IO uint32_t RESERVED5;
__IO uint32_t RESERVED6;
__IO uint32_t RESERVED7;
__IO uint32_t RESERVED8;
__IO uint32_t CNTR;
__IO uint32_t ISTR;
__IO uint32_t FNR;
__IO uint32_t DADDR;
__IO uint32_t BTABLE;
__IO uint32_t LPMCSR;
__IO uint32_t BCDR;
} USB_TypeDef;
typedef struct{
__IO uint16_t USB_ADDR_TX;
__IO uint16_t USB_COUNT_TX;
__IO uint16_t USB_ADDR_RX;
__IO uint16_t USB_COUNT_RX;
} USB_EPDATA_TypeDef;
typedef struct{
__IO USB_EPDATA_TypeDef EP[8];
} USB_BtableDef;
#endif // __USB_DEFS_H__

View File

@ -0,0 +1,538 @@
/*
* geany_encoding=koi8-r
* usb_lib.c
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <string.h> // memcpy
#include "stm32f0.h"
#include "usart.h"
#include "usb_lib.h"
#ifdef EBUG
#undef EBUG
#endif
ep_t endpoints[ENDPOINTS_NUM];
static usb_dev_t USB_Dev;
static usb_LineCoding lineCoding = {115200, 0, 0, 8};
static config_pack_t setup_packet;
static uint8_t ep0databuf[EP0DATABUF_SIZE];
static uint8_t ep0dbuflen = 0;
usb_LineCoding getLineCoding(){return lineCoding;}
// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor
#define bcdUSB_L 0x10
#define bcdUSB_H 0x01
#define bDeviceClass 0
#define bDeviceSubClass 0
#define bDeviceProtocol 0
#define bNumConfigurations 1
static const uint8_t USB_DeviceDescriptor[] = {
18, // bLength
0x01, // bDescriptorType - Device descriptor
bcdUSB_L, // bcdUSB_L - 1.10
bcdUSB_H, // bcdUSB_H
bDeviceClass, // bDeviceClass - USB_COMM
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0_BUFSZ, // bMaxPacketSize
0x7b, // idVendor_L PL2303: VID=0x067b, PID=0x2303
0x06, // idVendor_H
0x03, // idProduct_L
0x23, // idProduct_H
0x00, // bcdDevice_Ver_L
0x03, // bcdDevice_Ver_H
0x01, // iManufacturer
0x02, // iProduct
0x00, // iSerialNumber
bNumConfigurations // bNumConfigurations
};
static const uint8_t USB_DeviceQualifierDescriptor[] = {
10, //bLength
0x06, // bDescriptorType - Device qualifier
bcdUSB_L, // bcdUSB_L
bcdUSB_H, // bcdUSB_H
bDeviceClass, // bDeviceClass
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0_BUFSZ, // bMaxPacketSize0
bNumConfigurations, // bNumConfigurations
0x00 // Reserved
};
static const uint8_t USB_ConfigDescriptor[] = {
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
0x02, /* bDescriptorType: Configuration */
39, /* wTotalLength:no of returned bytes */
0x00,
0x01, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0xa0, /* bmAttributes - Bus powered, Remote wakeup */
0x32, /* MaxPower 100 mA */
/*---------------------------------------------------------------------------*/
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
0x04, /* bDescriptorType: Interface */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x03, /* bNumEndpoints: 3 endpoints used */
0xff, /* bInterfaceClass */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00, /* iInterface: */
///////////////////////////////////////////////////
/*Endpoint 1 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x81, /* bEndpointAddress IN1 */
0x03, /* bmAttributes: Interrupt */
0x0a, /* wMaxPacketSize LO: */
0x00, /* wMaxPacketSize HI: */
0x01, /* bInterval: */
/*Endpoint OUT2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x02, /* bEndpointAddress: OUT2 */
0x02, /* bmAttributes: Bulk */
(USB_RXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
(USB_RXBUFSZ >> 8),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN3 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x83, /* bEndpointAddress IN3 */
0x02, /* bmAttributes: Bulk */
(USB_TXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
(USB_TXBUFSZ >> 8),
0x00, /* bInterval: ignore for Bulk transfer */
};
_USB_LANG_ID_(USB_StringLangDescriptor, LANG_US);
// these descriptors are not used in PL2303 emulator!
_USB_STRING_(USB_StringSerialDescriptor, u"0");
_USB_STRING_(USB_StringManufacturingDescriptor, u"Prolific Technology Inc.");
_USB_STRING_(USB_StringProdDescriptor, u"USB-Serial Controller");
/*
* default handlers
*/
// SET_LINE_CODING
void WEAK linecoding_handler(usb_LineCoding __attribute__((unused)) *lc){
//MSG("linecoding_handler\n");
}
// SET_CONTROL_LINE_STATE
void WEAK clstate_handler(uint16_t __attribute__((unused)) val){
//MSG("clstate_handler\n");
}
// SEND_BREAK
void WEAK break_handler(){
//MSG("break_handler\n");
}
// handler of vendor requests
void WEAK vendor_handler(config_pack_t *packet){
if(packet->bmRequestType & 0x80){ // read
//SEND("Read");
uint8_t c;
switch(packet->wValue){
case 0x8484:
c = 2;
break;
case 0x0080:
c = 1;
break;
case 0x8686:
c = 0xaa;
break;
default:
c = 0;
}
EP_WriteIRQ(0, &c, 1);
}else{ // write ZLP
//SEND("Write");
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
/*SEND(" vendor, reqt=");
printuhex(packet->bmRequestType);
SEND(", wval=");
printuhex(packet->wValue);
usart_putchar('\n');*/
}
#ifdef EBUG
uint8_t _2wr = 0;
#define WRITEDUMP(str) do{MSG(str); _2wr = 1;}while(0)
#else
#define WRITEDUMP(str)
#endif
static void wr0(const uint8_t *buf, uint16_t size){
if(setup_packet.wLength < size) size = setup_packet.wLength;
EP_WriteIRQ(0, buf, size);
}
static inline void get_descriptor(){
switch(setup_packet.wValue){
case DEVICE_DESCRIPTOR:
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor));
break;
case CONFIGURATION_DESCRIPTOR:
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor));
break;
case STRING_LANG_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringLangDescriptor, STRING_LANG_DESCRIPTOR_SIZE_BYTE);
break;
case STRING_MAN_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringManufacturingDescriptor, USB_StringManufacturingDescriptor.bLength);
break;
case STRING_PROD_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringProdDescriptor, USB_StringProdDescriptor.bLength);
break;
case STRING_SN_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringSerialDescriptor, USB_StringSerialDescriptor.bLength);
break;
case DEVICE_QUALIFIER_DESCRIPTOR:
wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]);
break;
default:
WRITEDUMP("UNK_DES");
break;
}
}
static uint8_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured)
static inline void std_d2h_req(){
uint16_t status = 0; // bus powered
switch(setup_packet.bRequest){
case GET_DESCRIPTOR:
get_descriptor();
break;
case GET_STATUS:
EP_WriteIRQ(0, (uint8_t *)&status, 2); // send status: Bus Powered
break;
case GET_CONFIGURATION:
WRITEDUMP("GET_CONFIGURATION");
EP_WriteIRQ(0, &configuration, 1);
break;
default:
WRITEDUMP("80:WR_REQ");
break;
}
}
static inline void std_h2d_req(){
switch(setup_packet.bRequest){
case SET_ADDRESS:
// new address will be assigned later - after acknowlegement or request to host
USB_Dev.USB_Addr = setup_packet.wValue;
break;
case SET_CONFIGURATION:
// Now device configured
USB_Dev.USB_Status = USB_CONFIGURE_STATE;
configuration = setup_packet.wValue;
break;
default:
WRITEDUMP("0:WR_REQ");
break;
}
}
/*
bmRequestType: 76543210
7 direction: 0 - host->device, 1 - device->host
65 type: 0 - standard, 1 - class, 2 - vendor
4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other
*/
/**
* Endpoint0 (control) handler
* @param ep - endpoint state
* @return data written to EP0R
*/
static uint16_t EP0_Handler(ep_t ep){
uint16_t epstatus = ep.status; // EP0R on input -> return this value after modifications
uint8_t reqtype = setup_packet.bmRequestType & 0x7f;
uint8_t dev2host = (setup_packet.bmRequestType & 0x80) ? 1 : 0;
if ((ep.rx_flag) && (ep.setup_flag)){
switch(reqtype){
case STANDARD_DEVICE_REQUEST_TYPE: // standard device request
if(dev2host){
std_d2h_req();
}else{
std_h2d_req();
// send ZLP
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
epstatus = SET_NAK_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
break;
case STANDARD_ENDPOINT_REQUEST_TYPE: // standard endpoint request
if (setup_packet.bRequest == CLEAR_FEATURE){
// send ZLP
EP_WriteIRQ(0, (uint8_t *)0, 0);
epstatus = SET_NAK_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
}else{
WRITEDUMP("02:WR_REQ");
}
break;
case VENDOR_REQUEST_TYPE:
vendor_handler(&setup_packet);
epstatus = SET_NAK_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
break;
case CONTROL_REQUEST_TYPE:
switch(setup_packet.bRequest){
case GET_LINE_CODING:
EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding));
break;
case SET_LINE_CODING: // omit this for next stage, when data will come
break;
case SET_CONTROL_LINE_STATE:
clstate_handler(setup_packet.wValue);
break;
case SEND_BREAK:
break_handler();
break;
default:
WRITEDUMP("undef control req");
}
if(!dev2host) EP_WriteIRQ(0, (uint8_t *)0, 0); // write acknowledgement
epstatus = SET_VALID_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
break;
default:
EP_WriteIRQ(0, (uint8_t *)0, 0);
epstatus = SET_NAK_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
}
}else if (ep.rx_flag){ // got data over EP0 or host acknowlegement
if(ep.rx_cnt){
EP_WriteIRQ(0, (uint8_t *)0, 0);
if(setup_packet.bRequest == SET_LINE_CODING){
//WRITEDUMP("SET_LINE_CODING");
linecoding_handler((usb_LineCoding*)ep0databuf);
}
}
// Close transaction
epstatus = CLEAR_DTOG_RX(epstatus);
epstatus = CLEAR_DTOG_TX(epstatus);
// wait for new data from host
epstatus = SET_VALID_RX(epstatus);
epstatus = SET_STALL_TX(epstatus);
} else if (ep.tx_flag){ // package transmitted
// now we can change address after enumeration
if ((USB->DADDR & USB_DADDR_ADD) != USB_Dev.USB_Addr){
USB->DADDR = USB_DADDR_EF | USB_Dev.USB_Addr;
// change state to ADRESSED
USB_Dev.USB_Status = USB_ADRESSED_STATE;
}
// end of transaction
epstatus = CLEAR_DTOG_RX(epstatus);
epstatus = CLEAR_DTOG_TX(epstatus);
epstatus = SET_VALID_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
}
#ifdef EBUG
if(_2wr){
usart_putchar(' ');
if (ep.rx_flag) usart_putchar('r');
else usart_putchar('t');
printu(setup_packet.wLength);
if(ep.setup_flag) usart_putchar('s');
usart_putchar(' ');
usart_putchar('I');
printu(setup_packet.wIndex);
usart_putchar('V');
printu(setup_packet.wValue);
usart_putchar('R');
printu(setup_packet.bRequest);
usart_putchar('T');
printu(setup_packet.bmRequestType);
usart_putchar(' ');
usart_putchar('0' + ep0dbuflen);
usart_putchar(' ');
hexdump(ep0databuf, ep0dbuflen);
usart_putchar('\n');
}
#endif
return epstatus;
}
#undef WRITEDUMP
static uint16_t lastaddr = USB_EP0_BASEADDR;
/**
* Endpoint initialisation
* !!! when working with CAN bus change USB_BTABLE_SIZE to 768 !!!
* @param number - EP num (0...7)
* @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT)
* @param txsz - transmission buffer size @ USB/CAN buffer
* @param rxsz - reception buffer size @ USB/CAN buffer
* @param uint16_t (*func)(ep_t *ep) - EP handler function
* @return 0 if all OK
*/
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep)){
if(number >= ENDPOINTS_NUM) return 4; // out of configured amount
if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large
if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE) return 2; // out of btable
USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
if(rxsz & 1 || rxsz > 992) return 3; // wrong rx buffer size
uint16_t countrx = 0;
if(rxsz < 64) countrx = rxsz / 2;
else{
if(rxsz & 0x1f) return 3; // should be multiple of 32
countrx = 31 + rxsz / 32;
}
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr);
lastaddr += txsz;
USB_BTABLE->EP[number].USB_COUNT_TX = 0;
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
endpoints[number].rx_buf = (uint8_t *)(USB_BTABLE_BASE + lastaddr);
lastaddr += rxsz;
// buffer size: Table127 of RM
USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
endpoints[number].func = func;
return 0;
}
// standard IRQ handler
void usb_isr(){
if (USB->ISTR & USB_ISTR_RESET){
// Reinit registers
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM;
USB->ISTR = 0;
// Endpoint 0 - CONTROL
// ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes!
lastaddr = USB_EP0_BASEADDR; // roll back to beginning of buffer
EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler);
// clear address, leave only enable bit
USB->DADDR = USB_DADDR_EF;
// state is default - wait for enumeration
USB_Dev.USB_Status = USB_DEFAULT_STATE;
}
if(USB->ISTR & USB_ISTR_CTR){
// EP number
uint8_t n = USB->ISTR & USB_ISTR_EPID;
// copy status register
uint16_t epstatus = USB->EPnR[n];
// Calculate flags
endpoints[n].rx_flag = (epstatus & USB_EPnR_CTR_RX) ? 1 : 0;
endpoints[n].setup_flag = (epstatus & USB_EPnR_SETUP) ? 1 : 0;
endpoints[n].tx_flag = (epstatus & USB_EPnR_CTR_TX) ? 1 : 0;
// copy received bytes amount
endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
// check direction
if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
if(n == 0){ // control endpoint
if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
memcpy(&setup_packet, endpoints[0].rx_buf, sizeof(setup_packet));
ep0dbuflen = 0;
// interrupt handler will be called later
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
ep0dbuflen = endpoints[0].rx_cnt;
memcpy(ep0databuf, endpoints[0].rx_buf, ep0dbuflen);
}
}
}else{ // IN interrupt - transmit data, only CTR_TX == 1
// enumeration end could be here (if EP0)
}
// prepare status field for EP handler
endpoints[n].status = epstatus;
// call EP handler (even if it will change EPnR, it should return new status)
epstatus = endpoints[n].func(endpoints[n]);
// keep DTOG state
epstatus = KEEP_DTOG_TX(epstatus);
epstatus = KEEP_DTOG_RX(epstatus);
// clear all RX/TX flags
epstatus = CLEAR_CTR_RX(epstatus);
epstatus = CLEAR_CTR_TX(epstatus);
// refresh EPnR
USB->EPnR[n] = epstatus;
}
}
/**
* Write data to EP buffer (called from IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
uint8_t i;
if(size > USB_TXBUFSZ) size = USB_TXBUFSZ;
uint16_t N2 = (size + 1) >> 1;
// the buffer is 16-bit, so we should copy data as it would be uint16_t
uint16_t *buf16 = (uint16_t *)buf;
for (i = 0; i < N2; i++){
endpoints[number].tx_buf[i] = buf16[i];
}
USB_BTABLE->EP[number].USB_COUNT_TX = size;
}
/**
* Write data to EP buffer (called outside IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){
uint16_t status = USB->EPnR[number];
EP_WriteIRQ(number, buf, size);
status = SET_NAK_RX(status);
status = SET_VALID_TX(status);
status = KEEP_DTOG_TX(status);
status = KEEP_DTOG_RX(status);
USB->EPnR[number] = status;
}
/*
* Copy data from EP buffer into user buffer area
* @param *buf - user array for data
* @return amount of data read
*/
int EP_Read(uint8_t number, uint8_t *buf){
int n = endpoints[number].rx_cnt;
if(n){
for(int i = 0; i < n; ++i)
buf[i] = endpoints[number].rx_buf[i];
}
return n;
}
// USB status
uint8_t USB_GetState(){
return USB_Dev.USB_Status;
}

View File

@ -0,0 +1,202 @@
/*
* geany_encoding=koi8-r
* usb_lib.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, edward.emelianoff@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __USB_LIB_H__
#define __USB_LIB_H__
#include <wchar.h>
#include "usb_defs.h"
#define EP0DATABUF_SIZE (64)
// Max EP amount (EP0 + other used)
#define ENDPOINTS_NUM 4
// bmRequestType & 0x7f
#define STANDARD_DEVICE_REQUEST_TYPE 0
#define STANDARD_ENDPOINT_REQUEST_TYPE 2
#define VENDOR_REQUEST_TYPE 0x40
#define CONTROL_REQUEST_TYPE 0x21
// bRequest, standard; for bmRequestType == 0x80
#define GET_STATUS 0x00
#define GET_DESCRIPTOR 0x06
#define GET_CONFIGURATION 0x08
// for bmRequestType == 0
#define CLEAR_FEATURE 0x01
#define SET_FEATURE 0x03 // unused
#define SET_ADDRESS 0x05
#define SET_DESCRIPTOR 0x07 // unused
#define SET_CONFIGURATION 0x09
// for bmRequestType == 0x81, 1 or 0xB2
#define GET_INTERFACE 0x0A // unused
#define SET_INTERFACE 0x0B // unused
#define SYNC_FRAME 0x0C // unused
#define VENDOR_REQUEST 0x01 // unused
// Class-Specific Control Requests
#define SEND_ENCAPSULATED_COMMAND 0x00 // unused
#define GET_ENCAPSULATED_RESPONSE 0x01 // unused
#define SET_COMM_FEATURE 0x02 // unused
#define GET_COMM_FEATURE 0x03 // unused
#define CLEAR_COMM_FEATURE 0x04 // unused
#define SET_LINE_CODING 0x20
#define GET_LINE_CODING 0x21
#define SET_CONTROL_LINE_STATE 0x22
#define SEND_BREAK 0x23
// control line states
#define CONTROL_DTR 0x01
#define CONTROL_RTS 0x02
// wValue
#define DEVICE_DESCRIPTOR 0x100
#define CONFIGURATION_DESCRIPTOR 0x200
#define STRING_LANG_DESCRIPTOR 0x300
#define STRING_MAN_DESCRIPTOR 0x301
#define STRING_PROD_DESCRIPTOR 0x302
#define STRING_SN_DESCRIPTOR 0x303
#define DEVICE_QUALIFIER_DESCRIPTOR 0x600
// EPnR bits manipulation
#define CLEAR_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? R : (R & (~USB_EPnR_DTOG_RX))
#define SET_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? (R & (~USB_EPnR_DTOG_RX)) : R
#define TOGGLE_DTOG_RX(R) (R | USB_EPnR_DTOG_RX)
#define KEEP_DTOG_RX(R) (R & (~USB_EPnR_DTOG_RX))
#define CLEAR_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? R : (R & (~USB_EPnR_DTOG_TX))
#define SET_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? (R & (~USB_EPnR_DTOG_TX)) : R
#define TOGGLE_DTOG_TX(R) (R | USB_EPnR_DTOG_TX)
#define KEEP_DTOG_TX(R) (R & (~USB_EPnR_DTOG_TX))
#define SET_VALID_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX) | (R & (~USB_EPnR_STAT_RX))
#define SET_NAK_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_1) | (R & (~USB_EPnR_STAT_RX))
#define SET_STALL_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_0) | (R & (~USB_EPnR_STAT_RX))
#define KEEP_STAT_RX(R) (R & (~USB_EPnR_STAT_RX))
#define SET_VALID_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX) | (R & (~USB_EPnR_STAT_TX))
#define SET_NAK_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_1) | (R & (~USB_EPnR_STAT_TX))
#define SET_STALL_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_0) | (R & (~USB_EPnR_STAT_TX))
#define KEEP_STAT_TX(R) (R & (~USB_EPnR_STAT_TX))
#define CLEAR_CTR_RX(R) (R & (~USB_EPnR_CTR_RX))
#define CLEAR_CTR_TX(R) (R & (~USB_EPnR_CTR_TX))
#define CLEAR_CTR_RX_TX(R) (R & (~(USB_EPnR_CTR_TX | USB_EPnR_CTR_RX)))
// USB state: uninitialized, addressed, ready for use
#define USB_DEFAULT_STATE 0
#define USB_ADRESSED_STATE 1
#define USB_CONFIGURE_STATE 2
// EP types
#define EP_TYPE_BULK 0x00
#define EP_TYPE_CONTROL 0x01
#define EP_TYPE_ISO 0x02
#define EP_TYPE_INTERRUPT 0x03
#define LANG_US (uint16_t)0x0409
#define _USB_STRING_(name, str) \
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString[(sizeof(str) - 2) / 2]; \
\
} \
name = {sizeof(name), 0x03, str}
#define _USB_LANG_ID_(name, lng_id) \
\
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString; \
\
} \
name = {0x04, 0x03, lng_id}
#define STRING_LANG_DESCRIPTOR_SIZE_BYTE (4)
// EP0 configuration packet
typedef struct {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} config_pack_t;
// endpoints state
typedef struct __ep_t{
uint16_t *tx_buf; // transmission buffer address
uint8_t *rx_buf; // reception buffer address
uint16_t (*func)(); // endpoint action function
uint16_t status; // status flags
unsigned rx_cnt : 10; // received data counter
unsigned tx_flag : 1; // transmission flag
unsigned rx_flag : 1; // reception flag
unsigned setup_flag : 1; // this is setup packet (only for EP0)
} ep_t;
// USB status & its address
typedef struct {
uint8_t USB_Status;
uint16_t USB_Addr;
}usb_dev_t;
typedef struct {
uint32_t dwDTERate;
uint8_t bCharFormat;
#define USB_CDC_1_STOP_BITS 0
#define USB_CDC_1_5_STOP_BITS 1
#define USB_CDC_2_STOP_BITS 2
uint8_t bParityType;
#define USB_CDC_NO_PARITY 0
#define USB_CDC_ODD_PARITY 1
#define USB_CDC_EVEN_PARITY 2
#define USB_CDC_MARK_PARITY 3
#define USB_CDC_SPACE_PARITY 4
uint8_t bDataBits;
} __attribute__ ((packed)) usb_LineCoding;
typedef struct {
uint8_t bmRequestType;
uint8_t bNotificationType;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__ ((packed)) usb_cdc_notification;
extern ep_t endpoints[];
void USB_Init();
uint8_t USB_GetState();
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep));
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
int EP_Read(uint8_t number, uint8_t *buf);
usb_LineCoding getLineCoding();
void WEAK linecoding_handler(usb_LineCoding *lc);
void WEAK clstate_handler(uint16_t val);
void WEAK break_handler();
void WEAK vendor_handler(config_pack_t *packet);
#endif // __USB_LIB_H__

View File

@ -0,0 +1,3 @@
#define BUILD_NUMBER "48"
#define BUILD_DATE "2023-08-29"
#define BUILDNO 45