diff --git a/F0:F030,F042,F072/TSYS_controller/Makefile b/F0:F030,F042,F072/TSYS_controller/Makefile new file mode 100644 index 0000000..253bb3e --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/Makefile @@ -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 diff --git a/F0:F030,F042,F072/TSYS_controller/Makefile_old b/F0:F030,F042,F072/TSYS_controller/Makefile_old new file mode 100644 index 0000000..2081386 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/Makefile_old @@ -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 diff --git a/F0:F030,F042,F072/TSYS_controller/Readme.md b/F0:F030,F042,F072/TSYS_controller/Readme.md new file mode 100644 index 0000000..2824aaa --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/Readme.md @@ -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 diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cflags b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cflags new file mode 100644 index 0000000..68d5165 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cflags @@ -0,0 +1 @@ +-std=c17 \ No newline at end of file diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.config b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.config new file mode 100644 index 0000000..eebe71a --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.config @@ -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 diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator new file mode 100644 index 0000000..e94cbbd --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator @@ -0,0 +1 @@ +[General] diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user new file mode 100644 index 0000000..7ca48ef --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user @@ -0,0 +1,171 @@ + + + + + + EnvironmentId + {7bd84e39-ca37-46d3-be9d-99ebea85bc0d} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + true + false + 0 + true + true + 0 + 8 + true + false + 1 + true + false + true + *.md, *.MD, Makefile + false + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 8 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {65a14f9e-e008-4c1b-89df-4eaa4774b6e3} + 0 + 0 + 0 + + /Big/Data/00__Electronics/STM32/F0-nolib/TSYS_controller + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Сборка + Сборка + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Очистка + Очистка + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Развёртывание + Развёртывание + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.4.8-pre1 b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.4.8-pre1 new file mode 100644 index 0000000..87f66cb --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.4.8-pre1 @@ -0,0 +1,198 @@ + + + + + + EnvironmentId + {cf63021e-ef53-49b0-b03b-2f2570cdf3b6} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + false + 1 + true + true + 0 + 8 + true + 2 + true + true + true + true + + + + ProjectExplorer.Project.PluginSettings + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + {91347f2c-5221-46a7-80b1-0a054ca02f79} + 0 + 0 + 0 + + /home/eddy/Docs/SAO/BTA/MIRROR_CONTROL_termo/Project/STM32src/TSYS_controller + + + + all + + false + + + true + Сборка + + GenericProjectManager.GenericMakeStep + + 1 + Сборка + + ProjectExplorer.BuildSteps.Build + + + + + clean + + false + + + true + Сборка + + GenericProjectManager.GenericMakeStep + + 1 + Очистка + + ProjectExplorer.BuildSteps.Clean + + 2 + false + + По умолчанию + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Установка + + ProjectExplorer.BuildSteps.Deploy + + 1 + Конфигурация установки + + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + false + false + 1000 + + true + + false + false + false + false + true + 0.01 + 10 + true + 1 + 25 + + 1 + true + false + true + valgrind + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + + 2 + + + + + + + ProjectExplorer.CustomExecutableRunConfiguration + 3768 + false + true + false + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 18 + + + Version + 18 + + diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.cf63021 b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.cf63021 new file mode 100644 index 0000000..04a03d0 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.creator.user.cf63021 @@ -0,0 +1,160 @@ + + + + + + EnvironmentId + {cf63021e-ef53-49b0-b03b-2f2570cdf3b6} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + KOI8-R + false + 4 + false + 80 + true + true + 1 + false + false + false + 1 + true + true + 0 + 8 + true + false + 2 + true + true + true + *.md, *.MD, Makefile + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + true + Builtin.DefaultTidyAndClazy + 4 + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop + Desktop + {91347f2c-5221-46a7-80b1-0a054ca02f79} + 0 + 0 + 0 + + /home/eddy/Docs/SAO/BTA/MIRROR_CONTROL_termo/Project/STM32src/TSYS_controller + + + + all + + true + GenericProjectManager.GenericMakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + GenericProjectManager.GenericMakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + По умолчанию + GenericProjectManager.GenericBuildConfiguration + + 1 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + + 2 + + ProjectExplorer.CustomExecutableRunConfiguration + + false + true + false + true + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cxxflags b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cxxflags new file mode 100644 index 0000000..6435dfc --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.cxxflags @@ -0,0 +1 @@ +-std=c++17 \ No newline at end of file diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.files b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.files new file mode 100644 index 0000000..cb15fe1 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.files @@ -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 diff --git a/F0:F030,F042,F072/TSYS_controller/TSYS_controller.includes b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.includes new file mode 100644 index 0000000..3f4d0a1 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/TSYS_controller.includes @@ -0,0 +1,4 @@ +. +../inc +../inc/F0 +../inc/cm diff --git a/F0:F030,F042,F072/TSYS_controller/adc.c b/F0:F030,F042,F072/TSYS_controller/adc.c new file mode 100644 index 0000000..9d7ace9 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/adc.c @@ -0,0 +1,167 @@ +/* + * This file is part of the TSYS_controller project. + * Copyright 2019 Edward V. Emelianov . + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "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; +} diff --git a/F0:F030,F042,F072/TSYS_controller/adc.h b/F0:F030,F042,F072/TSYS_controller/adc.h new file mode 100644 index 0000000..62ce781 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/adc.h @@ -0,0 +1,31 @@ +/* + * This file is part of the TSYS_controller project. + * Copyright 2019 Edward V. Emelianov . + * + * 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 . + */ + +#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 diff --git a/F0:F030,F042,F072/TSYS_controller/can.c b/F0:F030,F042,F072/TSYS_controller/can.c new file mode 100644 index 0000000..2f9bddc --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/can.c @@ -0,0 +1,296 @@ +/* + * geany_encoding=koi8-r + * can.c + * + * Copyright 2018 Edward V. Emelianov + * + * 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 // 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; + } +} diff --git a/F0:F030,F042,F072/TSYS_controller/can.h b/F0:F030,F042,F072/TSYS_controller/can.h new file mode 100644 index 0000000..2023c73 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/can.h @@ -0,0 +1,73 @@ +/* + * geany_encoding=koi8-r + * can.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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__ diff --git a/F0:F030,F042,F072/TSYS_controller/can_process.c b/F0:F030,F042,F072/TSYS_controller/can_process.c new file mode 100644 index 0000000..1310141 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/can_process.c @@ -0,0 +1,341 @@ +/* + * geany_encoding=koi8-r + * can_process.c + * + * Copyright 2018 Edward V. Emelianov + * + * 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< 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; +} diff --git a/F0:F030,F042,F072/TSYS_controller/can_process.h b/F0:F030,F042,F072/TSYS_controller/can_process.h new file mode 100644 index 0000000..d78fc4b --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/can_process.h @@ -0,0 +1,61 @@ +/* + * geany_encoding=koi8-r + * can_process.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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); diff --git a/F0:F030,F042,F072/TSYS_controller/hardware.c b/F0:F030,F042,F072/TSYS_controller/hardware.c new file mode 100644 index 0000000..f1a0e8f --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/hardware.c @@ -0,0 +1,99 @@ +/* + * geany_encoding=koi8-r + * hardware.c - hardware-dependent macros & functions + * + * Copyright 2018 Edward V. Emelianov + * + * 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) +} diff --git a/F0:F030,F042,F072/TSYS_controller/hardware.h b/F0:F030,F042,F072/TSYS_controller/hardware.h new file mode 100644 index 0000000..c84ac00 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/hardware.h @@ -0,0 +1,97 @@ +/* + * geany_encoding=koi8-r + * hardware.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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__ diff --git a/F0:F030,F042,F072/TSYS_controller/i2c.c b/F0:F030,F042,F072/TSYS_controller/i2c.c new file mode 100644 index 0000000..16ced93 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/i2c.c @@ -0,0 +1,129 @@ +/* + * geany_encoding=koi8-r + * i2c.c + * + * Copyright 2017 Edward V. Emelianov + * + * 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; + } diff --git a/F0:F030,F042,F072/TSYS_controller/i2c.h b/F0:F030,F042,F072/TSYS_controller/i2c.h new file mode 100644 index 0000000..7c6ab65 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/i2c.h @@ -0,0 +1,41 @@ +/* + * geany_encoding=koi8-r + * i2c.h + * + * Copyright 2017 Edward V. Emelianov + * + * 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); diff --git a/F0:F030,F042,F072/TSYS_controller/ld/stm32f042k.ld b/F0:F030,F042,F072/TSYS_controller/ld/stm32f042k.ld new file mode 100644 index 0000000..e747253 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/ld/stm32f042k.ld @@ -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 + diff --git a/F0:F030,F042,F072/TSYS_controller/main.c b/F0:F030,F042,F072/TSYS_controller/main.c new file mode 100644 index 0000000..64a12ff --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/main.c @@ -0,0 +1,135 @@ +/* + * main.c + * + * Copyright 2017 Edward V. Emelianoff + * + * 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; +} diff --git a/F0:F030,F042,F072/TSYS_controller/proto.c b/F0:F030,F042,F072/TSYS_controller/proto.c new file mode 100644 index 0000000..4486cc4 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/proto.c @@ -0,0 +1,527 @@ +/* + * geany_encoding=koi8-r + * proto.c + * + * Copyright 2018 Edward V. Emelianov + * + * 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 + +#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'); +} diff --git a/F0:F030,F042,F072/TSYS_controller/proto.h b/F0:F030,F042,F072/TSYS_controller/proto.h new file mode 100644 index 0000000..48e544f --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/proto.h @@ -0,0 +1,51 @@ +/* + * geany_encoding=koi8-r + * proto.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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__ diff --git a/F0:F030,F042,F072/TSYS_controller/sensors_manage.c b/F0:F030,F042,F072/TSYS_controller/sensors_manage.c new file mode 100644 index 0000000..5f50c6b --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/sensors_manage.c @@ -0,0 +1,442 @@ +/* + * geany_encoding=koi8-r + * sensors_manage.c + * + * Copyright 2018 Edward V. Emelianov + * + * 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< 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< 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; + } +} diff --git a/F0:F030,F042,F072/TSYS_controller/sensors_manage.h b/F0:F030,F042,F072/TSYS_controller/sensors_manage.h new file mode 100644 index 0000000..aea0f15 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/sensors_manage.h @@ -0,0 +1,69 @@ +/* + * geany_encoding=koi8-r + * sensors_manage.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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__ diff --git a/F0:F030,F042,F072/TSYS_controller/tsys01.bin b/F0:F030,F042,F072/TSYS_controller/tsys01.bin new file mode 100755 index 0000000..d4fa982 Binary files /dev/null and b/F0:F030,F042,F072/TSYS_controller/tsys01.bin differ diff --git a/F0:F030,F042,F072/TSYS_controller/usart.c b/F0:F030,F042,F072/TSYS_controller/usart.c new file mode 100644 index 0000000..f149b12 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/usart.c @@ -0,0 +1,216 @@ +/* + * usart.c + * + * Copyright 2017 Edward V. Emelianoff + * + * 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 + +#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 diff --git a/F0:F030,F042,F072/TSYS_controller/usart.h b/F0:F030,F042,F072/TSYS_controller/usart.h new file mode 100644 index 0000000..f9764cf --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/usart.h @@ -0,0 +1,51 @@ +/* + * usart.h + * + * Copyright 2017 Edward V. Emelianoff + * + * 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__ diff --git a/F0:F030,F042,F072/TSYS_controller/usb.c b/F0:F030,F042,F072/TSYS_controller/usb.c new file mode 100644 index 0000000..03cd614 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/usb.c @@ -0,0 +1,183 @@ +/* + * geany_encoding=koi8-r + * usb.c - base functions for different USB types + * + * Copyright 2018 Edward V. Emelianov + * + * 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 + +#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; +} diff --git a/F0:F030,F042,F072/TSYS_controller/usb.h b/F0:F030,F042,F072/TSYS_controller/usb.h new file mode 100644 index 0000000..75d22b4 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/usb.h @@ -0,0 +1,37 @@ +/* + * geany_encoding=koi8-r + * usb.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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__ diff --git a/F0:F030,F042,F072/TSYS_controller/usb_defs.h b/F0:F030,F042,F072/TSYS_controller/usb_defs.h new file mode 100644 index 0000000..6ac673c --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/usb_defs.h @@ -0,0 +1,106 @@ +/* + * geany_encoding=koi8-r + * usb_defs.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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 + +/** + * 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__ diff --git a/F0:F030,F042,F072/TSYS_controller/usb_lib.c b/F0:F030,F042,F072/TSYS_controller/usb_lib.c new file mode 100644 index 0000000..4b1cbcb --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/usb_lib.c @@ -0,0 +1,538 @@ +/* + * geany_encoding=koi8-r + * usb_lib.c + * + * Copyright 2018 Edward V. Emelianov + * + * 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 // 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; +} diff --git a/F0:F030,F042,F072/TSYS_controller/usb_lib.h b/F0:F030,F042,F072/TSYS_controller/usb_lib.h new file mode 100644 index 0000000..0651e5f --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/usb_lib.h @@ -0,0 +1,202 @@ +/* + * geany_encoding=koi8-r + * usb_lib.h + * + * Copyright 2018 Edward V. Emelianov + * + * 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 +#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__ diff --git a/F0:F030,F042,F072/TSYS_controller/version.inc b/F0:F030,F042,F072/TSYS_controller/version.inc new file mode 100644 index 0000000..c467760 --- /dev/null +++ b/F0:F030,F042,F072/TSYS_controller/version.inc @@ -0,0 +1,3 @@ +#define BUILD_NUMBER "48" +#define BUILD_DATE "2023-08-29" +#define BUILDNO 45